diff --git a/src/main/java/com/github/kd_gaming1/packcore/command/ConfigManagerCommand.java b/src/main/java/com/github/kd_gaming1/packcore/command/ConfigManagerCommand.java new file mode 100644 index 0000000..aaef3a8 --- /dev/null +++ b/src/main/java/com/github/kd_gaming1/packcore/command/ConfigManagerCommand.java @@ -0,0 +1,42 @@ +package com.github.kd_gaming1.packcore.command; + +import com.github.kd_gaming1.packcore.PackCore; +import com.github.kd_gaming1.packcore.ui.screen.configmanager.ConfigManagerScreen; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import net.minecraft.client.MinecraftClient; +import net.minecraft.text.Text; + +public class ConfigManagerCommand { + + public static LiteralArgumentBuilder register() { + return ClientCommandManager.literal("configmanager").executes(ConfigManagerCommand::execute); + } + + private static int execute(CommandContext context) { + MinecraftClient client = context.getSource().getClient(); + + if (client == null) { + context.getSource().sendError(Text.literal("Unable to access Minecraft client")); + return 0; + } + + /* + After executing a command, the current screen will be closed (the chat hud). + And if you open a new screen in a command, that new screen will be closed + instantly along with the chat hud. Slightly delaying the opening of the + screen fixes this issue. + */ + client.send(() -> { + try { + client.setScreen(new ConfigManagerScreen()); + } catch (Exception e) { + PackCore.LOGGER.error("Failed to open config: {}", e.getMessage()); + } + }); + + return 1; + } +} diff --git a/src/main/java/com/github/kd_gaming1/packcore/command/GuideCommand.java b/src/main/java/com/github/kd_gaming1/packcore/command/GuideCommand.java new file mode 100644 index 0000000..d00479e --- /dev/null +++ b/src/main/java/com/github/kd_gaming1/packcore/command/GuideCommand.java @@ -0,0 +1,42 @@ +package com.github.kd_gaming1.packcore.command; + +import com.github.kd_gaming1.packcore.PackCore; +import com.github.kd_gaming1.packcore.ui.help.guide.GuideListScreen; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import net.minecraft.client.MinecraftClient; +import net.minecraft.text.Text; + +public class GuideCommand { + + public static LiteralArgumentBuilder register() { + return ClientCommandManager.literal("guide").executes(GuideCommand::execute); + } + + private static int execute(CommandContext context) { + MinecraftClient client = context.getSource().getClient(); + + if (client == null) { + context.getSource().sendError(Text.literal("Unable to access Minecraft client")); + return 0; + } + + /* + After executing a command, the current screen will be closed (the chat hud). + And if you open a new screen in a command, that new screen will be closed + instantly along with the chat hud. Slightly delaying the opening of the + screen fixes this issue. + */ + client.send(() -> { + try { + client.setScreen(new GuideListScreen()); + } catch (Exception e) { + PackCore.LOGGER.error("Failed to open guide: {}", e.getMessage()); + } + }); + + return 1; + } +} diff --git a/src/main/java/com/github/kd_gaming1/packcore/command/PackCoreCommand.java b/src/main/java/com/github/kd_gaming1/packcore/command/PackCoreCommand.java index 0b7fbee..2d44aed 100644 --- a/src/main/java/com/github/kd_gaming1/packcore/command/PackCoreCommand.java +++ b/src/main/java/com/github/kd_gaming1/packcore/command/PackCoreCommand.java @@ -1,336 +1,18 @@ package com.github.kd_gaming1.packcore.command; -import com.github.kd_gaming1.packcore.PackCore; -import com.github.kd_gaming1.packcore.config.PackCoreConfig; -import com.github.kd_gaming1.packcore.ui.help.guide.GuideListScreen; -import com.github.kd_gaming1.packcore.ui.screen.configmanager.ConfigManagerScreen; -import com.github.kd_gaming1.packcore.config.storage.ConfigFileRepository; -import com.github.kd_gaming1.packcore.integration.minecraft.PerformanceProfileService; -import com.github.kd_gaming1.packcore.integration.tabdesign.TabDesignManager; import com.mojang.brigadier.CommandDispatcher; -import com.mojang.brigadier.arguments.StringArgumentType; -import com.mojang.brigadier.context.CommandContext; import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; -import net.minecraft.client.MinecraftClient; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; - -import java.util.concurrent.CompletableFuture; public class PackCoreCommand { public static void registerCommands(CommandDispatcher dispatcher) { dispatcher.register(ClientCommandManager.literal("packcore") - .then(ClientCommandManager.literal("guide") - .executes(PackCoreCommand::openGuide)) - .then(ClientCommandManager.literal("configmanager") - .executes(PackCoreCommand::openConfig)) - .then(ClientCommandManager.literal("status") - .executes(context -> { - var source = context.getSource(); - var modpackInfo = PackCore.getModpackInfo(); - var currentConfig = ConfigFileRepository.getCurrentConfig(); - - source.sendFeedback(Text.literal("=== PackCore Status ===").formatted(Formatting.GOLD)); - source.sendFeedback(Text.literal("Modpack: " + modpackInfo.getName() + " v" + modpackInfo.getVersion())); - source.sendFeedback(Text.literal("Active Config: " + currentConfig.getName() + " v" + currentConfig.getVersion())); - source.sendFeedback(Text.literal("Custom Menu: " + (PackCoreConfig.enableCustomMenu ? "Enabled" : "Disabled"))); - source.sendFeedback(Text.literal("Config Applied: " + (PackCoreConfig.defaultConfigSuccessfullyApplied ? "Yes" : "No"))); - - return 1; - })) - .then(ClientCommandManager.literal("performance") - .then(ClientCommandManager.literal("list") - .executes(PackCoreCommand::listPerformanceProfiles)) - .then(ClientCommandManager.literal("apply") - .then(ClientCommandManager.argument("profile", StringArgumentType.word()) - .suggests((context, builder) -> { - builder.suggest("performance"); - builder.suggest("balanced"); - builder.suggest("quality"); - builder.suggest("shaders"); - return builder.buildFuture(); - }) - .executes(PackCoreCommand::applyPerformanceProfile)))) - .then(ClientCommandManager.literal("tabdesign") - .then(ClientCommandManager.literal("list") - .executes(PackCoreCommand::listTabDesigns)) - .then(ClientCommandManager.literal("apply") - .then(ClientCommandManager.argument("design", StringArgumentType.word()) - .suggests((context, builder) -> { - builder.suggest("skyhanni"); - builder.suggest("skyblocker"); - return builder.buildFuture(); - }) - .executes(PackCoreCommand::applyTabDesign))))); - } - - private static int openGuide(CommandContext context) { - MinecraftClient client = context.getSource().getClient(); - - if (client == null) { - context.getSource().sendError(Text.literal("Unable to access Minecraft client")); - return 0; - } - - /* - After executing a command, the current screen will be closed (the chat hud). - And if you open a new screen in a command, that new screen will be closed - instantly along with the chat hud. Slightly delaying the opening of the - screen fixes this issue. - */ - client.send(() -> { - try { - client.setScreen(new GuideListScreen()); - } catch (Exception e) { - PackCore.LOGGER.error("Failed to open guide: {}", e.getMessage()); - } - }); - - return 1; - } - - private static int openConfig(CommandContext context) { - MinecraftClient client = context.getSource().getClient(); - - if (client == null) { - context.getSource().sendError(Text.literal("Unable to access Minecraft client")); - return 0; - } - - /* - After executing a command, the current screen will be closed (the chat hud). - And if you open a new screen in a command, that new screen will be closed - instantly along with the chat hud. Slightly delaying the opening of the - screen fixes this issue. - */ - client.send(() -> { - try { - client.setScreen(new ConfigManagerScreen()); - } catch (Exception e) { - PackCore.LOGGER.error("Failed to open config: {}", e.getMessage()); - } - }); - - return 1; - } - - private static int applyPerformanceProfile(CommandContext context) { - String profileName = StringArgumentType.getString(context, "profile").toLowerCase(); - - PerformanceProfileService.PerformanceProfile profile; - - // Map string to enum - switch (profileName) { - case "performance" -> profile = PerformanceProfileService.PerformanceProfile.PERFORMANCE; - case "balanced" -> profile = PerformanceProfileService.PerformanceProfile.BALANCED; - case "quality" -> profile = PerformanceProfileService.PerformanceProfile.QUALITY; - case "shaders" -> profile = PerformanceProfileService.PerformanceProfile.SHADERS; - default -> { - context.getSource().sendError(Text.literal("Unknown performance profile: " + profileName) - .formatted(Formatting.RED)); - context.getSource().sendFeedback(Text.literal("Available profiles: performance, balanced, quality, shaders") - .formatted(Formatting.YELLOW)); - return 0; - } - } - - context.getSource().sendFeedback(Text.literal("Applying performance profile: " + profile.getDisplayName() + "...") - .formatted(Formatting.YELLOW)); - - // Apply the profile asynchronously to avoid blocking the main thread - CompletableFuture.runAsync(() -> { - try { - PerformanceProfileService.ProfileResult result = PerformanceProfileService.applyPerformanceProfile(profile); - - // Send feedback on main thread - MinecraftClient.getInstance().execute(() -> { - if (result.isFullySuccessful()) { - context.getSource().sendFeedback(Text.literal("✓ Performance profile '" + profile.getDisplayName() + "' applied successfully!") - .formatted(Formatting.GREEN)); - - // Show what was applied - if (result.isVanillaApplied()) { - context.getSource().sendFeedback(Text.literal(" ✓ Minecraft settings applied") - .formatted(Formatting.GRAY)); - } - if (result.isSodiumAvailable() && result.isSodiumApplied()) { - context.getSource().sendFeedback(Text.literal(" ✓ Sodium settings applied") - .formatted(Formatting.GRAY)); - } - if (result.isIrisAvailable() && result.isIrisApplied()) { - context.getSource().sendFeedback(Text.literal(" ✓ Iris/Shader settings applied") - .formatted(Formatting.GRAY)); - } - } else { - context.getSource().sendError(Text.literal("⚠ Performance profile applied with some issues:") - .formatted(Formatting.YELLOW)); - - if (!result.isVanillaApplied()) { - context.getSource().sendError(Text.literal(" ✗ Failed to apply Minecraft settings") - .formatted(Formatting.RED)); - } - if (result.isSodiumAvailable() && !result.isSodiumApplied()) { - context.getSource().sendError(Text.literal(" ✗ Failed to apply Sodium settings") - .formatted(Formatting.RED)); - } - if (result.isIrisAvailable() && !result.isIrisApplied()) { - context.getSource().sendError(Text.literal(" ✗ Failed to apply Iris/Shader settings") - .formatted(Formatting.RED)); - } - } - }); - - } catch (Exception e) { - MinecraftClient.getInstance().execute(() -> - context.getSource().sendError(Text.literal("✗ Failed to apply performance profile: " + e.getMessage()) - .formatted(Formatting.RED))); - } - }); - - return 1; - } - - private static int listPerformanceProfiles(CommandContext context) { - var availability = PerformanceProfileService.getSystemAvailability(); - - context.getSource().sendFeedback(Text.literal("=== PackCore Performance Profiles ===") - .formatted(Formatting.GOLD)); - - // Show available systems - context.getSource().sendFeedback(Text.literal("Available Systems:") - .formatted(Formatting.YELLOW)); - context.getSource().sendFeedback(Text.literal(" • Minecraft: ✓") - .formatted(Formatting.GREEN)); - context.getSource().sendFeedback(Text.literal(" • Sodium: " + (availability.sodiumAvailable() ? "✓" : "✗")) - .formatted(availability.sodiumAvailable() ? Formatting.GREEN : Formatting.RED)); - context.getSource().sendFeedback(Text.literal(" • Iris/Shaders: " + (availability.irisAvailable() ? "✓" : "✗")) - .formatted(availability.irisAvailable() ? Formatting.GREEN : Formatting.RED)); - - context.getSource().sendFeedback(Text.literal("")); - - // Show available profiles - context.getSource().sendFeedback(Text.literal("Available Profiles:") - .formatted(Formatting.YELLOW)); - - for (PerformanceProfileService.PerformanceProfile profile : PerformanceProfileService.PerformanceProfile.values()) { - String command = "/packcore performance apply " + profile.name().toLowerCase(); - context.getSource().sendFeedback(Text.literal(" • " + profile.getDisplayName() + " - " + profile.getDescription()) - .formatted(Formatting.WHITE)); - context.getSource().sendFeedback(Text.literal(" Command: " + command) - .formatted(Formatting.GRAY)); - } - - return 1; - } - - private static int applyTabDesign(CommandContext context) { - String designName = StringArgumentType.getString(context, "design").toLowerCase(); - - // Validate the design name - if (!designName.equals("skyhanni") && !designName.equals("skyblocker")) { - context.getSource().sendError(Text.literal("Unknown tab design: " + designName) - .formatted(Formatting.RED)); - context.getSource().sendFeedback(Text.literal("Available designs: skyhanni, skyblocker") - .formatted(Formatting.YELLOW)); - return 0; - } - - // Check mod availability - TabDesignManager.TabDesignAvailability availability = TabDesignManager.getAvailability(); - - if (designName.equals("skyhanni") && !availability.isSkyHanniAvailable()) { - context.getSource().sendError(Text.literal("✗ SkyHanni mod is not installed!") - .formatted(Formatting.RED)); - return 0; - } - - if (designName.equals("skyblocker") && !availability.isSkyblockerAvailable()) { - context.getSource().sendError(Text.literal("✗ Skyblocker mod is not installed!") - .formatted(Formatting.RED)); - return 0; - } - - String displayName = designName.equals("skyhanni") ? "SkyHanni Compact" : "Skyblocker Fancy"; - context.getSource().sendFeedback(Text.literal("Applying tab design: " + displayName + "...") - .formatted(Formatting.YELLOW)); - - // Apply the tab design asynchronously - CompletableFuture.runAsync(() -> { - try { - boolean success = TabDesignManager.applyTabDesign(designName); - - // Send feedback on main thread - MinecraftClient.getInstance().execute(() -> { - if (success) { - context.getSource().sendFeedback(Text.literal("✓ Tab design '" + displayName + "' applied successfully!") - .formatted(Formatting.GREEN)); - - if (designName.equals("skyhanni")) { - context.getSource().sendFeedback(Text.literal(" ℹ SkyHanni Compact tab list is now active") - .formatted(Formatting.GRAY)); - } else { - context.getSource().sendFeedback(Text.literal(" ℹ Skyblocker Fancy tab HUD is now active") - .formatted(Formatting.GRAY)); - } - } else { - context.getSource().sendError(Text.literal("⚠ Failed to apply tab design") - .formatted(Formatting.YELLOW)); - context.getSource().sendFeedback(Text.literal(" The mod may not be loaded properly") - .formatted(Formatting.GRAY)); - } - }); - - } catch (Exception e) { - MinecraftClient.getInstance().execute(() -> - context.getSource().sendError(Text.literal("✗ Failed to apply tab design: " + e.getMessage()) - .formatted(Formatting.RED))); - } - }); - - return 1; - } - - private static int listTabDesigns(CommandContext context) { - TabDesignManager.TabDesignAvailability availability = TabDesignManager.getAvailability(); - - context.getSource().sendFeedback(Text.literal("=== PackCore Tab Designs ===") - .formatted(Formatting.GOLD)); - - // Show available mods - context.getSource().sendFeedback(Text.literal("Available Mods:") - .formatted(Formatting.YELLOW)); - context.getSource().sendFeedback(Text.literal(" • SkyHanni: " + (availability.isSkyHanniAvailable() ? "✓" : "✗")) - .formatted(availability.isSkyHanniAvailable() ? Formatting.GREEN : Formatting.RED)); - context.getSource().sendFeedback(Text.literal(" • Skyblocker: " + (availability.isSkyblockerAvailable() ? "✓" : "✗")) - .formatted(availability.isSkyblockerAvailable() ? Formatting.GREEN : Formatting.RED)); - - context.getSource().sendFeedback(Text.literal("")); - - // Show available designs - context.getSource().sendFeedback(Text.literal("Available Designs:") - .formatted(Formatting.YELLOW)); - - if (availability.isSkyHanniAvailable()) { - context.getSource().sendFeedback(Text.literal(" • SkyHanni Compact - Minimalist compact tab list") - .formatted(Formatting.WHITE)); - context.getSource().sendFeedback(Text.literal(" Command: /packcore tabdesign apply skyhanni") - .formatted(Formatting.GRAY)); - } - - if (availability.isSkyblockerAvailable()) { - context.getSource().sendFeedback(Text.literal(" • Skyblocker Fancy - Feature-rich tab HUD") - .formatted(Formatting.WHITE)); - context.getSource().sendFeedback(Text.literal(" Command: /packcore tabdesign apply skyblocker") - .formatted(Formatting.GRAY)); - } - - if (!availability.isSkyHanniAvailable() && !availability.isSkyblockerAvailable()) { - context.getSource().sendFeedback(Text.literal(" ⚠ No compatible tab design mods found") - .formatted(Formatting.RED)); - } - - return 1; + .then(GuideCommand.register()) + .then(ConfigManagerCommand.register()) + .then(StatusCommand.register()) + .then(PerformanceCommand.register()) + .then(TabDesignCommand.register()) + ); } } \ No newline at end of file diff --git a/src/main/java/com/github/kd_gaming1/packcore/command/PerformanceCommand.java b/src/main/java/com/github/kd_gaming1/packcore/command/PerformanceCommand.java new file mode 100644 index 0000000..d77d1c5 --- /dev/null +++ b/src/main/java/com/github/kd_gaming1/packcore/command/PerformanceCommand.java @@ -0,0 +1,142 @@ +package com.github.kd_gaming1.packcore.command; + +import com.github.kd_gaming1.packcore.integration.minecraft.PerformanceProfileService; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import net.minecraft.client.MinecraftClient; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +import java.util.concurrent.CompletableFuture; + +public class PerformanceCommand { + + public static LiteralArgumentBuilder register() { + return ClientCommandManager.literal("performance") + .then(ClientCommandManager.literal("list") + .executes(PerformanceCommand::listPerformanceProfiles)) + .then(ClientCommandManager.literal("apply") + .then(ClientCommandManager.argument("profile", StringArgumentType.word()) + .suggests((context, builder) -> { + builder.suggest("performance"); + builder.suggest("balanced"); + builder.suggest("quality"); + builder.suggest("shaders"); + return builder.buildFuture(); + }) + .executes(PerformanceCommand::applyPerformanceProfile))); + } + + private static int applyPerformanceProfile(CommandContext context) { + String profileName = StringArgumentType.getString(context, "profile").toLowerCase(); + + PerformanceProfileService.PerformanceProfile profile; + + // Map string to enum + switch (profileName) { + case "performance" -> profile = PerformanceProfileService.PerformanceProfile.PERFORMANCE; + case "balanced" -> profile = PerformanceProfileService.PerformanceProfile.BALANCED; + case "quality" -> profile = PerformanceProfileService.PerformanceProfile.QUALITY; + case "shaders" -> profile = PerformanceProfileService.PerformanceProfile.SHADERS; + default -> { + context.getSource().sendError(Text.literal("Unknown performance profile: " + profileName) + .formatted(Formatting.RED)); + context.getSource().sendFeedback(Text.literal("Available profiles: performance, balanced, quality, shaders") + .formatted(Formatting.YELLOW)); + return 0; + } + } + + context.getSource().sendFeedback(Text.literal("Applying performance profile: " + profile.getDisplayName() + "...") + .formatted(Formatting.YELLOW)); + + // Apply the profile asynchronously to avoid blocking the main thread + CompletableFuture.runAsync(() -> { + try { + PerformanceProfileService.ProfileResult result = PerformanceProfileService.applyPerformanceProfile(profile); + + // Send feedback on main thread + MinecraftClient.getInstance().execute(() -> { + if (result.isFullySuccessful()) { + context.getSource().sendFeedback(Text.literal("✓ Performance profile '" + profile.getDisplayName() + "' applied successfully!") + .formatted(Formatting.GREEN)); + + // Show what was applied + if (result.isVanillaApplied()) { + context.getSource().sendFeedback(Text.literal(" ✓ Minecraft settings applied") + .formatted(Formatting.GRAY)); + } + if (result.isSodiumAvailable() && result.isSodiumApplied()) { + context.getSource().sendFeedback(Text.literal(" ✓ Sodium settings applied") + .formatted(Formatting.GRAY)); + } + if (result.isIrisAvailable() && result.isIrisApplied()) { + context.getSource().sendFeedback(Text.literal(" ✓ Iris/Shader settings applied") + .formatted(Formatting.GRAY)); + } + } else { + context.getSource().sendError(Text.literal("⚠ Performance profile applied with some issues:") + .formatted(Formatting.YELLOW)); + + if (!result.isVanillaApplied()) { + context.getSource().sendError(Text.literal(" ✗ Failed to apply Minecraft settings") + .formatted(Formatting.RED)); + } + if (result.isSodiumAvailable() && !result.isSodiumApplied()) { + context.getSource().sendError(Text.literal(" ✗ Failed to apply Sodium settings") + .formatted(Formatting.RED)); + } + if (result.isIrisAvailable() && !result.isIrisApplied()) { + context.getSource().sendError(Text.literal(" ✗ Failed to apply Iris/Shader settings") + .formatted(Formatting.RED)); + } + } + }); + + } catch (Exception e) { + MinecraftClient.getInstance().execute(() -> + context.getSource().sendError(Text.literal("✗ Failed to apply performance profile: " + e.getMessage()) + .formatted(Formatting.RED))); + } + }); + + return 1; + } + + private static int listPerformanceProfiles(CommandContext context) { + var availability = PerformanceProfileService.getSystemAvailability(); + + context.getSource().sendFeedback(Text.literal("=== PackCore Performance Profiles ===") + .formatted(Formatting.GOLD)); + + // Show available systems + context.getSource().sendFeedback(Text.literal("Available Systems:") + .formatted(Formatting.YELLOW)); + context.getSource().sendFeedback(Text.literal(" • Minecraft: ✓") + .formatted(Formatting.GREEN)); + context.getSource().sendFeedback(Text.literal(" • Sodium: " + (availability.sodiumAvailable() ? "✓" : "✗")) + .formatted(availability.sodiumAvailable() ? Formatting.GREEN : Formatting.RED)); + context.getSource().sendFeedback(Text.literal(" • Iris/Shaders: " + (availability.irisAvailable() ? "✓" : "✗")) + .formatted(availability.irisAvailable() ? Formatting.GREEN : Formatting.RED)); + + context.getSource().sendFeedback(Text.literal("")); + + // Show available profiles + context.getSource().sendFeedback(Text.literal("Available Profiles:") + .formatted(Formatting.YELLOW)); + + for (PerformanceProfileService.PerformanceProfile profile : PerformanceProfileService.PerformanceProfile.values()) { + String command = "/packcore performance apply " + profile.name().toLowerCase(); + context.getSource().sendFeedback(Text.literal(" • " + profile.getDisplayName() + " - " + profile.getDescription()) + .formatted(Formatting.WHITE)); + context.getSource().sendFeedback(Text.literal(" Command: " + command) + .formatted(Formatting.GRAY)); + } + + return 1; + } +} diff --git a/src/main/java/com/github/kd_gaming1/packcore/command/StatusCommand.java b/src/main/java/com/github/kd_gaming1/packcore/command/StatusCommand.java new file mode 100644 index 0000000..f30b9a2 --- /dev/null +++ b/src/main/java/com/github/kd_gaming1/packcore/command/StatusCommand.java @@ -0,0 +1,32 @@ +package com.github.kd_gaming1.packcore.command; + +import com.github.kd_gaming1.packcore.PackCore; +import com.github.kd_gaming1.packcore.config.PackCoreConfig; +import com.github.kd_gaming1.packcore.config.storage.ConfigFileRepository; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +public class StatusCommand { + + public static LiteralArgumentBuilder register() { + return ClientCommandManager.literal("status").executes(StatusCommand::execute); + } + + private static int execute(CommandContext context) { + var source = context.getSource(); + var modpackInfo = PackCore.getModpackInfo(); + var currentConfig = ConfigFileRepository.getCurrentConfig(); + + source.sendFeedback(Text.literal("=== PackCore Status ===").formatted(Formatting.GOLD)); + source.sendFeedback(Text.literal("Modpack: " + modpackInfo.getName() + " v" + modpackInfo.getVersion())); + source.sendFeedback(Text.literal("Active Config: " + currentConfig.getName() + " v" + currentConfig.getVersion())); + source.sendFeedback(Text.literal("Custom Menu: " + (PackCoreConfig.enableCustomMenu ? "Enabled" : "Disabled"))); + source.sendFeedback(Text.literal("Config Applied: " + (PackCoreConfig.defaultConfigSuccessfullyApplied ? "Yes" : "No"))); + + return 1; + } +} diff --git a/src/main/java/com/github/kd_gaming1/packcore/command/TabDesignCommand.java b/src/main/java/com/github/kd_gaming1/packcore/command/TabDesignCommand.java new file mode 100644 index 0000000..17dda7d --- /dev/null +++ b/src/main/java/com/github/kd_gaming1/packcore/command/TabDesignCommand.java @@ -0,0 +1,140 @@ +package com.github.kd_gaming1.packcore.command; + +import com.github.kd_gaming1.packcore.integration.tabdesign.TabDesignManager; +import com.mojang.brigadier.arguments.StringArgumentType; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.suggestion.SuggestionsBuilder; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; +import net.minecraft.client.MinecraftClient; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +import java.util.concurrent.CompletableFuture; + +public class TabDesignCommand { + + public static LiteralArgumentBuilder register() { + return ClientCommandManager.literal("tabdesign") + .then(ClientCommandManager.literal("list") + .executes(TabDesignCommand::listTabDesigns)) + .then(ClientCommandManager.literal("apply") + .then(ClientCommandManager.argument("design", StringArgumentType.word()) + .suggests((context, builder) -> { + builder.suggest("skyhanni"); + builder.suggest("skyblocker"); + return builder.buildFuture(); + }) + .executes(TabDesignCommand::applyTabDesign))); + } + + private static int applyTabDesign(CommandContext context) { + String designName = StringArgumentType.getString(context, "design").toLowerCase(); + + // Validate the design name + if (!designName.equals("skyhanni") && !designName.equals("skyblocker")) { + context.getSource().sendError(Text.literal("Unknown tab design: " + designName) + .formatted(Formatting.RED)); + context.getSource().sendFeedback(Text.literal("Available designs: skyhanni, skyblocker") + .formatted(Formatting.YELLOW)); + return 0; + } + + // Check mod availability + TabDesignManager.TabDesignAvailability availability = TabDesignManager.getAvailability(); + + if (designName.equals("skyhanni") && !availability.isSkyHanniAvailable()) { + context.getSource().sendError(Text.literal("✗ SkyHanni mod is not installed!") + .formatted(Formatting.RED)); + return 0; + } + + if (designName.equals("skyblocker") && !availability.isSkyblockerAvailable()) { + context.getSource().sendError(Text.literal("✗ Skyblocker mod is not installed!") + .formatted(Formatting.RED)); + return 0; + } + + String displayName = designName.equals("skyhanni") ? "SkyHanni Compact" : "Skyblocker Fancy"; + context.getSource().sendFeedback(Text.literal("Applying tab design: " + displayName + "...") + .formatted(Formatting.YELLOW)); + + // Apply the tab design asynchronously + CompletableFuture.runAsync(() -> { + try { + boolean success = TabDesignManager.applyTabDesign(designName); + + // Send feedback on main thread + MinecraftClient.getInstance().execute(() -> { + if (success) { + context.getSource().sendFeedback(Text.literal("✓ Tab design '" + displayName + "' applied successfully!") + .formatted(Formatting.GREEN)); + + if (designName.equals("skyhanni")) { + context.getSource().sendFeedback(Text.literal(" ℹ SkyHanni Compact tab list is now active") + .formatted(Formatting.GRAY)); + } else { + context.getSource().sendFeedback(Text.literal(" ℹ Skyblocker Fancy tab HUD is now active") + .formatted(Formatting.GRAY)); + } + } else { + context.getSource().sendError(Text.literal("⚠ Failed to apply tab design") + .formatted(Formatting.YELLOW)); + context.getSource().sendFeedback(Text.literal(" The mod may not be loaded properly") + .formatted(Formatting.GRAY)); + } + }); + + } catch (Exception e) { + MinecraftClient.getInstance().execute(() -> + context.getSource().sendError(Text.literal("✗ Failed to apply tab design: " + e.getMessage()) + .formatted(Formatting.RED))); + } + }); + + return 1; + } + + private static int listTabDesigns(CommandContext context) { + TabDesignManager.TabDesignAvailability availability = TabDesignManager.getAvailability(); + + context.getSource().sendFeedback(Text.literal("=== PackCore Tab Designs ===") + .formatted(Formatting.GOLD)); + + // Show available mods + context.getSource().sendFeedback(Text.literal("Available Mods:") + .formatted(Formatting.YELLOW)); + context.getSource().sendFeedback(Text.literal(" • SkyHanni: " + (availability.isSkyHanniAvailable() ? "✓" : "✗")) + .formatted(availability.isSkyHanniAvailable() ? Formatting.GREEN : Formatting.RED)); + context.getSource().sendFeedback(Text.literal(" • Skyblocker: " + (availability.isSkyblockerAvailable() ? "✓" : "✗")) + .formatted(availability.isSkyblockerAvailable() ? Formatting.GREEN : Formatting.RED)); + + context.getSource().sendFeedback(Text.literal("")); + + // Show available designs + context.getSource().sendFeedback(Text.literal("Available Designs:") + .formatted(Formatting.YELLOW)); + + if (availability.isSkyHanniAvailable()) { + context.getSource().sendFeedback(Text.literal(" • SkyHanni Compact - Minimalist compact tab list") + .formatted(Formatting.WHITE)); + context.getSource().sendFeedback(Text.literal(" Command: /packcore tabdesign apply skyhanni") + .formatted(Formatting.GRAY)); + } + + if (availability.isSkyblockerAvailable()) { + context.getSource().sendFeedback(Text.literal(" • Skyblocker Fancy - Feature-rich tab HUD") + .formatted(Formatting.WHITE)); + context.getSource().sendFeedback(Text.literal(" Command: /packcore tabdesign apply skyblocker") + .formatted(Formatting.GRAY)); + } + + if (!availability.isSkyHanniAvailable() && !availability.isSkyblockerAvailable()) { + context.getSource().sendFeedback(Text.literal(" ⚠ No compatible tab design mods found") + .formatted(Formatting.RED)); + } + + return 1; + } +} diff --git a/src/main/java/com/github/kd_gaming1/packcore/config/apply/ConfigApplyService.java b/src/main/java/com/github/kd_gaming1/packcore/config/apply/ConfigApplyService.java index e139019..eaf3e31 100644 --- a/src/main/java/com/github/kd_gaming1/packcore/config/apply/ConfigApplyService.java +++ b/src/main/java/com/github/kd_gaming1/packcore/config/apply/ConfigApplyService.java @@ -3,6 +3,7 @@ import com.github.kd_gaming1.packcore.config.backup.BackupManager; import com.github.kd_gaming1.packcore.config.storage.ConfigFileRepository; import com.github.kd_gaming1.packcore.config.model.ConfigMetadata; +import com.github.kd_gaming1.packcore.util.GsonUtils; import com.github.kd_gaming1.packcore.util.io.zip.UnzipService; import com.google.gson.Gson; import net.fabricmc.loader.api.FabricLoader; @@ -21,7 +22,7 @@ public class ConfigApplyService { private static final Logger LOGGER = LoggerFactory.getLogger(ConfigApplyService.class); private static final String PENDING_CONFIG_FILE = "packcore_pending_config.json"; - private static final Gson GSON = new Gson(); + private static final Gson GSON = GsonUtils.GSON; /** * Schedule a config to be applied on next game start diff --git a/src/main/java/com/github/kd_gaming1/packcore/config/backup/BackupManager.java b/src/main/java/com/github/kd_gaming1/packcore/config/backup/BackupManager.java index 5532e98..470a257 100644 --- a/src/main/java/com/github/kd_gaming1/packcore/config/backup/BackupManager.java +++ b/src/main/java/com/github/kd_gaming1/packcore/config/backup/BackupManager.java @@ -1,6 +1,7 @@ package com.github.kd_gaming1.packcore.config.backup; import com.github.kd_gaming1.packcore.config.PackCoreConfig; +import com.github.kd_gaming1.packcore.util.GsonUtils; import com.github.kd_gaming1.packcore.util.io.file.FileUtils; import com.github.kd_gaming1.packcore.util.io.zip.UnzipAsyncTask; import com.github.kd_gaming1.packcore.config.storage.ConfigFileRepository; @@ -30,7 +31,7 @@ */ public class BackupManager { private static final Logger LOGGER = LoggerFactory.getLogger(BackupManager.class); - private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + private static final Gson GSON = GsonUtils.GSON; private static final DateTimeFormatter TIMESTAMP_FORMAT = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss"); private static final String BACKUPS_DIR = "packcore/backups"; diff --git a/src/main/java/com/github/kd_gaming1/packcore/config/export/ConfigExportService.java b/src/main/java/com/github/kd_gaming1/packcore/config/export/ConfigExportService.java index f61d48d..f79fb09 100644 --- a/src/main/java/com/github/kd_gaming1/packcore/config/export/ConfigExportService.java +++ b/src/main/java/com/github/kd_gaming1/packcore/config/export/ConfigExportService.java @@ -1,5 +1,6 @@ package com.github.kd_gaming1.packcore.config.export; +import com.github.kd_gaming1.packcore.util.GsonUtils; import com.github.kd_gaming1.packcore.util.io.file.FileUtils; import com.github.kd_gaming1.packcore.ui.component.tree.FileTreeNode; import com.github.kd_gaming1.packcore.config.storage.ConfigFileRepository; @@ -26,7 +27,7 @@ */ public class ConfigExportService { private static final Logger LOGGER = LoggerFactory.getLogger(ConfigExportService.class); - private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + private static final Gson GSON = GsonUtils.GSON; private static final Set HIDDEN_FOLDERS = Set.of( "packcore", "logs", "crash-reports", "screenshots", diff --git a/src/main/java/com/github/kd_gaming1/packcore/config/storage/ConfigFileRepository.java b/src/main/java/com/github/kd_gaming1/packcore/config/storage/ConfigFileRepository.java index a16d5b3..ffa657a 100644 --- a/src/main/java/com/github/kd_gaming1/packcore/config/storage/ConfigFileRepository.java +++ b/src/main/java/com/github/kd_gaming1/packcore/config/storage/ConfigFileRepository.java @@ -1,6 +1,7 @@ package com.github.kd_gaming1.packcore.config.storage; import com.github.kd_gaming1.packcore.config.model.ConfigMetadata; +import com.github.kd_gaming1.packcore.util.GsonUtils; import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; import net.fabricmc.loader.api.FabricLoader; @@ -24,7 +25,7 @@ */ public class ConfigFileRepository { private static final Logger LOGGER = LoggerFactory.getLogger(ConfigFileRepository.class); - private static final Gson GSON = new Gson(); + private static final Gson GSON = GsonUtils.GSON; // Standard paths and filenames /** diff --git a/src/main/java/com/github/kd_gaming1/packcore/modpack/ModpackInfo.java b/src/main/java/com/github/kd_gaming1/packcore/modpack/ModpackInfo.java index 3cae7c1..344db22 100644 --- a/src/main/java/com/github/kd_gaming1/packcore/modpack/ModpackInfo.java +++ b/src/main/java/com/github/kd_gaming1/packcore/modpack/ModpackInfo.java @@ -1,5 +1,6 @@ package com.github.kd_gaming1.packcore.modpack; +import com.github.kd_gaming1.packcore.util.GsonUtils; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.annotations.SerializedName; @@ -49,7 +50,7 @@ public class ModpackInfo { // File handling private static final String CONFIG_FILE_NAME = "modpack-info.json"; - private static final Gson gson = new GsonBuilder().setPrettyPrinting().create(); + private static final Gson gson = GsonUtils.GSON; // File save location private static final Path runDir = FabricLoader.getInstance().getGameDir(); diff --git a/src/main/java/com/github/kd_gaming1/packcore/notification/UpdateNotifier.java b/src/main/java/com/github/kd_gaming1/packcore/notification/UpdateNotifier.java index 898ed43..1a8e294 100644 --- a/src/main/java/com/github/kd_gaming1/packcore/notification/UpdateNotifier.java +++ b/src/main/java/com/github/kd_gaming1/packcore/notification/UpdateNotifier.java @@ -69,14 +69,13 @@ private static void showInGameChatMessage(String currentVersion, String newVersi Text updateMessage = Text.literal("[" + modpackName + "] ") .formatted(Formatting.GOLD, Formatting.BOLD) .append(Text.literal("Update available! ").formatted(Formatting.YELLOW)) - .append(Text.literal(currentVersion + " → " + newVersion).formatted(Formatting.WHITE)) - .append(Text.literal(" [Click to view]").formatted(Formatting.AQUA, Formatting.UNDERLINE) + .append(Text.literal(currentVersion + " → " + newVersion + " ").formatted(Formatting.WHITE)) + .append(Text.literal("[Click to view]").formatted(Formatting.AQUA, Formatting.UNDERLINE) .styled(style -> style.withClickEvent(clickEvent))); player.sendMessage(updateMessage, false); } catch (IllegalArgumentException e) { - // Handle invalid URL gracefully - PackCore.LOGGER.error("Invalid Modrinth URL: " + modrinthUrl, e); + PackCore.LOGGER.error("Invalid Modrinth URL: {}", modrinthUrl, e); } } diff --git a/src/main/java/com/github/kd_gaming1/packcore/util/GsonUtils.java b/src/main/java/com/github/kd_gaming1/packcore/util/GsonUtils.java new file mode 100644 index 0000000..7b1b4e7 --- /dev/null +++ b/src/main/java/com/github/kd_gaming1/packcore/util/GsonUtils.java @@ -0,0 +1,38 @@ +package com.github.kd_gaming1.packcore.util; + +import com.google.gson.ExclusionStrategy; +import com.google.gson.FieldAttributes; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +public final class GsonUtils { + private GsonUtils() {} + + public static final ExclusionStrategy EXCLUSION_STRATEGY = new ExclusionStrategy() { + @Override + public boolean shouldSkipField(FieldAttributes attributes) { + return attributes.getAnnotation(Exclude.class) != null; + } + + @Override + public boolean shouldSkipClass(Class clazz) { + return false; + } + }; + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + public @interface Exclude {} + + public static final Gson GSON = new GsonBuilder() + .setPrettyPrinting() + .addSerializationExclusionStrategy(EXCLUSION_STRATEGY) + .create(); +} + +