YAML - CryptoMorin/KingdomsX GitHub Wiki

This page describes more specific details of how configs work in general. Kingdoms uses YAML (Yet Another Markup Language) for its configs. The most used config format for Minecraft configs in general and the easiest to read in all config formats that exist in general as well.
Kingdoms configs aren't completely compliant with YAML standard specifications.

Removed YAML Features

  • Manual tags aren't supported. The plugin will automatically determine the tag in the background and assign it to each option.
  • A single file can only have one YAML document attached to it. Multiple documents --- aren't allowed in a single file.

What is a config entry and section?

Sometimes you'll see a text mentioning config entries in the wiki or the developers in general. This is the address that helps you find an option in a YAML file. Sometimes it's written like one.two.three and other times one -> two -> three.

In both cases, the words being separated are the option names. So for example, if an option is said to be at one -> two -> three it means you have to look for something similar to this:

one:
  two:
    three: 'im the option!'

However, in most cases, it's not that easy as the option is surrounded by multiple other options, so in a more complicated example, we can find our path in the following YAML file like this:

some-option: 3

one: # <--- first option, lets look inside here only
  where-is-zero: idk
  
  two: # <--- Found the second path
    three: 'IM HERE!' # <--- Third path

hello-world:
  a: bye

Note

Usually a good way to know how many levels a certain option has, is to look at the spaces before the option. Kingdoms uses two spaces for each level of config options, (but not all configs are like this in general, but 2 is a pretty standard number.)
So if we look at our previous example:

one:
␣␣where-is-zero: idk
 
␣␣two:
␣␣␣␣three: 'IM HERE!'

Each ␣ here represents a space. So for each two spaces (␣␣) that option has 1 parent section. i.e. the option three here has 2 parent sections.

Now let's see a real practical example that's complicated. Usually the language file is the most crowded config file to find an option's address. So if you're told that the message for changing /k help header is commands -> help -> header, first look for commands then help then header (the entire section is marked because there are two options for it)

Tip

The word section for configs refers to a config option that holds other config options:

hello: # <-- A config section named "hello" that holds two options named "one" and "two"
  one: 1
  two: 2

Advanced YAML

In this section we will take a look at the main configuration system used for the plugin.
This is by no means a complete tutorial for YAML, however it gives you practical examples and also features that are not present in standard YAML.

Variables

In YAML files you can define variables using two things called anchor and alias.
Variables are obviously used to write something just once without writing it over and over again. Image you have this option:

# Lets say this option is for a turret that gives you the ability to configure which entities it targets for each level.
# Most leveling systems in the config already inherit their values from previous levels, but just for the sake of example.
targets:
  1: [ZOMBIE, SKELETON, ENDERMAN, PLAYER, WITHER_SKELETON, COW, PIG]
  2: [ZOMBIE, SKELETON, ENDERMAN, PLAYER, WITHER_SKELETON, COW, PIG]
  3: [ZOMBIE, SKELETON, ENDERMAN, PLAYER, WITHER_SKELETON, COW, PIG]

# This list might even become a lot longer and every time you want to add something, you have to do it 3 times
# So instead we can do this:

common-entites: &common-entities [ZOMBIE, SKELETON, ENDERMAN, PLAYER, WITHER_SKELETON, COW, PIG]
targets:
  1: *common-entites
  2: *common-entites
  3: *common-entites

&common-entities is called an anchor, which defines a variable. The option name and the anchor name can be different, they have nothing to do with each other. That's just there because some people like to define their variables like this:

targets:
  1: &common-entities [ZOMBIE, SKELETON, ENDERMAN, PLAYER, WITHER_SKELETON, COW, PIG]
  2: *common-entites
  3: *common-entites

*common-entities is called an alias. You use this when you want to refer to a variable defined before, not when defining a new variable. Note that before accessing a variable it must be defined before using an alias, for example, this won't work:

name: *zombie-name
zombie-name: &zombie-name "My name is zombie"

This of course works with config sections:

common-settings: &common-settings
  enabled:
    condition: true
    glow: true
    name: "Enabled"
  else:
    name: "Disabled"

first-option: *common-settings
second-option: *common-settings

is transferred to:

first-option: 
  enabled:
    condition: true
    glow: true
    name: "Enabled"
  else:
    name: "Disabled"
second-option:
  enabled:
    condition: true
    glow: true
    name: "Enabled"
  else:
    name: "Disabled"

Sometimes you want to change certain options from a section alias, you can do what's called merging:

common-settings: &common-settings
  condition: true
  glow: true
  name: "Enabled"

first-option:
  enabled:
    <<: *common-settings
    name: "&2Open"

# ->

first-option:
  enabled:
    condition: true
    glow: true
    name: "&2Open"

This puts all the options from common-settings but uses name: "&2Open" instead of name: "Enabled"

This also works with lists but with a slightly different format:

list1: &list
 - 1
 - 2

list2:
 - 0
 - <: *list
 - 3

# Gives you
list2:
 - 0
 - 1
 - 2
 - 3

Extra Exclusive Features

Kingdoms configs aren't 100% YAML compliant. Features such as explicit tags, directives, explicit document start and ends, and a few others. 99% of the time, people don't even bother which these features when dealing with configuration files, so there's no issue.

Kingdoms adds support for sequence merging. We discussed mapping merging above, however the standard YAML doesn't allow you to do the following while Kingdoms does:

sequence: &seq [ how, are ]
other-sequence:
  - hello
  - <<: *seq
  - you

other-sequence gets converted into:

other-sequence:
  - hello
  - how
  - are
  - you

As for practical use cases, this would be useful for adding multiple lines to lore of items inside GUIs that share the same description, but are different in some parts. (Example in nexus.yml)

YAML Functions

Kingdoms also adds a special syntax called alias functions. It's a more advanced form of aliases which allows you to build your own options.
A great practical example can be found in the default misc-upgrades GUI. Again to be clear the name [fn-misc] isn't important at all, but this format is chosen to avoid conflict. However the name fn-misc is important since all yaml functions should start with fn- prefix. This option takes 3 arguments of any type which can later be used inside the returned value. It's kinda similar to how Haskell works. The only important note here is that list parameters can be used inside of lists here without needing to do the - <: *argument thing. They automatically expand the list (Note that this only works with list-like lores, not when using the | format lores).

You can see how the function is called here. Just like section merging but with a normal yaml list after it.

Another way of calling functions which is compliant with YAML spec, is writing them this way:

'[style]': &fn-style
  args: [ "{content}" ]
  return: ...

option:
  '[fn]': *fn-style
  '{content}': |
    Hello how are you doing?
    Is everything alright?

As you can see, this style of calling functions is cleaner when there are two many arguments as it supports named parameters and lets you use all YAML features for each parameter in a compliant way such as using string literals.
The plugin is mostly distancing itself from the first format and starting to use the second format more often, because the most important thing about them is being YAML compliant which helps with external services like Crowdin.

YAML Modules

Kingdoms adds a new system to config files called modules. This basically allows you to link two .yml files together which in turn allows you to use YAML anchors and aliases from different config files. It also allows you to "extend" other .yml files by copying all or some of their options.

Here's a basic relationship between a parent that child module:

# parent.yml
(module): # <-- Special option
  description: 'Just a description describing what this module does.'

  # A list of parameters with their values set to the type that you expect.
  # This is an advanced topic by itself, if you don't know, just use "any" instead.
  # The name doesn't necessarily need to be surrounded by "<>" you could use any
  # text you want for the parameter name.
  parameters:
    <material>: Material
    <amount>: int

# From this point below, we will define some normal options
# that use the parameters that we expect to be given to this .yml file.

item1: 
  name: "First item"
  material: '<material>'
  amount: '<amount>'

item2: 
  name: "Second item"
  material: '<material>'
  amount: '<amount>'
# child.yml
(import): # <-- Special option
  parent: # <-- Name of the file without ".yml" extension.
    parameters:
      # Same parameter names, but with actual values.
      <material>: RED_WOOL
      <amount>: 10

some-random-option: hi
item2: # Override and add some settings
  material: GREEN_WOOL
  flags: [ ALL ]
  lore: |
    Recommended item.
    Get it now!

child.yml final results after being automatically processed by the plugin (These results are not written to any file, it's kept temporarily inside the memory):

item1: 
  name: "First item"
  material: 'RED_WOOL'
  amount: 10

item2: 
  name: "Second item"
  material: 'GREEN_WOOL'
  amount: 10
  flags: [ ALL ]
  lore: |
    Recommended item.
    Get it now!

The (parameters) is a list of values that the parent module accepts from child modules. The parameter name can be literally anything and it will get replaced in all of the options in the child module.

Note

It's recommended to surround your parameter names with special characters like <parameter> or @parameter@ because if you simply use the word itself (e.g. material and amount) then even the English words that are not meant to be replaced as arguments will also get replaced, however you can use any text you want for the parameter name.

(module):
  parameters:
    "{F<@!SF$}%{#%{}":
      type: any
      default: "Default value used when this parameter isn't specified"

When is this useful? When you have many configs that you want to share the same settings. Such as turrets and structures! Of course you can also make a module a parent and a child at the same time. If a child module inherits from a parent module that also has a parent module, the chain of copying will start from the very first parent module on the top that itself doesn't have any parents. The options are copied one by one until it reaches the last child module.

    [Root Parent]         ← Copying starts here and goes down.
          β”‚
          β”‚
          ↓
 [Intermediate Parent]
          β”‚
          β”‚
          ↓
 [Intermediate Parent]
          β”‚
          β”‚
          ↓
         ...
          β”‚
          β”‚
          ↓
   [Terminal Child]

Final Nodes

As you can see in the previous examples, a config section in the parent module will still copy any missing options for the same config section in the child module. Even if the child module has defined and overriden some options.

# parent.yml
item2: 
  name: "Second item"
  material: RED_WOOL
  amount: 10
  flags: [ ALL ]
# child.yml
item2: 
  name: "Second item"
  material: GREEN_WOOL
  lore: |
    Recommended item.
    Get it now!

Result:

item2: 
  name: "Second item"
  material: GREEN_WOOL
  amount: 10
  flags: [ ALL ]
  lore: |
    Recommended item.
    Get it now!

What if you don't want the flags option to be copied? One way would be to manually set flags: [] in the child module, but what if there are many more options and you just don't want to worry about what gets copied or not? What if you just want your parents to leave your item2 section completely alone without touching it?

You use the [Final] YAML annotation.

# child.yml

# [Final]
item2: 
  name: "Second item"
  material: GREEN_WOOL
  lore: |
    Recommended item.
    Get it now!

Result:

item2: 
  name: "Second item"
  material: GREEN_WOOL
  lore: |
    Recommended item.
    Get it now!

As you can see, the result exactly matches what was defined in the original child.yml, nothing more and nothing less.

Importing anchors

To prevent options from being copied entirely, you can use the extend option. This is usually used when you want to import YAML anchors only.

# parent.yml

my-anchor: &cool-anchor "IM COOL!!!"
another-cool-anchor: &other-cool-anchor [ hi, how, are, you ]
# child.yml
(import):
  parent:
    extend: false # Don't copy any values from parent.yml
    anchors: [ &cool cool-anchor, &other-cool-anchor other-cool-anchor ]
    #          β””β”€β”¬β”€β”˜ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜
    #            β”‚        β”‚
    #            β”‚        └─ Name of the anchor in parent.yml to import
    #            └─ New name of the anchor in the current file (you could use the same name too)

YAML Modules in Kingdoms

Parent modules specifically, are located in declarations folder. You can only define parent modules here, but all configs will be able to import these modules, including normal configs, language files and GUI files. For GUIs however, it's recommended to use the guis/<language>/templates folder instead. Which is yaml modules dedicated to that specific language which can be used for all the GUIs. So declarations is for all configs, but templates is only for GUIs of that specific language only (however you should define the same templates for all languages obviously.)

In kingdoms by default, all buildings (turrets and structures) share certain values. The default hierarchy for their configs is as follows:

                 [building.yml]
                βŸ‹              ⟍
              βŸ‹                  ⟍
            βŸ‹                      ⟍
          βŸ‹                          ⟍
    [turret.yml]               [structure.yml]
         |                             |
        ...                           ...

As you can see, for example, Turrets/arrow.yml imports declarations/turret.yml (which in turn imports declarations/building.yml) and Structures/extractor.yml imports declarations/structure.yml (which in turn imports declarations/building.yml).

YAML Annotations

These are values inside a config option's comments themselves. They're like metadata that determine extra information about an option. Different annotations can have different effects. All annotations are a single text surrounded by square brackets.

# Just some random comment here!
# [Annotation]
# [SecondAnnotation]
option: Hello

Current standard annotations:

  • [Final] Used in YAML modules to prevent the parent from modifying a config section.
  • [IgnoreIfSet] Basically the same as [Final] annotation but inside the parent modules. It can be used in parent modules to say that "if the child config has this section, use that option instead without copying."
  • [NoSync] Used for GUI synchronization to prevent guis from touching an option (similar to [Final] annotation.)
⚠️ **GitHub.com Fallback** ⚠️