-
Notifications
You must be signed in to change notification settings - Fork 8
Structure of a configuration file
As intermediary for configuration files, fiber uses a tree called the IR (immediate representation). The IR contains all information about your configuration: the structure, names, values, metadata, everything.
Let's first take a look at how such a tree is built. Later we will see how fiber can interact with your IR and how others can interact with it.
Suppose we're making a small mod: it has a feature named op_feature
some server owners may want to disable, and some visual elements the user may want to tweak. All of our visual elements go in a category 'gui', making the configuration easy to navigate.
A JSON representation of this configuration may look like this:
{
"gui": {
"flickering_lights": false,
"space_between_buttons": 10,
"title": "Config"
},
"op_feature": false
}
Let's get around to specifying the same configuration, but using fiber's IR!
Builders are the primary way to compose an IR. Everything starts with a ConfigTreeBuilder
:
ConfigTree.builder()
...
Now, we want to add our first entry: op_feature
. It is of type boolean
, is named op_feature
and has a default value of false
.
Primitive values like these are called scalar values. The other type is 'aggregate', which are values like collections and arrays.
We can declare a node like op_feature
by using withValue
. This method must take a name, a type, and the default value:
ConfigTree.builder()
.withValue("op_feature", ConfigTypes.BOOLEAN, false)
...
We can add more metadata to it such as comments, listeners, etc. We'll look into that later!
Next up is the gui
category: in the IR, this is called a branch. We can 'start' one by using fork(String name)
:
...
.fork("gui")
...
fork
will return an entirely new ConfigTreeBuilder
, one which we can add everything in the gui
category to - and then return to our root builder.
Just as with op_feature
, let's add flickering_lights
, space_between_buttons
, and title
:
...
.fork("gui")
.withValue("flickering_lights", ConfigTypes.BOOLEAN, false)
.withValue("space_between_buttons", ConfigTypes.INTEGER, 10)
.withValue("title", ConfigTypes.STRING, "Config")
.finishBranch()
...
finishBranch
tells the builder "I'm done with gui, take me back!". It returns the original builder you made.
All done! Just build the entire thing using build()
, and save the return value in a variable:
ConfigTree.builder()
.withValue("op_feature", ConfigTypes.BOOLEAN, false)
.fork("gui")
.withValue("flickering_lights", ConfigTypes.BOOLEAN, false)
.withValue("space_between_buttons", ConfigTypes.INTEGER, 10)
.withValue("title", ConfigTypes.STRING, "Config")
.finishBranch()
.build();
Fiber also supports defining your IR in a POJO, optionally annotated with all the necessary metadata. This does the heavy lifting (builders) for you, but has some caveats as well.
Start off by making a new class. It doesn't need to extend or implement anything.
class MyConfigution { }
To add op_feature
to our configuration, declare a new field of type boolean
and value false
.
class MyConfiguration {
boolean op_feature = false;
}
Creating the gui
branch is a little trickier. We'll have to create another class for the settings belonging in gui
, and declare another field for the actual branch itself.
class MyConfiguration {
boolean op_feature = false;
@Setting.Group
GuiBranch branch;
class GuiBranch {
boolean flickering_lights = false;
int space_between_buttons = 10;
String title = "Config";
}
}
The Setting.Group
annotation marks that fiber should treat that field as a branch, and not as a setting.
Lovely - our class now represents our configuration file, but there's some obvious faults: we can't do anything with it yet because it's not an IR, and the naming isn't very java-esque. We'll talk about converting it to an IR later, let's fix the naming first.
If you'd like to rename one field, annotate the field with Setting
and provide a name
:
@Setting(name = "flickering_lights")
boolean flickeringLights = false;
This way of renaming a predictable naming scheme isn't very elegant though. Instead, use fiber's built-in naming conventions!
Annotate MyConfiguration
with Settings
, and provide a namingConvention
.
@Settings(namingConvention = SnakeCaseConvention.class)
class MyConfiguration {
...
Specifying a naming convention tells fiber to run every setting's name through the specified formatter, and use those names instead.
For example, SettingNamingConvention.SNAKE_CASE
produces the following names:
Field name | Formatted name |
---|---|
fooBar | foo_bar |
example | example |
aVeryLongName | a_very_long_name |
Fields that received a custom name through their Setting
annotation have higher authority than the naming convention: they will not be ran through the formatter.
With the new naming convention, we can use normal names for our fields:
@Settings(namingConvention = SnakeCaseConvention.class)
class MyConfiguration {
boolean opFeature = false;
@Setting.Group
GuiBranch branch;
class GuiBranch {
boolean flickeringLights = false;
int spaceBetweenButtons = 10;
String title = "Config";
}
}
Create a builder and use the applyFromPojo
method:
MyConfiguration configuration = new MyConfiguration();
ConfigTree tree = ConfigTree.builder()
.applyFromPojo(configuration)
.build();
That's it! You'll be able to use tree
like any other IR, and any changes made to your settings will be set in the POJO as well. However, you may not modify the POJO directly - fiber can not pick up changes you make to the POJO.