Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,38 @@ describe("PluginPersistor", () => {
expect(persistor.entry("old.esp").enabled).toBe(true);
});

it("does not report an empty plugins.txt when only the game's own plugins are known", async () => {
paths.native = ["skyrim.esm"];
paths.pluginDir = tmpDir("plugin-vanilla-");
nodeFs.writeFileSync(path.join(paths.pluginDir, "plugins.txt"), "", { encoding: "latin1" });
const record = vi.fn();
const vanilla = new PluginPersistor(vi.fn(), () => false, record);
persistors.push(vanilla);
vanilla.setResetCallback(vi.fn(() => Promise.resolve()));
vanilla.setKnownPlugins({ "skyrim.esm": "Skyrim.esm" });

await vanilla.loadFiles("skyrimse");
await settle(SETTLE_MS);

expect(record).not.toHaveBeenCalled();
});

it("reports an empty plugins.txt when mod plugins are known", async () => {
paths.native = ["skyrim.esm"];
paths.pluginDir = tmpDir("plugin-emptied-");
nodeFs.writeFileSync(path.join(paths.pluginDir, "plugins.txt"), "", { encoding: "latin1" });
const record = vi.fn();
const modded = new PluginPersistor(vi.fn(), () => false, record);
persistors.push(modded);
modded.setResetCallback(vi.fn(() => Promise.resolve()));
modded.setKnownPlugins({ "skyrim.esm": "Skyrim.esm", "old.esp": "Old.esp" });

await modded.loadFiles("skyrimse");
await settle(SETTLE_MS);

expect(record.mock.calls[0]?.[0]).toBe("the plugin list on disk is empty");
});

it("does not offer the keep/revert choice for an empty plugins.txt", async () => {
const onExternalChange = vi.fn(() => Promise.resolve("keep" as const));
persistor.setExternalChangeCallback(onExternalChange);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -695,12 +695,14 @@ class PluginPersistor implements IPersistor {
// not even a header? I don't trust this. Read once more in case we caught a write
// mid-flight, then leave the current state alone: a truncated file is not a
if (retry) {
// still empty on the second read, so nobody was mid-write and the load order the
// file held is gone
this.recordFailure(
"the plugin list on disk is empty",
new Error("plugins.txt has no content, not even a header"),
);
// still empty on the second read, so nobody was mid-write; a game with only its own
// plugins leaves the file empty, so it only held a load order if mods add plugins
if (Object.keys(this.mKnownPlugins).length > this.mInstalledNative.length) {
this.recordFailure(
"the plugin list on disk is empty",
new Error("plugins.txt has no content, not even a header"),
);
}
// The persistor must still count as loaded, or serialize() drops every write
this.mLoaded = true;
return Promise.resolve();
Expand Down
Loading