Add validator to load_plugin

This commit is contained in:
Cooldude2606 committed 2026-03-24 17:36:19 +00:00
1 parent 71dfb8ca24
commit 790c2834f2
3 files changed
+165 -46

No files matched your search

+59 -22
View File
@@ -716,36 +716,73 @@ function validateFieldNames(
}
/**
* Add config fields defined by the provided plugin infos
* Add the config fields defined by the by a specific plugin infos
*/
export function addPluginFieldDefinitions(
pluginInfo: PluginNodeEnvInfo | PluginWebpackEnvInfo,
kind: "controllerConfigFields" | "hostConfigFields" | "instanceConfigFields",
Config: typeof classes.Config<any>
) {
// Define plugin.load_plugin
const fieldDef: classes.FieldDefinition = {
title: "Load Plugin",
type: "boolean",
initialValue: true,
restartRequired: true,
};
// The validator is dynamic based on the feature requirements of the plugin
if (kind === "instanceConfigFields" && pluginInfo.features) {
fieldDef.dependsOn = [];
const savePatching = pluginInfo.features.includes("SavePatching");
if (savePatching) {
fieldDef.dependsOn.push("factorio.enable_save_patching");
}
const scriptCommands = pluginInfo.features.includes("ScriptCommands");
if (scriptCommands) {
fieldDef.dependsOn.push("factorio.enable_script_commands");
}
if (scriptCommands || savePatching) {
fieldDef.validator = (enabled, config) => {
if (!enabled) {
return;
}
if (savePatching && !config.get("factorio.enable_save_patching")) {
throw new Error(`Plugin ${pluginInfo.name} requires save patching`);
}
if (scriptCommands && !config.get("factorio.enable_script_commands")) {
throw new Error(`Plugin ${pluginInfo.name} requires script commands`);
}
};
}
}
Config.fieldDefinitions[`${pluginInfo.name}.load_plugin`] = fieldDef;
// Merge in other fields from the plugin
const fields = pluginInfo[kind];
if (fields) {
validateFieldNames(pluginInfo.name, fields);
Object.assign(Config.fieldDefinitions, fields);
}
}
/**
* Add all config fields defined by the provided plugin infos
*
* @param {Array<Object>} pluginInfos - Array of plugin info objects.
*/
export function addPluginConfigFields(pluginInfos: PluginNodeEnvInfo[] | PluginWebpackEnvInfo[]) {
function pluginConfig(
pluginInfo: PluginNodeEnvInfo | PluginWebpackEnvInfo,
kind: "controllerConfigFields" | "hostConfigFields" | "instanceConfigFields",
Config: typeof ControllerConfig | typeof HostConfig | typeof InstanceConfig,
) {
(Config.fieldDefinitions as any)[`${pluginInfo.name}.load_plugin`] = {
title: "Load Plugin",
restartRequired: true,
type: "boolean",
initialValue: true,
};
const fields = pluginInfo[kind];
if (fields) {
validateFieldNames(pluginInfo.name, fields);
Object.assign(Config.fieldDefinitions, fields);
}
}
for (let pluginInfo of pluginInfos) {
pluginConfig(pluginInfo, "controllerConfigFields", ControllerConfig);
addPluginFieldDefinitions(pluginInfo, "controllerConfigFields", ControllerConfig);
if (pluginInfo.hostEntrypoint || pluginInfo.instanceEntrypoint) {
pluginConfig(pluginInfo, "hostConfigFields", HostConfig);
addPluginFieldDefinitions(pluginInfo, "hostConfigFields", HostConfig);
}
if (pluginInfo.instanceEntrypoint) {
pluginConfig(pluginInfo, "instanceConfigFields", InstanceConfig);
addPluginFieldDefinitions(pluginInfo, "instanceConfigFields", InstanceConfig);
}
}
}
+10
View File
@@ -7,6 +7,14 @@ import type { Logger } from "./logging";
import type { FieldDefinition } from "./config";
import type { PlayerStats } from "./data";
export const PluginFeatureFlags = [
/** The plugin requires module code to be patched into the save */
"SavePatching",
/** The plugin requires access to script commands over rcon */
"ScriptCommands",
] as const;
/** Used to define the plugin export in plugins */
export type PluginDeclaration = {
name: string;
@@ -26,6 +34,8 @@ export type PluginDeclaration = {
webEntrypoint?: string;
controlConfigFields?: Record<string, FieldDefinition>;
features?: (typeof PluginFeatureFlags)[number][];
messages?: any[];
routes?: string[];
}
+96 -24
View File
@@ -207,35 +207,107 @@ describe("lib/config/definitions/validators", function() {
);
});
it("should validate factorio.player_online_autosave_slots against autosave slots", function() {
const config = new InstanceConfig("controller", {
"factorio.settings": {
autosave_slots: 5,
},
describe("factorio.player_online_autosave_slots", function() {
it("should validate against autosave slots", function() {
const config = new InstanceConfig("controller", {
"factorio.settings": {
autosave_slots: 5,
},
});
// valid: equal
assert.doesNotThrow(() => config.set("factorio.player_online_autosave_slots", 5));
// valid: greater
assert.doesNotThrow(() => config.set("factorio.player_online_autosave_slots", 6));
// invalid: less than autosave_slots
assert.throws(
() => config.set("factorio.player_online_autosave_slots", 4),
/Value cannot be less than the number of autosave slots/
);
});
it("should allow when autosave_slots is not a number", function() {
const config = new InstanceConfig("controller", {
"factorio.settings": {
autosave_slots: null,
},
});
// valid: equal
assert.doesNotThrow(() => config.set("factorio.player_online_autosave_slots", 5));
// valid: greater
assert.doesNotThrow(() => config.set("factorio.player_online_autosave_slots", 6));
// invalid: less than autosave_slots
assert.throws(
() => config.set("factorio.player_online_autosave_slots", 4),
/Value cannot be less than the number of autosave slots/
);
assert.doesNotThrow(() => config.set("factorio.player_online_autosave_slots", 0));
assert.doesNotThrow(() => config.set("factorio.player_online_autosave_slots", 1));
});
});
it("should allow player_online_autosave_slots when autosave_slots is not a number", function() {
const config = new InstanceConfig("controller", {
"factorio.settings": {
autosave_slots: null,
},
});
describe("plugin.load_plugin", function() {
class TestInstanceConfig extends InstanceConfig {
static fieldDefinitions = { ...InstanceConfig.fieldDefinitions };
}
it("should validate against factorio.enable_save_patching", function() {
lib.addPluginFieldDefinitions(
{name: "save_patching", features: ["SavePatching"] },
"instanceConfigFields", TestInstanceConfig
);
assert.doesNotThrow(() => config.set("factorio.player_online_autosave_slots", 0));
assert.doesNotThrow(() => config.set("factorio.player_online_autosave_slots", 1));
const config = new TestInstanceConfig("controller");
// valid: does not require script commands
assert.doesNotThrow(() => config.set("factorio.enable_script_commands", false));
// valid: can have load disabled
assert.doesNotThrow(() => config.set("save_patching.load_plugin", false));
// valid: does not require save patching when not loaded
assert.doesNotThrow(() => config.set("factorio.enable_save_patching", false));
// invalid: requires save patching when loaded
assert.throws(
() => config.set("save_patching.load_plugin", true),
/requires save patching/
);
});
it("should validate against factorio.enable_script_commands", function() {
lib.addPluginFieldDefinitions(
{ name: "script_commands", features: ["ScriptCommands"] },
"instanceConfigFields", TestInstanceConfig
);
const config = new TestInstanceConfig("controller");
// valid: does not require save patching
assert.doesNotThrow(() => config.set("factorio.enable_save_patching", false));
// valid: can have load disabled
assert.doesNotThrow(() => config.set("script_commands.load_plugin", false));
// valid: does not require script commands when not loaded
assert.doesNotThrow(() => config.set("factorio.enable_script_commands", false));
// invalid: requires script commands when loaded
assert.throws(
() => config.set("script_commands.load_plugin", true),
/requires script commands/
);
});
it("should allow when no feature flags are present", function() {
lib.addPluginFieldDefinitions(
{ name: "no_features", features: [] },
"instanceConfigFields", TestInstanceConfig
);
const config = new TestInstanceConfig("controller");
// valid: does not require save patching
assert.doesNotThrow(() => config.set("factorio.enable_save_patching", false));
// valid: does not require script commands
assert.doesNotThrow(() => config.set("factorio.enable_script_commands", false));
// valid: can have load disabled
assert.doesNotThrow(() => config.set("script_commands.load_plugin", false));
// no invalid case
});
});
});