ConfigLib is an advanced, type-safe Configuration API designed to simplify configuration management for Bukkit and Forge developers.
- Type-Safety Configuration Handling
Ensures configuration values are used in a type-safe manner directly within your code, reducing potential runtime errors and improving maintainability. - Automatic JSON Mapping
Automatically maps configuration data to Java objects using JSON, eliminating the need for manual parsing and data transformation. - Automatic Configuration Reloading
Monitors configuration files and reloads them automatically when changes are detected, ensuring your application always works with the latest settings. - Automatic Configuration Saving
Automatically saves updated configuration values to disk, preventing data loss and ensuring persistence. - Command Generation for Configuration Management
Seamlessly integrates with CommandLib to generate commands for managing configurations via the command line.
To ensure stability, we recommend replacing latest.release with a specific version such as 0.16.0.
You can find the latest version on
the CommandLib Release Page
and ConfigLib Release Page.
Bukkit
plugins {
id "com.github.johnrengelman.shadow" version "6.1.0"
}
repositories {
maven { url 'https://jitpack.io' }
}
dependencies {
implementation "com.github.TeamKun.CommandLib:bukkit:latest.release"
implementation 'com.github.TeamKun.ConfigLib:bukkit:latest.release'
}
shadowJar {
archiveFileName = "${rootProject.name}-${project.version}.jar"
relocate "net.kunmc.lab.commandlib", "${project.group}.${project.name.toLowerCase()}.commandlib"
relocate "net.kunmc.lab.configlib", "${project.group}.${project.name.toLowerCase()}.configlib"
}
tasks.build.dependsOn tasks.shadowJarForge
plugins {
id "com.github.johnrengelman.shadow" version "6.1.0"
}
repositories {
maven { url 'https://jitpack.io' }
}
dependencies {
implementation "com.github.TeamKun.CommandLib:forge:latest.release"
implementation "com.github.TeamKun.ConfigLib:forge:latest.release"
}
shadowJar {
archiveFileName = "${rootProject.name}-${project.version}.jar"
dependencies {
include(dependency("com.github.TeamKun.CommandLib:forge:.*"))
include(dependency("com.github.TeamKun.ConfigLib:forge:.*"))
}
relocate "net.kunmc.lab.commandlib", "${project.group}.${project.name.toLowerCase()}.commandlib"
relocate "net.kunmc.lab.configlib", "${project.group}.${project.name.toLowerCase()}.configlib"
finalizedBy("reobfShadowJar")
}
reobf {
shadowJar {
}
}Defining Configuration Classes
public final class TestConfig extends BaseConfig {
public final IntegerValue integerValue = new IntegerValue(10);
public final StringValue stringValue = new StringValue("testValue");
public TestConfig(Plugin plugin) {
super(plugin);
initialize();
}
}Generating and Registering Configuration Commands
public final class TestPlugin extends JavaPlugin {
public void onEnable() {
TestConfig testConfig = new TestConfig(this);
Command root = new Command("test") {
};
// The following commands will be generated:
// /test config get <key> - Gets a specific configuration value.
// /test config list - Gets all configuration values.
// /test config modify <key> <value> - Sets a specific configuration value.
// /test config reload - Reloads the configuration file. You may not need it because there's automatic reloading.
root.addChildren(new ConfigCommandBuilder(testConfig).build());
CommandLib.register(this, root);
}
}Registering Multiple Configurations to Commands
public final class TestPlugin extends JavaPlugin {
public void onEnable() {
TestConfigA testConfigA = new TestConfigA(this);
TestConfigB testConfigB = new TestConfigB(this);
Command root = new Command("test") {
};
root.addChildren(new ConfigCommandBuilder(testConfigA).addConfig(testConfigB)
.build());
CommandLib.register(this, root);
}
}Listening to Configuration Changes
public final class TestConfig extends BaseConfig {
public final IntegerValue integerValue = new IntegerValue(10).onModify(x -> {
System.out.println("Changed integerValue to " + x);
});
public TestConfig(Plugin plugin) {
super(plugin);
initialize();
}
}Defining Custom Value Classes
In this section, we explain how to implement a custom SingleValue class and a custom ListValue class, based on the
following example class.
// Represents a custom data structure with an integer and a string.
// Used as a value in the configuration.
class TestClass {
private final int n;
private final String s;
public TestClass(int n, String s) {
this.n = n;
this.s = s;
}
@Override
public String toString() {
return "TestClass{" + "n=" + n + ", s='" + s + '\'' + '}';
}
}import java.util.List;
// Custom SingleValue implementation for TestClass.
// Allows storing and manipulating a single instance of TestClass in configurations.
public final class TestClassValue extends SingleValue<TestClass, TestClassValue> {
public TestClassValue(TestClass initialValue) {
super(initialValue);
}
@Override
protected List<ArgumentDefinition<TestClass>> argumentDefinitions() {
List<ArgumentDefinition<TestClass>> res = new ArrayList<>();
// Defines the arguments required to construct a TestClass instance.
res.add(new ArgumentDefinition(new IntegerArgument("n"), new StringArgument("s"), (n, s, ctx) -> {
// Converts command arguments to a TestClass instance.
return new TestClass(n, s);
}));
return res;
}
// Converts a TestClass instance to its string representation.
// This string will be used for command completion suggestions and for get/list command results.
@Override
protected String valueToString(TestClass testClass) {
return testClass.toString();
}
}// Custom ListValue implementation for TestClass.
// Allows managing a list of TestClass instances in configurations.
public final class TestClassListValue extends ListValue<TestClass, TestClassListValue> {
public TestClassListValue(List<TestClass> initialValue) {
super(initialValue);
}
@Override
protected List<ArgumentDefinition<List<TestClass>>> argumentDefinitionsForAdd() {
List<ArgumentDefinition<List<TestClass>>> res = new ArrayList<>();
// Defines the arguments required for the Add command.
// These arguments will be used to construct a new TestClass instance and add it to the list.
res.add(new ArgumentDefinition(new IntegerArgument("n"), new StringArgument("s"), (n, s, ctx) -> {
// Converts command arguments to a new TestClass instance and adds it to the list.
return Collections.singletonList(new TestClass(n, s));
}));
return res;
}
@Override
protected List<ArgumentDefinition<List<TestClass>>> argumentDefinitionsForRemove() {
List<ArgumentDefinition<List<TestClass>>> res = new ArrayList<>();
// Defines the arguments required for the Remove command.
// These arguments will be used to identify which TestClass instance to remove from the list.
res.add(new ArgumentDefinition(new StringArgument("target", opt -> {
opt.suggestionAction(sb -> {
for (TestClass v : value) {
sb.suggest(v.toString());
}
});
}, StringArgument.Type.PHRASE_QUOTED), (input, ctx) -> {
// Finds a TestClass instance to remove based on user input.
TestClass target = value.stream()
.filter(x -> x.toString()
.equals(input))
.findFirst()
.orElseThrow(() -> new InvalidArgumentException(input + " is invalid"));
return Collections.singletonList(target);
}));
return res;
}
// Converts a TestClass instance to its string representation.
// This string will be used for command completion suggestions and for get/list command results.
@Override
protected String elementToString(TestClass testClass) {
return testClass.toString();
}
}These custom Value classes can be used in the same way as built-in Value classes.
public final class TestConfig extends BaseConfig {
public final TestClassValue testClassValue = new TestClassValue(null);
public final TestClassListValue testClassListValue = new TestClassListValue(new ArrayList<>());
public TestConfig(Plugin plugin) {
super(plugin);
initialize();
}
}- Calling
initialize()initialize()must be called at the end of the concrete config class constructor, after all fields have been assigned. This ensures that the initial config load and file watching start only after the subclass is fully constructed.public final class TestConfig extends BaseConfig { public final IntegerValue integerValue = new IntegerValue(10); public TestConfig(Plugin plugin) { super(plugin); initialize(); // Must be the last statement } }
- Asynchronous Change Detection
Change detection, including when modifying values with the
setmethod, is handled asynchronously. Keep this in mind to avoid race conditions in your application logic.