Merge pull request #943 from Cooldude2606/ci/oidc

OIDC Publish, Node version bump, and 2.1 tests working
This commit is contained in:
Daniel authored and GitHub committed 2026-07-19 00:20:31 +02:00
commit f2b28968a0
12 files changed
+279 -32

No files matched your search

+4 -4
View File
@@ -13,9 +13,9 @@ jobs:
strategy:
matrix:
# Supported versions are active lts and maintenance see https://nodejs.org/en/about/releases/
node-version: [20.x, 22.x]
node-version: [22.x, 24.x]
# Supported versions are latest stable and one minor version before stable (even if from a previous major)
factorio-version: [1.1.110, 2.0.47]
factorio-version: [1.1.110, 2.0.77, 2.1.8]
steps:
- uses: actions/checkout@v4
@@ -54,7 +54,7 @@ jobs:
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 20.x
node-version: 24.x
- run: pnpm i --no-frozen-lockfile
- run: pnpm run lint
@@ -65,7 +65,7 @@ jobs:
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 20.x
node-version: 24.x
- run: pnpm i --no-frozen-lockfile
- run: pnpm run build-mod
# Upload build to artifacts
+6 -4
View File
@@ -6,6 +6,10 @@ on:
types:
- released
permissions:
id-token: write
contents: read
jobs:
npm:
runs-on: ubuntu-latest
@@ -15,12 +19,10 @@ jobs:
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 22.x
node-version: 24.x
registry-url: https://registry.npmjs.org
- run: pnpm install --no-frozen-lockfile
- run: pnpm -r --filter='./{plugins,packages}/*' publish --no-git-checks
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- uses: softprops/action-gh-release@v2
with:
files: pnpm-lock.yaml
@@ -33,7 +35,7 @@ jobs:
- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
with:
node-version: 22.x
node-version: 24.x
- run: pnpm install --no-frozen-lockfile
- run: pnpm -r --filter='./{plugins,packages}/*' run build-mod --output-dir ../../dist
- run: node ./publish.js factorio ./dist
+20
View File
@@ -9,3 +9,23 @@ debugCommands.add(new lib.Command({
control.keepOpen = true;
},
}));
debugCommands.add(new lib.Command({
definition: [
"parse-exchange-string <exchange-string>",
"Parse and print the contents of an exchange string",
(yargs) => {
yargs.positional("exchange-string", { describe: "String to parse", type: "string" });
yargs.option("pretty", { alias: "p", type: "boolean", description: "Pretty output" });
}],
handler: async function(args: { exchangeString: string, pretty: boolean }, control: Control) {
const result = lib.readMapExchangeString(args.exchangeString);
if (args.pretty) {
// eslint-disable-next-line no-console
console.log(JSON.stringify(result, null, 2));
} else {
// eslint-disable-next-line no-console
console.log(JSON.stringify(result));
}
},
}));
@@ -16,6 +16,10 @@
{
"version": "2.0.20",
"factorio_version": "2.0"
},
{
"version": "2.0.21",
"factorio_version": "2.1"
}
],
"title": "Clusterio Library (Alpha)",
+2 -4
View File
@@ -659,10 +659,8 @@ export default class Host extends lib.Link {
return await this.downloadMod(mod);
}
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");
async fetchMods(mods: Iterable<lib.ModRecord>, factorioVersion: lib.PartialVersion) {
const builtinModNames = lib.ModPack.getBuiltinModNames(factorioVersion);
const modInfos: Promise<lib.ModInfo>[] = [];
for (const mod of mods) {
if (builtinModNames.includes(mod.name)) {
+1 -3
View File
@@ -797,9 +797,7 @@ end`.replace(/\r?\n/g, " ");
}
this.activeModPack = modPack;
// TODO validate factorioVersion
const mods = await this._host.fetchMods(modPack.mods.values());
const mods = await this._host.fetchMods(modPack.mods.values(), modPack.factorioVersion);
await fs.mkdir(this.path("mods"), { recursive: true });
+46 -12
View File
@@ -5,6 +5,8 @@ class MapReaderState {
last_position = { x: 0, y: 0 };
/** True when a version greater than 2.0.0 is detected */
v2 = false;
/** True when a version greater than 2.1.0 is detected */
v2_1 = false;
constructor(
public buf: Buffer
) { }
@@ -262,7 +264,23 @@ function readEnemyEvolution(state: MapReaderState) {
}
function readEnemyExpansion(state: MapReaderState) {
return {
return state.v2_1 ? {
enabled: readOptional(state, readBool),
max_expansion_distance: readOptional(state, readUInt32),
min_expansion_distance: readOptional(state, readUInt32), // v2.1
friendly_base_influence_radius: readOptional(state, readUInt32),
enemy_building_influence_radius: readOptional(state, readUInt32),
building_coefficient: readOptional(state, readDouble),
other_base_coefficient: readOptional(state, readDouble),
neighbouring_chunk_coefficient: readOptional(state, readDouble),
neighbouring_base_chunk_coefficient: readOptional(state, readDouble),
max_colliding_tiles_coefficient: readOptional(state, readDouble),
settler_group_min_size: readOptional(state, readUInt32),
settler_group_max_size: readOptional(state, readUInt32),
evolution_group_size_factor: readOptional(state, readDouble), // v2.1
min_expansion_cooldown: readOptional(state, readUInt32),
max_expansion_cooldown: readOptional(state, readUInt32),
} : {
enabled: readOptional(state, readBool),
max_expansion_distance: readOptional(state, readUInt32),
friendly_base_influence_radius: readOptional(state, readUInt32),
@@ -355,17 +373,32 @@ function readAsteroids(state: MapReaderState) {
}
function readMapSettings(state: MapReaderState) {
return state.v2 ? {
pollution: readPollution(state),
steering: readSteering(state),
enemy_evolution: readEnemyEvolution(state),
enemy_expansion: readEnemyExpansion(state),
unit_group: readUnitGroup(state),
path_finder: readPathFinder(state),
max_failed_behavior_count: readUInt32(state),
difficulty_settings: readDifficultySettings(state),
asteroids: readAsteroids(state), // v2
} : {
if (state.v2_1) {
return {
pollution: readPollution(state),
// steering removed v2.1
enemy_evolution: readEnemyEvolution(state),
enemy_expansion: readEnemyExpansion(state),
unit_group: readUnitGroup(state),
path_finder: readPathFinder(state),
max_failed_behavior_count: readUInt32(state),
difficulty_settings: readDifficultySettings(state),
asteroids: readAsteroids(state), // v2
};
} else if (state.v2) {
return {
pollution: readPollution(state),
steering: readSteering(state),
enemy_evolution: readEnemyEvolution(state),
enemy_expansion: readEnemyExpansion(state),
unit_group: readUnitGroup(state),
path_finder: readPathFinder(state),
max_failed_behavior_count: readUInt32(state),
difficulty_settings: readDifficultySettings(state),
asteroids: readAsteroids(state), // v2
};
}
return {
pollution: readPollution(state),
steering: readSteering(state),
enemy_evolution: readEnemyEvolution(state),
@@ -427,6 +460,7 @@ export function readMapExchangeString(exchangeString: string) {
try {
const version = readVersion(state);
state.v2 = version[0] >= 2;
state.v2_1 = version[0] >= 2 && version[1] >= 1;
data = {
version: version,
unknown: readUInt8(state),
+9 -2
View File
@@ -1150,8 +1150,15 @@ describe("Integration of Clusterio", function() {
await execCtl("instance start 44");
const instance = (await getInstances()).get(44);
const isV2 = lib.integerPartialVersion(instance.factorioVersion) > lib.integerPartialVersion("2.0");
const exchangeString = (isV2 ? testStrings.modified_v2 : testStrings.modified).replace(/[\n\r]+/g, "");
const args = `base/freeplay --seed 1234 --map-exchange-string "${exchangeString}"`;
const isV2_1 = lib.integerPartialVersion(instance.factorioVersion) > lib.integerPartialVersion("2.1");
let exchangeString = testStrings.modified;
if (isV2_1) {
exchangeString = testStrings.modified_v2_1;
} else if (isV2) {
exchangeString = testStrings.modified_v2;
}
// eslint-disable-next-line max-len
const args = `base/freeplay --seed 1234 --map-exchange-string "${exchangeString.replace(/[\n\r]+/g, "")}"`;
await execCtl("instance stop 44");
await execCtl(`instance load-scenario test ${args}`);
+10 -3
View File
@@ -363,7 +363,11 @@ before(async function() {
controllerProcess = await spawnNode("controller:", "../../packages/controller run", /Started controller/);
haveFactorioInstall = (await _listFactorioVersions(factorioDir)).versions.size > 0;
const factorioVersions = (await _listFactorioVersions(factorioDir)).versions;
const latestFactorioVersion = [...factorioVersions.values()]
.sort((a, b) => lib.integerFullVersion(b) - lib.integerFullVersion(a))[0];
haveFactorioInstall = factorioVersions.size > 0;
const relativeFactorioDir = path.isAbsolute(factorioDir) ? factorioDir : path.join("..", "..", factorioDir);
await execCtlProcess("host create-config --id 4 --name host --generate-token");
await execHost(`config set host.factorio_directory ${relativeFactorioDir}`);
@@ -382,8 +386,11 @@ before(async function() {
const testPack = lib.ModPack.fromJSON({});
testPack.id = 12;
testPack.name = "subspace_storage-pack";
testPack.factorioVersion = "2.0.0";
testPack.mods.set("clusterio_lib", { name: "clusterio_lib", enabled: true, version: "2.0.20" });
testPack.factorioVersion = latestFactorioVersion;
const [major, minor] = lib.normaliseMajorMinorVersion(latestFactorioVersion).split(".");
testPack.mods.set("clusterio_lib", { name: "clusterio_lib", enabled: true, version: `2.0.${major}${minor}` });
await control.sendTo("controller", new lib.ModPackCreateRequest(testPack));
await control.sendTo(
"controller",
+10
View File
@@ -59,6 +59,16 @@ describe("lib/factorio/exchange_string", function() {
});
});
it("should parse post v2.1 strings", function() {
assert.deepEqual(lib.readMapExchangeString(testStrings.modified_v2_1), {
version: [2, 1, 9, 3],
unknown: 0,
map_gen_settings: testSettings.modified_v2_1_map_gen_settings,
map_settings: testSettings.modified_v2_1_map_settings,
checksum: 3180296799,
});
});
it("should handle malformed strings", function() {
assert.throws(
() => lib.readMapExchangeString("<<blah>>"),
+150
View File
@@ -467,6 +467,154 @@ const modified_v2_map_settings = {
},
};
const modified_v2_1_map_gen_settings = {
"autoplace_controls": {
"coal": { "frequency": 1, "size": 1, "richness": 1 },
"copper-ore": { "frequency": 0.5, "size": 1, "richness": 1 },
"crude-oil": { "frequency": 0.5, "size": 1, "richness": 4 },
"enemy-base": { "frequency": 2, "size": 1.5, "richness": 1 },
"iron-ore": { "frequency": 1.5, "size": 1, "richness": 1 },
"nauvis_cliff": { "frequency": 1, "size": 1, "richness": 1 },
"rocks": { "frequency": 1, "size": 1, "richness": 1 },
"starting_area_moisture": { "frequency": 1, "size": 1, "richness": 1 },
"stone": { "frequency": 2, "size": 1, "richness": 1 },
"trees": { "frequency": 1.3333333730697632, "size": 0.3333333432674408, "richness": 1 },
"uranium-ore": { "frequency": 1, "size": 1.3333333730697632, "richness": 1 },
"water": { "frequency": 0.5, "size": 0.3333333432674408, "richness": 1 },
},
"autoplace_settings": {},
"default_enable_all_autoplace_controls": true,
"seed": 3378377395,
"width": 2000000,
"height": 2000000,
"area_to_generate_at_start": {
"left_top": { "x": -224, "y": -224 },
"right_bottom": { "x": 224, "y": 224 },
"orientation": { "x": 0, "y": -32767 },
},
"starting_area": 0.3333333432674408,
"peaceful_mode": false,
"no_enemies_mode": false,
"starting_points": [
{ "x": 0, "y": 0 },
],
"property_expression_names": {
"control:aux:bias": "-0.200000",
"control:aux:frequency": "2.000000",
"control:moisture:bias": "-0.200000",
"control:moisture:frequency": "0.333333",
"elevation": "elevation_lakes",
},
"cliff_settings": {
"name": "cliff",
"control": "",
"cliff_elevation_0": 10,
"cliff_elevation_interval": 40,
"richness": 1,
"cliff_smoothing": 1,
},
"territory_settings": null,
};
const modified_v2_1_map_settings = {
"pollution": {
"enabled": true,
"diffusion_ratio": 0.02,
"min_to_diffuse": 15,
"ageing": 1.5,
"expected_max_per_chunk": 150,
"min_to_show_per_chunk": 50,
"min_pollution_to_damage_trees": 3785,
"pollution_with_max_forest_damage": 150,
"pollution_per_tree_damage": 50,
"pollution_restored_per_tree_damage": 3714,
"max_pollution_to_restore_trees": 20,
"enemy_attack_pollution_consumption_modifier": 1,
},
"enemy_evolution": {
"enabled": true,
"time_factor": 0.00002,
"destroy_factor": 0.002,
"pollution_factor": 0.000012,
},
"enemy_expansion": {
"enabled": true,
"max_expansion_distance": 13,
"min_expansion_distance": 3,
"friendly_base_influence_radius": 6,
"enemy_building_influence_radius": 3,
"building_coefficient": 0.5,
"other_base_coefficient": 3,
"neighbouring_chunk_coefficient": 0.5,
"neighbouring_base_chunk_coefficient": 0.5,
"max_colliding_tiles_coefficient": 0.8,
"settler_group_min_size": 5,
"settler_group_max_size": 20,
"evolution_group_size_factor": 4,
"min_expansion_cooldown": 169200,
"max_expansion_cooldown": 414000,
},
"unit_group": {
"min_group_gathering_time": 3600,
"max_group_gathering_time": 36000,
"max_wait_time_for_late_members": 7200,
"max_group_radius": 30,
"min_group_radius": 5,
"max_member_speedup_when_behind": 1.4,
"max_member_slowdown_when_ahead": 0.6,
"max_group_slowdown_factor": 0.3,
"max_group_member_fallback_factor": 3,
"member_disown_distance": 10,
"tick_tolerance_when_member_arrives": 60,
"max_gathering_unit_groups": 30,
"max_unit_group_size": 200,
},
"path_finder": {
"fwd2bwd_ratio": 5,
"goal_pressure_ratio": 2,
"use_path_cache": true,
"max_steps_worked_per_tick": 1000,
"max_work_done_per_tick": 8000,
"short_cache_size": 5,
"long_cache_size": 25,
"short_cache_min_cacheable_distance": 10,
"short_cache_min_algo_steps_to_cache": 50,
"long_cache_min_cacheable_distance": 30,
"cache_max_connect_to_cache_steps_multiplier": 100,
"cache_accept_path_start_distance_ratio": 0.2,
"cache_accept_path_end_distance_ratio": 0.15,
"negative_cache_accept_path_start_distance_ratio": 0.3,
"negative_cache_accept_path_end_distance_ratio": 0.3,
"cache_path_start_distance_rating_multiplier": 10,
"cache_path_end_distance_rating_multiplier": 20,
"stale_enemy_with_same_destination_collision_penalty": 30,
"ignore_moving_enemy_collision_distance": 5,
"enemy_with_different_destination_collision_penalty": 30,
"general_entity_collision_penalty": 10,
"general_entity_subsequent_collision_penalty": 3,
"extended_collision_penalty": 3,
"max_clients_to_accept_any_new_request": 10,
"max_clients_to_accept_short_new_request": 100,
"direct_distance_to_consider_short_request": 100,
"short_request_max_steps": 1000,
"short_request_ratio": 0.5,
"min_steps_to_check_path_find_termination": 2000,
"start_to_goal_cost_multiplier_to_terminate_path_find": 2000,
"overload_levels": [0, 100, 500],
"overload_multipliers": [2, 3, 4],
"negative_path_cache_delay_interval": 20,
},
"max_failed_behavior_count": 3,
"difficulty_settings": {
"technology_price_multiplier": 12,
"spoil_time_modifier": 1,
},
"asteroids": {
"spawning_rate": 1,
"max_ray_portals_expanded_per_tick": 100,
},
};
const modified_space_age_map_gen_settings = {
autoplace_controls: {
"aquilo_crude_oil": { frequency: 1, size: 1, richness: 1 },
@@ -553,5 +701,7 @@ module.exports = {
modified_map_settings,
modified_v2_map_gen_settings,
modified_v2_map_settings,
modified_v2_1_map_gen_settings,
modified_v2_1_map_settings,
modified_space_age_map_gen_settings,
};
+17
View File
@@ -49,6 +49,22 @@ Ln6IET6h2alhiMgvQ3SGxnDDDd32daT/yCD5fQ8R3WMEyeN/PENaNmy
E5utYOI24z/cT+oQf48FdgozaFZg6qX+1KqW+GYyy4qNZgFNY/0IAHj
RI5Q/27QvL<<<`;
const modified_v2_1 = `
>>>eNp1U89rE0EUftM0NYlVc4iCIjWHHkRICNFTkezoRTx4ELxKnGwm
unSz087uVlsP5uDBg1CQXvSiVyt6EY9CwIu9if4DlXqwt1aK9CDEeZm
dyZrgg/f49nu/37BTQCAPGYDZaVcwH6DnGC24YmmJy4qQHAAsnXdl3O
YV4fkplhZ4wLurlRYLMZgC9IeunCdFoCv0bYXZgMUrXth0fa/TSXfMS
uEuhmnmVBgxGXnB3SaTnDW7wgujGKulksJIBEnThIkk5+GbzU1HaQOZ
o7FkgRd39SQ95HXkfRZxiXuYSCDvf93e6j2eA9TBIygPBqgKbas4VCA
9HU0UmUi26IogksJfYPGDhZbHwnylVq3XUE6mXR3Jl2MeuKu5erX2r9
ssN5Z+ZsI/qlGrXhxKnvt8hUWeCE5Y1PTZIg+zyZGhfEXpVXs4QsjD0
rtr39c2HKJXqNIEHCZMv2WY6wl49pb+zzVtXHDJgD1HNdm/t/Pkw+FB
g/x5vf/1RuuOQ279KC6HF3YbynkMj5lBM2ORzt02U+XoOKPAi+couw7
JYkZplFakZG9jCkjt5gyQ4nHFv3qqTPmsjWiYeiVKOkP57RB9xB0Dvk
00n6fkMnaZQ/MZTXbUFKhaRYN1Sug54z09ClH5dUjP0MYvvcaWafsp1
X9skPmJ46b3GGNscC4FCtiwbc3PTPrUX46YL/qSDp8BMOqAJI8C+u/S
peyl8e7GC3Ce2le3AIs019Y//gU3lOV1<<<`;
const modified_space_age = `
>>>eNp1VEFoE0EUnUkb27TWBomFSqkBexAhMVZPRbJbL+LBg+BV1slm
kgzd7LYzs9HYgz304EEQpJf2Yq9W9CIevBW8WFAQPXmr1IOCh1aL9CD
@@ -74,5 +90,6 @@ module.exports = {
default: default_,
modified,
modified_v2,
modified_v2_1,
modified_space_age,
};