Skip to content

Config Manager

boxbeam edited this page Feb 13, 2024 · 24 revisions

Handling config is always a pain. It seems there's no way to do it in a way that doesn't feel annoying - at least not with the Spigot API alone. With RedLib, all your config problems will go away.

RedLib's ConfigManager is in the redempt.redlib.config package. Do not import anything from the redempt.redlib.configmanager package, as it is deprecated and has no interop with the new API.

To work with the config manager, you must first create a class which will be the "target" for config. There are two ways to do this: You can create a target object, or a target class. The only difference will be whether the fields are static, or belonging to an object. A target class may look like this:

public class ConfigExample {

	public static int num = 5;

}

And a similar target object would look like this:

@ConfigMappable
public class ConfigExample {

	public int num = 5;

}

When you invoke the config manager, your target will act as the base of all config data. Once you've created a target, you can begin using the config manager. Here is how it might be used in your onEnable:

@Override
public void onEnable() {
	ConfigManager config = ConfigManager.create(this).target(ConfigExample.class).saveDefaults().load();
}

For the rest of this tutorial, we will be using target classes rather than objects. If you want to use a target object, simply create an instance and pass it to the target method instead of the class.

Instantiating a ConfigManager with a plugin instance works on the default config file for the plugin, config.yml in its plugin folder. You can specify other config paths by passing a Path or File, or by passing in the plugin and name of the config file.

Once the ConfigManager is instantiated, calling target specifies the class or object to save from and load to. Calling saveDefaults() will save values that currently are not assigned in config, and load() will load all values from config into the target. You can also call save() to save all values, overwriting existing ones, or reload() to reload the config from disk and load all values again. With the example target above, we will end up with a config that looks like this:

num: 5

And 5 will be loaded into the num field.

You can also specify the path to the value in config manually by adding an annotation above the field:

@ConfigName("some-value")
public int num = 5;

Obviously, though, you often want to store much more complicated structures in config. You can easily store lists and maps, and add comments to config sections:

public class ConfigExample {

	@Comment("A list of all blocks to allow to be placed in mines")
	@Comment("Empty list will result in all blocks being allowed to be placed")
	public static List<Material> blocks = new ArrayList<>();
	@Comment("Map of block types to their weights to control how often they appear")
	public static Map<Material, Double> weights = new HashMap<>();

}

These will be stored in config like this:

# A list of all blocks to allow to be placed in mines
# Empty list will result in all blocks being allowed to be placed
blocks:
- STONE
- COBBLESTONE
- APPLE
...
# Map of block types to their weights to control how often they appear
weights:
  STONE: 1.0
  COBBLESTONE: 2.0
  APPLE: 0.5

You can create a collection or map of any type that the config manager knows how to handle. It can automatically handle all enum types using Enum#valueOf and Enum#toString for loading and saving respectively.

You can also have it serialize more complex objects in config. You might want to store a group which has an owner and members. You would start by registering the UUID type so it can be stored in config:

ConfigManager.create(this).addConverter(UUID.class, UUID::fromString, UUID::toString).target(ConfigExample.class).saveDefaults().load();

Calling addConverter tells the config manager how to handle the UUID type. We can now begin defining our Group class:

@ConfigMappable
public class Group {

	@ConfigPath
	private String name;
	private UUID owner;
	private Set<UUID> members;

	private Group() {}

	public Group(String name, UUID owner) {
		this.name = name;
		this.owner = owner;
	}

	@ConfigPostInit
	public void postInit() {
		System.out.println("Loaded from config!");
	}

}

Since this class has the @ConfigMappable annotation, it can be automatically deserialized from config, and all non-transient fields will be populated. It must have a constructor with no arguments, so that the ConfigManager can instantiate it. It will then populate all the fields, and if the class has a method annotated with @ConfigPostInit, it will be run. If you have a class which has subclasses that need to be saved in config, you must annotate it with @ConfigSubclassable. This will make all fields of that type store the exact type of the object, so the config manager knows how to convert back.

The @ConfigPath annotation will be populated with the last element of the path to the object in config. For example, if the path is groups.coolKids, then the name field will be populated with "coolKids". You can use any type which can be converted from a string for ConfigPath, so UUID or enum types would also be allowed here.

Record types can also be converted, and don't require the @ConfigMappable annotation:

public record Person (@ConfigPath String name, int age) {}

If you had a person named John who was 43 years old, it would look like this in config:

John:
  age: 43

And the fields would be populated accordingly. Now with Group defined, in your target class, you could do this:

public static Map<String, Group> groups = new HashMap<>();

This Map would be populated with groups in config, and each Group would also have its name from config populated.

For Kotlin users, the @ConfigConstructor annotation can allow you to use the config manager to construct data classes:

@ConfigMappable
data class Person @ConfigConstructor constructor(val name: String, val age: Int) {}

That's about all there is to know about the config manager, the rest of it is just mixing and matching parts to create the structure you want in config to manage your data. For more info, read the documentation at https://redempt.dev/javadoc/com/github/Redempt/RedLib/redempt/redlib/config/package-summary.html.

Clone this wiki locally