mirror of
https://github.com/PHIDIAS0303/ExpCluster.git
synced 2026-09-22 01:04:01 +00:00
The module no longer presents the interface of the legacy expcore.roles module. Nothing outside this repository depends on it, so rather than carry the legacy action strings and the transform which mapped them onto permission names, call sites now check the clusterio permission name directly. That removes the one invariant which silently broke every check if the lua and typescript transforms drifted, and makes a check in lua greppable against its definition. - player_allowed and player_has_flag become player_has_permission; flags were only permissions with a change trigger, which define_permission_trigger now provides for any permission. - on_role_assigned and on_role_unassigned become one on_player_roles_changed event carrying the assigned and unassigned names. Every consumer registered both for the same handler. It is also raised for connected players when a role is edited on the controller, which the old events never were, and for changes made on the controller to the roles a player holds. - player_outranks and player_outranks_role replace the repeated comparison of highest role indexes, and apply the core.admin bypass consistently, which two of the six call sites did not. - get_role takes a name, clusterio id, or role; get_roles replaces get_roles_ordered. The config views of the roles are gone, with role:get_player_names covering the one use of config.players. - Roles carry a permission group, which the legacy system mapped roles to and the first version of the plugin dropped. A player is moved into the group of their most privileged role which names one. It is edited with the other in game properties. - skip_checks is dropped from assign_player and unassign_player. A player object with index 0 is treated as the server, which is how exp_commands represents rcon. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
206 lines
5.6 KiB
TypeScript
206 lines
5.6 KiB
TypeScript
import React, { useContext, useEffect, useState } from "react";
|
|
import { Button, Col, ColorPicker, Form, Input, InputNumber, Row, Switch, Tooltip } from "antd";
|
|
|
|
import * as lib from "@clusterio/lib";
|
|
import { ControlContext, SectionHeader, useAccount, notifyErrorHandler } from "@clusterio/web_ui";
|
|
|
|
import { RoleColor, RoleMetaRecord, RoleMetaUpdateRequest } from "../../messages";
|
|
import type { WebPlugin } from "..";
|
|
|
|
const MS_PER_HOUR = 3600000;
|
|
|
|
type RoleFormValues = {
|
|
order: number;
|
|
priority: number;
|
|
shortHand: string;
|
|
tag: string;
|
|
color: string | { toRgb(): { r: number, g: number, b: number } } | null;
|
|
permissionGroup: string;
|
|
autoAssignHours: number | null;
|
|
blockAutoAssign: boolean;
|
|
};
|
|
|
|
/**
|
|
* In game properties of a role, shown at the end of the core role page.
|
|
*
|
|
* The name, description and permissions of a role are owned by clusterio, this
|
|
* only covers the properties which only mean something in game.
|
|
*/
|
|
export default function RoleProperties(props: { plugin: WebPlugin, role?: lib.Role }) {
|
|
const control = useContext(ControlContext);
|
|
const account = useAccount();
|
|
const [form] = Form.useForm<RoleFormValues>();
|
|
const [applying, setApplying] = useState(false);
|
|
|
|
const [roles] = props.plugin.useRoles();
|
|
const record = props.role ? roles.get(props.role.id) : undefined;
|
|
const meta = record?.meta;
|
|
const canUpdate = account.hasPermission("core.role.update");
|
|
|
|
useEffect(() => {
|
|
if (!meta) {
|
|
return;
|
|
}
|
|
form.setFieldsValue({
|
|
order: meta.order,
|
|
priority: meta.priority,
|
|
shortHand: meta.shortHand,
|
|
tag: meta.tag,
|
|
color: meta.color ? `rgb(${meta.color.r}, ${meta.color.g}, ${meta.color.b})` : null,
|
|
permissionGroup: meta.permissionGroup,
|
|
autoAssignHours: meta.autoAssignOnlineTimeMs === null
|
|
? null
|
|
: meta.autoAssignOnlineTimeMs / MS_PER_HOUR,
|
|
blockAutoAssign: meta.blockAutoAssign,
|
|
});
|
|
}, [meta, form]);
|
|
|
|
if (!props.role || !meta) {
|
|
return null;
|
|
}
|
|
|
|
async function apply() {
|
|
const values = form.getFieldsValue();
|
|
|
|
let color: RoleColor | null = null;
|
|
if (values.color && typeof values.color !== "string") {
|
|
const rgb = values.color.toRgb();
|
|
color = new RoleColor(Math.round(rgb.r), Math.round(rgb.g), Math.round(rgb.b));
|
|
} else if (typeof values.color === "string") {
|
|
const match = values.color.match(/(\d+)\D+(\d+)\D+(\d+)/);
|
|
if (match) {
|
|
color = new RoleColor(Number(match[1]), Number(match[2]), Number(match[3]));
|
|
}
|
|
}
|
|
|
|
await control.send(new RoleMetaUpdateRequest(new RoleMetaRecord(
|
|
props.role!.id,
|
|
values.order,
|
|
values.priority,
|
|
values.shortHand ?? "",
|
|
values.tag ?? "",
|
|
color,
|
|
values.permissionGroup ?? "",
|
|
values.autoAssignHours === null || values.autoAssignHours === undefined
|
|
? null
|
|
: values.autoAssignHours * MS_PER_HOUR,
|
|
values.blockAutoAssign ?? false,
|
|
)));
|
|
}
|
|
|
|
const labelled = (text: string, tip: string) => <Tooltip title={tip}>{text}</Tooltip>;
|
|
|
|
return <>
|
|
<SectionHeader
|
|
title="In Game Properties"
|
|
extra={canUpdate ? <Button
|
|
type="primary"
|
|
loading={applying}
|
|
onClick={() => {
|
|
setApplying(true);
|
|
apply()
|
|
.catch(notifyErrorHandler("Error updating role properties"))
|
|
.finally(() => setApplying(false));
|
|
}}
|
|
>Apply in game properties</Button> : undefined}
|
|
/>
|
|
<Form
|
|
form={form}
|
|
disabled={!canUpdate}
|
|
labelCol={{ span: 12 }}
|
|
wrapperCol={{ span: 12 }}
|
|
labelWrap
|
|
>
|
|
<Row gutter={[16, 0]}>
|
|
<Col xs={24} sm={12} xl={6}>
|
|
<Form.Item
|
|
name="order"
|
|
label={labelled("Order", "A lower value is a more privileged role")}
|
|
>
|
|
<InputNumber style={{ width: "100%" }} />
|
|
</Form.Item>
|
|
</Col>
|
|
|
|
<Col xs={24} sm={12} xl={6}>
|
|
<Form.Item
|
|
name="priority"
|
|
label={labelled(
|
|
"Priority",
|
|
"Only the roles with the highest priority a player holds apply, "
|
|
+ "which is how Jail suppresses every other role"
|
|
)}
|
|
>
|
|
<InputNumber style={{ width: "100%" }} />
|
|
</Form.Item>
|
|
</Col>
|
|
|
|
<Col xs={24} sm={12} xl={6}>
|
|
<Form.Item
|
|
name="shortHand"
|
|
label={labelled("Short hand", "Short form of the name, used where space is limited")}
|
|
>
|
|
<Input />
|
|
</Form.Item>
|
|
</Col>
|
|
|
|
<Col xs={24} sm={12} xl={6}>
|
|
<Form.Item
|
|
name="tag"
|
|
label={labelled("Tag", "Shown next to the names of players with this role")}
|
|
>
|
|
<Input />
|
|
</Form.Item>
|
|
</Col>
|
|
|
|
<Col xs={24} sm={12} xl={6}>
|
|
<Form.Item
|
|
name="color"
|
|
label={labelled("Colour", "Used for the role name and tag in game")}
|
|
>
|
|
<ColorPicker allowClear format="rgb" disabledAlpha />
|
|
</Form.Item>
|
|
</Col>
|
|
|
|
<Col xs={24} sm={12} xl={6}>
|
|
<Form.Item
|
|
name="permissionGroup"
|
|
label={labelled(
|
|
"Permission group",
|
|
"Factorio permission group players are moved into while this is their "
|
|
+ "most privileged role which names one. Leave empty to not change their group"
|
|
)}
|
|
>
|
|
<Input />
|
|
</Form.Item>
|
|
</Col>
|
|
|
|
<Col xs={24} sm={12} xl={6}>
|
|
<Form.Item
|
|
name="autoAssignHours"
|
|
label={labelled(
|
|
"Auto assign after (hours)",
|
|
"Granted once a player reaches this much online time across the cluster. "
|
|
+ "Leave empty to never grant it automatically"
|
|
)}
|
|
>
|
|
<InputNumber min={0} placeholder="Never" style={{ width: "100%" }} />
|
|
</Form.Item>
|
|
</Col>
|
|
|
|
<Col xs={24} sm={12} xl={6}>
|
|
<Form.Item
|
|
name="blockAutoAssign"
|
|
label={labelled(
|
|
"Block auto assign",
|
|
"Players holding this role are never granted any other role automatically"
|
|
)}
|
|
valuePropName="checked"
|
|
>
|
|
<Switch />
|
|
</Form.Item>
|
|
</Col>
|
|
</Row>
|
|
</Form>
|
|
</>;
|
|
}
|