Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AutoConfig #3723

Merged
merged 22 commits into from
Oct 15, 2019
Merged

AutoConfig #3723

merged 22 commits into from
Oct 15, 2019

Conversation

eviltak
Copy link
Member

@eviltak eviltak commented Aug 13, 2019

Motivation

The goal of FCs (as listed in #2668) was to allow the "engine and modules [to be] able to publish their configuration settings, as needed." This is currently achieved by creating a map of settings which can be added to and accessed at runtime (see #2757). However, after looking at #3258 and existing config classes, it looks like that settings will only ever be accessed dynamically at runtime -- there shouldn't ever be a need for settings to be published at runtime, since all the settings to be published should be known at the time of writing.

This is evident from the fact that all configs are currently stored as hardcoded classes, with concrete fields as the "settings". FlexibleConfigs, thus, might be a bit too flexible for our uses! This PR thus proposes a hybrid approach, one which addresses all the shortcomings of both the FC and the concrete class approaches while also in my opinion being the most suitable to our use case.

The new approach

The hybrid approach involves using concrete config classes (which are marked as such by inheriting the AutoConfig class) with public final fields that are of type Setting (the FC setting). Here is the AudioConfig as an actual, usable AutoConfig in action (no seriously, that is all that is required to publish an AutoConfig 😄):

public class AudioConfig extends AutoConfig {
public final Setting<Float> soundVolume =
setting(
type(Float.class),
defaultValue(1.0f),
// From AudioSettingsScreen
constraint(new NumberRangeConstraint<>(0.0f, 1.0f, true, true))
);
public final Setting<Float> musicVolume =
setting(
type(Float.class),
defaultValue(1.0f),
constraint(new NumberRangeConstraint<>(0.0f, 1.0f, true, true))
);
// TODO: Convert into Setting -- no uses yet
private boolean disableSound;
}

Notes

  • This PR only converts AudioConfig to an AutoConfig as a proof-of-concept -- other engine configs will be covered in future PRs
  • This PR removes FlexibleConfig and all related types with the intention of replacing them with AutoConfig, but it is possible for both to coexist if needed
  • AutoConfig UI will be very similar to FC UI and hence will be included in UI for editing arbitrary types and AutoConfigs #3699
  • AutoConfigs are saved in /homeDir/configs under a subdirectory named after the module declaring them

The DSL

Rather than use the newly introduced FlexibleConfig.SettingEntry API, settings are constructed using a DSL of sorts, enumerated by the AutoConfig.setting static method and the SettingArgument class (and its static factory methods). Couple of examples are above in AudioConfig.

Another example using all setting parameters:

Setting<Double> setting = AutoConfig.setting(
type(Double.class),
defaultValue(defaultValue),
constraint(constraint),
name(name),
description(description)
);

All the methods used are static and stateless. They can also be statically imported to make setting declaration easy to read.

The DSL is also fully type-checked and compiler enforced; it is also possible to make some parameters compulsory (as is done for the type and defaultValue parameters) and the others optional. It is easy to employ this DSL in FCs too, if needed.

Advantages

Versus FCs

  • Each setting is now accessed via the field in the config class (an instance of which will be retrievable via the context); this can be enforced by the compiler and doesn't require a unique identifier.
  • The type of the setting is stored in the field type and enforced by the compiler, allowing accessors of the setting to not have to guess what the type of the setting could be.
  • Modules/code that require access to a config/setting must be able to access the corresponding config class, and therefore must have the config declaring module as a dependency.

Versus concrete classes (currently used)

  • Only those classes marked by the AutoConfig base type are treated as a config class and stored in a AutoConfigManager.
  • The AutoConfigManager then looks through the fields of each loaded Config and recognizes those fields with the type Setting as a setting in the config and handles the UI/serialization automagically.
  • Modules/classes can thus create configs as they please -- all they have to do is create a class that is marked as a AutoConfig and publish settings in its fields.
    This will work very much like ComponentSystem and ComponentSystemManager do -- to create a ComponentSystem, all a module does is create a class that inherits the ComponentSystem interface. All ComponentSystems in the ModuleEnvironment are loaded by the ComponentSystemManager, and are initialized/handled/called as needed.
  • All the Config class needs to do to publish a setting is to declare a public final field of type Setting and initialize it with the constraints and the defaults; no other methods needed.
  • Unlike the current hardcoded config classes, getters and setters for each setting aren't needed since the Setting interface encapsulates all of the responsibilities that the getters and setters currently fulfill via the getValue and setValue methods.
    This includes checking if the value is valid (via the SettingConstraint) and notifying subscribers of any changes.
  • The AutoConfig base type could also contain helpful utility methods like subscribeAll and unsubscribeAll, which subscribes and unsubscribes a listener to and from all settings in a config.

Future work

@eviltak eviltak added Topic: Architecture Requests, Issues and Changes related to software architecture, programming patterns, etc. Api labels Aug 13, 2019
@eviltak eviltak requested review from Cervator and vampcat August 13, 2019 15:44
@eviltak eviltak self-assigned this Aug 13, 2019
@GooeyHub
Copy link
Member

Hooray Jenkins reported success with all tests good!

2 similar comments
@GooeyHub
Copy link
Member

Hooray Jenkins reported success with all tests good!

@GooeyHub
Copy link
Member

Hooray Jenkins reported success with all tests good!

@eviltak eviltak marked this pull request as ready for review August 14, 2019 12:18
@GooeyHub
Copy link
Member

Hooray Jenkins reported success with all tests good!

1 similar comment
@GooeyHub
Copy link
Member

Hooray Jenkins reported success with all tests good!

Copy link
Member

@skaldarnar skaldarnar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm convinced by this changeset, some minor things that could be done/added/changed, but it could also be done in another iteration.
Thanks a lot @eviltak

Copy link
Contributor

@Qwertygiy Qwertygiy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested, played around with the values to explore different available options, and with the exception of the "description" key (which is intentionally pending for UI to deal with) everything functioned as expected.

@eviltak
Copy link
Member Author

eviltak commented Oct 15, 2019

Rebased, please re-fetch the branch.

@GooeyHub
Copy link
Member

Uh oh, something went wrong with the build. Need to check on that

@GooeyHub
Copy link
Member

Hooray Jenkins reported success with all tests good!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Topic: Architecture Requests, Issues and Changes related to software architecture, programming patterns, etc.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants