Merge pull request #684 from Cooldude2606/feature/2.0-scenarios

Add support for 2.0 builtin mods and scenarios
This commit is contained in:
Cooldude2606 authored and GitHub committed 2024-11-22 13:42:40 +00:00
commit 4c64ef9d15
12 files changed
+143 -37

No files matched your search

+1
View File
@@ -31,6 +31,7 @@ node_modules
Factorio*
/factorio
!/test/file/factorio
/instances
/mods
/database
+7
View File
@@ -25,6 +25,10 @@ Many thanks to the following for contributing to this release:
## Version 2.0.0-alpha.19
### Major Features
- Added support for 2.0 builtin mods during mod pack creation. [#684](https://github.com/clusterio/clusterio/pull/684).
### Features
- Added Datastore and Datastore Provider classes to the library to support savable Maps [#629](https://github.com/clusterio/clusterio/pull/629).
@@ -37,6 +41,7 @@ Many thanks to the following for contributing to this release:
- Changed default role selection to be a dropdown menu rather than id [#664](https://github.com/clusterio/clusterio/pull/664).
- Added credential option to config entries to allow writing sensitive data that can't be read back remotely.
- Allow hidden and readonly graphical representations of config values. [#665](https://github.com/clusterio/clusterio/pull/665).
- Added locale exporting for all builtin mods, not just for the base mod. [#684](https://github.com/clusterio/clusterio/pull/684).
- Added option for silent testing which only shows the results, can be ran using `pnpm silent-test` [#655](https://github.com/clusterio/clusterio/pull/655).
### Changes
@@ -68,6 +73,8 @@ Many thanks to the following for contributing to this release:
- Updated display name and description of `controller.external_address` to avoid confusion. [#674](https://github.com/clusterio/clusterio/pull/674)
- Fixed invalid transient state during server start. [#676](https://github.com/clusterio/clusterio/issues/676)
- Fixed host user sync to instances after reconnecting to the controller. [#678](https://github.com/clusterio/clusterio/pull/678)
- Fixed WebUI adding and displaying the wrong base mod version. [#684](https://github.com/clusterio/clusterio/pull/684).
- Fixed WebUI inconsistent pattern matching for factorio version. [#684](https://github.com/clusterio/clusterio/pull/684).
### Breaking Changes
+4 -3
View File
@@ -618,11 +618,12 @@ export default class Host extends lib.Link {
}
async fetchMods(mods: Iterable<lib.ModRecord>) {
// This is better than the previous hard coded names
// But it really shouldn't be a hard coded version either
const builtinModNames = lib.ModPack.getBuiltinModNames("2.0");
const modInfos: Promise<lib.ModInfo>[] = [];
for (const mod of mods) {
// XXX These mods ship with Factorio, but the list of mods may
// change and should not be hardcoded here.
if (["core", "base"].includes(mod.name)) {
if (builtinModNames.includes(mod.name)) {
continue;
}
modInfos.push(this.fetchMod(mod));
+12 -4
View File
@@ -103,7 +103,8 @@ async function loadFile(server: FactorioServer, modVersions: Map<string, string>
let [, mod, filePath] = match;
if (["core", "base"].includes(mod)) {
const builtinModNames = ["core", ...lib.ModPack.getBuiltinModNames(server.version!)];
if (builtinModNames.includes(mod)) {
try {
return await fs.readFile(server.dataPath(mod, filePath));
} catch (err: any) {
@@ -396,11 +397,18 @@ async function exportLocale(
}
}
let baseLocaleFilePath = server.dataPath("base", "locale", languageCode, "base.cfg");
mergeLocale(lib.parse(await fs.readFile(baseLocaleFilePath, "utf8")));
const builtinModNames = [
// Filter if on builtin mods so "core" does not need to be specified in modOrder
"core", ...lib.ModPack.getBuiltinModNames(server.version!).filter(mod => modOrder.includes(mod)),
];
for (const builtinModName of builtinModNames) {
const localeFilePath = server.dataPath(builtinModName, "locale", languageCode, `${builtinModName}.cfg`);
mergeLocale(lib.parse(await fs.readFile(localeFilePath, "utf8")));
}
const builtinModNamesExport = ["export", ...builtinModNames];
for (let mod of modOrder) {
if (["core", "base", "export"].includes(mod)) {
if (builtinModNamesExport.includes(mod)) {
continue;
}
+9 -6
View File
@@ -202,12 +202,6 @@ export class PatchInfo {
}
}
const knownScenarios: Record<string, lib.ModuleInfo> = {
// First seen in 0.17.63
"4e866186ebe297f1038fd325b09df1a1f5e2fdd1": new lib.ModuleInfo("freeplay", "0.17.63", ["freeplay", "silo-script"]),
};
/**
* Generates control.lua code for loading the Clusterio modules
*
@@ -349,6 +343,14 @@ function reorderDependencies(modules: SaveModule[]) {
}
}
const knownScenarios: Record<string, lib.ModuleInfo> = {
// First seen in 0.17.63
"4e866186ebe297f1038fd325b09df1a1f5e2fdd1": new lib.ModuleInfo("freeplay", "0.17.63", [], ["scenario"]),
// First seen in 2.0 TODO Uncomment once clusterio lib supports 2.0
// The rest of the code has already been updated to support the new control.lua file for 2.0
// "bcbdde18ce4ec16ebfd93bd694cd9b12ef969c9a": new lib.ModuleInfo("freeplay", "2.0.0", [], ["scenario"]),
};
/**
* Patch a save with the given modules
*
@@ -387,6 +389,7 @@ export async function patch(savePath: string, modules: SaveModule[]) {
if (controlHash in knownScenarios) {
patchInfo = new PatchInfo(0, knownScenarios[controlHash], []);
root.file("scenario.lua", controlFile.nodeStream("nodebuffer"));
} else {
throw new Error(`Unable to patch save, unknown scenario (${controlHash})`);
}
+33 -2
View File
@@ -173,8 +173,11 @@ export default class ModPack {
if (json.updated_at_ms) { modPack.updatedAtMs = json.updated_at_ms; }
if (json.is_deleted) { modPack.isDeleted = json.is_deleted; }
if (!modPack.mods.has("base")) {
modPack.mods.set("base", { name: "base", enabled: true, version: modPack.factorioVersion });
const builtinMods = ModPack.getBuiltinMods(modPack.factorioVersion);
for (const builtinMod of builtinMods) {
if (!modPack.mods.has(builtinMod.name)) {
modPack.mods.set(builtinMod.name, builtinMod);
}
}
return modPack;
@@ -379,4 +382,32 @@ export default class ModPack {
this.settings[settingType].set(prototype.name, { value });
}
}
/**
* Returns an array of ModInfo containing all mods included in factorio
*
* @param factorioVersion - Factorio version to get the default mods for
* @return Built in mods for the given version
*/
static getBuiltinMods(factorioVersion: string) {
let defaultMods: ModRecord[] = [
// "core" not included because core cannot be disabled
{ name: "base", enabled: true, version: factorioVersion },
];
const integerVersion = integerFactorioVersion(factorioVersion);
if (integerVersion >= integerFactorioVersion("1.2")) {
defaultMods = defaultMods.concat([
{ name: "elevated-rails", enabled: false, version: factorioVersion },
{ name: "quality", enabled: false, version: factorioVersion },
{ name: "space-age", enabled: false, version: factorioVersion },
]);
}
return defaultMods;
}
static getBuiltinModNames(factorioVersion: string) {
return this.getBuiltinMods(factorioVersion).map(builtinMod => builtinMod.name);
}
}
@@ -679,7 +679,10 @@ function applyModPackChanges(modPack: lib.ModPack, changes: ModChange[]) {
let modifiedModPack = modPack.shallowClone();
for (let change of changes) {
// Only modify fields that change to reduce re-renders
if (["mods.set", "mods.delete"].includes(change.type) && modifiedModPack.mods === modPack.mods) {
if (
["mods.set", "mods.delete", "factorioVersion"].includes(change.type)
&& modifiedModPack.mods === modPack.mods
) {
modifiedModPack.mods = new Map(modifiedModPack.mods);
}
if (
@@ -697,22 +700,30 @@ function applyModPackChanges(modPack: lib.ModPack, changes: ModChange[]) {
modifiedModPack.name = change.value;
} else if (change.type === "description") {
modifiedModPack.description = change.value;
} else if (change.type === "factorioVersion") {
modifiedModPack.factorioVersion = change.value;
} else if (change.type === "mods.set") {
if (change.name) {
modifiedModPack.mods.set(change.name, change.value as lib.ModRecord);
}
modifiedModPack.mods.set(change.name, change.value as lib.ModRecord);
} else if (change.type === "mods.delete") {
modifiedModPack.mods.delete(change.name!);
modifiedModPack.mods.delete(change.name);
} else if (change.type === "settings.set") {
if (change.name) {
modifiedModPack.settings[change.scope].set(change.name, change.value);
}
modifiedModPack.settings[change.scope].set(change.name, change.value);
} else if (change.type === "settings.delete") {
if (change.name) {
modifiedModPack.settings[change.scope].delete(change.name);
modifiedModPack.settings[change.scope].delete(change.name);
} else if (change.type === "factorioVersion") {
const newBuiltinMods = lib.ModPack.getBuiltinMods(change.value);
for (const newBuiltinMod of newBuiltinMods) {
const builtinMod = modifiedModPack.mods.get(newBuiltinMod.name);
if (builtinMod) { newBuiltinMod.enabled = builtinMod.enabled; }
modifiedModPack.mods.set(newBuiltinMod.name, newBuiltinMod);
}
const oldBuiltinMods = lib.ModPack.getBuiltinMods(modifiedModPack.factorioVersion);
for (const oldBuiltinMod of oldBuiltinMods) {
if (!newBuiltinMods.some(builtinMod => builtinMod.name === oldBuiltinMod.name)) {
modifiedModPack.mods.delete(oldBuiltinMod.name);
}
}
modifiedModPack.factorioVersion = change.value;
} else {
throw new Error(`Unknown change type ${change.type}`);
}
@@ -879,8 +890,9 @@ export default function ModPackViewPage() {
name="factorioVersion"
style={{ marginBottom: 0 }}
rules={[{
pattern: /^\d+\.\d+\.\d+?$/,
message: "Must be an a.b.c version number.",
required: true,
pattern: /^\d+\.\d+(\.\d+)?$/,
message: "Must be an a.b or a.b.c version number.",
}]}
>
<Input
@@ -894,7 +906,9 @@ export default function ModPackViewPage() {
changes={changes}
onChange={pushChange}
onRevert={revertChange}
builtInMods={["base", "core"]}
builtInMods={
lib.ModPack.getBuiltinMods(modifiedModPack.factorioVersion).map(builtinMod => builtinMod.name)
}
/>
<SettingsTable
modPack={modifiedModPack}
+5 -4
View File
@@ -96,10 +96,11 @@ function CreateModPackButton() {
} catch {
return; // Validation failed
}
const modPack = lib.ModPack.fromJSON({} as any);
modPack.name = values.name;
modPack.factorioVersion = values.factorioVersion;
if (values.description) { modPack.description = values.description; }
const modPack = lib.ModPack.fromJSON({
name: values.name,
description: values.description,
factorio_version: values.factorioVersion,
} as any);
await control.send(new lib.ModPackCreateRequest(modPack));
navigate(`/mods/mod-packs/${modPack.id}/view`);
})().catch(notifyErrorHandler("Error creating mod pack"));
@@ -1,4 +1,4 @@
; Test data for exportLocale
[test]
key-a=1
key-b=2
base-a=1
base-b=2
@@ -0,0 +1,4 @@
; Test data for exportLocale
[test]
core-a=1
core-b=2
+10 -1
View File
@@ -17,7 +17,16 @@ describe("host/src/export", function() {
it("returns a flattened mapping with locale definitions", async function() {
let locale = await _exportLocale(testServer, new Map(), ["base"], "en");
assert.deepEqual(locale, new Map([["test.key-a", "1"], ["test.key-b", "2"]]));
assert.deepEqual(locale, new Map([
["test.core-a", "1"], ["test.core-b", "2"],
["test.base-a", "1"], ["test.base-b", "2"],
]));
});
it("returns a filtered locale definitions based on mod order", async function() {
let locale = await _exportLocale(testServer, new Map(), [], "en");
assert.deepEqual(locale, new Map([
["test.core-a", "1"], ["test.core-b", "2"],
]));
});
});
});
+27
View File
@@ -378,5 +378,32 @@ describe("lib/data/ModPack", function() {
/* eslint-enable indent */
});
});
describe("getBuiltinMods()", function() {
it("should work with versions before 2.0", function() {
const builtinMods = ModPack.getBuiltinMods("1.1");
assert.deepEqual(builtinMods, [
{ name: "base", enabled: true, version: "1.1" },
]);
});
it("should work with versions after 2.0", function() {
const builtinMods = ModPack.getBuiltinMods("2.0");
assert.deepEqual(builtinMods, [
{ name: "base", enabled: true, version: "2.0" },
{ name: "elevated-rails", enabled: false, version: "2.0" },
{ name: "quality", enabled: false, version: "2.0" },
{ name: "space-age", enabled: false, version: "2.0" },
]);
});
});
describe("getBuiltinModNames()", function() {
it("should work with versions before 2.0", function() {
const builtinModNames = ModPack.getBuiltinModNames("1.1");
assert.deepEqual(builtinModNames, ["base"]);
});
it("should work with versions after 2.0", function() {
const builtinModNames = ModPack.getBuiltinModNames("2.0");
assert.deepEqual(builtinModNames, ["base", "elevated-rails", "quality", "space-age"]);
});
});
});
});