- The instance sends every value of a feature, defaults included, so
update_config is one loop which sets each field and compares. The
stored table is the last set of values, and _apply is gone.
- values_equal compares table sizes before the contents.
- Protected entities are registered with register_on_object_destroyed
and forgotten in on_object_destroyed, which covers every way an
entity can go. The four removal handlers and the validity checks are
gone. Entities destroyed while the feature is disabled are swept when
it is enabled again. Saves from before the registrations table get it
through the Storage on_init callback.
- Spawn area applied the water offset twice and the pattern offset
never. The pattern tiles now move with the entities above them.
- pruneFeatureValues reads a validation result instead of catching an
error, and FeaturesPage hands its pending state and unsaved bar to
helpers.
- Shorter comments throughout.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Review points from Cooldude:
- Feature.register(name[, config]) is required again and returns a
feature object. Each feature has its config, its fields, and its
overrides, so one registry holds all the data. Only the overrides are
stored. On load each feature points at its own stored table again.
- The methods are feature:guard(lib), feature:update_config(enabled,
values), and feature:is_enabled(). Feature.get(name) looks up a
feature, and commands flag the feature object.
- update_config raises on_config_updated once per changed value, with
the feature name, path, old value, and new value. The gui refresh only
reacts to the "enabled" path.
- apply only sets values. on_apply is gone. Lookup sets built from
lists use Feature.to_set, which is cached by the list itself. A
replaced list gets a new set, so the sets are also correct after a
load. Protection exposes getters for its protected names and types.
Web: settings use small inputs, the same size as the config pages.
Fields are shaped like clusterio config field definitions. A field
with an inputComponent renders the component registered with the web
interface, so the role setting now uses core's role input, and other
plugin inputs can be used later.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Framework:
- Intervals are gone. Update periods are hardcoded on_nth_tick values
again, since nobody has needed to change them.
- apply is a single loop over the declared fields and logs a warning
when an override is invalid. receive_update also warns about
unknown keys.
- Nested tables are addressed as "section.key". Lists of strings can be
overridden. Features.optional declares a value with no default.
- Features.on_apply rebuilds values derived from the config, such as
lookup sets. It also runs during on_load.
exp_roles is assumed to be present:
- AFK Kick picks a role (active_role_id) instead of a custom check
function.
- Chat Auto Reply, Protection, and Nuke Protection no longer take a
permission as a setting.
New features, each chosen for an edge case:
- Discord Alerts: conditional handlers, and a command map that becomes
a list.
- Entity Protection: lists turned into sets at load, and bookkeeping
that must keep running while disabled.
- Nuke Protection: config keyed by defines, flattened to a banned items
list.
- Popups: one config shared by two control files.
- Spawn Area: nested sections, and settings that only apply when the
map is created.
Page: now laid out like the role page. It has a search across every
setting, one long list with a sticky sidebar to jump between sections,
and the enabled switch separated at the front of each header with
Reset to defaults on the right. Changed rows are highlighted, and a
bottom bar offers Revert and Apply. Role settings use a role picker and
lists use a tag select.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`assert` returns every argument it is given, so `return assert(x, msg)`
was leaking the message as a second return value.
`get_tile` is documented as taking x and y. The async function class
declared `@operator call` with no parameters, which made every async
call look over supplied.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Guards now cover the statements that follow them, and a couple of reads
are restructured so the narrowing survives.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The api documents both as a union with a positional array because both
are accepted as input, which makes every read of `.x` or `.left_top`
optional. Everything read back from the game uses the named form, so
the positional variant is removed and we now always write it that way
too.
The spawn area config offsets and the mine depletion search areas were
the only positional writes left, and apply_offset no longer needs its
fallback.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
spawn_area accepts both position forms on purpose, so it asserts that
one of the two is present rather than narrowing the type.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
emmylua deserialises an unknown diagnostic code to a catch all that
matches no checker, so every `invisible`, `nil-check` and
`global-element` suppression was silently doing nothing. Renamed to the
emmylua codes. `name-style-check` has no equivalent and is dropped, it
was already disabled under luals.
The `get_tile` suppressions referenced an api typedef bug from 2024
which no longer reproduces.
need-check-nil is deferred rather than disabled on merit: a third of the
407 findings come from MapPosition and BoundingBox being aliased as
`struct|[double, double]`, so every `entity.position.x` reads as
possibly nil. The rest need per site knowledge of runtime invariants.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Classes contributed to by more than one file now carry the `partial`
attribute. trains.lua declared its command as `ExpCommand_Artillery`,
which was a copy paste.
`Color.0` and `MapPosition.0` became `.struct` in fmtk 2.1.6.
emmylua rejects `@type` on a function statement or a `do` block, so
those are moved onto the local or replaced by `@param` and `@return`.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>