Clusterio #984 converted its packages to ESM, so a CommonJS plugin can
no longer import @clusterio/lib and plugin entrypoints must name the
full file. Every package gets "type": "module", entrypoints and relative
imports carry the .js extension, and the webpack configs become .cjs as
webpack-cli loads them with require. tsconfig.node.json moves from
node16 to nodenext to match clusterio, which needs it for the JSON
import attributes in its sources.
The tap tests and the shared test helpers use import and
import.meta.dirname. The test scripts pass --type-strip-only so tap
leaves lib's .ts subpath imports to Node's own type stripping, as its
TypeScript loader compiled them without a default export. The lua
runner resolves fengari from the test file it is given, since a bare
import from the shared test directory does not reach the plugin's
devDependencies.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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>
- Use the core role permissions rather than defining new ones. Assignments are
only sent between the controller and an instance, so they are no longer
addressed to control and need no permission at all.
- Split the datastore reconciliation into ensureRoleMeta, sweepRoleMeta and
applyAutoAssign, all run on init as well as when roles change. Properties left
behind by a role deleted while the plugin was not running are now swept up,
where before they would be inherited by the next role given that id.
- Use a switch in onControllerConfigFieldChanged, matching instance.ts.
- Resolve pending assignments on initialise. A pending role is either confirmed,
and so now held by synced_players, or it never landed; either way it stops
being held locally. Roles assigned with assign_player_local are untouched.
- Drop the emit_updates juggling in reject_assignment, sync is already false.
- Send each permission name once and reference it by index, see the benchmark in
the pull request. Single role updates stay in the plain form.
- Lay the role properties out in columns, name the apply button for the section
it belongs to, and give every field a tooltip.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Clusterio already stores roles, the permissions they grant, and which user holds
which role, along with a web UI for all three. What it does not have is the
properties a role only needs in game, or a way for an instance to learn about
any of it, since role and user updates are only sent to control connections.
This plugin fills both gaps. The controller keeps a record per role holding the
order, priority, short hand, tag, colour and auto assign threshold, created
automatically for any role which does not have one. It then rebroadcasts roles
and assignments on its own events so instances can follow them.
The lua module presents the same interface the legacy expcore.roles module did,
so the call sites can be moved over without being rewritten. Permission checks
translate the legacy action strings using the same mapping exp_scenario defines.
Two things replace features the legacy system had:
- Priority replaces disallow. Only the roles with the highest priority a player
holds are considered, so Jail can suppress every other role including the
default one, without needing to take roles away first.
- Assignments made in game are applied locally and then sent to the controller,
which keeps assign_player synchronous for callers. A role which should never
leave this map, such as one earned from time on the map, is assigned with
assign_player_local instead.
Roles earned from online time across the cluster are granted by the controller
from the threshold on the role, using the online time clusterio already tracks.
Nothing requires this plugin yet; moving the call sites off expcore.roles and
removing the legacy config is left for a follow up.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>