mirror of
https://github.com/clusterio/clusterio.git
synced 2026-11-14 23:24:52 +09:00
Add suport for color mod setting (#568)
Implement the color-setting type in ModPack and display a color picker for it in the Web UI. This type of setting was added in Factorio 1.1.77.
This commit is contained in:
1 parent
d6d6c052aa
commit
72264f0c53
7 files changed
+114
-13
No files matched your search
@@ -1,5 +1,12 @@
|
||||
# Changelog
|
||||
|
||||
## Version 2.0.0-alpha.15
|
||||
|
||||
### Features
|
||||
|
||||
- Added support for `color-settings` Mod settings (#568)[https://github.com/clusterio/clusterio/issues/568].
|
||||
|
||||
|
||||
## Version 2.0.0-alpha.14
|
||||
|
||||
This release does not retain backwards compatibility with 2.0.0-alpha.13 meaning all parts of the cluster needs to updated at the same time.
|
||||
|
||||
@@ -1097,7 +1097,13 @@ modPackCommands.add(new lib.Command({
|
||||
|
||||
function setModPackSettings(
|
||||
modPack: lib.ModPack,
|
||||
args: { boolSetting?: string[], intSetting?: string[], doubleSetting?: string[], stringSetting?: string[] },
|
||||
args: {
|
||||
boolSetting?: string[],
|
||||
intSetting?: string[],
|
||||
doubleSetting?: string[],
|
||||
stringSetting?: string[],
|
||||
colorSetting?: string[],
|
||||
},
|
||||
) {
|
||||
function doSettings(settings: string[], cast: (value: string) => (string | number | boolean)) {
|
||||
for (let i = 0; i + 2 < settings.length; i += 3) {
|
||||
@@ -1134,6 +1140,13 @@ function setModPackSettings(
|
||||
return number;
|
||||
});
|
||||
doSettings(args.stringSetting || [], value => value);
|
||||
doSettings(args.colorSetting || [], value => {
|
||||
let color = JSON.parse(value);
|
||||
if (typeof color !== "object" || color === null) {
|
||||
throw new lib.CommandError("color value must be a JSON object with r, g, b, a properties");
|
||||
}
|
||||
return color;
|
||||
});
|
||||
}
|
||||
|
||||
function setModPackMods(modPack: lib.ModPack, mods: string[] | undefined) {
|
||||
@@ -1173,6 +1186,7 @@ modPackCommands.add(new lib.Command({
|
||||
"int-setting": { describe: "Set int setting", array: true, nargs: 3, type: "string" },
|
||||
"double-setting": { describe: "Set double setting", array: true, nargs: 3, type: "string" },
|
||||
"string-setting": { describe: "Set string setting", array: true, nargs: 3, type: "string" },
|
||||
"color-setting": { describe: "Set color setting", array: true, nargs: 3, type: "string" },
|
||||
});
|
||||
}],
|
||||
handler: async function(
|
||||
@@ -1186,6 +1200,7 @@ modPackCommands.add(new lib.Command({
|
||||
intSetting?: string[],
|
||||
doubleSetting?: string[],
|
||||
stringSetting?: string[],
|
||||
colorSetting?: string[],
|
||||
},
|
||||
control: Control,
|
||||
) {
|
||||
@@ -1244,6 +1259,7 @@ modPackCommands.add(new lib.Command({
|
||||
"int-setting": { describe: "Set int setting", array: true, nargs: 3, type: "string" },
|
||||
"double-setting": { describe: "Set double setting", array: true, nargs: 3, type: "string" },
|
||||
"string-setting": { describe: "Set string setting", array: true, nargs: 3, type: "string" },
|
||||
"color-setting": { describe: "Set color setting", array: true, nargs: 3, type: "string" },
|
||||
"remove-setting": { describe: "Remove a setting", array: true, nargs: 2, type: "string" },
|
||||
});
|
||||
}],
|
||||
@@ -1261,6 +1277,7 @@ modPackCommands.add(new lib.Command({
|
||||
intSetting?: string[],
|
||||
doubleSetting?: string[],
|
||||
stringSetting?: string[],
|
||||
colorSetting?: string[],
|
||||
removeSetting?: string[],
|
||||
},
|
||||
control: Control,
|
||||
|
||||
@@ -8,16 +8,24 @@ import { integerFactorioVersion } from "./version";
|
||||
import type { Logger } from "../logging";
|
||||
|
||||
|
||||
export const ModSettingColor = Type.Object({
|
||||
r: Type.Number(),
|
||||
g: Type.Number(),
|
||||
b: Type.Number(),
|
||||
a: Type.Number(),
|
||||
});
|
||||
export type ModSettingColor = Static<typeof ModSettingColor>;
|
||||
|
||||
/**
|
||||
* A setting for a mod.
|
||||
*/
|
||||
export interface ModSetting {
|
||||
/** Value of the given mod setting. */
|
||||
value: boolean | number | string;
|
||||
value: boolean | number | string | ModSettingColor;
|
||||
}
|
||||
|
||||
const ModSettingJsonSchema = Type.Object({
|
||||
"value": Type.Union([Type.Boolean(), Type.Number(), Type.String()]),
|
||||
"value": Type.Union([Type.Boolean(), Type.Number(), Type.String(), ModSettingColor]),
|
||||
});
|
||||
|
||||
const ModSettingsJsonSchema = Type.Record(Type.String(), ModSettingJsonSchema);
|
||||
@@ -335,7 +343,7 @@ export default class ModPack {
|
||||
* @param logger - Logger used to report warnings on.
|
||||
*/
|
||||
fillDefaultSettings(settingPrototypes: Record<string, object>, logger: Logger) {
|
||||
const knownTypes = ["bool-setting", "int-setting", "double-setting", "string-setting"];
|
||||
const knownTypes = ["bool-setting", "int-setting", "double-setting", "string-setting", "color-setting"];
|
||||
let prototypes = Object.entries(settingPrototypes)
|
||||
.filter(([type, _]) => knownTypes.includes(type))
|
||||
.flatMap(([_, settings]) => Object.values(settings))
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
export { default as ExportManifest } from "./ExportManifest";
|
||||
export { default as ModInfo } from "./ModInfo";
|
||||
export { default as ModPack, ModSetting, ModRecord } from "./ModPack";
|
||||
export { default as ModPack, ModSetting, ModSettingColor, ModRecord } from "./ModPack";
|
||||
export { default as ModuleInfo } from "./ModuleInfo";
|
||||
export { default as Permission } from "./Permission";
|
||||
export { default as PlayerStats } from "./PlayerStats";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { Fragment, memo, useCallback, useEffect, useContext, useRef, useState } from "react";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import {
|
||||
Button, Card, Checkbox, Col, ConfigProvider, Descriptions, Form, Input, Pagination,
|
||||
Button, Card, ColorPicker, Checkbox, Col, ConfigProvider, Descriptions, Form, Input, Pagination,
|
||||
Popconfirm, Row, Table, Tag, Typography, Select, Skeleton, Space, Spin, Switch, Modal, Tooltip,
|
||||
TablePaginationConfig,
|
||||
} from "antd";
|
||||
@@ -404,7 +404,7 @@ function hasChange(changes: ModChange[], field:Partial<ModChange>) {
|
||||
}
|
||||
|
||||
// picks out the value property from settings.
|
||||
function pickValue(map: Map<string, lib.ModSetting>): [string, string|number|boolean][] {
|
||||
function pickValue(map: Map<string, lib.ModSetting>): [string, lib.ModSetting["value"]][] {
|
||||
return [...map].map(([name, value]) => [name, value.value]);
|
||||
}
|
||||
|
||||
@@ -420,6 +420,25 @@ function groupToMap(array: any[], fn: (...args:any) => any) {
|
||||
return map;
|
||||
}
|
||||
|
||||
function InputColor(props: {
|
||||
value: lib.ModSettingColor,
|
||||
onChange: (value: lib.ModSettingColor) => void,
|
||||
}) {
|
||||
const value = props.value;
|
||||
return <ColorPicker
|
||||
defaultFormat="rgb"
|
||||
defaultValue={`rgba(${value.r * 255}, ${value.g * 255}, ${value.b * 255}, ${value.a}`}
|
||||
onChangeComplete={(color) => {
|
||||
const rgb = color.toRgb();
|
||||
props.onChange({
|
||||
r: rgb.r / 255,
|
||||
g: rgb.g / 255,
|
||||
b: rgb.b / 255,
|
||||
a: rgb.a,
|
||||
});
|
||||
}}
|
||||
/>;
|
||||
}
|
||||
type SettingsTableFieldProps = {
|
||||
name: string;
|
||||
type: string;
|
||||
@@ -431,11 +450,15 @@ type SettingsTableFieldProps = {
|
||||
const SettingsTableField = memo((props: SettingsTableFieldProps) => {
|
||||
const name = props.name;
|
||||
const isBoolean = props.type === "bool-setting";
|
||||
const isColor = props.type === "color-setting";
|
||||
const isNumber = ["int-setting", "double-setting"].includes(props.type);
|
||||
const isSelect = Boolean(props.allowedValues);
|
||||
let input;
|
||||
if (isBoolean) {
|
||||
input = <Checkbox />;
|
||||
} else if (isColor) {
|
||||
const PartialInputColor = InputColor as () => React.JSX.Element;
|
||||
input = <PartialInputColor />;
|
||||
} else if (isSelect) {
|
||||
const options = Object.values(props.allowedValues).map(value => ({
|
||||
value,
|
||||
@@ -484,7 +507,7 @@ function SettingsTable(props: SettingsTableProps) {
|
||||
.map(mod => [mod.name, mod.title])
|
||||
);
|
||||
|
||||
const types = ["bool-setting", "int-setting", "double-setting", "string-setting"];
|
||||
const types = ["bool-setting", "int-setting", "double-setting", "string-setting", "color-setting"];
|
||||
let prototypes = Object.entries(props.prototypes)
|
||||
.filter(([type, _]) => types.includes(type))
|
||||
.flatMap<any>(([_, settingPrototypes]: [string, any]) => Object.values(settingPrototypes))
|
||||
@@ -492,7 +515,7 @@ function SettingsTable(props: SettingsTableProps) {
|
||||
|
||||
function controls(
|
||||
scope: "startup"|"runtime-global"|"runtime-per-user",
|
||||
storedFields: [string, string|number|boolean][]
|
||||
storedFields: [string, lib.ModSetting["value"]][]
|
||||
) {
|
||||
let fields = new Map(prototypes
|
||||
.filter(p => p.setting_type === scope)
|
||||
@@ -508,6 +531,8 @@ function SettingsTable(props: SettingsTableProps) {
|
||||
type = "double-setting";
|
||||
} else if (typeof value === "string") {
|
||||
type = "string-setting";
|
||||
} else if (typeof value === "object" && "r" in value) {
|
||||
type = "color-setting";
|
||||
} else {
|
||||
throw Error(`Unhandled setting type ${typeof value}`);
|
||||
}
|
||||
|
||||
@@ -81,6 +81,10 @@ async function getUsers() {
|
||||
return new Map(users.map(user => [user.name, user]));
|
||||
}
|
||||
|
||||
function jsonArg(value) {
|
||||
return `"${JSON.stringify(value).replace(/"/g, process.platform === "win32" ? '""' : '\\"')}"`;
|
||||
}
|
||||
|
||||
describe("Integration of Clusterio", function() {
|
||||
describe("clusteriocontroller", function() {
|
||||
describe("bootstrap generate-user-token", function() {
|
||||
@@ -502,8 +506,7 @@ describe("Integration of Clusterio", function() {
|
||||
];
|
||||
|
||||
for (let [prop, value] of testConfigs) {
|
||||
value = `"${JSON.stringify(value).replace(/"/g, process.platform === "win32" ? '""' : '\\"')}"`;
|
||||
let args = `test factorio.settings ${prop} ${value}`;
|
||||
let args = `test factorio.settings ${prop} ${jsonArg(value)}`;
|
||||
await execCtl(`instance config set-prop ${args}`);
|
||||
}
|
||||
|
||||
@@ -876,6 +879,7 @@ describe("Integration of Clusterio", function() {
|
||||
assert(modPacks.some(modPack => modPack.name === "empty-pack"), "created pack is not in the list");
|
||||
});
|
||||
it("should allow setting all fields", async function() {
|
||||
const color = { r: 1, g: 0, b: 1, a: 0 };
|
||||
await execCtl(
|
||||
"mod-pack create full-pack 0.17.59 " +
|
||||
"--description Description " +
|
||||
@@ -883,7 +887,8 @@ describe("Integration of Clusterio", function() {
|
||||
"--bool-setting startup MyBool true " +
|
||||
"--int-setting runtime-global MyInt 1235 " +
|
||||
"--double-setting runtime-global MyDouble 12.25 " +
|
||||
"--string-setting runtime-per-user MyString a-string"
|
||||
"--string-setting runtime-per-user MyString a-string " +
|
||||
`--color-setting runtime-per-user MyColor ${jsonArg(color)}`
|
||||
);
|
||||
let modPacks = await getControl().send(new lib.ModPackListRequest());
|
||||
let modPack = modPacks.find(entry => entry.name === "full-pack");
|
||||
@@ -898,6 +903,7 @@ describe("Integration of Clusterio", function() {
|
||||
reference.settings["runtime-global"].set("MyInt", { value: 1235 });
|
||||
reference.settings["runtime-global"].set("MyDouble", { value: 12.25 });
|
||||
reference.settings["runtime-per-user"].set("MyString", { value: "a-string" });
|
||||
reference.settings["runtime-per-user"].set("MyColor", { value: color });
|
||||
reference.updatedAtMs = modPack.updatedAtMs;
|
||||
assert.deepEqual(modPack, reference);
|
||||
});
|
||||
|
||||
@@ -39,6 +39,7 @@ describe("lib/data/ModPack", function() {
|
||||
},
|
||||
"runtime-per-user": {
|
||||
"string-setting": { "value": "a string" },
|
||||
"color-setting": { "value": { "r": 1, "g": 1, "b": 0, "a": 1 } },
|
||||
},
|
||||
}}));
|
||||
check(ModPack.fromJSON({ export_manifest: { assets: { setting: "settings.json" }}}));
|
||||
@@ -60,6 +61,7 @@ describe("lib/data/ModPack", function() {
|
||||
},
|
||||
"runtime-per-user": {
|
||||
"string-setting": { "value": "a string" },
|
||||
"color-setting": { "value": { "r": 1, "g": 1, "b": 0, "a": 1 } },
|
||||
},
|
||||
},
|
||||
export_manifest: { assets: { setting: "settings.json" }},
|
||||
@@ -101,6 +103,14 @@ describe("lib/data/ModPack", function() {
|
||||
default_value: "a string",
|
||||
},
|
||||
},
|
||||
"color-setting": {
|
||||
"color": {
|
||||
name: "color",
|
||||
type: "color-setting",
|
||||
setting_type: "runtime-per-user",
|
||||
default_value: { "r": 1, "g": 1, "b": 1, "a": 1 },
|
||||
},
|
||||
},
|
||||
};
|
||||
const mockLogger = { warn: () => {} };
|
||||
it("should fill in defaults for settings", function() {
|
||||
@@ -115,6 +125,7 @@ describe("lib/data/ModPack", function() {
|
||||
},
|
||||
"runtime-per-user": {
|
||||
"string": { "value": "a string" },
|
||||
"color": { "value": { "r": 1, "g": 1, "b": 1, "a": 1 } },
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -128,6 +139,7 @@ describe("lib/data/ModPack", function() {
|
||||
},
|
||||
"runtime-per-user": {
|
||||
"string": { "value": "spam" },
|
||||
"color": { "value": { "r": 0, "g": 0.5, "b": 1, "a": 1 } },
|
||||
},
|
||||
}});
|
||||
pack.fillDefaultSettings(prototypes, mockLogger);
|
||||
@@ -140,6 +152,7 @@ describe("lib/data/ModPack", function() {
|
||||
},
|
||||
"runtime-per-user": {
|
||||
"string": { "value": "spam" },
|
||||
"color": { "value": { "r": 0, "g": 0.5, "b": 1, "a": 1 } },
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -204,6 +217,7 @@ describe("lib/data/ModPack", function() {
|
||||
},
|
||||
"runtime-per-user": {
|
||||
"string-setting": { "value": "a string" },
|
||||
"color-setting": { "value": { "r": 1, "g": 0.5, "b": 0, "a": 1 } },
|
||||
},
|
||||
}});
|
||||
|
||||
@@ -248,7 +262,7 @@ describe("lib/data/ModPack", function() {
|
||||
|
||||
...istr("runtime-per-user"), // name
|
||||
Uint8Array.from([5, 0]), // dictionary
|
||||
new Uint8Array(Uint32Array.from([1]).buffer), // items
|
||||
new Uint8Array(Uint32Array.from([2]).buffer), // items
|
||||
|
||||
...istr("string-setting"), // name
|
||||
Uint8Array.from([5, 0]), // dictinary
|
||||
@@ -257,6 +271,30 @@ describe("lib/data/ModPack", function() {
|
||||
...istr("value"), // name
|
||||
Uint8Array.from([3, 0, 0, "a string".length]), // string
|
||||
Buffer.from("a string"),
|
||||
|
||||
...istr("color-setting"), // name
|
||||
Uint8Array.from([5, 0]), // dictinary
|
||||
new Uint8Array(Uint32Array.from([1]).buffer), // items
|
||||
|
||||
...istr("value"), // name
|
||||
Uint8Array.from([5, 0]), // dictinary
|
||||
new Uint8Array(Uint32Array.from([4]).buffer), // items
|
||||
|
||||
...istr("r"), // name
|
||||
Uint8Array.from([2, 0]), // number
|
||||
new Uint8Array(Float64Array.from([1]).buffer),
|
||||
|
||||
...istr("g"), // name
|
||||
Uint8Array.from([2, 0]), // number
|
||||
new Uint8Array(Float64Array.from([0.5]).buffer),
|
||||
|
||||
...istr("b"), // name
|
||||
Uint8Array.from([2, 0]), // number
|
||||
new Uint8Array(Float64Array.from([0]).buffer),
|
||||
|
||||
...istr("a"), // name
|
||||
Uint8Array.from([2, 0]), // number
|
||||
new Uint8Array(Float64Array.from([1]).buffer),
|
||||
])
|
||||
);
|
||||
/* eslint-enable indent */
|
||||
|
||||
Reference in new issue
Block a user