The Config System#
In this section we will cover:
How Terra defines how configs must be structured.
How these structured configs are interpreted and utilized by Terra.
How we can write configs that abide by these prescribed structures.
Templates#
Most configurations follow what we will call a template. Templates can be thought of as special versions of maps which specify rules on how the contained key-value pairs must be structured. Many different templates are defined by Terra and can each be regarded as their own types, just like strings and integers.
Parameters#
One of the main components of any given template is It’s specification of parameters. Parameters are what makes our data useful, as just about all world generation behavior in Terra is determined by parameters.
The specification of a parameter inside a template involves:
What the name of the parameter is (which is also how it is referenced inside a template).
Whether the parameter is optional or required.
What the types can the parameter be set to.
To set parameters within a template, we use the key to denote which parameter we want to set, and the value to what the parameter should be set to. For example, here are a couple parameters specified within a Terra config pack manifest that provides some basic information about the pack:
1id: COOL_CONFIG_PACK
2version: 1.0.0
3author: Anon Y. Mous
1{
2 "id": "COOL_CONFIG_PACK",
3 "version": "1.0.0",
4 "author": "Anon Y. Mous"
5}
Parameters may also be nested under multiple maps within a template. For
example in the below config, the top level object may abide by a
template and specify a parameter that is nested, which we can see has
the value a parameter value
.
1a:
2 nested:
3 parameter: a parameter value
1{
2 "a": {
3 "nested": {
4 "parameter": "a parameter value"
5 }
6 }
7}
When referring to parameters within templates, we use a combination of
the parent key(s) separated by dots .
to identify the desired
parameter. Using the above config, we would refer to the specified
parameter as a.nested.parameter
.
Some parameter conventions to keep in mind:
The parent key object(s) of a parameter will always be of type string, where all characters are lowercase, and dashes
-
are used in place of spaces.The type of the value object is considered the parameter’s type.
Working With Templates#
For the sake of explanation, let’s invent a new template to work with
called AnimalTemplate
. The AnimalTemplate
type specifies:
A required parameter called
color
that must be of typestring
A required parameter called
legs
that must be of typeinteger
.
We can then write a new config using our new type assuming our top level
object is of type AnimalTemplate
:
1color: grey
2legs: 4
1{
2 "color": "grey"
3 "legs": 4
4}
Because AnimalTemplate
contains these parameter specifications, if
we write a config that does not abide by them, then Terra will fail to
load the config. For example, the following config would not load
because 1. color
has not been specified and is a required parameter,
and 2. legs
is not of the required type integer:
1legs: two
1{
2 "legs": "two"
3}
If we were to document AnimalTemplate
, it may look like this:
Defines the attributes of an animal.
REQUIRED
- color
String
The color of the animal.
- legs
Integer
How many legs the animal has.
Great, now that we have a template to describe an animal, let’s create a new template that describes a zoo of animals:
Defines a zoo of animals.
REQUIRED
- animals
Map[String:AnimalTemplate]
A collection of animals.
OPTIONAL
- description
String
A description of the zoo and It’s animals.
The interesting thing to note here with ZooTemplate
is we have now
treated AnimalTemplate
as the required value type of the animals
parameter. This ability to utilize templates like any other type allows
for highly complex config specs, and is one of the key features of
Terra’s config system.
We can now use AnimalTemplate
s within our new ZooTemplate
and
create a config able to be read and interpreted by the config loader
like so:
1description: A zoo of Australian animals.
2animals:
3 koala:
4 color: grey
5 legs: 4
6 kangaroo:
7 color: brown
8 legs: 2
1{
2 "description": "A zoo of Australian animals.",
3 "animals": {
4 "koala": {
5 "color": "grey",
6 "legs": 4
7 },
8 "kangaroo": {
9 "color": "brown",
10 "legs": 2
11 }
12 }
13}
Config Types#
Now that we have covered what templates are, what they do, and how we
write configs according to them, we have run into an issue: how does
Terra know which template the config should use in the first place? For
top level objects, there isn’t a template It’s contained in to specify
the type, so what tells Terra to use ZooTemplate
instead of any
other template?
Choosing Templates#
This is where the type
parameter comes in handy. The type
parameter is a standardized way of specifying the template a Map
will follow, and is available for use in places where multiple templates
may be applicable.
Registries#
In order to tell Terra we want our config to use ZooTemplate
, we
must set the type
to something called a registry key. Registry
keys allow us to use things in a registry.
A registry can be thought of as an internal Map
that Terra uses to
store similar things. Because of this, we can also think of registry
keys as working the same way as Map
keys. Many registries exist in
Terra and all have different purposes; registry entries can be
created both by Terra (typically using addons), and by configuration
files, depending on use.
In this instance, we will be working with the config registry. The
config registry contains a bunch of templates like our ZooTemplate
,
which are registered internally by Terra. The purpose of the config
registry is to allow us to specify a config’s template using the
type
key.
Let’s assume that ZooTemplate
has been registered under the name /
registry key ZOO
by Terra. Now what we can do is simply set the
type
parameter to ZOO
in our config from above, signifying to
Terra that our config top level object will be of type ZooTemplate
:
1type: ZOO
2description: A zoo of Australian animals.
3animals:
4 koala:
5 color: grey
6 legs: 4
7 kangaroo:
8 color: brown
9 legs: 2
1{
2 "type": "ZOO",
3 "description": "A zoo of Australian animals.",
4 "animals": {
5 "koala": {
6 "color": "grey",
7 "legs": 4
8 },
9 "kangaroo": {
10 "color": "brown",
11 "legs": 2
12 }
13 }
14}
And with that, we have now designed a new config type that Terra is capable of interpreting.
Registering Configs#
Great! Now we are able to easily create new zoos just by making new
config files for each one, but we have run into another issue: how would
each zoo be kept track of? How would we be able to reference specific
zoos in other configs? Perhaps we could put all of our zoo configs
inside a single Map
and keep everything inside one config file, and
use the key name to refer to each individual zoo - but that could get
cumbersome. What if we wanted to make hundreds of zoos, how would we
keep them all organized?
To solve this issue, we can make use of registries. Let’s introduce a
new registry called the zoo registry. As a config developer, you
won’t need to worry about creating registries, as they are provided by
addons and the API, so we can assume the zoo registry is created by one
of our installed addons. New registry entries inside our new zoo
registry can be made by simply creating ZOO
configs, and we can let
Terra handle the registration of each config automatically.
To do this we will also need a way of choosing a registry key for each
zoo we want to make. We want control over the registry key, so what we
can do is introduce a new parameter in ZooTemplate
called id
.
What id
does is simply sets the registry key of configs when they
are automatically registered by Terra, allowing us to access all of our
ZOO
configs from anywhere via the zoo registry.
And with this, here is what our final config looks like:
1id: AUSTRALIAN_ZOO
2type: ZOO
3description: A zoo of Australian animals.
4animals:
5 koala:
6 color: grey
7 legs: 4
8 kangaroo:
9 color: brown
10 legs: 2
1{
2 "id": "AUSTRALIAN_ZOO",
3 "type": "ZOO",
4 "description": "A zoo of Australian animals.",
5 "animals": {
6 "koala": {
7 "color": "grey",
8 "legs": 4
9 },
10 "kangaroo": {
11 "color": "brown",
12 "legs": 2
13 }
14 }
15}
Now in any other config that requires a zoo, we can specify our
AUSTRALIAN_ZOO
and it will automatically be grabbed from the zoo
registry for use.