diff --git a/commandapi-core/pom.xml b/commandapi-core/pom.xml index 25f7e2958..099f1a69a 100644 --- a/commandapi-core/pom.xml +++ b/commandapi-core/pom.xml @@ -65,12 +65,20 @@ ${project.version} provided + + org.jetbrains annotations 24.1.0 provided + + org.spongepowered + configurate-yaml + 4.1.2 + provided + diff --git a/commandapi-core/src/main/java/dev/jorel/commandapi/config/ConfigGenerator.java b/commandapi-core/src/main/java/dev/jorel/commandapi/config/ConfigGenerator.java new file mode 100644 index 000000000..53dd09ec9 --- /dev/null +++ b/commandapi-core/src/main/java/dev/jorel/commandapi/config/ConfigGenerator.java @@ -0,0 +1,79 @@ +package dev.jorel.commandapi.config; + +import org.jetbrains.annotations.ApiStatus; + +import java.util.Arrays; +import java.util.Map; +import java.util.Set; + +@ApiStatus.Internal +public class ConfigGenerator { + + private final DefaultedConfig defaultedConfig; + + private ConfigGenerator(DefaultedConfig defaultedConfig) { + this.defaultedConfig = defaultedConfig; + } + + public static ConfigGenerator createNew(DefaultedConfig defaultedConfig) { + return new ConfigGenerator(defaultedConfig); + } + + public void populateDefaultConfig(ConfigurationAdapter adapter) { + for (Map.Entry> commentedConfigOption : defaultedConfig.getAllOptions().entrySet()) { + adapter.setValue(commentedConfigOption.getKey(), commentedConfigOption.getValue().option()); + adapter.setComment(commentedConfigOption.getKey(), commentedConfigOption.getValue().comment().toArray(new String[0])); + } + } + + @SuppressWarnings("unchecked") + public ConfigurationAdapter generateWithNewValues(ConfigurationAdapter existingConfig) { + ConfigurationAdapter updatedConfig = existingConfig.createNew(); + + boolean shouldRemoveValues = shouldRemoveOptions(existingConfig); + + boolean wasConfigUpdated = false; + for (Map.Entry> commentedConfigOption : defaultedConfig.getAllOptions().entrySet()) { + String path = commentedConfigOption.getKey(); + + // Update config option + if (existingConfig.contains(path)) { + updatedConfig.tryCreateSection(path, (C) defaultedConfig); + updatedConfig.setValue(path, existingConfig.getValue(path)); + } else { + wasConfigUpdated = true; + updatedConfig.tryCreateSection(path, (C) defaultedConfig); + updatedConfig.setValue(path, commentedConfigOption.getValue().option()); + } + + // Update config option comment + String[] defaultComment = commentedConfigOption.getValue().comment().toArray(new String[0]); + String[] configComment = existingConfig.getComment(path); + + if (!Arrays.equals(defaultComment, configComment)) { + wasConfigUpdated = true; + } + + updatedConfig.setComment(path, commentedConfigOption.getValue().comment().toArray(new String[0])); + } + if (shouldRemoveValues) { + wasConfigUpdated = true; + } + return (wasConfigUpdated) ? updatedConfig : null; + } + + private boolean shouldRemoveOptions(ConfigurationAdapter config) { + Set configOptions = config.getKeys(); + Set defaultConfigOptions = defaultedConfig.getAllOptions().keySet(); + + boolean shouldRemoveOptions = false; + for (String option : configOptions) { + if (!defaultConfigOptions.contains(option)) { + shouldRemoveOptions = true; + break; + } + } + return shouldRemoveOptions; + } + +} diff --git a/commandapi-core/src/main/java/dev/jorel/commandapi/config/ConfigurationAdapter.java b/commandapi-core/src/main/java/dev/jorel/commandapi/config/ConfigurationAdapter.java new file mode 100644 index 000000000..1815a8d21 --- /dev/null +++ b/commandapi-core/src/main/java/dev/jorel/commandapi/config/ConfigurationAdapter.java @@ -0,0 +1,28 @@ +package dev.jorel.commandapi.config; + +import org.jetbrains.annotations.ApiStatus; + +import java.util.Set; + +@ApiStatus.Internal +public interface ConfigurationAdapter { + + void setValue(String key, Object value); + + void setComment(String key, String[] comment); + + Object getValue(String key); + + String[] getComment(String key); + + Set getKeys(); + + boolean contains(String key); + + void tryCreateSection(String key, DefaultConfiguration defaultConfiguration); + + Configuration config(); + + ConfigurationAdapter createNew(); + +} diff --git a/commandapi-core/src/main/java/dev/jorel/commandapi/config/DefaultedConfig.java b/commandapi-core/src/main/java/dev/jorel/commandapi/config/DefaultedConfig.java index a1c110523..c8cc709c1 100644 --- a/commandapi-core/src/main/java/dev/jorel/commandapi/config/DefaultedConfig.java +++ b/commandapi-core/src/main/java/dev/jorel/commandapi/config/DefaultedConfig.java @@ -10,8 +10,8 @@ @ApiStatus.Internal public abstract class DefaultedConfig { - final Map> allOptions = new LinkedHashMap<>(); - final Map allSections = new LinkedHashMap<>(); + protected final Map> allOptions = new LinkedHashMap<>(); + protected final Map allSections = new LinkedHashMap<>(); public static final CommentedConfigOption VERBOSE_OUTPUTS = new CommentedConfigOption<>( List.of( @@ -46,4 +46,19 @@ public abstract class DefaultedConfig { ), false ); + public static final CommentedSection SECTION_MESSAGE = new CommentedSection( + List.of( + "Messages", + "Controls messages that the CommandAPI displays to players" + ) + ); + + public final Map> getAllOptions() { + return allOptions; + } + + public final Map getAllSections() { + return allSections; + } + } diff --git a/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/config/BukkitConfigurationAdapter.java b/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/config/BukkitConfigurationAdapter.java new file mode 100644 index 000000000..888c087ec --- /dev/null +++ b/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/config/BukkitConfigurationAdapter.java @@ -0,0 +1,103 @@ +package dev.jorel.commandapi.config; + +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.YamlConfiguration; +import org.jetbrains.annotations.ApiStatus; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +@ApiStatus.Internal +public record BukkitConfigurationAdapter(YamlConfiguration config) implements ConfigurationAdapter { + + @Override + public void setValue(String key, Object value) { + config.set(key, value); + } + + @Override + public void setComment(String key, String[] comment) { + config.setComments(key, Arrays.asList(comment)); + } + + @Override + public Object getValue(String key) { + return config.get(key); + } + + @Override + public String[] getComment(String key) { + List comments = config.getStringList(key); + comments.removeIf(Objects::isNull); + return comments.toArray(new String[0]); + } + + @Override + public Set getKeys() { + return config.getKeys(false); + } + + @Override + public boolean contains(String key) { + return config.contains(key); + } + + @Override + public void tryCreateSection(String key, DefaultedBukkitConfig defaultedBukkitConfig) { + if (!key.contains(".")) { + return; + } + + // Collect config keys + Set keys = getKeys(); + keys.removeIf(k -> !config.isConfigurationSection(k)); + + // Collect sections + String[] paths = key.split("\\."); + List sectionCandidates = new ArrayList<>(Arrays.asList(paths).subList(0, paths.length - 1)); + + // Create new sections + ConfigurationSection root = null; + StringBuilder pathSoFar = new StringBuilder(); + for (String sectionCandidate : sectionCandidates) { + if (pathSoFar.isEmpty()) { + pathSoFar.append(sectionCandidate); + } else { + pathSoFar.append(".").append(sectionCandidate); + } + if (keys.contains(sectionCandidate) && root == null) { + root = config.getConfigurationSection(sectionCandidate); + } else if (root == null) { + root = config.createSection(sectionCandidate); + root.setComments(sectionCandidate, defaultedBukkitConfig.getAllSections().get(pathSoFar.toString()).comment()); + } else { + ConfigurationSection section = root.getConfigurationSection(sectionCandidate); + if (section == null) { + root = root.createSection(sectionCandidate); + root.setComments(sectionCandidate, defaultedBukkitConfig.getAllSections().get(pathSoFar.toString()).comment()); + } else { + root = section; + } + } + } + } + + @Override + public ConfigurationAdapter createNew() { + return new BukkitConfigurationAdapter(new YamlConfiguration()); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BukkitConfigurationAdapter that = (BukkitConfigurationAdapter) o; + String thisConfigString = config.saveToString(); + String thatConfigString = that.config.saveToString(); + return thisConfigString.equals(thatConfigString); + } + +} diff --git a/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/config/ConfigGenerator.java b/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/config/ConfigGenerator.java deleted file mode 100644 index 38e792575..000000000 --- a/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/config/ConfigGenerator.java +++ /dev/null @@ -1,138 +0,0 @@ -package dev.jorel.commandapi.config; - -import org.bukkit.configuration.InvalidConfigurationException; -import org.bukkit.configuration.file.YamlConfiguration; -import org.jetbrains.annotations.ApiStatus; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; - -@ApiStatus.Internal -public class ConfigGenerator { - - private final DefaultedBukkitConfig defaultedBukkitConfig; - - private ConfigGenerator() { - this.defaultedBukkitConfig = DefaultedBukkitConfig.createDefault(); - } - - private ConfigGenerator(DefaultedBukkitConfig defaultedBukkitConfig) { - this.defaultedBukkitConfig = defaultedBukkitConfig; - } - - public static ConfigGenerator createNew() { - return new ConfigGenerator(); - } - - public static ConfigGenerator createNew(DefaultedBukkitConfig defaultedBukkitConfig) { - return new ConfigGenerator(defaultedBukkitConfig); - } - - public YamlConfiguration generateDefaultConfig() throws InvalidConfigurationException { - YamlConfiguration config = new YamlConfiguration(); - Set sections = new HashSet<>(); - for (Map.Entry> commentedConfigOption : defaultedBukkitConfig.getAllOptions().entrySet()) { - String path = commentedConfigOption.getKey(); - - tryCreateSection(config, path, sections); - - config.set(path, commentedConfigOption.getValue().option()); - config.setComments(path, commentedConfigOption.getValue().comment()); - } - return process(config.saveToString()); - } - - public YamlConfiguration generateWithNewValues(YamlConfiguration existingConfig) throws InvalidConfigurationException { - YamlConfiguration config = new YamlConfiguration(); - - boolean shouldRemoveValues = shouldRemoveOptions(existingConfig); - - boolean wasConfigUpdated = false; - Set sections = new HashSet<>(); - for (Map.Entry> commentedConfigOption : defaultedBukkitConfig.getAllOptions().entrySet()) { - String path = commentedConfigOption.getKey(); - - // Update config option - if (existingConfig.contains(path)) { - tryCreateSection(config, path, sections); - config.set(path, existingConfig.get(path)); - } else { - wasConfigUpdated = true; - tryCreateSection(config, path, sections); - config.set(path, commentedConfigOption.getValue().option()); - } - - // Update config option comments - // Comments are kinda stupid, some elements are apparently null elements - // Also, both, YamlConfiguration#getComments(String) and CommentedConfigOption#comments() return unmodifiable list - // which by itself apparently aren't able to be checked for equality by the equals() method - // As a result, we wrap them in new ArrayLists first to be able to compare them - List existingComment = new ArrayList<>(existingConfig.getComments(path)); - existingComment.removeIf(Objects::isNull); - List defaultComment = new ArrayList<>(commentedConfigOption.getValue().comment()); - - if (!existingComment.equals(defaultComment)) { - wasConfigUpdated = true; - } - config.setComments(path, commentedConfigOption.getValue().comment()); - } - if (shouldRemoveValues) { - wasConfigUpdated = true; - } - return (wasConfigUpdated) ? process(config.saveToString()) : null; - } - - private YamlConfiguration process(String configAsString) throws InvalidConfigurationException { - String[] configStrings = configAsString.split("\n"); - StringBuilder configBuilder = new StringBuilder(); - for (String configString : configStrings) { - configBuilder.append(configString).append("\n"); - if (!configString.contains("#")) { - configBuilder.append("\n"); - } - } - YamlConfiguration config = new YamlConfiguration(); - config.loadFromString(configBuilder.toString()); - return config; - } - - private void tryCreateSection(YamlConfiguration config, String path, Set existingSections) { - if (path.contains(".")) { - // We have to create a section, or multiple if applicable, first, if it doesn't exist already - String[] sectionNames = path.split("\\."); - // The last value is the config option - for (int i = 0; i < sectionNames.length - 1; i++) { - String sectionName = sectionNames[i]; - if (!existingSections.contains(sectionName)) { - config.createSection(sectionName); - List comment = defaultedBukkitConfig.getAllSections().get(sectionName).comment(); - if (comment != null) { - config.setComments(sectionName, comment); - } - } - existingSections.add(sectionName); - } - } - } - - private boolean shouldRemoveOptions(YamlConfiguration config) { - Set configOptions = config.getKeys(true); - configOptions.removeIf(config::isConfigurationSection); - - Set defaultConfigOptions = defaultedBukkitConfig.getAllOptions().keySet(); - - boolean shouldRemoveOptions = false; - for (String option : configOptions) { - if (!defaultConfigOptions.contains(option)) { - shouldRemoveOptions = true; - break; - } - } - return shouldRemoveOptions; - } - -} diff --git a/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/config/DefaultedBukkitConfig.java b/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/config/DefaultedBukkitConfig.java index 847873168..2f21db861 100644 --- a/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/config/DefaultedBukkitConfig.java +++ b/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-core/src/main/java/dev/jorel/commandapi/config/DefaultedBukkitConfig.java @@ -78,32 +78,28 @@ public class DefaultedBukkitConfig extends DefaultedConfig { ), new ArrayList<>() ); - public static final CommentedSection SECTION_MESSAGE = new CommentedSection( - List.of( - "Messages", - "Controls messages that the CommandAPI displays to players" - ) - ); - - private DefaultedBukkitConfig() {} + private DefaultedBukkitConfig() { + } public static DefaultedBukkitConfig createDefault() { - DefaultedBukkitConfig config = new DefaultedBukkitConfig(); - config.allOptions.put("verbose-outputs", VERBOSE_OUTPUTS); - config.allOptions.put("silent-logs", SILENT_LOGS); - config.allOptions.put("messages.missing-executor-implementation", MISSING_EXECUTOR_IMPLEMENTATION); - config.allOptions.put("create-dispatcher-json", CREATE_DISPATCHER_JSON); - config.allOptions.put("use-latest-nms-version", USE_LATEST_NMS_VERSION); - config.allOptions.put("be-lenient-for-minor-versions", BE_LENIENT_FOR_MINOR_VERSIONS); - config.allOptions.put("hook-paper-reload", SHOULD_HOOK_PAPER_RELOAD); - config.allOptions.put("skip-initial-datapack-reload", SKIP_RELOAD_DATAPACKS); - config.allOptions.put("plugins-to-convert", PLUGINS_TO_CONVERT); - config.allOptions.put("other-commands-to-convert", OTHER_COMMANDS_TO_CONVERT); - config.allOptions.put("skip-sender-proxy", SKIP_SENDER_PROXY); - - config.allSections.put("messages", SECTION_MESSAGE); - - return config; + return DefaultedBukkitConfig.create( + Map.ofEntries( + Map.entry("verbose-outputs", VERBOSE_OUTPUTS), + Map.entry("silent-logs", SILENT_LOGS), + Map.entry("messages.missing-executor-implementation", MISSING_EXECUTOR_IMPLEMENTATION), + Map.entry("create-dispatcher-json", CREATE_DISPATCHER_JSON), + Map.entry("use-latest-nms-version", USE_LATEST_NMS_VERSION), + Map.entry("be-lenient-for-minor-versions", BE_LENIENT_FOR_MINOR_VERSIONS), + Map.entry("hook-paper-reload", SHOULD_HOOK_PAPER_RELOAD), + Map.entry("skip-initial-datapack-reload", SKIP_RELOAD_DATAPACKS), + Map.entry("plugins-to-convert", PLUGINS_TO_CONVERT), + Map.entry("other-commands-to-convert", OTHER_COMMANDS_TO_CONVERT), + Map.entry("skip-sender-proxy", SKIP_SENDER_PROXY) + ), + Map.ofEntries( + Map.entry("messages", SECTION_MESSAGE) + ) + ); } public static DefaultedBukkitConfig create(Map> options, Map sections) { @@ -114,12 +110,4 @@ public static DefaultedBukkitConfig create(Map> return config; } - - public Map> getAllOptions() { - return allOptions; - } - - public Map getAllSections() { - return allSections; - } } diff --git a/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-plugin-mojang-mapped/src/main/java/dev/jorel/commandapi/CommandAPIMain.java b/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-plugin-mojang-mapped/src/main/java/dev/jorel/commandapi/CommandAPIMain.java index 81d28e47b..066b49c25 100644 --- a/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-plugin-mojang-mapped/src/main/java/dev/jorel/commandapi/CommandAPIMain.java +++ b/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-plugin-mojang-mapped/src/main/java/dev/jorel/commandapi/CommandAPIMain.java @@ -20,15 +20,10 @@ *******************************************************************************/ package dev.jorel.commandapi; -import java.io.File; -import java.io.IOException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - +import dev.jorel.commandapi.config.BukkitConfigurationAdapter; import dev.jorel.commandapi.config.ConfigGenerator; +import dev.jorel.commandapi.config.ConfigurationAdapter; +import dev.jorel.commandapi.config.DefaultedBukkitConfig; import org.bukkit.Bukkit; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; @@ -36,6 +31,12 @@ import org.bukkit.plugin.Plugin; import org.bukkit.plugin.java.JavaPlugin; +import java.io.File; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + /** * Main CommandAPI plugin entrypoint */ @@ -148,12 +149,13 @@ public void onEnable() { @Override public void saveDefaultConfig() { File configFile = new File(getDataFolder(), "config.yml"); - ConfigGenerator configGenerator = ConfigGenerator.createNew(); + ConfigGenerator configGenerator = ConfigGenerator.createNew(DefaultedBukkitConfig.createDefault()); if (!getDataFolder().exists()) { getDataFolder().mkdir(); try { - YamlConfiguration defaultConfig = configGenerator.generateDefaultConfig(); - defaultConfig.save(configFile); + ConfigurationAdapter bukkitConfigurationAdapter = new BukkitConfigurationAdapter(new YamlConfiguration()); + configGenerator.populateDefaultConfig(bukkitConfigurationAdapter); + bukkitConfigurationAdapter.config().save(configFile); } catch (Exception e) { getLogger().severe("Could not create default config file! This is (probably) a bug."); getLogger().severe("Error message: " + e.getMessage()); @@ -165,13 +167,14 @@ public void saveDefaultConfig() { return; } // Update the config if necessary - YamlConfiguration existingConfig = YamlConfiguration.loadConfiguration(configFile); try { - YamlConfiguration updatedConfig = configGenerator.generateWithNewValues(existingConfig); + YamlConfiguration existingYamlConfig = YamlConfiguration.loadConfiguration(configFile); + ConfigurationAdapter existingConfig = new BukkitConfigurationAdapter(existingYamlConfig); + ConfigurationAdapter updatedConfig = configGenerator.generateWithNewValues(existingConfig); if (updatedConfig == null) { return; } - updatedConfig.save(configFile); + updatedConfig.config().save(configFile); } catch (Exception e) { getLogger().severe("Could not update config! This is (probably) a bug."); getLogger().severe("Error message: " + e.getMessage()); diff --git a/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-plugin/src/main/java/dev/jorel/commandapi/CommandAPIMain.java b/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-plugin/src/main/java/dev/jorel/commandapi/CommandAPIMain.java index 059828922..14be512bb 100644 --- a/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-plugin/src/main/java/dev/jorel/commandapi/CommandAPIMain.java +++ b/commandapi-platforms/commandapi-bukkit/commandapi-bukkit-plugin/src/main/java/dev/jorel/commandapi/CommandAPIMain.java @@ -27,7 +27,10 @@ import java.util.Map; import java.util.Map.Entry; +import dev.jorel.commandapi.config.BukkitConfigurationAdapter; import dev.jorel.commandapi.config.ConfigGenerator; +import dev.jorel.commandapi.config.ConfigurationAdapter; +import dev.jorel.commandapi.config.DefaultedBukkitConfig; import org.bukkit.Bukkit; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; @@ -147,12 +150,13 @@ public void onEnable() { @Override public void saveDefaultConfig() { File configFile = new File(getDataFolder(), "config.yml"); - ConfigGenerator configGenerator = ConfigGenerator.createNew(); + ConfigGenerator configGenerator = ConfigGenerator.createNew(DefaultedBukkitConfig.createDefault()); if (!getDataFolder().exists()) { getDataFolder().mkdir(); try { - YamlConfiguration defaultConfig = configGenerator.generateDefaultConfig(); - defaultConfig.save(configFile); + ConfigurationAdapter bukkitConfigurationAdapter = new BukkitConfigurationAdapter(new YamlConfiguration()); + configGenerator.populateDefaultConfig(bukkitConfigurationAdapter); + bukkitConfigurationAdapter.config().save(configFile); } catch (Exception e) { getLogger().severe("Could not create default config file! This is (probably) a bug."); getLogger().severe("Error message: " + e.getMessage()); @@ -164,13 +168,14 @@ public void saveDefaultConfig() { return; } // Update the config if necessary - YamlConfiguration existingConfig = YamlConfiguration.loadConfiguration(configFile); try { - YamlConfiguration updatedConfig = configGenerator.generateWithNewValues(existingConfig); + YamlConfiguration existingYamlConfig = YamlConfiguration.loadConfiguration(configFile); + ConfigurationAdapter existingConfig = new BukkitConfigurationAdapter(existingYamlConfig); + ConfigurationAdapter updatedConfig = configGenerator.generateWithNewValues(existingConfig); if (updatedConfig == null) { return; } - updatedConfig.save(configFile); + updatedConfig.config().save(configFile); } catch (Exception e) { getLogger().severe("Could not update config! This is (probably) a bug."); getLogger().severe("Error message: " + e.getMessage()); diff --git a/commandapi-platforms/commandapi-velocity/commandapi-velocity-plugin/src/main/java/dev/jorel/commandapi/CommandAPIMain.java b/commandapi-platforms/commandapi-velocity/commandapi-velocity-plugin/src/main/java/dev/jorel/commandapi/CommandAPIMain.java index 8b19495df..8f5c838f2 100644 --- a/commandapi-platforms/commandapi-velocity/commandapi-velocity-plugin/src/main/java/dev/jorel/commandapi/CommandAPIMain.java +++ b/commandapi-platforms/commandapi-velocity/commandapi-velocity-plugin/src/main/java/dev/jorel/commandapi/CommandAPIMain.java @@ -8,12 +8,17 @@ import com.velocitypowered.api.plugin.Plugin; import com.velocitypowered.api.plugin.annotation.DataDirectory; import com.velocitypowered.api.proxy.ProxyServer; +import dev.jorel.commandapi.config.ConfigGenerator; +import dev.jorel.commandapi.config.ConfigurationAdapter; +import dev.jorel.commandapi.config.DefaultedVelocityConfig; +import dev.jorel.commandapi.config.VelocityConfigurationAdapter; +import org.spongepowered.configurate.CommentedConfigurationNode; import org.spongepowered.configurate.ConfigurationNode; +import org.spongepowered.configurate.yaml.NodeStyle; import org.spongepowered.configurate.yaml.YamlConfigurationLoader; import java.io.File; import java.io.IOException; -import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; import java.util.logging.Logger; @@ -34,16 +39,35 @@ public class CommandAPIMain { public CommandAPIMain(ProxyServer server, Logger logger, @DataDirectory Path dataDirectory) { // Try to find the config file Path configFile = dataDirectory.resolve("config.yml"); + YamlConfigurationLoader loader = YamlConfigurationLoader.builder() + .nodeStyle(NodeStyle.BLOCK) + .path(configFile) + .build(); // If the config doesn't exist, load it from the resources - if(!Files.exists(configFile)) { + ConfigGenerator configGenerator = ConfigGenerator.createNew(DefaultedVelocityConfig.createDefault()); + if (!Files.exists(configFile)) { try { Files.createDirectories(configFile.getParent()); } catch (IOException ignored) { } - try (InputStream defaultConfig = getClass().getClassLoader().getResourceAsStream("config.yml")) { - Files.copy(defaultConfig, configFile); + try { + ConfigurationAdapter velocityConfigurationAdapter = new VelocityConfigurationAdapter(loader, loader.createNode()); + configGenerator.populateDefaultConfig(velocityConfigurationAdapter); + loader.save(velocityConfigurationAdapter.config()); + } catch (IOException e) { + throw new RuntimeException(e); + } + } else { + try { + // If the config does exist, update it if necessary + CommentedConfigurationNode existingYamlConfig = loader.load(); + ConfigurationAdapter existingConfig = new VelocityConfigurationAdapter(loader, existingYamlConfig); + ConfigurationAdapter updatedConfig = configGenerator.generateWithNewValues(existingConfig); + if (updatedConfig != null) { + loader.save(updatedConfig.config()); + } } catch (IOException e) { throw new RuntimeException(e); } @@ -52,7 +76,7 @@ public CommandAPIMain(ProxyServer server, Logger logger, @DataDirectory Path dat // Load the file as a yaml node ConfigurationNode configYAML; try { - configYAML = YamlConfigurationLoader.builder().path(configFile).build().load(); + configYAML = loader.load(); } catch (IOException e) { throw new RuntimeException(e); } @@ -74,13 +98,13 @@ public void onProxyInitialization(ProxyInitializeEvent event) { // Enable CommandAPI.onEnable(); } - + @Subscribe public void onProxyShutdown(ProxyShutdownEvent event) { // Shut down CommandAPI.onDisable(); } - + // On /velocity reload @Subscribe public void onProxyReload(ProxyReloadEvent event) { diff --git a/commandapi-platforms/commandapi-velocity/commandapi-velocity-plugin/src/main/java/dev/jorel/commandapi/config/DefaultedVelocityConfig.java b/commandapi-platforms/commandapi-velocity/commandapi-velocity-plugin/src/main/java/dev/jorel/commandapi/config/DefaultedVelocityConfig.java new file mode 100644 index 000000000..5027a5a2a --- /dev/null +++ b/commandapi-platforms/commandapi-velocity/commandapi-velocity-plugin/src/main/java/dev/jorel/commandapi/config/DefaultedVelocityConfig.java @@ -0,0 +1,36 @@ +package dev.jorel.commandapi.config; + +import org.jetbrains.annotations.ApiStatus; + +import java.util.Map; + +@SuppressWarnings("ClassEscapesDefinedScope") +@ApiStatus.Internal +public class DefaultedVelocityConfig extends DefaultedConfig { + + private DefaultedVelocityConfig() {} + + public static DefaultedVelocityConfig createDefault() { + return DefaultedVelocityConfig.create( + Map.ofEntries( + Map.entry("verbose-outputs", VERBOSE_OUTPUTS), + Map.entry("silent-logs", SILENT_LOGS), + Map.entry("messages.missing-executor-implementation", MISSING_EXECUTOR_IMPLEMENTATION), + Map.entry("create-dispatcher-json", CREATE_DISPATCHER_JSON) + ), + Map.ofEntries( + Map.entry("messages", SECTION_MESSAGE) + ) + ); + } + + public static DefaultedVelocityConfig create(Map> options, Map sections) { + DefaultedVelocityConfig config = new DefaultedVelocityConfig(); + + config.allOptions.putAll(options); + config.allSections.putAll(sections); + + return config; + } + +} diff --git a/commandapi-platforms/commandapi-velocity/commandapi-velocity-plugin/src/main/java/dev/jorel/commandapi/config/VelocityConfigurationAdapter.java b/commandapi-platforms/commandapi-velocity/commandapi-velocity-plugin/src/main/java/dev/jorel/commandapi/config/VelocityConfigurationAdapter.java new file mode 100644 index 000000000..240c81d50 --- /dev/null +++ b/commandapi-platforms/commandapi-velocity/commandapi-velocity-plugin/src/main/java/dev/jorel/commandapi/config/VelocityConfigurationAdapter.java @@ -0,0 +1,100 @@ +package dev.jorel.commandapi.config; + +import org.spongepowered.configurate.CommentedConfigurationNode; +import org.spongepowered.configurate.ConfigurationNode; +import org.spongepowered.configurate.serialize.SerializationException; +import org.spongepowered.configurate.yaml.YamlConfigurationLoader; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +@SuppressWarnings("ConfusingArgumentToVarargsMethod") +public record VelocityConfigurationAdapter(YamlConfigurationLoader loader, CommentedConfigurationNode config) implements ConfigurationAdapter { + + @Override + public void setValue(String key, Object value) { + setValue0(key, value); + } + + @Override + public void setComment(String key, String[] comment) { + config.node(key.split("\\.")).comment(String.join("\n", comment)); + } + + @Override + public Object getValue(String key) { + return getValue0(key); + } + + @SuppressWarnings("DataFlowIssue") + @Override + public String[] getComment(String key) { + return config.node(key.split("\\.")).comment().split("\n"); + } + + @Override + public Set getKeys() { + Set keys = new HashSet<>(); + for (Object key : config.childrenMap().keySet()) { + keys.add((String) key); + } + return keys; + } + + @Override + public boolean contains(String key) { + return !config.node(key.split("\\.")).virtual(); + } + + @Override + public void tryCreateSection(String key, DefaultedVelocityConfig defaultedVelocityConfig) { + if (!key.contains(".")) { + return; + } + String[] path = key.split("\\."); + List sectionCandidates = new ArrayList<>(Arrays.asList(path).subList(0, path.length - 1)); + + StringBuilder pathSoFar = new StringBuilder(); + for (String section : sectionCandidates) { + if (pathSoFar.isEmpty()) { + pathSoFar.append(section); + } else { + pathSoFar.append(".").append(section); + } + if (config.node(pathSoFar.toString().split("\\.")).comment() == null) { + config.node(pathSoFar.toString().split("\\.")).comment(String.join("\n", defaultedVelocityConfig.getAllSections().get(pathSoFar.toString()).comment())); + } + } + } + + @Override + public CommentedConfigurationNode config() { + return config; + } + + @Override + public ConfigurationAdapter createNew() { + return new VelocityConfigurationAdapter(loader, loader.createNode()); + } + + private void setValue0(String key, Object value) { + try { + config.node(key.split("\\.")).set(value); + } catch (SerializationException e) { + e.printStackTrace(System.err); + } + } + + private Object getValue0(String key) { + try { + return config.node(key.split("\\.")).get(Object.class); + } catch (SerializationException e) { + e.printStackTrace(System.err); + return null; + } + } + +}