mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-13 07:04:53 +09:00
Merge pull request #684 from Cooldude2606/feature/2.0-scenarios
Add support for 2.0 builtin mods and scenarios
This commit is contained in:
12 files changed
+143
-37
No files matched your search
@@ -31,6 +31,7 @@ node_modules
|
||||
|
||||
Factorio*
|
||||
/factorio
|
||||
!/test/file/factorio
|
||||
/instances
|
||||
/mods
|
||||
/database
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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})`);
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
@@ -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"],
|
||||
]));
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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"]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user