diff --git a/src/main/java/com/triassic/geyserdebuginfo/GeyserDebugInfo.java b/src/main/java/com/triassic/geyserdebuginfo/GeyserDebugInfo.java index f6c7ad8..3da3251 100644 --- a/src/main/java/com/triassic/geyserdebuginfo/GeyserDebugInfo.java +++ b/src/main/java/com/triassic/geyserdebuginfo/GeyserDebugInfo.java @@ -2,8 +2,8 @@ import com.triassic.geyserdebuginfo.command.commands.ReloadCommand; import com.triassic.geyserdebuginfo.command.commands.ToggleCommand; -import com.triassic.geyserdebuginfo.configuration.ConfigurationLoader; import com.triassic.geyserdebuginfo.configuration.Configuration; +import com.triassic.geyserdebuginfo.configuration.ConfigurationContainer; import com.triassic.geyserdebuginfo.listener.PlayerJoinListener; import com.triassic.geyserdebuginfo.manager.BossBarManager; import com.triassic.geyserdebuginfo.manager.PlaceholderManager; @@ -18,14 +18,19 @@ import org.geysermc.geyser.api.event.lifecycle.GeyserPreInitializeEvent; import org.geysermc.geyser.api.event.lifecycle.GeyserShutdownEvent; import org.geysermc.geyser.api.extension.Extension; +import org.geysermc.geyser.api.extension.ExtensionLogger; -import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.stream.Stream; public class GeyserDebugInfo implements Extension { - private File dataFolder; - + @Getter + private Path dataFolder; + @Getter + private ExtensionLogger logger; @Getter private Configuration config; @Getter @@ -35,23 +40,34 @@ public class GeyserDebugInfo implements Extension { @Getter private PlaceholderManager placeholderManager; - /** - * Initializes the extension. - * Sets up the data folder, loads configuration, - * and registers event listeners. - */ + private ConfigurationContainer configContainer; + @Subscribe - public void onPostInitialize(GeyserPreInitializeEvent event) { + public void onPreInitialize(GeyserPreInitializeEvent event) { long startTime = System.currentTimeMillis(); - this.dataFolder = this.dataFolder().toFile(); - if (!dataFolder.exists() && !dataFolder.mkdirs()) - logger().error("Failed to create data folder " + dataFolder.getAbsolutePath()); + this.logger = logger(); + this.dataFolder = dataFolder(); + + try { + if (Files.notExists(dataFolder)) + Files.createDirectories(dataFolder); + } catch (IOException e) { + logger.error("Failed to create data folder " + dataFolder.toAbsolutePath(), e); + extensionManager().disable(this); + return; + } + + this.configContainer = new ConfigurationContainer(dataFolder, logger, Configuration.class); + this.config = configContainer.get(); - this.config = new ConfigurationLoader(this) - .load(Configuration.class); + if (config == null) { + logger.error("Failed to load the configuration. Please check config.yml for issues."); + extensionManager().disable(this); + return; + } - this.playerDataManager = new PlayerDataManager(dataFolder, this.logger(), false); + this.playerDataManager = new PlayerDataManager(this.dataFolder().toFile(), logger(), false); this.placeholderManager = new PlaceholderManager(); this.bossBarManager = new BossBarManager(this); this.eventBus().register(new PlayerJoinListener(this)); @@ -66,15 +82,7 @@ public void onPostInitialize(GeyserPreInitializeEvent event) { new TextModifierProvider() ).forEach(placeholderManager::registerProvider); - logger().info("Enabled in " + (System.currentTimeMillis() - startTime) + "ms"); - } - - /** - * Loads the configuration file from the specified data folder. - * This method attempts to load the configuration file and logs an error if the process fails. - */ - private void loadConfig() { - + logger.info("Enabled in " + (System.currentTimeMillis() - startTime) + "ms"); } /** @@ -98,10 +106,11 @@ public void onShutdown(GeyserShutdownEvent event) { playerDataManager.savePlayerData(); } - /** - * Reloads the configuration settings. - */ - public synchronized void reload() { - // TODO: add reloading for configuration. + public boolean reloadConfig() { + boolean reloaded = configContainer.reload(); + if (reloaded) + this.config = configContainer.get(); + + return reloaded; } } \ No newline at end of file diff --git a/src/main/java/com/triassic/geyserdebuginfo/command/commands/ReloadCommand.java b/src/main/java/com/triassic/geyserdebuginfo/command/commands/ReloadCommand.java index 69b6e1f..34c0284 100644 --- a/src/main/java/com/triassic/geyserdebuginfo/command/commands/ReloadCommand.java +++ b/src/main/java/com/triassic/geyserdebuginfo/command/commands/ReloadCommand.java @@ -1,9 +1,9 @@ package com.triassic.geyserdebuginfo.command.commands; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.geyser.api.command.CommandSource; import com.triassic.geyserdebuginfo.GeyserDebugInfo; import com.triassic.geyserdebuginfo.command.Command; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.api.command.CommandSource; public class ReloadCommand implements Command { @@ -29,7 +29,11 @@ public org.geysermc.geyser.api.command.Command createCommand() { } private void execute(@NonNull CommandSource commandSource, org.geysermc.geyser.api.command.Command command, @NonNull String[] strings) { - instance.reload(); - commandSource.sendMessage("§aGeyserDebugInfo configuration has been reloaded."); + if (instance.reloadConfig()) { + commandSource.sendMessage("§aGeyserDebugInfo configuration has been reloaded."); + System.out.println(instance.getConfig().getConfigVersion()); + } else { + commandSource.sendMessage("§cFailed to reload GeyserDebugInfo configuration, check console for details."); + } } } diff --git a/src/main/java/com/triassic/geyserdebuginfo/command/commands/ToggleCommand.java b/src/main/java/com/triassic/geyserdebuginfo/command/commands/ToggleCommand.java index dce54ef..3be499c 100644 --- a/src/main/java/com/triassic/geyserdebuginfo/command/commands/ToggleCommand.java +++ b/src/main/java/com/triassic/geyserdebuginfo/command/commands/ToggleCommand.java @@ -1,13 +1,13 @@ package com.triassic.geyserdebuginfo.command.commands; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.geyser.api.command.CommandSource; -import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; -import org.geysermc.geyser.session.GeyserSession; import com.triassic.geyserdebuginfo.GeyserDebugInfo; import com.triassic.geyserdebuginfo.command.Command; import com.triassic.geyserdebuginfo.manager.BossBarManager; import com.triassic.geyserdebuginfo.manager.PlayerDataManager; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.api.command.CommandSource; +import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; +import org.geysermc.geyser.session.GeyserSession; public class ToggleCommand implements Command { diff --git a/src/main/java/com/triassic/geyserdebuginfo/configuration/Configuration.java b/src/main/java/com/triassic/geyserdebuginfo/configuration/Configuration.java index 27b10ef..ea00e5f 100644 --- a/src/main/java/com/triassic/geyserdebuginfo/configuration/Configuration.java +++ b/src/main/java/com/triassic/geyserdebuginfo/configuration/Configuration.java @@ -2,8 +2,9 @@ import lombok.Getter; import org.spongepowered.configurate.objectmapping.ConfigSerializable; +import org.spongepowered.configurate.objectmapping.meta.Comment; +import org.spongepowered.configurate.objectmapping.meta.Setting; -import java.util.Arrays; import java.util.List; @Getter @@ -11,15 +12,62 @@ @SuppressWarnings("FieldMayBeFinal") public class Configuration { - private long refreshInterval = 50; - private List displayFormat = Arrays.asList( - "Geyser Debug Information", - "", - "%player_world%", - "", - "XYZ: %player_x% / %player_y% / %player_z%", - "Block: %player_x:floor% %player_y:floor% %player_z:floor% [%player_relative_x% %player_relative_y% %player_relative_z%]", - "Chunk: %player_chunk_x% %player_chunk_y% %player_chunk_z% [%player_global_x% %player_global_z% in %player_region_file%]", - "Facing: %player_facing% (%player_yaw% / %player_pitch%)" - ); + @Setting("display") + private Display display = new Display(); + + @Setting("config-version") + @Comment("Used internally, do not change.") + private int configVersion = 1; + + @Getter + @ConfigSerializable + public static class Display { + + @Setting("actionbar") + private ActionBar actionBar = new ActionBar(); + + @Setting("bossbar") + private BossBar bossBar = new BossBar(); + + @Getter + @ConfigSerializable + public static class ActionBar { + @Setting("enabled") + private boolean enabled = true; + + @Setting("visible-by-default") + private boolean visibleByDefault = false; + + @Setting("refresh-interval") + private int refreshInterval = 50; + + @Setting("text") + private String text = "%player_x% %player_y% %player_z%"; + } + + @Getter + @ConfigSerializable + public static class BossBar { + @Setting("enabled") + private boolean enabled = true; + + @Setting("visible-by-default") + private boolean visibleByDefault = false; + + @Setting("refresh-interval") + private int refreshInterval = 50; + + @Setting("text") + private List text = List.of( + "Geyser Debug Information", + "", + "%player_world%", + "", + "XYZ: %player_x% / %player_y% / %player_z%", + "Block: %player_x:floor% %player_y:floor% %player_z:floor% [%player_relative_x% %player_relative_y% %player_relative_z%]", + "Chunk: %player_chunk_x% %player_chunk_y% %player_chunk_z% [%player_global_x% %player_global_z% in %player_region_file%]", + "Facing: %player_facing% (%player_yaw% / %player_pitch%)" + ); + } + } } diff --git a/src/main/java/com/triassic/geyserdebuginfo/configuration/ConfigurationContainer.java b/src/main/java/com/triassic/geyserdebuginfo/configuration/ConfigurationContainer.java new file mode 100644 index 0000000..7da81ba --- /dev/null +++ b/src/main/java/com/triassic/geyserdebuginfo/configuration/ConfigurationContainer.java @@ -0,0 +1,98 @@ +package com.triassic.geyserdebuginfo.configuration; + +import org.geysermc.geyser.api.extension.ExtensionLogger; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.configurate.CommentedConfigurationNode; +import org.spongepowered.configurate.yaml.NodeStyle; +import org.spongepowered.configurate.yaml.YamlConfigurationLoader; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.concurrent.atomic.AtomicReference; + +public class ConfigurationContainer { + + private static final String HEADER = """ + GeyserDebugInfo Configuration File + A Geyser extension that strives to provide F3-like debug information for Bedrock Edition players. + + Report any issues on our GitHub repository: + https://github.com/RealTriassic/GeyserDebugInfo"""; + + private final Path configFile; + private final ExtensionLogger logger; + private final YamlConfigurationLoader loader; + private final Class configClass; + private final AtomicReference config = new AtomicReference<>(); + + public ConfigurationContainer( + final Path dataFolder, + final ExtensionLogger logger, + final Class configClass) { + this.logger = logger; + this.configClass = configClass; + this.configFile = dataFolder.resolve("config.yml"); + + this.loader = YamlConfigurationLoader.builder() + .indent(2) + .path(configFile) + .nodeStyle(NodeStyle.BLOCK) + .defaultOptions(options -> options + .shouldCopyDefaults(true) + .header(HEADER)) + .build(); + + this.load(); + } + + /** + * Loads the configuration from the file. + * If loading fails, the previous configuration is retained. + * + * @return true if the configuration was loaded successfully, false otherwise. + */ + private boolean load() { + try { + final Configuration loadedConfig = loadConfig(); + config.set(loadedConfig); + return true; + } catch (Throwable e) { + logger.error("Failed to load configuration", e); + return false; + } + } + + /** + * Loads the configuration from the file and creates a new Configuration object. + * + * @return the loaded Configuration object. + * @throws IOException if an error occurs while reading or parsing the file. + */ + private Configuration loadConfig() throws IOException { + CommentedConfigurationNode node = loader.load(); + Configuration loadedConfig = node.get(configClass); + + if (Files.notExists(configFile)) { + node.set(configClass, loadedConfig); + loader.save(node); + } + + return loadedConfig; + } + + /** + * Reloads the configuration from the file. + * If reloading fails, the previous configuration is retained. + * + * @return true if the configuration was reloaded successfully, false otherwise. + */ + public boolean reload() { + return load(); + } + + @Nullable + public Configuration get() { + return config.get(); + } +} \ No newline at end of file diff --git a/src/main/java/com/triassic/geyserdebuginfo/configuration/ConfigurationLoader.java b/src/main/java/com/triassic/geyserdebuginfo/configuration/ConfigurationLoader.java deleted file mode 100644 index 9ffd7a4..0000000 --- a/src/main/java/com/triassic/geyserdebuginfo/configuration/ConfigurationLoader.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.triassic.geyserdebuginfo.configuration; - -import com.triassic.geyserdebuginfo.GeyserDebugInfo; -import org.geysermc.geyser.api.extension.ExtensionLogger; -import org.jetbrains.annotations.Nullable; -import org.spongepowered.configurate.CommentedConfigurationNode; -import org.spongepowered.configurate.yaml.NodeStyle; -import org.spongepowered.configurate.yaml.YamlConfigurationLoader; - -import java.io.File; -import java.io.IOException; - -public class ConfigurationLoader { - - private static final String HEADER = """ - GeyserDebugInfo Configuration File - A Geyser extension that strives to provide F3-like debug information for Bedrock Edition players. - - Report any issues on our GitHub repository: - https://github.com/RealTriassic/GeyserDebugInfo"""; - - private final File configFile; - private final ExtensionLogger logger; - - public ConfigurationLoader( - GeyserDebugInfo instance - ) { - this.configFile = new File(instance.dataFolder().toFile(), "config.yml"); - this.logger = instance.logger(); - } - - @Nullable - public T load(Class configClass) { - try { - return load0(configClass); - } catch (IOException e) { - logger.error("Failed to load configuration", e); - return null; - } - } - - private T load0(Class configClass) throws IOException { - final YamlConfigurationLoader loader = YamlConfigurationLoader.builder() - .file(configFile) - .indent(2) - .nodeStyle(NodeStyle.BLOCK) - .defaultOptions(options -> options.shouldCopyDefaults(true) - .header(HEADER)) - .build(); - - final CommentedConfigurationNode node = loader.load(); - T config = node.get(configClass); - - if (!configFile.exists()) { - node.set(configClass, config); - loader.save(node); - } - - return config; - } -} \ No newline at end of file diff --git a/src/main/java/com/triassic/geyserdebuginfo/listener/PlayerJoinListener.java b/src/main/java/com/triassic/geyserdebuginfo/listener/PlayerJoinListener.java index c73446f..c343b75 100644 --- a/src/main/java/com/triassic/geyserdebuginfo/listener/PlayerJoinListener.java +++ b/src/main/java/com/triassic/geyserdebuginfo/listener/PlayerJoinListener.java @@ -1,13 +1,13 @@ package com.triassic.geyserdebuginfo.listener; +import com.triassic.geyserdebuginfo.GeyserDebugInfo; +import com.triassic.geyserdebuginfo.manager.BossBarManager; +import com.triassic.geyserdebuginfo.manager.PlayerDataManager; import org.geysermc.event.subscribe.Subscribe; import org.geysermc.geyser.api.event.bedrock.SessionDisconnectEvent; import org.geysermc.geyser.api.event.bedrock.SessionJoinEvent; import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; import org.geysermc.geyser.session.GeyserSession; -import com.triassic.geyserdebuginfo.GeyserDebugInfo; -import com.triassic.geyserdebuginfo.manager.BossBarManager; -import com.triassic.geyserdebuginfo.manager.PlayerDataManager; /** * This class is responsible for listening for player connection events. diff --git a/src/main/java/com/triassic/geyserdebuginfo/manager/BossBarManager.java b/src/main/java/com/triassic/geyserdebuginfo/manager/BossBarManager.java index b9360a6..0ec566f 100644 --- a/src/main/java/com/triassic/geyserdebuginfo/manager/BossBarManager.java +++ b/src/main/java/com/triassic/geyserdebuginfo/manager/BossBarManager.java @@ -1,14 +1,12 @@ package com.triassic.geyserdebuginfo.manager; -import com.triassic.geyserdebuginfo.configuration.Configuration; +import com.triassic.geyserdebuginfo.GeyserDebugInfo; import net.kyori.adventure.text.Component; import org.geysermc.geyser.entity.type.player.SessionPlayerEntity; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.session.cache.BossBar; import org.jetbrains.annotations.NotNull; -import com.triassic.geyserdebuginfo.GeyserDebugInfo; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -22,7 +20,7 @@ */ public class BossBarManager { - private final Configuration config; + private final GeyserDebugInfo instance; private final PlaceholderManager placeholderManager; private final PlayerDataManager playerDataManager; private final HashMap bossBars; @@ -31,13 +29,13 @@ public class BossBarManager { public BossBarManager( final GeyserDebugInfo instance ) { - this.config = instance.getConfig(); + this.instance = instance; this.playerDataManager = instance.getPlayerDataManager(); this.placeholderManager = instance.getPlaceholderManager(); this.bossBars = new HashMap<>(); this.executor = Executors.newSingleThreadScheduledExecutor(); - executor.scheduleAtFixedRate(this::updateAllBossBars, 0, config.getRefreshInterval(), TimeUnit.MILLISECONDS); + executor.scheduleAtFixedRate(this::updateAllBossBars, 0, instance.getConfig().getDisplay().getBossBar().getRefreshInterval(), TimeUnit.MILLISECONDS); } /** @@ -105,7 +103,7 @@ private void updateBossBar(final @NotNull SessionPlayerEntity player) { BossBar bossBar = bossBars.get(player); if (bossBar != null) { - List displayFormat = config.getDisplayFormat(); + List displayFormat = instance.getConfig().getDisplay().getBossBar().getText(); String displayText = displayFormat.stream() .map(line -> placeholderManager.setPlaceholders(player.getSession(), line)) diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml deleted file mode 100644 index dd1b6ad..0000000 --- a/src/main/resources/config.yml +++ /dev/null @@ -1,25 +0,0 @@ -# GeyserDebugInfo Configuration File -# A Geyser extension that strives to provide F3-like debug information for Bedrock Edition players. - -# Report any issues on our GitHub repository: -# https://github.com/RealTriassic/GeyserDebugInfo - -display: - actionbar: - enabled: true - refresh-interval: 50 - text: "%player_x% %player_y% %player_z%" - bossbar: - enabled: true - refresh-interval: 50 - text: - - "Geyser Debug Information" - - "" - - "%player_world%" - - "" - - "XYZ: %player_x% / %player_y% / %player_z%" - - "Block: %player_x:floor% %player_y:floor% %player_z:floor% [%player_relative_x% %player_relative_y% %player_relative_z%]" - - "Chunk: %player_chunk_x% %player_chunk_y% %player_chunk_z% [%player_global_x% %player_global_z% in %player_region_file%]" - - "Facing: %player_facing% (%player_yaw% / %player_pitch%)" - -config-version: 1 \ No newline at end of file