Repository navigation
Conversation
Settings becomes a tree of records: each nested record is a section of config.yml and each component an option, with its comment in @comment and its default in @default. ConfigMapper binds a parsed file to the records and writes them back, so key names no longer come from field names by reflection. config.yml keeps its keys, order, comments and defaults. - Settings.override and Settings#with change an option through an accessor reference, so a wrong value type is a compile error. - Reloading builds a new tree and swaps one reference. - Strings are escaped when written, an unreadable file is not overwritten, and an unchanged file is not rewritten. - LimitResolver prepares each limit group once per loaded configuration instead of on every getLimit call. - PROTOCOL_SUPPORT_FIX moves to FaweCache#setProtocolSupportFix, since a record cannot hold a runtime flag. Config and ConfigOptComputation are removed and every use of the old Settings fields is migrated.
Settings becomes a tree of records: each nested record is a section of config.yml and each component an option, with its comment in @comment and its default in @default. ConfigMapper binds a parsed file to the records and writes them back, so key names no longer come from field names by reflection. config.yml keeps its keys, order, comments and defaults. - Settings.override and Settings#with change an option through an accessor reference, so a wrong value type is a compile error. - Reloading builds a new tree and swaps one reference. - Strings are escaped when written, an unreadable file is not overwritten, and an unchanged file is not rewritten. - LimitResolver prepares each limit group once per loaded configuration instead of on every getLimit call. - PROTOCOL_SUPPORT_FIX moves to FaweCache#setProtocolSupportFix, since a record cannot hold a runtime flag. Config and ConfigOptComputation are removed and every use of the old Settings fields is migrated.
6ef49ee to
4adf724
Compare
There was a problem hiding this comment.
🔵 Needs a closer look
It is an explicitly Work-In-Progress draft that rewrites the core configuration system and mechanically touches ~70 call sites, which warrants human review before approval.
3 open issues
What changed in this PR
This PR replaces FAWE's legacy reflection-based Config.java configuration system with an immutable, record-based Settings API. Configuration sections become nested Java records whose components are annotated with @Comment/@Default, and a new mapping layer (ConfigMapper, ConfigYaml, ConfigTree, LimitResolver) handles record↔YAML serialization, migration of legacy keys, and limit-group resolution. Roughly 70 call sites are mechanically migrated from uppercase field access (Settings.settings().QUEUE.PARALLEL_THREADS) to accessor methods (Settings.settings().queue().parallelThreads()). The PR is explicitly a Work-In-Progress draft.
Changes:
- Introduce an immutable record-based
Settingsmodel plus a new serialization/migration framework and comprehensive unit tests. - Rewrite
Fawe.setupConfigs()to load/save viaSettings.load/savewith aBuildInfo, and move theFaweCache.protocolSupportFixflag off the global settings object. - Migrate ~70 call sites to the new accessor API and delete
Config.java/ConfigOptComputation.java.
| File | Description |
|---|---|
.../configuration/Settings.java |
New immutable record config model, load/save/migrate/defaults and BuildInfo (context only; not in diff). |
.../configuration/ConfigMapper.java |
Reflection-based record↔YAML mapping, coercion, and defaults. |
.../configuration/ConfigYaml.java |
Hand-rolled UTF-8 YAML reader/writer with quoting/escaping. |
.../configuration/ConfigTree.java |
Dotted-path tree used by migration. |
.../configuration/LimitResolver.java |
Merges limit groups into a resolved FaweLimit ("most generous" semantics). |
.../configuration/{Option,Default,Comment}.java |
Annotations/metadata driving option paths, defaults and comments. |
.../core/Fawe.java |
Rewritten setupConfigs() using Path, Settings.load/save, and BuildInfo. |
.../core/FaweCache.java |
New protocolSupportFix field + setter replacing the old global setting. |
.../test/.../SettingsTest.java, ConfigMapperTest.java |
New unit tests covering save/load, migration, limits and fuzzed text round-tripping. |
.../test/resources/.../config.yml |
Expected-output fixture mirroring the @Comment/@Default text; carries over several comment typos. |
| ~70 migrated call sites | Mechanical switch from uppercase fields to accessor methods. |
Review effort: Balanced
…to refactor/v3/settings-records
Co-authored-by: MattBDev <4009945+MattBDev@users.noreply.github.com>
Co-authored-by: MattBDev <4009945+MattBDev@users.noreply.github.com>
|
Marking the PR as ready for review but I still have to update the PR body. Should be done Saturday. I think I have a minor tweak or two to I have to push as well. |
There was a problem hiding this comment.
🟡 Changes recommended
An unresolved critical finding remains: a typo in the config.yml comment (clipboad.on-disk instead of clipboard.use-disk) that must be mirrored in the Settings.java @Comment or the defaultsAreWrittenAsExpected test will fail.
1 open issue
3 resolved since last review
Review effort: Balanced
Co-authored-by: MattBDev <4009945+MattBDev@users.noreply.github.com>
SirYwell
left a comment
There was a problem hiding this comment.
What is the future updating strategy with this? E.g., if we want to rename entries etc.
Migration is handled in |
…consistency. This work isn't complete but to complete it would go outside the scope of this PR.
|
@copilot Apply the following suggested code to the |
Co-authored-by: MattBDev <4009945+MattBDev@users.noreply.github.com>
... Applied the suggested |
moveIntoLimits addressed each group by a dotted path while iterating the limits map. A group such as vip.plus created a vip section in that map, which threw ConcurrentModificationException and made the whole config be ignored. Groups are now written to through their own maps.
| INSTANCE.setTarget(MethodHandles.constant(Settings.class, updated)); | ||
| MutableCallSite.syncAll(new MutableCallSite[] {INSTANCE}); |
There was a problem hiding this comment.
This should probably delegate to the set method?
There was a problem hiding this comment.
Updated to delegate through set(...), matching the shared update path and keeping this change minimal.
Co-authored-by: MattBDev <4009945+MattBDev@users.noreply.github.com>


Description
FAWE's
config.ymlis now loaded into read-only Java records. Each setting's default is written next to the setting, and code changes a setting with a method reference such asSettings.History::compressionLevel.Removed API
Settings.settings().QUEUE.PARALLEL_THREADSSettings.settings().queue().parallelThreads()Settings.IMPSettings.settings()Settings.settings().EXPERIMENTAL.PERSISTENT_BRUSHES = falseSettings.override(Settings.Experimental::persistentBrushes, false)Settings.settings().reload(File)Settings.set(Settings.load(Path))Settings.settings().save(File)settings.save(Path)Settings.settings().LIMITS.get("default")Settings.settings().defaultLimits()Settings.settings().PROTOCOL_SUPPORT_FIXFaweCache.INSTANCE.setProtocolSupportFix(boolean)Configand its annotations@Comment,@DefaultPerformance
The new code loads and reloads about twice as fast, works out limits 2.4 to 3 times as fast, and allocates less memory, except when saving. Reading a setting costs the same.
Saving is slower. It takes about 12% longer on the stock file and about 50% longer with 8 limit groups, which is 0.1 to 0.5 ms more, and it allocates 3 to 4 times as much memory. That's the cost of writing through SnakeYAML instead of by hand. FAWE only saves at startup and on
/fawe reload.getLimit, player in no group, stock filegetLimit, player in two groups, 8 groupsA speed-up below 1× means the new code is slower.