diff --git a/src/main/java/land/chipmunk/chipmunkmod/ChipmunkMod.java b/src/main/java/land/chipmunk/chipmunkmod/ChipmunkMod.java index 7fdd54c..ba9568b 100644 --- a/src/main/java/land/chipmunk/chipmunkmod/ChipmunkMod.java +++ b/src/main/java/land/chipmunk/chipmunkmod/ChipmunkMod.java @@ -1,5 +1,7 @@ package land.chipmunk.chipmunkmod; +import land.chipmunk.chipmunkmod.config.ChipmunkModMigrations; +import land.chipmunk.chipmunkmod.config.Configuration; import land.chipmunk.chipmunkmod.modules.KaboomCheck; import land.chipmunk.chipmunkmod.modules.SelfCare; import land.chipmunk.chipmunkmod.util.configurate.ConfigurateUtilities; @@ -45,6 +47,7 @@ public class ChipmunkMod implements ModInitializer { } public static Configuration loadConfig() throws IOException { + final ChipmunkModMigrations migrations = new ChipmunkModMigrations(); final ObjectMapper.Factory customFactory = ObjectMapper.factoryBuilder() .defaultNamingScheme(NamingSchemes.CAMEL_CASE) .build(); @@ -61,14 +64,19 @@ public class ChipmunkMod implements ModInitializer { // Configurate will create parent directories for us, so we don't need to do it final BasicConfigurationNode node = loader.load(); - final boolean isBlank = node.empty(); + if (node.empty()) { // Config empty, fill it with defaults + final Configuration defaults = new Configuration(); + node.set(Configuration.class, defaults); + migrations.setLatest(node); + loader.save(node); - final Configuration config = node.get(Configuration.class); - if (isBlank) { - node.set(Configuration.class, config); + return defaults; + } + + if (migrations.migrate(node)) { // Migrated, write new config loader.save(node); } - return config; + return node.get(Configuration.class); } } diff --git a/src/main/java/land/chipmunk/chipmunkmod/config/ChipmunkModMigrations.java b/src/main/java/land/chipmunk/chipmunkmod/config/ChipmunkModMigrations.java new file mode 100644 index 0000000..44dbb50 --- /dev/null +++ b/src/main/java/land/chipmunk/chipmunkmod/config/ChipmunkModMigrations.java @@ -0,0 +1,12 @@ +package land.chipmunk.chipmunkmod.config; + +import land.chipmunk.chipmunkmod.config.migration.AbstractMigrationManager; +import land.chipmunk.chipmunkmod.config.migrations.MigrationV0; + +public final class ChipmunkModMigrations extends AbstractMigrationManager { + public ChipmunkModMigrations() { + super("version"); + + this.register(new MigrationV0()); // unversioned -> v0 + } +} diff --git a/src/main/java/land/chipmunk/chipmunkmod/Configuration.java b/src/main/java/land/chipmunk/chipmunkmod/config/Configuration.java similarity index 92% rename from src/main/java/land/chipmunk/chipmunkmod/Configuration.java rename to src/main/java/land/chipmunk/chipmunkmod/config/Configuration.java index 4f06a0a..aac1d4d 100644 --- a/src/main/java/land/chipmunk/chipmunkmod/Configuration.java +++ b/src/main/java/land/chipmunk/chipmunkmod/config/Configuration.java @@ -1,90 +1,89 @@ -package land.chipmunk.chipmunkmod; - -import net.kyori.adventure.text.Component; -import net.minecraft.util.math.BlockBox; -import net.minecraft.util.math.BlockPos; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.spongepowered.configurate.objectmapping.ConfigSerializable; - -@ConfigSerializable -public class Configuration { - public CommandManager commands = new CommandManager(); - public CommandCore core = new CommandCore(); - public Bots bots = new Bots(); - public CustomChat customChat = new CustomChat(); - public boolean fullbright = false; // unused, but it is here for old configs - public String autoSkinUsername = "off"; - - @ConfigSerializable - public static class CommandManager { - public String prefix = "."; - } - - @ConfigSerializable - public static class CommandCore { - public BlockBox relativeArea = BlockBox.create(new BlockPos(0, 0, 0), new BlockPos(15, 0, 15)); - } - - @ConfigSerializable - public static class Bots { - public BotInfo hbot = new BotInfo("#", null); - public BotInfo sbot = new BotInfo(":", null); - public BotInfo chipmunk = new BotInfo("'", null); - public ChomeNSBotInfo chomens = new ChomeNSBotInfo("*", null, null, null); - public BotInfo fnfboyfriend = new BotInfo("~", null); - public BotInfo nbot = new BotInfo("?", null); - public BotInfo kittycorp = new BotInfo("^", null); - public TestBotInfo testbot = new TestBotInfo("-", null); - } - - @ConfigSerializable - public static class ChomeNSBotInfo { - public String prefix; - public @Nullable String key; - public @Nullable String authKey; - public @Nullable String formatKey; - - public ChomeNSBotInfo() {} - public ChomeNSBotInfo (String prefix, @Nullable String key, @Nullable String authKey, @Nullable String formatKey) { - this.prefix = prefix; - this.key = key; - this.authKey = authKey; - this.formatKey = formatKey; - } - } - - @ConfigSerializable - public static class TestBotInfo { - public String prefix; - public @Nullable String webhookUrl; - - public TestBotInfo() {} - public TestBotInfo (String prefix, @Nullable String webhookUrl) { - this.prefix = prefix; - this.webhookUrl = webhookUrl; - } - } - - @ConfigSerializable - public static class BotInfo { - public String prefix; - public @Nullable String key; - - public BotInfo() {} - public BotInfo (String prefix, @Nullable String key) { - this.prefix = prefix; - this.key = key; - } - } - - @ConfigSerializable - public static class CustomChat { - public @NotNull Component format = - Component.translatable("chat.type.text", - Component.selector("UUID"), - Component.empty() - .append(Component.text("MESSAGE")) - ); - } -} +package land.chipmunk.chipmunkmod.config; + +import net.kyori.adventure.text.Component; +import net.minecraft.util.math.BlockBox; +import net.minecraft.util.math.BlockPos; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.configurate.objectmapping.ConfigSerializable; + +@ConfigSerializable +public class Configuration { + public CommandManager commands = new CommandManager(); + public CommandCore core = new CommandCore(); + public Bots bots = new Bots(); + public CustomChat customChat = new CustomChat(); + public String autoSkinUsername = "off"; + + @ConfigSerializable + public static class CommandManager { + public String prefix = "."; + } + + @ConfigSerializable + public static class CommandCore { + public BlockBox relativeArea = BlockBox.create(new BlockPos(0, 0, 0), new BlockPos(15, 0, 15)); + } + + @ConfigSerializable + public static class Bots { + public BotInfo hbot = new BotInfo("#", null); + public BotInfo sbot = new BotInfo(":", null); + public BotInfo chipmunk = new BotInfo("'", null); + public ChomeNSBotInfo chomens = new ChomeNSBotInfo("*", null, null, null); + public BotInfo fnfboyfriend = new BotInfo("~", null); + public BotInfo nbot = new BotInfo("?", null); + public BotInfo kittycorp = new BotInfo("^", null); + public TestBotInfo testbot = new TestBotInfo("-", null); + } + + @ConfigSerializable + public static class ChomeNSBotInfo { + public String prefix; + public @Nullable String key; + public @Nullable String authKey; + public @Nullable String formatKey; + + public ChomeNSBotInfo() {} + public ChomeNSBotInfo (String prefix, @Nullable String key, @Nullable String authKey, @Nullable String formatKey) { + this.prefix = prefix; + this.key = key; + this.authKey = authKey; + this.formatKey = formatKey; + } + } + + @ConfigSerializable + public static class TestBotInfo { + public String prefix; + public @Nullable String webhookUrl; + + public TestBotInfo() {} + public TestBotInfo (String prefix, @Nullable String webhookUrl) { + this.prefix = prefix; + this.webhookUrl = webhookUrl; + } + } + + @ConfigSerializable + public static class BotInfo { + public String prefix; + public @Nullable String key; + + public BotInfo() {} + public BotInfo (String prefix, @Nullable String key) { + this.prefix = prefix; + this.key = key; + } + } + + @ConfigSerializable + public static class CustomChat { + public @NotNull Component format = + Component.translatable("chat.type.text", + Component.selector("UUID"), + Component.empty() + .append(Component.text("MESSAGE")) + ); + } +} diff --git a/src/main/java/land/chipmunk/chipmunkmod/config/migration/AbstractMigrationManager.java b/src/main/java/land/chipmunk/chipmunkmod/config/migration/AbstractMigrationManager.java new file mode 100644 index 0000000..c5239a1 --- /dev/null +++ b/src/main/java/land/chipmunk/chipmunkmod/config/migration/AbstractMigrationManager.java @@ -0,0 +1,63 @@ +package land.chipmunk.chipmunkmod.config.migration; + +import land.chipmunk.chipmunkmod.ChipmunkMod; +import org.spongepowered.configurate.ConfigurateException; +import org.spongepowered.configurate.ConfigurationNode; +import org.spongepowered.configurate.transformation.ConfigurationTransformation; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + +// Makes it easier for forks to add their own config versioning structure +public abstract class AbstractMigrationManager { + private final List<ConfigMigration> migrations = new ArrayList<>(); + private final Object[] versionKey; + + public AbstractMigrationManager(final Object... versionKey) { + this.versionKey = versionKey; + } + + protected void register(final ConfigMigration migration) { + this.migrations.add(migration); + } + + protected void registerFrom(final int version, final Supplier<ConfigurationTransformation> supplier) { + this.migrations.add(ConfigMigration.from(version, supplier)); + } + + public ConfigurationTransformation.Versioned create() { + final ConfigurationTransformation.VersionedBuilder builder = ConfigurationTransformation.versionedBuilder() + .versionKey(versionKey); + + this.migrations.forEach(migration -> builder.addVersion(migration.version(), migration.create())); + + return builder.build(); + } + + public <N extends ConfigurationNode> void setLatest(final N config) throws ConfigurateException { + final ConfigurationTransformation.Versioned transforms = this.create(); + config.node(transforms.versionKey()).set(Integer.class, transforms.latestVersion()); + } + + public <N extends ConfigurationNode> boolean migrate(final N config) throws ConfigurateException { + final ConfigurationTransformation.Versioned transforms = this.create(); + final int version = transforms.version(config); + + if (version > transforms.latestVersion()) { + ChipmunkMod.LOGGER.warn("Latest config version is {}, but yours is {}! Are you from the future?", + transforms.latestVersion(), version); + return false; + } + + transforms.apply(config); + + final int newVersion = transforms.version(config); + if (version != newVersion) { + ChipmunkMod.LOGGER.info("Migrated config from version {} to {}!", version, newVersion); + return true; + } + + return false; + } +} diff --git a/src/main/java/land/chipmunk/chipmunkmod/config/migration/ConfigMigration.java b/src/main/java/land/chipmunk/chipmunkmod/config/migration/ConfigMigration.java new file mode 100644 index 0000000..54b384a --- /dev/null +++ b/src/main/java/land/chipmunk/chipmunkmod/config/migration/ConfigMigration.java @@ -0,0 +1,22 @@ +package land.chipmunk.chipmunkmod.config.migration; + +import org.spongepowered.configurate.transformation.ConfigurationTransformation; + +import java.util.function.Supplier; + +public interface ConfigMigration { + int version(); + + ConfigurationTransformation create(); + + static ConfigMigration from(final int version, final Supplier<ConfigurationTransformation> supplier) { + return new ConfigMigrationImpl(version, supplier); + } + + record ConfigMigrationImpl(int version, Supplier<ConfigurationTransformation> supplier) implements ConfigMigration { + @Override + public ConfigurationTransformation create() { + return this.supplier.get(); + } + } +} diff --git a/src/main/java/land/chipmunk/chipmunkmod/config/migrations/MigrationV0.java b/src/main/java/land/chipmunk/chipmunkmod/config/migrations/MigrationV0.java new file mode 100644 index 0000000..b2a3cda --- /dev/null +++ b/src/main/java/land/chipmunk/chipmunkmod/config/migrations/MigrationV0.java @@ -0,0 +1,21 @@ +package land.chipmunk.chipmunkmod.config.migrations; + +import land.chipmunk.chipmunkmod.config.migration.ConfigMigration; +import org.spongepowered.configurate.transformation.ConfigurationTransformation; +import org.spongepowered.configurate.transformation.TransformAction; + +import static org.spongepowered.configurate.NodePath.path; + +public final class MigrationV0 implements ConfigMigration { + @Override + public int version() { + return 0; + } + + @Override + public ConfigurationTransformation create() { + return ConfigurationTransformation.builder() + .addAction(path("fullbright"), TransformAction.remove()) + .build(); + } +} diff --git a/src/main/java/land/chipmunk/chipmunkmod/util/BotValidationUtilities.java b/src/main/java/land/chipmunk/chipmunkmod/util/BotValidationUtilities.java index 9998051..940b549 100644 --- a/src/main/java/land/chipmunk/chipmunkmod/util/BotValidationUtilities.java +++ b/src/main/java/land/chipmunk/chipmunkmod/util/BotValidationUtilities.java @@ -2,8 +2,7 @@ package land.chipmunk.chipmunkmod.util; import com.mojang.brigadier.Command; import land.chipmunk.chipmunkmod.ChipmunkMod; -import land.chipmunk.chipmunkmod.Configuration; -import land.chipmunk.chipmunkmod.commands.SayCommand; +import land.chipmunk.chipmunkmod.config.Configuration; import land.chipmunk.chipmunkmod.modules.Chat; import land.chipmunk.chipmunkmod.modules.CustomChat; import net.minecraft.client.MinecraftClient;