From 1ad04bb3f2e796159f9e7d1cb7fc2c2f0f75da14 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Sat, 25 Jan 2025 19:41:00 +0200 Subject: [PATCH 01/52] Send logs to admins in chat --- .../GithubResourcepackManager.java | 12 ++-- .../platform/PlatformLogging.java | 19 +++++ .../platform/PlatformMain.java | 2 + .../fabric/FabricPlatformCommand.java | 41 ++++++++++- .../fabric/FabricPlatformLogging.java | 27 +++++++ .../platform/fabric/FabricPlatformMain.java | 72 +++++++++++++++++++ .../platform/paper/PaperPlatformCommand.java | 40 ++++++++++- .../platform/paper/PaperPlatformLogging.java | 28 ++++++++ .../platform/paper/PaperPlatformMain.java | 51 ++++++++++++- .../platform/paper/PaperPlugin.java | 22 +++++- 10 files changed, 302 insertions(+), 12 deletions(-) diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java index 42f4c98..1abd458 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java @@ -1,16 +1,14 @@ package top.offsetmonkey538.githubresourcepackmanager; import org.apache.commons.io.FileUtils; +import org.jetbrains.annotations.Nullable; import top.offsetmonkey538.githubresourcepackmanager.config.ModConfig; import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; import top.offsetmonkey538.githubresourcepackmanager.handler.GitHandler; import top.offsetmonkey538.githubresourcepackmanager.handler.PackHandler; import top.offsetmonkey538.githubresourcepackmanager.networking.MainHttpHandler; -import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformCommand; +import top.offsetmonkey538.githubresourcepackmanager.platform.*; import top.offsetmonkey538.githubresourcepackmanager.config.ConfigManager; -import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformMain; -import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformServerProperties; -import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformText; import top.offsetmonkey538.githubresourcepackmanager.utils.*; import top.offsetmonkey538.meshlib.api.HttpHandlerRegistry; @@ -18,6 +16,8 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.*; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; import java.util.regex.Pattern; import static top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging.LOGGER; @@ -44,6 +44,8 @@ private GithubResourcepackManager() { public static void initialize() { + PlatformMain.INSTANCE.registerLogToAdminListener(); + PlatformCommand.INSTANCE.registerGithubRpManagerCommand(); ConfigManager.loadConfig(); @@ -142,7 +144,7 @@ public static void updatePack(final UpdateType updateType) { // Send chat message try { - sendUpdateMessage(wasUpdated, placeholders); + if (!failed) sendUpdateMessage(wasUpdated, placeholders); } catch (GithubResourcepackManagerException e) { LOGGER.error("Failed to send update message in chat!", e); } diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformLogging.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformLogging.java index 9cf032d..8a84d63 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformLogging.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformLogging.java @@ -1,5 +1,9 @@ package top.offsetmonkey538.githubresourcepackmanager.platform; +import org.jetbrains.annotations.Nullable; + +import java.util.function.BiConsumer; + import static top.offsetmonkey538.githubresourcepackmanager.platform.ServiceLoader.load; public interface PlatformLogging { @@ -30,4 +34,19 @@ default void error(String message, Throwable error, Object... args) { void warn(String message, Throwable error); void error(String message); void error(String message, Throwable error); + + void addListener(LogLevel level, LogListener listener); + void removeListener(LogLevel level, LogListener listener); + + enum LogLevel { + DEBUG, + INFO, + WARN, + ERROR + } + + @FunctionalInterface + interface LogListener { + void accept(String message, @Nullable Throwable error); + } } diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformMain.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformMain.java index a3f80d6..93e5db8 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformMain.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformMain.java @@ -23,4 +23,6 @@ public interface PlatformMain { * @param work the stuff to run */ void runOnServerStart(Runnable work); + + void registerLogToAdminListener(); } diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformCommand.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformCommand.java index 3e58206..2bee8e1 100644 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformCommand.java +++ b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformCommand.java @@ -1,6 +1,7 @@ package top.offsetmonkey538.githubresourcepackmanager.platform.fabric; import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.context.CommandContext; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; import net.minecraft.command.CommandRegistryAccess; import net.minecraft.command.ControlFlowAware; @@ -9,15 +10,24 @@ import net.minecraft.server.command.CommandManager; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.HoverEvent; +import net.minecraft.text.MutableText; +import net.minecraft.text.Style; import net.minecraft.text.Text; +import net.minecraft.util.Colors; +import net.minecraft.util.Formatting; +import org.apache.commons.lang3.exception.ExceptionUtils; import top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformCommand; +import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging; import java.util.Optional; import static com.mojang.brigadier.arguments.BoolArgumentType.bool; +import static com.mojang.brigadier.arguments.BoolArgumentType.getBool; import static net.minecraft.server.command.CommandManager.argument; import static net.minecraft.server.command.CommandManager.literal; +import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.MOD_ID; public class FabricPlatformCommand implements PlatformCommand { @Override @@ -57,14 +67,14 @@ public static void register(CommandDispatcher dispatcher, C .requires(source -> source.hasPermissionLevel(2)) .executes( context -> { - GithubResourcepackManager.updatePack(GithubResourcepackManager.UpdateType.COMMAND); + runTriggerUpdate(context, false); return 1; } ) .then(argument("force", bool()) .executes( context -> { - GithubResourcepackManager.updatePack(GithubResourcepackManager.UpdateType.COMMAND_FORCE); + runTriggerUpdate(context, getBool(context, "force")); return 1; } ) @@ -72,4 +82,31 @@ public static void register(CommandDispatcher dispatcher, C ) ); } + + private static void runTriggerUpdate(CommandContext context, boolean force) { + final PlatformLogging.LogListener infoListener = (message, error) -> { + context.getSource().sendMessage(Text.literal(String.format("[%s] %s", MOD_ID, message))); + }; + final PlatformLogging.LogListener warnListener = (message, error) -> { + MutableText text = Text + .literal(String.format("[%s] %s", MOD_ID, message)) + .setStyle(Style.EMPTY.withColor(Formatting.YELLOW)); + + if (error != null) text.setStyle(Style.EMPTY.withColor(Formatting.YELLOW).withHoverEvent( + new HoverEvent( + HoverEvent.Action.SHOW_TEXT, + Text.literal(ExceptionUtils.getRootCauseMessage(error)) + ) + )); + + context.getSource().sendMessage(text); + }; + PlatformLogging.LOGGER.addListener(PlatformLogging.LogLevel.INFO, infoListener); + PlatformLogging.LOGGER.addListener(PlatformLogging.LogLevel.WARN, warnListener); + + GithubResourcepackManager.updatePack(force ? GithubResourcepackManager.UpdateType.COMMAND_FORCE : GithubResourcepackManager.UpdateType.COMMAND); + + PlatformLogging.LOGGER.removeListener(PlatformLogging.LogLevel.INFO, infoListener); + PlatformLogging.LOGGER.removeListener(PlatformLogging.LogLevel.WARN, warnListener); + } } diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformLogging.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformLogging.java index 0184c28..45cb414 100644 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformLogging.java +++ b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformLogging.java @@ -3,40 +3,67 @@ import org.slf4j.Logger; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + public class FabricPlatformLogging implements PlatformLogging { private static Logger logger; + private static final Map> listeners = new HashMap<>(Map.of( + LogLevel.DEBUG, new ArrayList<>(), + LogLevel.INFO, new ArrayList<>(), + LogLevel.WARN, new ArrayList<>(), + LogLevel.ERROR, new ArrayList<>() + )); @Override public void debug(String message) { + listeners.get(LogLevel.DEBUG).forEach(consumer -> consumer.accept(message, null)); logger.debug(message); } @Override public void info(String message) { + listeners.get(LogLevel.INFO).forEach(consumer -> consumer.accept(message, null)); logger.info(message); } @Override public void warn(String message) { + listeners.get(LogLevel.WARN).forEach(consumer -> consumer.accept(message, null)); logger.warn(message); } @Override public void warn(String message, Throwable error) { + listeners.get(LogLevel.WARN).forEach(consumer -> consumer.accept(message, error)); logger.warn(message, error); } @Override public void error(String message) { + listeners.get(LogLevel.ERROR).forEach(consumer -> consumer.accept(message, null)); logger.error(message); } @Override public void error(String message, Throwable error) { + listeners.get(LogLevel.ERROR).forEach(consumer -> consumer.accept(message, error)); logger.error(message, error); } + @Override + public void addListener(LogLevel level, LogListener listener) { + listeners.get(level).add(listener); + } + + @Override + public void removeListener(LogLevel level, LogListener listener) { + listeners.get(level).remove(listener); + } + public static void setLogger(Logger logger) { FabricPlatformLogging.logger = logger; } diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java index 132aac0..9dfd416 100644 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java +++ b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java @@ -1,22 +1,36 @@ package top.offsetmonkey538.githubresourcepackmanager.platform.fabric; +import com.mojang.authlib.GameProfile; import net.fabricmc.api.DedicatedServerModInitializer; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; +import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents; import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.entity.player.PlayerEntity; import net.minecraft.server.MinecraftServer; +import net.minecraft.text.HoverEvent; +import net.minecraft.text.MutableText; +import net.minecraft.text.Style; +import net.minecraft.text.Text; +import net.minecraft.util.Colors; +import net.minecraft.util.Formatting; +import org.apache.commons.lang3.exception.ExceptionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager; +import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformMain; import java.nio.file.Path; +import java.util.LinkedList; import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.MOD_ID; +import static top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging.LOGGER; public class FabricPlatformMain implements PlatformMain, DedicatedServerModInitializer { public static final FabricPlatformMain INSTANCE = (FabricPlatformMain) PlatformMain.INSTANCE; private static MinecraftServer minecraftServer; + private static final LinkedList messageQueue = new LinkedList<>(); @Override public void onInitializeServer() { @@ -25,6 +39,13 @@ public void onInitializeServer() { GithubResourcepackManager.initialize(); ServerLifecycleEvents.SERVER_STARTING.register(minecraftServer1 -> minecraftServer = minecraftServer1); + ServerPlayConnectionEvents.JOIN.register((serverPlayNetworkHandler, packetSender, minecraftServer1) -> { + if (!minecraftServer1.getPlayerManager().isOperator(serverPlayNetworkHandler.player.getGameProfile())) return; + + for (Text text : messageQueue) { + serverPlayNetworkHandler.player.sendMessage(text); + } + }); } public static MinecraftServer getServer() { @@ -41,4 +62,55 @@ public Path getConfigDir() { public void runOnServerStart(Runnable work) { ServerLifecycleEvents.SERVER_STARTED.register(minecraftServer1 -> work.run()); } + + @Override + public void registerLogToAdminListener() { + LOGGER.addListener(PlatformLogging.LogLevel.ERROR, (message, error) -> { + MutableText text = Text + .literal(String.format("[%s] %s", MOD_ID, message)) + .setStyle(Style.EMPTY.withColor(Formatting.RED)); + if (error != null) text = text.setStyle(Style.EMPTY.withColor(Formatting.RED).withHoverEvent( + new HoverEvent( + HoverEvent.Action.SHOW_TEXT, + Text.literal(ExceptionUtils.getRootCauseMessage(error)) + ) + )); + + boolean sent = false; + for (final PlayerEntity player : getServer().getPlayerManager().getPlayerList()) { + if (!getServer().getPlayerManager().isOperator(player.getGameProfile())) continue; + player.sendMessage(text, false); + sent = true; + } + if (sent) return; + + messageQueue.addLast(text); + }); + } + + //public void registerLogToAdminListener() { + // LOGGER.addListener(PlatformLogging.LogLevel.ERROR, (message, error) -> { + // Component text = Component + // .text(String.format("[%s] %s", MOD_ID, message)) + // .color(NamedTextColor.RED); + // + // if (error != null) text = text.hoverEvent( + // HoverEvent.showText( + // Component.text(ExceptionUtils.getRootCauseMessage(error)) + // ) + // ); + // + // + // boolean sent = false; + // for (final OfflinePlayer operator : plugin.getServer().getOperators()) { + // if (operator.getPlayer() == null) continue; + // operator.getPlayer().sendMessage(text); + // sent = true; + // } + // + // if (sent) return; + // + // plugin.messageQueue.addLast(text); + // }); + //} } diff --git a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformCommand.java b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformCommand.java index 36b7082..773c133 100644 --- a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformCommand.java +++ b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformCommand.java @@ -1,20 +1,29 @@ package top.offsetmonkey538.githubresourcepackmanager.platform.paper; import com.mojang.brigadier.Command; +import com.mojang.brigadier.context.CommandContext; +import io.papermc.paper.command.brigadier.CommandSourceStack; import io.papermc.paper.command.brigadier.Commands; import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents; import net.kyori.adventure.resource.ResourcePackInfo; import net.kyori.adventure.resource.ResourcePackRequest; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.HoverEvent; +import net.kyori.adventure.text.format.NamedTextColor; import net.minecraft.server.MinecraftServer; +import org.apache.commons.lang.exception.ExceptionUtils; import org.bukkit.entity.Player; import top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformCommand; +import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging; import java.net.URI; import static com.mojang.brigadier.arguments.BoolArgumentType.bool; +import static com.mojang.brigadier.arguments.BoolArgumentType.getBool; import static io.papermc.paper.command.brigadier.Commands.argument; import static io.papermc.paper.command.brigadier.Commands.literal; +import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.MOD_ID; public class PaperPlatformCommand implements PlatformCommand { @SuppressWarnings("UnstableApiUsage") @@ -55,14 +64,14 @@ public void registerGithubRpManagerCommand() { .requires(source -> source.getSender().isOp()) .executes( context -> { - GithubResourcepackManager.updatePack(GithubResourcepackManager.UpdateType.COMMAND); + runTriggerUpdate(context, false); return 1; } ) .then(argument("force", bool()) .executes( context -> { - GithubResourcepackManager.updatePack(GithubResourcepackManager.UpdateType.COMMAND_FORCE); + runTriggerUpdate(context, getBool(context, "force")); return 1; } ) @@ -73,6 +82,33 @@ public void registerGithubRpManagerCommand() { }); } + @SuppressWarnings("UnstableApiUsage") + private void runTriggerUpdate(CommandContext context, boolean force) { + final PlatformLogging.LogListener infoListener = (message, error) -> { + context.getSource().getSender().sendMessage(Component.text(String.format("[%s] %s", MOD_ID, message))); + }; + final PlatformLogging.LogListener warnListener = (message, error) -> { + Component text = Component + .text(String.format("[%s] %s", MOD_ID, message)) + .color(NamedTextColor.YELLOW); + + if (error != null) text = text.hoverEvent( + HoverEvent.showText( + Component.text(ExceptionUtils.getRootCauseMessage(error)) + ) + ); + + context.getSource().getSender().sendMessage(text); + }; + PlatformLogging.LOGGER.addListener(PlatformLogging.LogLevel.INFO, infoListener); + PlatformLogging.LOGGER.addListener(PlatformLogging.LogLevel.WARN, warnListener); + + GithubResourcepackManager.updatePack(force ? GithubResourcepackManager.UpdateType.COMMAND_FORCE : GithubResourcepackManager.UpdateType.COMMAND); + + PlatformLogging.LOGGER.removeListener(PlatformLogging.LogLevel.INFO, infoListener); + PlatformLogging.LOGGER.removeListener(PlatformLogging.LogLevel.WARN, warnListener); + } + //@Override //public void registerGithubRpManagerCommand() { // CommandRegistrationCallback.EVENT.register(FabricPlatformCommand::register); diff --git a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformLogging.java b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformLogging.java index e89673a..737d7b1 100644 --- a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformLogging.java +++ b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformLogging.java @@ -1,44 +1,72 @@ package top.offsetmonkey538.githubresourcepackmanager.platform.paper; +import org.jetbrains.annotations.Nullable; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; import java.util.logging.Level; import java.util.logging.Logger; public class PaperPlatformLogging implements PlatformLogging { private static Logger logger; + private static final Map> listeners = new HashMap<>(Map.of( + LogLevel.DEBUG, new ArrayList<>(), + LogLevel.INFO, new ArrayList<>(), + LogLevel.WARN, new ArrayList<>(), + LogLevel.ERROR, new ArrayList<>() + )); @Override public void debug(String message) { + listeners.get(LogLevel.DEBUG).forEach(consumer -> consumer.accept(message, null)); logger.log(Level.FINE, message); } @Override public void info(String message) { + listeners.get(LogLevel.INFO).forEach(consumer -> consumer.accept(message, null)); logger.log(Level.INFO, message); } @Override public void warn(String message) { + listeners.get(LogLevel.WARN).forEach(consumer -> consumer.accept(message, null)); logger.log(Level.WARNING, message); } @Override public void warn(String message, Throwable error) { + listeners.get(LogLevel.WARN).forEach(consumer -> consumer.accept(message, error)); logger.log(Level.WARNING, message, error); } @Override public void error(String message) { + listeners.get(LogLevel.ERROR).forEach(consumer -> consumer.accept(message, null)); logger.log(Level.SEVERE, message); } @Override public void error(String message, Throwable error) { + listeners.get(LogLevel.ERROR).forEach(consumer -> consumer.accept(message, error)); logger.log(Level.SEVERE, message, error); } + @Override + public void addListener(LogLevel level, LogListener listener) { + listeners.get(level).add(listener); + } + + @Override + public void removeListener(LogLevel level, LogListener listener) { + listeners.get(level).remove(listener); + } + public static void setLogger(Logger logger) { PaperPlatformLogging.logger = logger; } diff --git a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java index c20d396..e1679f2 100644 --- a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java +++ b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java @@ -1,12 +1,32 @@ package top.offsetmonkey538.githubresourcepackmanager.platform.paper; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.event.HoverEvent; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextColor; +import net.kyori.adventure.util.RGBLike; +import org.apache.commons.lang.exception.ExceptionUtils; +import org.bukkit.OfflinePlayer; +import org.bukkit.entity.Player; import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.Nullable; +import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformMain; +import java.io.PrintStream; +import java.io.PrintWriter; import java.nio.file.Path; +import java.util.LinkedList; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.stream.Stream; + +import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.MOD_ID; +import static top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging.LOGGER; public class PaperPlatformMain implements PlatformMain { - private static JavaPlugin plugin; + private static PaperPlugin plugin; @Override public Path getConfigDir() { @@ -18,7 +38,34 @@ public void runOnServerStart(Runnable work) { work.run(); } - public static void setPlugin(JavaPlugin plugin) { + @Override + public void registerLogToAdminListener() { + LOGGER.addListener(PlatformLogging.LogLevel.ERROR, (message, error) -> { + Component text = Component + .text(String.format("[%s] %s", MOD_ID, message)) + .color(NamedTextColor.RED); + + if (error != null) text = text.hoverEvent( + HoverEvent.showText( + Component.text(ExceptionUtils.getRootCauseMessage(error)) + ) + ); + + + boolean sent = false; + for (final OfflinePlayer operator : plugin.getServer().getOperators()) { + if (operator.getPlayer() == null) continue; + operator.getPlayer().sendMessage(text); + sent = true; + } + + if (sent) return; + + plugin.messageQueue.addLast(text); + }); + } + + public static void setPlugin(PaperPlugin plugin) { PaperPlatformMain.plugin = plugin; } diff --git a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlugin.java b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlugin.java index caf4270..3428ea3 100644 --- a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlugin.java +++ b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlugin.java @@ -1,13 +1,23 @@ package top.offsetmonkey538.githubresourcepackmanager.platform.paper; +import net.kyori.adventure.text.Component; +import org.bukkit.Bukkit; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.plugin.java.JavaPlugin; import top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager; import top.offsetmonkey538.meshlib.MeshLib; -public class PaperPlugin extends JavaPlugin { +import java.util.LinkedList; + +public class PaperPlugin extends JavaPlugin implements Listener { + public final LinkedList messageQueue = new LinkedList<>(); @Override public void onEnable() { + Bukkit.getPluginManager().registerEvents(this, this); MeshLib.initialize(); PaperPlatformMain.setPlugin(this); @@ -15,4 +25,14 @@ public void onEnable() { GithubResourcepackManager.initialize(); } + + @EventHandler(priority = EventPriority.MONITOR) + public void onPlayerJoin(PlayerJoinEvent event) { + if (!event.getPlayer().isOp()) return; + + for (Component text : messageQueue) { + event.getPlayer().sendMessage(text); + } + messageQueue.clear(); + } } From f90eaa1addd65d82624dab0ae7ca21cadb1ae20c Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Sat, 14 Jun 2025 13:30:32 +0300 Subject: [PATCH 02/52] Restructure config Nothing is implemented yet :( --- .../GithubResourcepackManager.java | 7 +- .../config/ConfigManager.java | 14 +- .../config/ModConfig.java | 189 +++++++++++++++--- .../handler/GitHandler.java | 6 +- .../platform/fabric/FabricPlatformText.java | 6 +- .../platform/paper/PaperPlatformText.java | 6 +- 6 files changed, 183 insertions(+), 45 deletions(-) diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java index 42f4c98..30f19a6 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java @@ -159,8 +159,9 @@ public static void updatePack(final UpdateType updateType) { } private static void triggerWebhook(boolean wasUpdated, Map placeholders, UpdateType updateType) throws GithubResourcepackManagerException { - if (config.webhookUrl == null || config.webhookBody == null) return; - if (config.webhookBody.contains("discord") && !wasUpdated) { + // todo: change logic to match enable/disable and fail/success webhooks + if (config.resourcePackProvider.successWebhook.url == null || config.resourcePackProvider.successWebhook.body == null) return; + if (config.resourcePackProvider.successWebhook.body.contains("discord") && !wasUpdated) { LOGGER.info("Not sending discord webhook because pack was not updated."); return; } @@ -172,7 +173,7 @@ private static void triggerWebhook(boolean wasUpdated, Map place WebhookSender.send(webhookBody, config.getWebhookUrl(), updateType, gitHandler.getWasUpdated()); } catch (IOException e) { - throw new GithubResourcepackManagerException("Failed to read content of webhook body file '%s'!", e, config.webhookBody); + throw new GithubResourcepackManagerException("Failed to read content of webhook body file '%s'!", e, config.resourcePackProvider.successWebhook.body); } } diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ConfigManager.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ConfigManager.java index 87dc2d9..6fda41d 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ConfigManager.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ConfigManager.java @@ -74,14 +74,14 @@ public static void loadConfig() { private static @NotNull List checkConfigErrors() { final List errors = new ArrayList<>(); - if (config.serverPublicIp == null) errors.add("Field 'serverPublicIp' not set!"); - if (config.repoUrl == null) errors.add("Field 'repoUrl' not set!"); - if (config.isRepoPrivate) { - if (config.githubUsername == null) errors.add("Field 'githubUsername' not set, but 'isRepoPrivate' is true!"); - if (config.githubToken == null) errors.add("Field 'githubToken' not set, but 'isRepoPrivate' is true!"); + if (config.serverInfo.publicIp == null) errors.add("Field 'serverInfo.publicIp' not set!"); + if (config.repositoryInfo.url == null) errors.add("Field 'repositoryInfo.url' not set!"); + if (config.repositoryInfo.isPrivate) { + if (config.repositoryInfo.username == null) errors.add("Field 'repositoryInfo.username' not set, but 'repositoryInfo.isPrivate' is true!"); + if (config.repositoryInfo.token == null) errors.add("Field 'repositoryInfo.token' not set, but 'repositoryInfo.isPrivate' is true!"); } else { - if (config.githubUsername != null) errors.add("Field 'githubUsername' set, but 'isRepoPrivate' is false! Can be unset"); - if (config.githubToken != null) errors.add("Field 'githubToken' set, but 'isRepoPrivate' is false! Can be unset"); + if (config.repositoryInfo.username != null) errors.add("Field 'repositoryInfo.username' set, but 'repositoryInfo.isPrivate' is false! Can be unset"); + if (config.repositoryInfo.token != null) errors.add("Field 'repositoryInfo.token' set, but 'repositoryInfo.isPrivate' is false! Can be unset"); } return errors; } diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java index 2cbb7fa..561376a 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java @@ -3,6 +3,7 @@ import blue.endless.jankson.Comment; import blue.endless.jankson.Jankson; import blue.endless.jankson.JsonGrammar; +import blue.endless.jankson.api.Marshaller; import top.offsetmonkey538.githubresourcepackmanager.config.webhook.BasicWebhook; import top.offsetmonkey538.githubresourcepackmanager.config.webhook.DefaultWebhookBody; import top.offsetmonkey538.githubresourcepackmanager.config.webhook.discord.BasicMessage; @@ -21,24 +22,109 @@ import static top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging.LOGGER; public class ModConfig { + @Comment("!!!!Please check the wiki for how to set up the mod. It is linked on both the Modrinth and GitHub pages!!!!") - public String packUpdateMessage = "Server resourcepack has been updated!\nPlease click {packUpdateCommand} to get the most up to date pack."; - public String packUpdateMessageHoverMessage = "{longDescription}"; - @Comment("The public ip of your server (\"123.45.67.89\" or \"play.coolserver.net\")") - public String serverPublicIp = null; - @Comment("If set, this port will be used in the server.properties file instead of the Minecraft server port. HTTP server will still be hosted on the Minecraft port. Only useful when running the server behind a proxy like nginx, traefik, cloudflare tunnel, etc.") - public String proxyPort = null; - @Comment("Should be \"[YOUR BRANCH NAME HERE]\". Common names include \"master\" and \"main\"") - public String branch = "master"; - public String repoUrl = null; - @Comment("Where the mod will search for resource packs in the cloned repository") - public String resourcePackRoot = ""; - public boolean isRepoPrivate = false; - public String githubUsername = null; - @Comment("PLEASE DON'T SHARE THIS WITH ANYONE EVER") - public String githubToken = null; - public String webhookUrl = null; - public String webhookBody = null; + public ServerInfo serverInfo = new ServerInfo(); + public RepositoryInfo repositoryInfo = new RepositoryInfo(); + public ResourcePackProvider resourcePackProvider = new ResourcePackProvider(); + public DataPackProvider dataPackProvider = new DataPackProvider(); + + + public static class ServerInfo { + @Comment("The public ip of your server (\"123.45.67.89\" or \"play.coolserver.net\")") + public String publicIp = null; + + @Comment("If set, this port will be used in the server.properties file instead of the Minecraft server port. HTTP server will still be hosted on the Minecraft port. Only useful when running the server behind a proxy like nginx, traefik, cloudflare tunnel, etc.") + public String proxyPort = null; + } + + public static class RepositoryInfo { + @Comment("Should be \"[YOUR BRANCH NAME HERE]\". Common names include \"master\" and \"main\"") + public String branch = "master"; + @Comment("The URL of your repository. For example \"https://github.com/MyName/MyRepository\"") + public String url = null; + + @Comment("Whether or not the repository is private. Username and token will need to be populated when this is set to 'true'!") + public boolean isPrivate = false; + + @Comment("The two values below only need to be set when 'isPrivate' is true!") + public String username = null; + @Comment("PLEASE DO NOT SHARE THIS WITH ANYONE") + public String token = null; + } + + public static class ResourcePackProvider { + @Comment("Whether or not the resource pack provider is enabled. Default: true") + public boolean enabled = true; + @Comment("Where the mod will search for resource packs in the cloned repository. MUST NOT be same as or child of the 'rootLocation' of the datapack provider") + public String rootLocation = "/resourcepacks"; + + @Comment("Message sent in chat when pack has been updated. May be 'null' to disable.") + public String updateMessage = "Server resourcepack has been updated!\nPlease click {packUpdateCommand} to get the most up to date pack."; + @Comment("Message shown when hovering over the 'updateMessage' text. May be 'null' to disable.") + public String updateMessageHoverMessage = "{longDescription}"; + + @Comment("Webhook to be sent when pack updating succeeded") + public WebhookInfo successWebhook = new WebhookInfo(); + @Comment("Webhook to be sent when pack updating failed") + public WebhookInfo failWebhook = new WebhookInfo(); + } + + public static class DataPackProvider { + @Comment("Whether or not the data pack provider is enabled. Default: false") + public boolean enabled = false; + @Comment("Where the mod will search for data packs in the cloned repository. MUST NOT be same as or child of the 'rootLocation' of the resourcepack provider") + public String rootLocation = "/datapacks"; + + @Comment("Message sent TO ADMINS in chat when pack has been updated. May be 'null' to disable.") + public String updateMessage = "Server datapacks has been updated!\nNew packs (if any) have been enabled automatically.\nPlease run '/reload' or restart the server to reload datapacks."; + @Comment("Message shown when hovering over the 'updateMessage' text. May be 'null' to disable.") + public String updateMessageHoverMessage = "{longDescription}"; + + @Comment("Webhook to be sent when pack updating succeeded") + public WebhookInfo successWebhook = new WebhookInfo(); + @Comment("Webhook to be sent when pack updating failed") + public WebhookInfo failWebhook = new WebhookInfo(); + } + + public static class WebhookInfo { + + public WebhookInfo() { + + } + public WebhookInfo(boolean enabled, String url, String body) { + this.enabled = enabled; + this.url = url; + this.body = body; + } + + @Comment("Whether or not this webhook is enabled") + public boolean enabled = false; + + @Comment("The URL to send the webhook to. For example \"https://discord.com/api/webhooks/1234567890123456789/eW91J3JlIG5vdCBzdGVhbGluZyBhIHRva2Vu_bm9wZQ==_eWVyJyBub3Q=\" or something custom like \"https://api.example.com/NDI6IHRoZSBtZWFuaW5nIG9mIGxpZmUsIHRoZSB1bml2ZXJzZSwgYW5kIGV2ZXJ5dGhpbmc=\"") + public String url = null; + @Comment("The relative path from the config directory to a webhook body file. For example \"discord/basic_message.json\" or \"discord/embed_message.json\"") + public String body = null; + } + + //@Comment("!!!!Please check the wiki for how to set up the mod. It is linked on both the Modrinth and GitHub pages!!!!") + //public String packUpdateMessage = "Server resourcepack has been updated!\nPlease click {packUpdateCommand} to get the most up to date pack."; + //public String packUpdateMessageHoverMessage = "{longDescription}"; + //@Comment("The public ip of your server (\"123.45.67.89\" or \"play.coolserver.net\")") + //public String serverPublicIp = null; + //@Comment("If set, this port will be used in the server.properties file instead of the Minecraft server port. HTTP server will still be hosted on the Minecraft port. Only useful when running the server behind a proxy like nginx, traefik, cloudflare tunnel, etc.") + //public String proxyPort = null; + //@Comment("Should be \"[YOUR BRANCH NAME HERE]\". Common names include \"master\" and \"main\"") + //public String branch = "master"; + //public String repoUrl = null; + //@Comment("Where the mod will search for resource packs in the cloned repository") + //public String resourcePackRoot = ""; + //public boolean isRepoPrivate = false; + //public String githubUsername = null; + //@Comment("PLEASE DON'T SHARE THIS WITH ANYONE EVER") + //public String githubToken = null; + //public String webhookUrl = null; + //public String webhookBody = null; protected String getName() { @@ -46,7 +132,7 @@ protected String getName() { } protected int getConfigVersion() { - return 2; + return 3; } protected List getDatafixers() { @@ -60,6 +146,57 @@ protected List getDatafixers() { (original, jankson) -> { // 1 -> 2 // noop + }, + (original, jankson) -> { + // 2 -> 3 + + final Marshaller marsh = jankson.getMarshaller(); + + // Server Info + final ServerInfo serverInfo = new ServerInfo(); + + serverInfo.publicIp = marsh.marshall(String.class, original.get("serverPublicIp")); + serverInfo.proxyPort = marsh.marshall(String.class, original.get("proxyPort")); + + original.put("serverInfo", jankson.toJson(serverInfo)); + + // Repository Info + final RepositoryInfo repositoryInfo = new RepositoryInfo(); + + repositoryInfo.branch = marsh.marshall(String.class, original.get("branch")); + repositoryInfo.url = marsh.marshall(String.class, original.get("repoUrl")); + repositoryInfo.isPrivate = marsh.marshall(Boolean.class, original.get("isRepoPrivate")); + repositoryInfo.username = marsh.marshall(String.class, original.get("githubUsername")); + repositoryInfo.token = marsh.marshall(String.class, original.get("githubToken")); + + original.put("repositoryInfo", jankson.toJson(repositoryInfo)); + + // Resource Pack Provider + final ResourcePackProvider resourcePackProvider = new ResourcePackProvider(); + + resourcePackProvider.enabled = true; + resourcePackProvider.rootLocation = marsh.marshall(String.class, original.get("resourcePackRoot")); + resourcePackProvider.updateMessage = marsh.marshall(String.class, original.get("packUpdateMessage")); + resourcePackProvider.updateMessageHoverMessage = marsh.marshall(String.class, original.get("packUpdateMessageHoverMessage")); + + final String webhookUrl = marsh.marshall(String.class, original.get("webhookUrl")); + final String webhookBody = marsh.marshall(String.class, original.get("webhookBody")); + + if (webhookUrl != null && webhookBody != null) { + final WebhookInfo webhookInfo = new WebhookInfo( + true, + webhookUrl, + webhookBody + ); + + resourcePackProvider.successWebhook = webhookInfo; + + if (!webhookBody.contains("discord")) { + resourcePackProvider.failWebhook = webhookInfo; + } + } + + original.put("resourcePackProvider", jankson.toJson(resourcePackProvider)); } ); } @@ -85,31 +222,31 @@ public void createDefaultWebhooks() { public String getPackUrl(String outputFileName) { return String.format( "http://%s:%s/%s/%s", - serverPublicIp, - proxyPort == null ? PlatformServerProperties.INSTANCE.getServerPort() : proxyPort, + serverInfo.publicIp, + serverInfo.proxyPort == null ? PlatformServerProperties.INSTANCE.getServerPort() : serverInfo.proxyPort, MOD_URI, outputFileName ); } public URI getWebhookUrl() { - if (webhookUrl == null) return null; - return URI.create(webhookUrl); + if (resourcePackProvider.successWebhook.url == null) return null; + return URI.create(resourcePackProvider.successWebhook.url); } public Path getWebhookBody() { - if (webhookBody == null) return null; - return CURRENT_CONFIG_FILE_PATH.getParent().resolve(webhookBody); + if (resourcePackProvider.successWebhook.body == null) return null; + return CURRENT_CONFIG_FILE_PATH.getParent().resolve(resourcePackProvider.successWebhook.body); } public Path getResourcePackRoot() { - return REPO_ROOT_FOLDER.resolve(config.resourcePackRoot.startsWith("/") ? config.resourcePackRoot.substring(1) : config.resourcePackRoot); + return REPO_ROOT_FOLDER.resolve(config.resourcePackProvider.rootLocation.startsWith("/") ? config.resourcePackProvider.rootLocation.substring(1) : config.resourcePackProvider.rootLocation); } public Path getPacksDir() { return getResourcePackRoot().resolve("packs"); } public String getGithubRef() { - return "refs/heads/" + branch; + return "refs/heads/" + repositoryInfo.branch; } } diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/GitHandler.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/GitHandler.java index 94ed0c5..8a0f3db 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/GitHandler.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/GitHandler.java @@ -68,8 +68,8 @@ private static CommitProperties getLatestCommitProperties(String lastCommitHash, private static void updateRepository(boolean retry) throws GithubResourcepackManagerException { // Create credentials provider if repository is private CredentialsProvider credentialsProvider = null; - if (config.isRepoPrivate) - credentialsProvider = new UsernamePasswordCredentialsProvider(config.githubUsername, config.githubToken); + if (config.repositoryInfo.isPrivate) + credentialsProvider = new UsernamePasswordCredentialsProvider(config.repositoryInfo.username, config.repositoryInfo.token); // If the repo folder doesn't exist, clone the repository. if (!REPO_ROOT_FOLDER.toFile().exists()) cloneRepository(credentialsProvider); @@ -119,7 +119,7 @@ private static void updateRepository(boolean retry) throws GithubResourcepackMan private static void cloneRepository(CredentialsProvider credentialsProvider) throws GithubResourcepackManagerException { try { Git git = Git.cloneRepository() - .setURI(config.repoUrl) + .setURI(config.repositoryInfo.url) .setDirectory(REPO_ROOT_FOLDER.toFile()) .setBranch(config.getGithubRef()) .setCredentialsProvider(credentialsProvider) diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java index ab38519..e31d02e 100644 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java +++ b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java @@ -17,15 +17,15 @@ public void sendUpdateMessage(Map placeholders) throws GithubRes final PlayerManager playerManager = FabricPlatformMain.getServer().getPlayerManager(); if (playerManager == null) return; - String message = config.packUpdateMessage; + String message = config.resourcePackProvider.updateMessage; final String[] splitMessage = message.split("\n"); final HoverEvent hoverEvent; try { - hoverEvent = config.packUpdateMessageHoverMessage == null ? null : new HoverEvent( + hoverEvent = config.resourcePackProvider.updateMessageHoverMessage == null ? null : new HoverEvent( HoverEvent.Action.SHOW_TEXT, TextUtils.INSTANCE.getStyledText( - StringUtils.replacePlaceholders(config.packUpdateMessageHoverMessage, placeholders).replace("\\n", "\n") + StringUtils.replacePlaceholders(config.resourcePackProvider.updateMessageHoverMessage, placeholders).replace("\\n", "\n") ) ); } catch (Exception e) { diff --git a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformText.java b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformText.java index e3e44a1..4e270c6 100644 --- a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformText.java +++ b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformText.java @@ -15,15 +15,15 @@ public class PaperPlatformText implements PlatformText { @Override public void sendUpdateMessage(Map placeholders) throws GithubResourcepackManagerException { - String message = config.packUpdateMessage; + String message = config.resourcePackProvider.updateMessage; final String[] splitMessage = message.split("\n"); final HoverEvent hoverEvent; try { - hoverEvent = config.packUpdateMessageHoverMessage == null ? null : new HoverEvent( + hoverEvent = config.resourcePackProvider.updateMessageHoverMessage == null ? null : new HoverEvent( HoverEvent.Action.SHOW_TEXT, getStyledText( - StringUtils.replacePlaceholders(config.packUpdateMessageHoverMessage, placeholders).replace("\\n", "\n") + StringUtils.replacePlaceholders(config.resourcePackProvider.updateMessageHoverMessage, placeholders).replace("\\n", "\n") ) ); } catch (Exception e) { From ef8a16343dfc28483b442992b1562ad524f14762 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Sat, 14 Jun 2025 19:57:46 +0300 Subject: [PATCH 03/52] Restructure folders --- .../GithubResourcepackManager.java | 32 +++++++++++-------- .../config/ModConfig.java | 2 +- .../handler/GitHandler.java | 10 +++--- .../handler/PackHandler.java | 2 +- .../networking/WebhookHttpHandler.java | 1 - .../platform/PlatformServerProperties.java | 2 +- .../utils/StringUtils.java | 6 ++-- 7 files changed, 30 insertions(+), 25 deletions(-) diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java index 30f19a6..18640e2 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java @@ -30,12 +30,16 @@ private GithubResourcepackManager() { public static final String MOD_ID = "github-resourcepack-manager"; public static final String MOD_URI = "gh-rp-manager"; - public static final Path RESOURCEPACK_FOLDER = PlatformMain.INSTANCE.getConfigDir().resolve(".resource-pack"); - public static final Path REPO_ROOT_FOLDER = RESOURCEPACK_FOLDER.resolve("git"); - public static final Path OUTPUT_FOLDER = RESOURCEPACK_FOLDER.resolve("output"); - public static final Pattern PACK_NAME_PATTERN = Pattern.compile("\\d+-"); - public static final UUID PACK_UUID = UUID.fromString("60ab8dc7-08d1-4f5f-a9a8-9a01d048b7b9"); + public static final Path DATA_FOLDER = PlatformMain.INSTANCE.getConfigDir().resolve(".packs"); + public static final Path GIT_FOLDER = DATA_FOLDER.resolve("git"); + public static final Path RESOURCEPACK_FOLDER = DATA_FOLDER.resolve("resource-pack"); + public static final Path DATAPACK_FOLDER = DATA_FOLDER.resolve("data-pack"); + + public static final Path RESOURCEPACK_OUTPUT_FOLDER = RESOURCEPACK_FOLDER.resolve("output"); + + public static final Pattern RESOURCEPACK_NAME_PATTERN = Pattern.compile("\\d+-"); + public static final UUID RESOURCEPACK_UUID = UUID.fromString("60ab8dc7-08d1-4f5f-a9a8-9a01d048b7b9"); public static ModConfig config; @@ -61,19 +65,21 @@ public static void initialize() { private static void createFolderStructure() throws GithubResourcepackManagerException { try { - Files.createDirectories(OUTPUT_FOLDER); + Files.createDirectories(RESOURCEPACK_OUTPUT_FOLDER); + Files.createDirectories(DATAPACK_FOLDER); + Files.createDirectories(GIT_FOLDER); } catch (IOException e) { - throw new GithubResourcepackManagerException("Failed to create directory '%s'!", OUTPUT_FOLDER); + throw new GithubResourcepackManagerException("Failed to create directory '%s'!", RESOURCEPACK_OUTPUT_FOLDER); } } public static void updatePack(final UpdateType updateType) { - LOGGER.info("Updating resourcepack..."); + LOGGER.info("Updating packs..."); if (updateType == UpdateType.COMMAND_FORCE) { LOGGER.warn("Forced pack update! Deleting data directory and continuing..."); try { - FileUtils.deleteDirectory(RESOURCEPACK_FOLDER.toFile()); + FileUtils.deleteDirectory(DATA_FOLDER.toFile()); } catch (IOException e) { LOGGER.error("Failed to delete directory!", e); return; @@ -101,12 +107,12 @@ public static void updatePack(final UpdateType updateType) { // Get the location of the old pack, if it exists. - final String oldPackName = getOldPackName(); - final Path oldPackPath = oldPackName == null ? null : OUTPUT_FOLDER.resolve(oldPackName); + final String oldResourcePackName = getOldPackName(); + final Path oldResourcePackPath = oldResourcePackName == null ? null : RESOURCEPACK_OUTPUT_FOLDER.resolve(oldResourcePackName); // Check if pack was updated - final boolean wasUpdated = gitHandler.getWasUpdated() || oldPackPath == null || !oldPackPath.toFile().exists(); + final boolean wasUpdated = gitHandler.getWasUpdated() || oldResourcePackPath == null || !oldResourcePackPath.toFile().exists(); if (!wasUpdated) { LOGGER.info("Pack hasn't changed since last update. Skipping new pack generation."); } @@ -117,7 +123,7 @@ public static void updatePack(final UpdateType updateType) { LOGGER.info("Getting pack location..."); failed = false; try { - packHandler.generatePack(wasUpdated, oldPackPath, oldPackName); + packHandler.generatePack(wasUpdated, oldResourcePackPath, oldResourcePackName); } catch (GithubResourcepackManagerException e) { LOGGER.error("Failed to generate pack!", e); failed = true; diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java index 561376a..7329ad9 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java @@ -240,7 +240,7 @@ public Path getWebhookBody() { } public Path getResourcePackRoot() { - return REPO_ROOT_FOLDER.resolve(config.resourcePackProvider.rootLocation.startsWith("/") ? config.resourcePackProvider.rootLocation.substring(1) : config.resourcePackProvider.rootLocation); + return GIT_FOLDER.resolve(config.resourcePackProvider.rootLocation.startsWith("/") ? config.resourcePackProvider.rootLocation.substring(1) : config.resourcePackProvider.rootLocation); } public Path getPacksDir() { return getResourcePackRoot().resolve("packs"); diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/GitHandler.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/GitHandler.java index 8a0f3db..d62d79c 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/GitHandler.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/GitHandler.java @@ -72,11 +72,11 @@ private static void updateRepository(boolean retry) throws GithubResourcepackMan credentialsProvider = new UsernamePasswordCredentialsProvider(config.repositoryInfo.username, config.repositoryInfo.token); // If the repo folder doesn't exist, clone the repository. - if (!REPO_ROOT_FOLDER.toFile().exists()) cloneRepository(credentialsProvider); + if (!GIT_FOLDER.toFile().exists()) cloneRepository(credentialsProvider); // Pull from the remote boolean updateFailed = false; - try (Git git = Git.open(REPO_ROOT_FOLDER.toFile())) { + try (Git git = Git.open(GIT_FOLDER.toFile())) { final PullResult result = git.pull() .setCredentialsProvider(credentialsProvider) .setContentMergeStrategy(ContentMergeStrategy.THEIRS) @@ -106,7 +106,7 @@ private static void updateRepository(boolean retry) throws GithubResourcepackMan LOGGER.info("Deleting git folder and trying again..."); try { - FileUtils.deleteDirectory(REPO_ROOT_FOLDER.toFile()); + FileUtils.deleteDirectory(GIT_FOLDER.toFile()); } catch (IOException e) { LOGGER.error("Failed to delete directory!", e); } @@ -120,7 +120,7 @@ private static void cloneRepository(CredentialsProvider credentialsProvider) thr try { Git git = Git.cloneRepository() .setURI(config.repositoryInfo.url) - .setDirectory(REPO_ROOT_FOLDER.toFile()) + .setDirectory(GIT_FOLDER.toFile()) .setBranch(config.getGithubRef()) .setCredentialsProvider(credentialsProvider) .call(); @@ -143,7 +143,7 @@ private static Ref getLatestCommit() throws GithubResourcepackManagerException { } private static Repository getRepository() throws GithubResourcepackManagerException { - try (Git git = Git.open(REPO_ROOT_FOLDER.toFile())) { + try (Git git = Git.open(GIT_FOLDER.toFile())) { return git.getRepository(); } catch (IOException e) { throw new GithubResourcepackManagerException("Failed to open repository!", e); diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/PackHandler.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/PackHandler.java index c20635f..91e7b9b 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/PackHandler.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/PackHandler.java @@ -184,7 +184,7 @@ private Path handleOldPackAndGetOutputPackPath(boolean wasUpdated, @Nullable Pat } catch (IOException e) { LOGGER.error("Failed to delete old pack!", new GithubResourcepackManagerException("Failed to delete old pack '%s'!", e, oldPackPath)); } - return OUTPUT_FOLDER.resolve(newPackName); + return RESOURCEPACK_OUTPUT_FOLDER.resolve(newPackName); } private String generateRandomPackName(@Nullable String oldPackNameString) { diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/WebhookHttpHandler.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/WebhookHttpHandler.java index 52bfd94..51f0f7d 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/WebhookHttpHandler.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/WebhookHttpHandler.java @@ -60,6 +60,5 @@ public static void handleRequest(ChannelHandlerContext ctx, FullHttpRequest requ LOGGER.debug("Tracked branch has been updated, updating local pack..."); GithubResourcepackManager.updatePack(GithubResourcepackManager.UpdateType.WEBHOOK); - LOGGER.debug("Local pack has been updated."); } } diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformServerProperties.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformServerProperties.java index bb728b0..833666e 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformServerProperties.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformServerProperties.java @@ -34,7 +34,7 @@ default void updatePackProperties(PackHandler packHandler) throws GithubResource LOGGER.info("New resource pack url: '%s'", resourcePackUrl); LOGGER.info("New resource pack sha1: '%s'", resourcePackSha1); setProperties(Map.of( - "resource-pack-id", PACK_UUID.toString(), + "resource-pack-id", RESOURCEPACK_UUID.toString(), "resource-pack", resourcePackUrl, "resource-pack-sha1", resourcePackSha1 )); diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/utils/StringUtils.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/utils/StringUtils.java index 67a731b..3f8d63b 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/utils/StringUtils.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/utils/StringUtils.java @@ -6,7 +6,7 @@ import java.util.Map; import java.util.regex.Matcher; -import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.PACK_NAME_PATTERN; +import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.RESOURCEPACK_NAME_PATTERN; import static top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging.LOGGER; public final class StringUtils { @@ -48,7 +48,7 @@ public static String replacePlaceholders(String string, Map plac public static int extractPriorityFromFile(File file) { final String filename = file.getName(); - final Matcher matcher = PACK_NAME_PATTERN.matcher(filename); + final Matcher matcher = RESOURCEPACK_NAME_PATTERN.matcher(filename); if (!matcher.find()) { LOGGER.error("File '%s' doesn't start with priority!", file); @@ -61,7 +61,7 @@ public static int extractPriorityFromFile(File file) { public static String nameWithoutPriorityString(File file) throws GithubResourcepackManagerException { final String filename = file.getName(); - final Matcher matcher = PACK_NAME_PATTERN.matcher(filename); + final Matcher matcher = RESOURCEPACK_NAME_PATTERN.matcher(filename); if (!matcher.find()) throw new GithubResourcepackManagerException("File '%s' doesn't start with priority!", file); From da9ab1bb4dc807e3c522fb1840feb572048ab6fe Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Sat, 14 Jun 2025 19:58:51 +0300 Subject: [PATCH 04/52] Add method for getting diff between a commit hash and the head. Will be used for detecting changes to resource and data packs independently --- .../handler/GitHandler.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/GitHandler.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/GitHandler.java index d62d79c..d9e8607 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/GitHandler.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/GitHandler.java @@ -5,6 +5,8 @@ import org.eclipse.jgit.api.PullResult; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.errors.RepositoryNotFoundException; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectReader; import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.merge.ContentMergeStrategy; @@ -13,10 +15,12 @@ import org.eclipse.jgit.revwalk.RevWalk; import org.eclipse.jgit.transport.CredentialsProvider; import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider; +import org.eclipse.jgit.treewalk.CanonicalTreeParser; import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; import top.offsetmonkey538.githubresourcepackmanager.git.CommitProperties; import java.io.IOException; +import java.util.List; import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.*; import static top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging.LOGGER; @@ -142,6 +146,37 @@ private static Ref getLatestCommit() throws GithubResourcepackManagerException { } } + private static List getDiff(String startingHash) throws GithubResourcepackManagerException { + try (Git git = Git.open(GIT_FOLDER.toFile())) { + final Repository repository = git.getRepository(); + + final ObjectId headCommit = repository.resolve("HEAD^{tree}"); + final ObjectId startingCommit = repository.resolve("%s^{tree}".formatted(startingHash)); + + try (final ObjectReader repoReader = repository.newObjectReader()) { + final CanonicalTreeParser headTreeParser = new CanonicalTreeParser(); + headTreeParser.reset(repoReader, headCommit); + + final CanonicalTreeParser startingTreeParser = new CanonicalTreeParser(); + startingTreeParser.reset(repoReader, startingCommit); + + return git + .diff() + .setNewTree(headTreeParser) + .setOldTree(startingTreeParser) + .call() + + .stream() + .map(entry -> "/dev/null".equals(entry.getNewPath()) ? entry.getOldPath() : entry.getNewPath()) + .toList(); + } catch (GitAPIException e) { + throw new RuntimeException(e); + } + } catch (IOException e) { + throw new GithubResourcepackManagerException("Failed to open repository!", e); + } + } + private static Repository getRepository() throws GithubResourcepackManagerException { try (Git git = Git.open(GIT_FOLDER.toFile())) { return git.getRepository(); From e450e669166ed2f9045f6fc54c41a8176ec053e8 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Sat, 14 Jun 2025 21:38:40 +0300 Subject: [PATCH 05/52] Refactor resourcepack handling --- .../GithubResourcepackManager.java | 81 +++++++++---------- .../config/ConfigManager.java | 17 ++++ .../config/ModConfig.java | 54 ++++++++++--- .../discord/basic/BasicFailMessage.java | 15 ++++ .../BasicSuccessMessage.java} | 6 +- .../discord/embed/EmbedFailMessage.java | 22 +++++ .../EmbedSuccessMessage.java} | 8 +- .../handler/GitHandler.java | 12 +-- ...kHandler.java => ResourcePackHandler.java} | 2 +- .../networking/FileHttpHandler.java | 4 +- .../platform/PlatformServerProperties.java | 4 +- .../utils/WebhookSender.java | 14 ++-- 12 files changed, 162 insertions(+), 77 deletions(-) create mode 100644 common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/webhook/discord/basic/BasicFailMessage.java rename common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/webhook/discord/{BasicMessage.java => basic/BasicSuccessMessage.java} (81%) create mode 100644 common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/webhook/discord/embed/EmbedFailMessage.java rename common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/webhook/discord/{EmbedMessage.java => embed/EmbedSuccessMessage.java} (86%) rename common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/{PackHandler.java => ResourcePackHandler.java} (99%) diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java index 18640e2..fdeefbc 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java @@ -4,14 +4,13 @@ import top.offsetmonkey538.githubresourcepackmanager.config.ModConfig; import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; import top.offsetmonkey538.githubresourcepackmanager.handler.GitHandler; -import top.offsetmonkey538.githubresourcepackmanager.handler.PackHandler; +import top.offsetmonkey538.githubresourcepackmanager.handler.ResourcePackHandler; import top.offsetmonkey538.githubresourcepackmanager.networking.MainHttpHandler; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformCommand; import top.offsetmonkey538.githubresourcepackmanager.config.ConfigManager; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformMain; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformServerProperties; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformText; -import top.offsetmonkey538.githubresourcepackmanager.utils.*; import top.offsetmonkey538.meshlib.api.HttpHandlerRegistry; import java.io.IOException; @@ -43,9 +42,7 @@ private GithubResourcepackManager() { public static ModConfig config; - public static GitHandler gitHandler; - public static PackHandler packHandler; - + public static ResourcePackHandler resourcePackHandler; public static void initialize() { PlatformCommand.INSTANCE.registerGithubRpManagerCommand(); @@ -93,7 +90,7 @@ public static void updatePack(final UpdateType updateType) { } // Git stuff - gitHandler = new GitHandler(); + final GitHandler gitHandler = new GitHandler(); LOGGER.info("Updating git repository..."); boolean failed = false; @@ -105,35 +102,48 @@ public static void updatePack(final UpdateType updateType) { } if (!failed) LOGGER.info("Successfully updated git repository!"); + if (config.resourcePackProvider.enabled) { + LOGGER.info(""); + LOGGER.info("Updating resource pack..."); + updateResourcePack(gitHandler, updateType, failed); + LOGGER.info("Resource pack updated!"); + } + if (config.dataPackProvider.enabled) { + LOGGER.info(""); + LOGGER.info("Updating data pack..."); + // TODO: implement datapack stuff + LOGGER.info("Data pack updated!"); + } + } + private static void updateResourcePack(final GitHandler gitHandler, final UpdateType updateType, boolean updateFailed) { // Get the location of the old pack, if it exists. - final String oldResourcePackName = getOldPackName(); + final String oldResourcePackName = getOldResourcePackName(); final Path oldResourcePackPath = oldResourcePackName == null ? null : RESOURCEPACK_OUTPUT_FOLDER.resolve(oldResourcePackName); - // Check if pack was updated - final boolean wasUpdated = gitHandler.getWasUpdated() || oldResourcePackPath == null || !oldResourcePackPath.toFile().exists(); + final boolean wasUpdated = gitHandler.getChangedFiles().stream().anyMatch(it -> it.startsWith(config.resourcePackProvider.getRootLocation())) || oldResourcePackPath == null || !oldResourcePackPath.toFile().exists(); if (!wasUpdated) { LOGGER.info("Pack hasn't changed since last update. Skipping new pack generation."); } // Generate pack - packHandler = new PackHandler(); + resourcePackHandler = new ResourcePackHandler(); LOGGER.info("Getting pack location..."); - failed = false; + boolean failed = false; try { - packHandler.generatePack(wasUpdated, oldResourcePackPath, oldResourcePackName); + resourcePackHandler.generatePack(wasUpdated, oldResourcePackPath, oldResourcePackName); } catch (GithubResourcepackManagerException e) { LOGGER.error("Failed to generate pack!", e); - failed = true; + failed = updateFailed = true; } - if (!failed) LOGGER.info("Pack location is '%s'!", packHandler.getOutputPackPath()); + if (!failed) LOGGER.info("Pack location is '%s'!", resourcePackHandler.getOutputPackPath()); // Update server.properties file. try { - PlatformServerProperties.INSTANCE.updatePackProperties(packHandler); + PlatformServerProperties.INSTANCE.updatePackProperties(resourcePackHandler); } catch (GithubResourcepackManagerException e) { LOGGER.error("Failed to update server.properties file!", e); } @@ -141,7 +151,7 @@ public static void updatePack(final UpdateType updateType) { // Generate placeholder map final Map placeholders = new HashMap<>(); if (gitHandler.getCommitProperties() != null) placeholders.putAll(gitHandler.getCommitProperties().toPlaceholdersMap()); - placeholders.put("{downloadUrl}", config.getPackUrl(packHandler.getOutputPackName())); + placeholders.put("{downloadUrl}", config.getPackUrl(resourcePackHandler.getOutputPackName())); placeholders.put("{updateType}", updateType.name()); placeholders.put("{wasUpdated}", String.valueOf(wasUpdated)); LOGGER.info("Placeholders: %s", placeholders); @@ -153,33 +163,20 @@ public static void updatePack(final UpdateType updateType) { LOGGER.error("Failed to send update message in chat!", e); } - // Trigger webhook - try { - triggerWebhook(wasUpdated, placeholders, updateType); - } catch (GithubResourcepackManagerException e) { - LOGGER.error("Failed to trigger webhook!", e); - } - - - LOGGER.info("Resourcepack updated!"); - } - - private static void triggerWebhook(boolean wasUpdated, Map placeholders, UpdateType updateType) throws GithubResourcepackManagerException { - // todo: change logic to match enable/disable and fail/success webhooks - if (config.resourcePackProvider.successWebhook.url == null || config.resourcePackProvider.successWebhook.body == null) return; - if (config.resourcePackProvider.successWebhook.body.contains("discord") && !wasUpdated) { - LOGGER.info("Not sending discord webhook because pack was not updated."); + // Trigger webhooks + if (!wasUpdated) { + LOGGER.info("Not sending webhook because pack was not updated."); return; } - - try { - //noinspection DataFlowIssue: Only returns null when `config.webhookBody` is null, which we have already checked - String webhookBody = Files.readString(config.getWebhookBody()); - webhookBody = StringUtils.replacePlaceholders(webhookBody, placeholders, true); - - WebhookSender.send(webhookBody, config.getWebhookUrl(), updateType, gitHandler.getWasUpdated()); - } catch (IOException e) { - throw new GithubResourcepackManagerException("Failed to read content of webhook body file '%s'!", e, config.resourcePackProvider.successWebhook.body); + if (!updateFailed) try { + config.resourcePackProvider.successWebhook.trigger(true, placeholders, updateType); + } catch (GithubResourcepackManagerException e) { + LOGGER.error("Failed to trigger success webhook!", e); + } + else try { + config.resourcePackProvider.failWebhook.trigger(false, placeholders, updateType); + } catch (GithubResourcepackManagerException e) { + LOGGER.error("Failed to trigger fail webhook!", e); } } @@ -192,7 +189,7 @@ private static void sendUpdateMessage(boolean wasUpdated, final Map errors, final ModConfig.WebhookInfo webhook, final String webhookPath) { + if (!webhook.enabled) return; + + if (webhook.url == null) errors.add("Field '%s.url' not set, but '%s.enabled' is true!".formatted(webhookPath, webhookPath)); + if (webhook.body == null) errors.add("Field '%s.body' not set, but '%s.enabled' is true!".formatted(webhookPath, webhookPath)); + + final Path bodyPath = webhook.getBodyPath(); + if (bodyPath != null && !Files.exists(bodyPath)) errors.add("Field '%s.body' points to file '%s', which doesn't exist!".formatted(webhookPath, bodyPath.toAbsolutePath())); + } + private static ModConfig load() { final Jankson jankson = Jankson.builder().build(); final File configFile = CURRENT_CONFIG_FILE_PATH.toFile(); diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java index 7329ad9..93b19a0 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java @@ -4,17 +4,24 @@ import blue.endless.jankson.Jankson; import blue.endless.jankson.JsonGrammar; import blue.endless.jankson.api.Marshaller; +import org.jetbrains.annotations.Nullable; import top.offsetmonkey538.githubresourcepackmanager.config.webhook.BasicWebhook; import top.offsetmonkey538.githubresourcepackmanager.config.webhook.DefaultWebhookBody; -import top.offsetmonkey538.githubresourcepackmanager.config.webhook.discord.BasicMessage; -import top.offsetmonkey538.githubresourcepackmanager.config.webhook.discord.EmbedMessage; +import top.offsetmonkey538.githubresourcepackmanager.config.webhook.discord.basic.BasicFailMessage; +import top.offsetmonkey538.githubresourcepackmanager.config.webhook.discord.basic.BasicSuccessMessage; +import top.offsetmonkey538.githubresourcepackmanager.config.webhook.discord.embed.EmbedFailMessage; +import top.offsetmonkey538.githubresourcepackmanager.config.webhook.discord.embed.EmbedSuccessMessage; +import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformServerProperties; +import top.offsetmonkey538.githubresourcepackmanager.utils.StringUtils; +import top.offsetmonkey538.githubresourcepackmanager.utils.WebhookSender; import java.io.IOException; import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; import java.util.List; +import java.util.Map; import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.*; import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.config; @@ -68,6 +75,10 @@ public static class ResourcePackProvider { public WebhookInfo successWebhook = new WebhookInfo(); @Comment("Webhook to be sent when pack updating failed") public WebhookInfo failWebhook = new WebhookInfo(); + + public String getRootLocation() { + return rootLocation.startsWith("/") ? rootLocation.substring(1) : rootLocation; + } } public static class DataPackProvider { @@ -85,10 +96,13 @@ public static class DataPackProvider { public WebhookInfo successWebhook = new WebhookInfo(); @Comment("Webhook to be sent when pack updating failed") public WebhookInfo failWebhook = new WebhookInfo(); + + public String getRootLocation() { + return rootLocation.startsWith("/") ? rootLocation.substring(1) : rootLocation; + } } public static class WebhookInfo { - public WebhookInfo() { } @@ -105,6 +119,24 @@ public WebhookInfo(boolean enabled, String url, String body) { public String url = null; @Comment("The relative path from the config directory to a webhook body file. For example \"discord/basic_message.json\" or \"discord/embed_message.json\"") public String body = null; + + public void trigger(final boolean updateSucceeded, final Map placeholders, final UpdateType updateType) throws GithubResourcepackManagerException { + if (!enabled) return; + + try { + //noinspection DataFlowIssue: Only returns null when `body` is null, which we have already checked + String webhookBody = Files.readString(getBodyPath()); + webhookBody = StringUtils.replacePlaceholders(webhookBody, placeholders, true); + + WebhookSender.send(webhookBody, config.getWebhookUrl(), updateType, updateSucceeded); + } catch (IOException e) { + throw new GithubResourcepackManagerException("Failed to read content of webhook body file '%s'!", e, config.resourcePackProvider.successWebhook.body); + } + } + + public @Nullable Path getBodyPath() { + return body == null ? null : CURRENT_CONFIG_FILE_PATH.getParent().resolve(body); + } } //@Comment("!!!!Please check the wiki for how to set up the mod. It is linked on both the Modrinth and GitHub pages!!!!") @@ -203,7 +235,14 @@ protected List getDatafixers() { public void createDefaultWebhooks() { final Jankson jankson = new Jankson.Builder().build(); - final List webhookBodies = List.of(new BasicWebhook(), new BasicMessage(), new EmbedMessage()); + final List webhookBodies = List.of( + new BasicWebhook(), + + new BasicSuccessMessage(), + new BasicFailMessage(), + new EmbedSuccessMessage(), + new EmbedFailMessage() + ); for (DefaultWebhookBody webhook : webhookBodies) { final Path location = CURRENT_CONFIG_FILE_PATH.getParent().resolve(webhook.getName()); @@ -234,13 +273,8 @@ public URI getWebhookUrl() { return URI.create(resourcePackProvider.successWebhook.url); } - public Path getWebhookBody() { - if (resourcePackProvider.successWebhook.body == null) return null; - return CURRENT_CONFIG_FILE_PATH.getParent().resolve(resourcePackProvider.successWebhook.body); - } - public Path getResourcePackRoot() { - return GIT_FOLDER.resolve(config.resourcePackProvider.rootLocation.startsWith("/") ? config.resourcePackProvider.rootLocation.substring(1) : config.resourcePackProvider.rootLocation); + return GIT_FOLDER.resolve(config.resourcePackProvider.getRootLocation()); } public Path getPacksDir() { return getResourcePackRoot().resolve("packs"); diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/webhook/discord/basic/BasicFailMessage.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/webhook/discord/basic/BasicFailMessage.java new file mode 100644 index 0000000..a52ef58 --- /dev/null +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/webhook/discord/basic/BasicFailMessage.java @@ -0,0 +1,15 @@ +package top.offsetmonkey538.githubresourcepackmanager.config.webhook.discord.basic; + +import top.offsetmonkey538.githubresourcepackmanager.config.webhook.DefaultWebhookBody; + +public final class BasicFailMessage implements DefaultWebhookBody { + + public final String username = "GitHub Resource Pack Manager"; + public final String avatar_url = "https://github.com/OffsetMods538/Github-Resourcepack-Manager/blob/master/src/main/resources/assets/github-resourcepack-manager/icon.png?raw=true"; + public final String content = "Pack update failed!"; + + @Override + public String getName() { + return "discord/basic/fail.json"; + } +} diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/webhook/discord/BasicMessage.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/webhook/discord/basic/BasicSuccessMessage.java similarity index 81% rename from common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/webhook/discord/BasicMessage.java rename to common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/webhook/discord/basic/BasicSuccessMessage.java index 640a4d4..6f29821 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/webhook/discord/BasicMessage.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/webhook/discord/basic/BasicSuccessMessage.java @@ -1,8 +1,8 @@ -package top.offsetmonkey538.githubresourcepackmanager.config.webhook.discord; +package top.offsetmonkey538.githubresourcepackmanager.config.webhook.discord.basic; import top.offsetmonkey538.githubresourcepackmanager.config.webhook.DefaultWebhookBody; -public final class BasicMessage implements DefaultWebhookBody { +public final class BasicSuccessMessage implements DefaultWebhookBody { public final String username = "GitHub Resource Pack Manager"; public final String avatar_url = "https://github.com/OffsetMods538/Github-Resourcepack-Manager/blob/master/src/main/resources/assets/github-resourcepack-manager/icon.png?raw=true"; @@ -10,6 +10,6 @@ public final class BasicMessage implements DefaultWebhookBody { @Override public String getName() { - return "discord/basic_message.json"; + return "discord/basic/success.json"; } } diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/webhook/discord/embed/EmbedFailMessage.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/webhook/discord/embed/EmbedFailMessage.java new file mode 100644 index 0000000..11e7313 --- /dev/null +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/webhook/discord/embed/EmbedFailMessage.java @@ -0,0 +1,22 @@ +package top.offsetmonkey538.githubresourcepackmanager.config.webhook.discord.embed; + +import top.offsetmonkey538.githubresourcepackmanager.config.webhook.DefaultWebhookBody; + +public final class EmbedFailMessage implements DefaultWebhookBody { + + public final String username = "GitHub Resource Pack Manager"; + public final String avatar_url = "https://github.com/OffsetMods538/Github-Resourcepack-Manager/blob/master/src/main/resources/assets/github-resourcepack-manager/icon.png?raw=true"; + public final Embed[] embeds = new Embed[] { + new Embed( + "Pack update failed!", + 0xFF0000 + ) + }; + + public record Embed(String title, int color) {} + + @Override + public String getName() { + return "discord/embed/fail.json"; + } +} diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/webhook/discord/EmbedMessage.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/webhook/discord/embed/EmbedSuccessMessage.java similarity index 86% rename from common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/webhook/discord/EmbedMessage.java rename to common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/webhook/discord/embed/EmbedSuccessMessage.java index b5057d2..3af4d13 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/webhook/discord/EmbedMessage.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/webhook/discord/embed/EmbedSuccessMessage.java @@ -1,8 +1,8 @@ -package top.offsetmonkey538.githubresourcepackmanager.config.webhook.discord; +package top.offsetmonkey538.githubresourcepackmanager.config.webhook.discord.embed; import top.offsetmonkey538.githubresourcepackmanager.config.webhook.DefaultWebhookBody; -public final class EmbedMessage implements DefaultWebhookBody { +public final class EmbedSuccessMessage implements DefaultWebhookBody { public final String username = "GitHub Resource Pack Manager"; public final String avatar_url = "https://github.com/OffsetMods538/Github-Resourcepack-Manager/blob/master/src/main/resources/assets/github-resourcepack-manager/icon.png?raw=true"; @@ -10,7 +10,7 @@ public final class EmbedMessage implements DefaultWebhookBody { new Embed( "New update for pack released!", "Download [here]({downloadUrl})!", - 16722304, + 0xFF2980, new Field[] { new Field( "Description", @@ -25,6 +25,6 @@ public record Field(String name, String value) {} @Override public String getName() { - return "discord/embed_message.json"; + return "discord/embed/success.json"; } } diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/GitHandler.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/GitHandler.java index d9e8607..6f55825 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/GitHandler.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/GitHandler.java @@ -1,6 +1,6 @@ package top.offsetmonkey538.githubresourcepackmanager.handler; -import org.apache.commons.io.FileUtils; +import org.apache.commons.io.file.PathUtils; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.PullResult; import org.eclipse.jgit.api.errors.GitAPIException; @@ -28,7 +28,7 @@ public class GitHandler { private CommitProperties commitProperties; - private boolean wasUpdated; + private List changedFiles; public void updateRepositoryAndGenerateCommitProperties() throws GithubResourcepackManagerException { String originalCommitHash; @@ -45,7 +45,7 @@ public void updateRepositoryAndGenerateCommitProperties() throws GithubResourcep final String newCommitHash = getLatestCommitHash(); commitProperties = getLatestCommitProperties(originalCommitHash, newCommitHash); - wasUpdated = !newCommitHash.equals(originalCommitHash); + changedFiles = getDiff(originalCommitHash); } @@ -110,7 +110,7 @@ private static void updateRepository(boolean retry) throws GithubResourcepackMan LOGGER.info("Deleting git folder and trying again..."); try { - FileUtils.deleteDirectory(GIT_FOLDER.toFile()); + PathUtils.deleteDirectory(GIT_FOLDER); } catch (IOException e) { LOGGER.error("Failed to delete directory!", e); } @@ -189,7 +189,7 @@ public CommitProperties getCommitProperties() { return commitProperties; } - public boolean getWasUpdated() { - return wasUpdated; + public List getChangedFiles() { + return changedFiles; } } diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/PackHandler.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/ResourcePackHandler.java similarity index 99% rename from common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/PackHandler.java rename to common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/ResourcePackHandler.java index 91e7b9b..ac0eebb 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/PackHandler.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/ResourcePackHandler.java @@ -20,7 +20,7 @@ import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.*; import static top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging.LOGGER; -public class PackHandler { +public class ResourcePackHandler { private Path outputPackPath; public void generatePack(boolean wasUpdated, Path oldPackPath, String oldPackName) throws GithubResourcepackManagerException { diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/FileHttpHandler.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/FileHttpHandler.java index 217b1a0..3ce42b4 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/FileHttpHandler.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/FileHttpHandler.java @@ -10,14 +10,14 @@ import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE; import static io.netty.handler.codec.http.HttpResponseStatus.OK; import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; -import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.packHandler; +import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.resourcePackHandler; import static top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging.LOGGER; public final class FileHttpHandler { private FileHttpHandler() {} public static void handleRequest(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception { - final File fileToServe = packHandler.getOutputPackFile(); + final File fileToServe = resourcePackHandler.getOutputPackFile(); final long fileLength = fileToServe.length(); diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformServerProperties.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformServerProperties.java index 833666e..98a82bd 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformServerProperties.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformServerProperties.java @@ -2,7 +2,7 @@ import com.google.common.hash.Hashing; import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; -import top.offsetmonkey538.githubresourcepackmanager.handler.PackHandler; +import top.offsetmonkey538.githubresourcepackmanager.handler.ResourcePackHandler; import java.io.IOException; import java.util.Map; @@ -19,7 +19,7 @@ public interface PlatformServerProperties { void setProperties(Map properties); void reload() throws GithubResourcepackManagerException; - default void updatePackProperties(PackHandler packHandler) throws GithubResourcepackManagerException { + default void updatePackProperties(ResourcePackHandler packHandler) throws GithubResourcepackManagerException { final String resourcePackUrl = config.getPackUrl(packHandler.getOutputPackName()); final String resourcePackSha1; try { diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/utils/WebhookSender.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/utils/WebhookSender.java index 9f88c8c..217655a 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/utils/WebhookSender.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/utils/WebhookSender.java @@ -14,11 +14,11 @@ private WebhookSender() { } - public static void send(String body, URI url, GithubResourcepackManager.UpdateType updateType, boolean isUpdated) throws GithubResourcepackManagerException { + public static void send(String body, URI url, GithubResourcepackManager.UpdateType updateType, boolean updateSucceeded) throws GithubResourcepackManagerException { final HttpRequest request = HttpRequest.newBuilder(url) .header("Content-Type", "application/json") .header("X-Resource-Pack-Update-Type", updateType.name()) - .header("X-Resource-Pack-Is-Updated", String.valueOf(isUpdated)) + .header("X-Resource-Pack-Update-Succeeded", String.valueOf(updateSucceeded)) .POST(HttpRequest.BodyPublishers.ofString(body)) .build(); @@ -31,11 +31,6 @@ public static void send(String body, URI url, GithubResourcepackManager.UpdateTy throw new GithubResourcepackManagerException("Failed to send http request!", e); } - final int statusCode = response.statusCode(); - if (!(statusCode >= 200 && statusCode < 300)) { - throw new GithubResourcepackManagerException("Http status code '%s'! Response was: '%s'.", statusCode, response.body()); - } - // From JDK 21 the HttpClient class extends AutoCloseable, but as we want to support Minecraft versions // that use JDK 17, where HttpClient doesn't extend AutoCloseable, we need to check if it's // an instance of AutoCloseable before trying to close it. @@ -47,5 +42,10 @@ public static void send(String body, URI url, GithubResourcepackManager.UpdateTy throw new IllegalStateException(e); } } + + final int statusCode = response.statusCode(); + if (statusCode < 200 || statusCode >= 300) { + throw new GithubResourcepackManagerException("Http status code '%s'! Response was: '%s'.", statusCode, response.body()); + } } } From 239eef867ce37112c29ba2cb831454ef0dc753e0 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Sun, 15 Jun 2025 12:47:45 +0300 Subject: [PATCH 06/52] Fix crash when there's no previous commit. (Like a force update or fresh installation) Getting diff would fail --- .../GithubResourcepackManager.java | 7 ++++-- .../handler/GitHandler.java | 24 ++++++++++++------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java index fdeefbc..1781107 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java @@ -122,7 +122,10 @@ private static void updateResourcePack(final GitHandler gitHandler, final Update final Path oldResourcePackPath = oldResourcePackName == null ? null : RESOURCEPACK_OUTPUT_FOLDER.resolve(oldResourcePackName); // Check if pack was updated - final boolean wasUpdated = gitHandler.getChangedFiles().stream().anyMatch(it -> it.startsWith(config.resourcePackProvider.getRootLocation())) || oldResourcePackPath == null || !oldResourcePackPath.toFile().exists(); + final boolean wasUpdated = + gitHandler.getChangedFiles().map(changes -> changes.stream().anyMatch(it -> it.startsWith(config.resourcePackProvider.getRootLocation()))).orElse(true) + || oldResourcePackPath == null + || !oldResourcePackPath.toFile().exists(); if (!wasUpdated) { LOGGER.info("Pack hasn't changed since last update. Skipping new pack generation."); } @@ -138,7 +141,7 @@ private static void updateResourcePack(final GitHandler gitHandler, final Update LOGGER.error("Failed to generate pack!", e); failed = updateFailed = true; } - if (!failed) LOGGER.info("Pack location is '%s'!", resourcePackHandler.getOutputPackPath()); + if (!failed) LOGGER.info("Pack location is '%s'!", resourcePackHandler.getOutputPackPath().toAbsolutePath()); // Update server.properties file. diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/GitHandler.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/GitHandler.java index 6f55825..5dd0112 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/GitHandler.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/GitHandler.java @@ -16,11 +16,13 @@ import org.eclipse.jgit.transport.CredentialsProvider; import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider; import org.eclipse.jgit.treewalk.CanonicalTreeParser; +import org.jetbrains.annotations.Nullable; import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; import top.offsetmonkey538.githubresourcepackmanager.git.CommitProperties; import java.io.IOException; import java.util.List; +import java.util.Optional; import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.*; import static top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging.LOGGER; @@ -28,7 +30,7 @@ public class GitHandler { private CommitProperties commitProperties; - private List changedFiles; + private @Nullable List changedFiles; public void updateRepositoryAndGenerateCommitProperties() throws GithubResourcepackManagerException { String originalCommitHash; @@ -45,7 +47,14 @@ public void updateRepositoryAndGenerateCommitProperties() throws GithubResourcep final String newCommitHash = getLatestCommitHash(); commitProperties = getLatestCommitProperties(originalCommitHash, newCommitHash); - changedFiles = getDiff(originalCommitHash); + + try { + changedFiles = getDiff(originalCommitHash); + } catch (Exception e) { + // Most likely to happen with a fresh install or a forced update so should be fine to just assume this + LOGGER.error("Failed to get diff between commit '%s' and '%s'! Assuming pack directories were updated anyway...", e, originalCommitHash, newCommitHash); + changedFiles = null; + } } @@ -146,12 +155,13 @@ private static Ref getLatestCommit() throws GithubResourcepackManagerException { } } - private static List getDiff(String startingHash) throws GithubResourcepackManagerException { + private static List getDiff(String startingHash) throws IOException, GitAPIException { try (Git git = Git.open(GIT_FOLDER.toFile())) { final Repository repository = git.getRepository(); final ObjectId headCommit = repository.resolve("HEAD^{tree}"); final ObjectId startingCommit = repository.resolve("%s^{tree}".formatted(startingHash)); + if (startingCommit == null) throw new IllegalArgumentException("Previous commit (hash '%s') doesn't exist!".formatted(startingHash)); try (final ObjectReader repoReader = repository.newObjectReader()) { final CanonicalTreeParser headTreeParser = new CanonicalTreeParser(); @@ -169,11 +179,9 @@ private static List getDiff(String startingHash) throws GithubResourcepa .stream() .map(entry -> "/dev/null".equals(entry.getNewPath()) ? entry.getOldPath() : entry.getNewPath()) .toList(); - } catch (GitAPIException e) { - throw new RuntimeException(e); } } catch (IOException e) { - throw new GithubResourcepackManagerException("Failed to open repository!", e); + throw new IOException("Failed to open repository!", e); } } @@ -189,7 +197,7 @@ public CommitProperties getCommitProperties() { return commitProperties; } - public List getChangedFiles() { - return changedFiles; + public Optional> getChangedFiles() { + return Optional.ofNullable(changedFiles); } } From 1d0ea71950706462c5ce81de3e316c5fb9b56fed Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Sun, 15 Jun 2025 12:51:09 +0300 Subject: [PATCH 07/52] Use resource pack root dir as 'packs' folder Makes more sense to have /resourcepack/0-pack.zip /resourcepack/1-pack.zip than /resourcepack/packs/0-pack.zip /resourcepack/packs/1-pack.zip --- .../config/ModConfig.java | 2 +- .../handler/ResourcePackHandler.java | 64 ++++++++++--------- 2 files changed, 35 insertions(+), 31 deletions(-) diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java index 93b19a0..8718a3d 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java @@ -276,7 +276,7 @@ public URI getWebhookUrl() { public Path getResourcePackRoot() { return GIT_FOLDER.resolve(config.resourcePackProvider.getRootLocation()); } - public Path getPacksDir() { + public Path getResourcePackPacksDir() { return getResourcePackRoot().resolve("packs"); } diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/ResourcePackHandler.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/ResourcePackHandler.java index ac0eebb..2304678 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/ResourcePackHandler.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/ResourcePackHandler.java @@ -3,6 +3,7 @@ import org.apache.commons.io.FileUtils; import org.apache.commons.io.filefilter.HiddenFileFilter; import org.jetbrains.annotations.Nullable; +import top.offsetmonkey538.githubresourcepackmanager.config.ModConfig; import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; import top.offsetmonkey538.githubresourcepackmanager.utils.MyFileUtils; import top.offsetmonkey538.githubresourcepackmanager.utils.StringUtils; @@ -34,10 +35,10 @@ public void generatePack(boolean wasUpdated, Path oldPackPath, String oldPackNam } private void generateNewPack() throws GithubResourcepackManagerException { - final boolean isMultiPack; + final List sourcePacks; try { - isMultiPack = getPackType(); + sourcePacks = gatherSourcePacks(); } catch (GithubResourcepackManagerException e) { throw new GithubResourcepackManagerException("Failed to determine pack type!", e); } @@ -53,16 +54,15 @@ private void generateNewPack() throws GithubResourcepackManagerException { final File tempOutputDir = MyFileUtils.createDir(tmpDir.resolve("output").toFile()); // Extract packs into the temporary packs directory - final List sourcePacks; try { - sourcePacks = extractSourcePacks(isMultiPack, tempOutputDir); + extractSourcePacks(sourcePacks, tempOutputDir); } catch (GithubResourcepackManagerException e) { throw new GithubResourcepackManagerException("Failed to extract source packs!", e); } // Write file with source pack names try { - writeSourcePacksFile(isMultiPack, sourcePacks, tempOutputDir); + writeSourcePacksFile(sourcePacks, tempOutputDir); } catch (GithubResourcepackManagerException e) { throw new GithubResourcepackManagerException("Failed to write pack content file!", e); } @@ -82,8 +82,8 @@ private void generateNewPack() throws GithubResourcepackManagerException { } } - private void writeSourcePacksFile(boolean isMultiPack, List sourcePacks, File outputDir) throws GithubResourcepackManagerException { - if (!isMultiPack) return; + private void writeSourcePacksFile(List sourcePacks, File outputDir) throws GithubResourcepackManagerException { + if (sourcePacks.size() == 1) return; final Path sourcePacksFile = outputDir.toPath().resolve("content.txt"); MyFileUtils.createNewFile(sourcePacksFile.toFile()); @@ -101,13 +101,7 @@ private void writeSourcePacksFile(boolean isMultiPack, List sourcePacks, F } } - private List extractSourcePacks(boolean isMultiPack, File tempOutputDir) throws GithubResourcepackManagerException { - // Gather source packs - final List sourcePacks; - - if (!isMultiPack) sourcePacks = List.of(config.getResourcePackRoot().toFile()); - else sourcePacks = gatherSourcePacks(); - + private void extractSourcePacks(List sourcePacks, File tempOutputDir) throws GithubResourcepackManagerException { // Extract source packs for (File sourcePack : sourcePacks) { if (sourcePack.isDirectory()) { @@ -126,13 +120,11 @@ private List extractSourcePacks(boolean isMultiPack, File tempOutputDir) t LOGGER.error("'%s' is not a valid pack! Ignoring...", sourcePack); sourcePacks.remove(sourcePack); } - - return sourcePacks; } - private List gatherSourcePacks() throws GithubResourcepackManagerException { + private List gatherSourcePacksFrom(final Path packsDir) throws GithubResourcepackManagerException { // Gather resource packs - final File[] sourcePacksArray = config.getPacksDir().toFile().listFiles(); + final File[] sourcePacksArray = packsDir.toFile().listFiles(); if (sourcePacksArray == null) throw new GithubResourcepackManagerException("Repository contains empty 'packs' folder!"); // Return source packs sorted in correct order. @@ -142,35 +134,47 @@ private List gatherSourcePacks() throws GithubResourcepackManagerException } /** - * Get the type of the pack. Either multi or single pack. + * Gather list of {@link File}s to construct the final pack from. + *

+ * If there's a {@code pack.mcmeta} file in the {@link ModConfig#getResourcePackRoot() resource pack root directory}, the root directory will be the only file. + *

+ *

+ * If there's a {@link ModConfig#getResourcePackPacksDir() packs directory} in the pack root, all files directly in the packs directory will be returned. + *

+ *

+ * If there's neither a {@code pack.mcmeta} file or {@link ModConfig#getResourcePackPacksDir() packs directory} in the pack root, all files directly in the pack root will be returned. + *

* - * @return true if type is multi pack, false if it's single pack. - * @throws GithubResourcepackManagerException when the type of the pack couldn't be determined. + * @return A list of {@link File}s to construct the final pack from. May include directories and .zip files. + * @throws GithubResourcepackManagerException when the source packs could not be determined. */ - private boolean getPackType() throws GithubResourcepackManagerException { - LOGGER.info("Checking for 'pack.mcmeta' in repository root..."); + private List gatherSourcePacks() throws GithubResourcepackManagerException { + LOGGER.info("Checking for 'pack.mcmeta' in resource pack root..."); final boolean hasPackMcmeta = config.getResourcePackRoot().resolve("pack.mcmeta").toFile().exists(); LOGGER.info("%sFound!", hasPackMcmeta ? "" : "Not "); - LOGGER.info("Checking for 'packs' directory in repository root..."); - final boolean hasPacksFolder = config.getPacksDir().toFile().exists() && config.getPacksDir().toFile().isDirectory(); + LOGGER.info("Checking for 'packs' directory in resource pack root..."); + Path packsDir = config.getResourcePackPacksDir(); + final boolean hasPacksFolder = Files.exists(packsDir) && Files.isDirectory(packsDir); LOGGER.info("%sFound!", hasPacksFolder ? "" : "Not "); if (hasPackMcmeta && hasPacksFolder) { - throw new GithubResourcepackManagerException("Found both 'pack.mcmeta' and the 'packs' directory in repository root '%s'!", config.getPacksDir().toAbsolutePath()); + throw new GithubResourcepackManagerException("Found both 'pack.mcmeta' and the 'packs' directory in resource pack root '%s'!", config.getResourcePackPacksDir().toAbsolutePath()); } if (!hasPackMcmeta && !hasPacksFolder) { - throw new GithubResourcepackManagerException("Found neither 'pack.mcmeta' nor the 'packs' directory in repository root '%s'!", config.getPacksDir().toAbsolutePath()); + LOGGER.info("Found neither 'pack.mcmeta' nor the 'packs' directory in resource pack root '%s'!", config.getResourcePackPacksDir().toAbsolutePath()); + LOGGER.info("Assuming resource pack root '%s' as 'packs' directory.", config.getResourcePackPacksDir().toAbsolutePath()); + packsDir = config.getResourcePackRoot(); } if (hasPackMcmeta) { - LOGGER.info("Using repository root as resource pack."); - return false; + LOGGER.info("Using resource pack root as resource pack."); + return List.of(config.getResourcePackRoot().toFile()); } LOGGER.info("Using 'packs' directory for resource packs."); - return true; + return gatherSourcePacksFrom(packsDir); } From 95640aa6c1e40dd69c9d6b2b67fac84653a5c573 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Sun, 15 Jun 2025 14:34:23 +0300 Subject: [PATCH 08/52] Exclude hidden files from pack generation Using repository root as 'packs' dir would crash because '.git' isn't a valid pack lol --- .../handler/ResourcePackHandler.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/ResourcePackHandler.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/ResourcePackHandler.java index 2304678..f927434 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/ResourcePackHandler.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/ResourcePackHandler.java @@ -129,6 +129,12 @@ private List gatherSourcePacksFrom(final Path packsDir) throws GithubResou // Return source packs sorted in correct order. return Stream.of(sourcePacksArray) + .filter(file -> { + final boolean hidden = file.getName().startsWith("."); + if (!hidden) return true; + LOGGER.warn("Excluding hidden file '%s'", file.getAbsolutePath()); + return false; + }) .sorted(Comparator.comparingInt(StringUtils::extractPriorityFromFile).reversed()) .toList(); } From ddd7619082fe0ad03f78648b7009c76481d35cbe Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Sun, 15 Jun 2025 14:36:21 +0300 Subject: [PATCH 09/52] Update gh workflows --- .github/workflows/build_artifacts.yml | 4 +++- .github/workflows/publish.yml | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_artifacts.yml b/.github/workflows/build_artifacts.yml index 22007db..497f54c 100644 --- a/.github/workflows/build_artifacts.yml +++ b/.github/workflows/build_artifacts.yml @@ -20,7 +20,9 @@ jobs: distribution: 'temurin' - name: Setup Gradle - uses: gradle/actions/setup-gradle@v3 + uses: gradle/actions/setup-gradle@v4 + with: + cache-read-only: false - name: Build with Gradle run: ./gradlew build diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index d7898d3..c75535a 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -22,7 +22,7 @@ jobs: distribution: 'temurin' - name: Setup Gradle - uses: gradle/actions/setup-gradle@v3 + uses: gradle/actions/setup-gradle@v4 with: cache-read-only: false From deb6542bd8ec9861080866855e92f908ef7c35e2 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Sun, 15 Jun 2025 14:38:53 +0300 Subject: [PATCH 10/52] Cleanup --- .../GithubResourcepackManager.java | 2 +- .../config/ConfigManager.java | 1 - .../config/ModConfig.java | 21 ------------ .../platform/PlatformMain.java | 2 +- .../platform/fabric/FabricPlatformMain.java | 1 - .../platform/paper/PaperPlatformCommand.java | 34 ------------------- .../platform/paper/PaperPlatformMain.java | 33 ------------------ .../paper/PaperPlatformServerProperties.java | 17 ---------- 8 files changed, 2 insertions(+), 109 deletions(-) diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java index 1781107..f374f41 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java @@ -42,7 +42,7 @@ private GithubResourcepackManager() { public static ModConfig config; - public static ResourcePackHandler resourcePackHandler; + public static ResourcePackHandler resourcePackHandler; public static void initialize() { PlatformCommand.INSTANCE.registerGithubRpManagerCommand(); diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ConfigManager.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ConfigManager.java index 15dba31..3e6650b 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ConfigManager.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ConfigManager.java @@ -16,7 +16,6 @@ import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; -import java.util.stream.Stream; import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.*; import static top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging.LOGGER; diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java index 8718a3d..1b12dea 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java @@ -24,7 +24,6 @@ import java.util.Map; import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.*; -import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.config; import static top.offsetmonkey538.githubresourcepackmanager.config.ConfigManager.CURRENT_CONFIG_FILE_PATH; import static top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging.LOGGER; @@ -139,26 +138,6 @@ public void trigger(final boolean updateSucceeded, final Map pla } } - //@Comment("!!!!Please check the wiki for how to set up the mod. It is linked on both the Modrinth and GitHub pages!!!!") - //public String packUpdateMessage = "Server resourcepack has been updated!\nPlease click {packUpdateCommand} to get the most up to date pack."; - //public String packUpdateMessageHoverMessage = "{longDescription}"; - //@Comment("The public ip of your server (\"123.45.67.89\" or \"play.coolserver.net\")") - //public String serverPublicIp = null; - //@Comment("If set, this port will be used in the server.properties file instead of the Minecraft server port. HTTP server will still be hosted on the Minecraft port. Only useful when running the server behind a proxy like nginx, traefik, cloudflare tunnel, etc.") - //public String proxyPort = null; - //@Comment("Should be \"[YOUR BRANCH NAME HERE]\". Common names include \"master\" and \"main\"") - //public String branch = "master"; - //public String repoUrl = null; - //@Comment("Where the mod will search for resource packs in the cloned repository") - //public String resourcePackRoot = ""; - //public boolean isRepoPrivate = false; - //public String githubUsername = null; - //@Comment("PLEASE DON'T SHARE THIS WITH ANYONE EVER") - //public String githubToken = null; - //public String webhookUrl = null; - //public String webhookBody = null; - - protected String getName() { return MOD_ID + "/" + MOD_ID; } diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformMain.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformMain.java index a3f80d6..07b9820 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformMain.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformMain.java @@ -13,7 +13,7 @@ public interface PlatformMain { * Example: .minecraft/config/github-resourcepack-manager/ * Example: .minecraft/plugins/Github-Resourcepack-Manager/ * - * @return + * @return config directory for the mod */ Path getConfigDir(); diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java index 132aac0..f7f54a6 100644 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java +++ b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java @@ -4,7 +4,6 @@ import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; import net.fabricmc.loader.api.FabricLoader; import net.minecraft.server.MinecraftServer; -import org.slf4j.Logger; import org.slf4j.LoggerFactory; import top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformMain; diff --git a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformCommand.java b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformCommand.java index 36b7082..ac2d89f 100644 --- a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformCommand.java +++ b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformCommand.java @@ -72,38 +72,4 @@ public void registerGithubRpManagerCommand() { ); }); } - - //@Override - //public void registerGithubRpManagerCommand() { - // CommandRegistrationCallback.EVENT.register(FabricPlatformCommand::register); - //} - - //public static void register(CommandDispatcher dispatcher, CommandRegistryAccess commandRegistryAccess, CommandManager.RegistrationEnvironment registrationEnvironment) { - // dispatcher.register(literal("gh-rp-manager") - // .requires(ServerCommandSource::isExecutedByPlayer) - // .then(literal("request-pack").executes( - // context -> { - // final ServerPlayerEntity player = context.getSource().getPlayerOrThrow(); - // final MinecraftServer.ServerResourcePackProperties resourcePackProperties = context.getSource().getServer().getResourcePackProperties().orElse(null); - // if (resourcePackProperties == null) { - // context.getSource().sendFeedback(() -> Text.literal("Failed to send pack update packet to client!"), true); - // return 0; - // } - - // player.networkHandler.send( - // new ResourcePackSendS2CPacket( - // resourcePackProperties.id(), - // resourcePackProperties.url(), - // resourcePackProperties.hash(), - // resourcePackProperties.isRequired(), - // Optional.ofNullable(resourcePackProperties.prompt()) - // ), - // null - // ); - - // return ControlFlowAware.Command.SINGLE_SUCCESS; - // }) - // ) - // ); - //} } diff --git a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java index c20d396..07068ae 100644 --- a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java +++ b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java @@ -25,37 +25,4 @@ public static void setPlugin(JavaPlugin plugin) { public static JavaPlugin getPlugin() { return plugin; } - - - //@Override - //public void onInitializeServer() { - // GithubResourcepackManager.initialize(); - - // ServerLifecycleEvents.SERVER_STARTING.register(minecraftServer1 -> minecraftServer = minecraftServer1); - //} - - //public MinecraftServer getServer() { - // return minecraftServer; - //} - - - //@Override - //public Logger getLogger() { - // return LOGGER; - //} - - //@Override - //public Path getConfigDir() { - // return FabricLoader.getInstance().getConfigDir(); - //} - - //@Override - //public Path getGameDir() { - // return FabricLoader.getInstance().getGameDir(); - //} - - //@Override - //public void runOnServerStart(Runnable work) { - // ServerLifecycleEvents.SERVER_STARTED.register(minecraftServer1 -> work.run()); - //} } diff --git a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformServerProperties.java b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformServerProperties.java index 45ef937..1021a6c 100644 --- a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformServerProperties.java +++ b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformServerProperties.java @@ -1,25 +1,17 @@ package top.offsetmonkey538.githubresourcepackmanager.platform.paper; -import joptsimple.OptionSet; import net.minecraft.server.MinecraftServer; import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.server.dedicated.DedicatedServerProperties; import net.minecraft.server.dedicated.DedicatedServerSettings; -import org.bukkit.packs.ResourcePack; import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; -import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformServerProperties; import xyz.jpenilla.reflectionremapper.ReflectionRemapper; -import xyz.jpenilla.reflectionremapper.proxy.ReflectionProxyFactory; -import xyz.jpenilla.reflectionremapper.proxy.annotation.FieldGetter; -import xyz.jpenilla.reflectionremapper.proxy.annotation.FieldSetter; -import xyz.jpenilla.reflectionremapper.proxy.annotation.Proxies; import java.lang.reflect.Field; import java.nio.file.Path; import java.util.Map; import java.util.Optional; -import java.util.Properties; public class PaperPlatformServerProperties implements PlatformServerProperties { @Override @@ -67,13 +59,4 @@ public void reload() throws GithubResourcepackManagerException { throw new GithubResourcepackManagerException("Failed to reload 'server.properties' file!", e); } } - - - //@Override - //public void reload() { - // final ServerPropertiesLoader propertiesLoader = ((MinecraftDedicatedServerAccessor) FabricPlatformMain.INSTANCE.getServer()).getPropertiesLoader(); - // final ServerPropertiesLoaderAccessor propertiesLoaderAccess = (ServerPropertiesLoaderAccessor) propertiesLoader; - - // propertiesLoaderAccess.setPropertiesHandler(ServerPropertiesHandler.load(propertiesLoaderAccess.getPath())); - //} } From 02af56a0e460d137d6d38d53a15ceae452f6c190 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Mon, 16 Jun 2025 12:30:44 +0300 Subject: [PATCH 11/52] They.... they actually work? Implement datapack handling --- .../GithubResourcepackManager.java | 68 +++++- .../config/ModConfig.java | 21 +- .../handler/DataPackHandler.java | 209 ++++++++++++++++++ .../handler/ResourcePackHandler.java | 16 +- .../platform/PlatformServerProperties.java | 2 + .../platform/PlatformText.java | 3 +- .../utils/StringUtils.java | 15 +- .../platform/fabric/FabricPlatformMain.java | 3 + .../FabricPlatformServerProperties.java | 7 + .../platform/fabric/FabricPlatformText.java | 24 +- .../paper/PaperPlatformServerProperties.java | 6 + .../platform/paper/PaperPlatformText.java | 24 +- 12 files changed, 359 insertions(+), 39 deletions(-) create mode 100644 common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/DataPackHandler.java diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java index f374f41..bee1ed4 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java @@ -1,8 +1,11 @@ package top.offsetmonkey538.githubresourcepackmanager; import org.apache.commons.io.FileUtils; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import top.offsetmonkey538.githubresourcepackmanager.config.ModConfig; import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; +import top.offsetmonkey538.githubresourcepackmanager.handler.DataPackHandler; import top.offsetmonkey538.githubresourcepackmanager.handler.GitHandler; import top.offsetmonkey538.githubresourcepackmanager.handler.ResourcePackHandler; import top.offsetmonkey538.githubresourcepackmanager.networking.MainHttpHandler; @@ -111,7 +114,7 @@ public static void updatePack(final UpdateType updateType) { if (config.dataPackProvider.enabled) { LOGGER.info(""); LOGGER.info("Updating data pack..."); - // TODO: implement datapack stuff + updateDataPack(gitHandler, updateType, failed); LOGGER.info("Data pack updated!"); } } @@ -126,9 +129,7 @@ private static void updateResourcePack(final GitHandler gitHandler, final Update gitHandler.getChangedFiles().map(changes -> changes.stream().anyMatch(it -> it.startsWith(config.resourcePackProvider.getRootLocation()))).orElse(true) || oldResourcePackPath == null || !oldResourcePackPath.toFile().exists(); - if (!wasUpdated) { - LOGGER.info("Pack hasn't changed since last update. Skipping new pack generation."); - } + if (!wasUpdated) LOGGER.info("Pack hasn't changed since last update. Skipping new pack generation."); // Generate pack resourcePackHandler = new ResourcePackHandler(); @@ -154,6 +155,7 @@ private static void updateResourcePack(final GitHandler gitHandler, final Update // Generate placeholder map final Map placeholders = new HashMap<>(); if (gitHandler.getCommitProperties() != null) placeholders.putAll(gitHandler.getCommitProperties().toPlaceholdersMap()); + placeholders.put("{packType}", "resource"); placeholders.put("{downloadUrl}", config.getPackUrl(resourcePackHandler.getOutputPackName())); placeholders.put("{updateType}", updateType.name()); placeholders.put("{wasUpdated}", String.valueOf(wasUpdated)); @@ -161,7 +163,7 @@ private static void updateResourcePack(final GitHandler gitHandler, final Update // Send chat message try { - sendUpdateMessage(wasUpdated, placeholders); + sendUpdateMessage(config.resourcePackProvider.updateMessage, config.resourcePackProvider.updateMessageHoverMessage, wasUpdated, placeholders); } catch (GithubResourcepackManagerException e) { LOGGER.error("Failed to send update message in chat!", e); } @@ -183,13 +185,65 @@ private static void updateResourcePack(final GitHandler gitHandler, final Update } } - private static void sendUpdateMessage(boolean wasUpdated, final Map placeholders) throws GithubResourcepackManagerException { + private static void updateDataPack(final GitHandler gitHandler, final UpdateType updateType, boolean updateFailed) { + // Check if pack was updated + final boolean wasUpdated = gitHandler.getChangedFiles().map(changes -> changes.stream().anyMatch(it -> it.startsWith(config.dataPackProvider.getRootLocation()))).orElse(true); + if (!wasUpdated) { + LOGGER.info("Pack hasn't changed since last update. Datapack processing will be skipped."); + return; + } + + // Generate pack + final DataPackHandler dataPackHandler = new DataPackHandler(); + + LOGGER.info("Getting pack location..."); + try { + dataPackHandler.generatePack(); + } catch (GithubResourcepackManagerException e) { + LOGGER.error("Failed to generate pack!", e); + updateFailed = true; + } + + + // Generate placeholder map + final Map placeholders = new HashMap<>(); + if (gitHandler.getCommitProperties() != null) placeholders.putAll(gitHandler.getCommitProperties().toPlaceholdersMap()); + placeholders.put("{packType}", "data"); + placeholders.put("{updateType}", updateType.name()); + placeholders.put("{wasUpdated}", String.valueOf(wasUpdated)); + LOGGER.info("Placeholders: %s", placeholders); + + // Send chat message + try { + sendUpdateMessage(config.dataPackProvider.updateMessage, config.dataPackProvider.updateMessageHoverMessage, wasUpdated, placeholders, true); + } catch (GithubResourcepackManagerException e) { + LOGGER.error("Failed to send update message in chat!", e); + } + + // Trigger webhooks + if (!updateFailed) try { + config.dataPackProvider.successWebhook.trigger(true, placeholders, updateType); + } catch (GithubResourcepackManagerException e) { + LOGGER.error("Failed to trigger success webhook!", e); + } + else try { + config.dataPackProvider.failWebhook.trigger(false, placeholders, updateType); + } catch (GithubResourcepackManagerException e) { + LOGGER.error("Failed to trigger fail webhook!", e); + } + } + + private static void sendUpdateMessage(final String updateMessage, @Nullable final String updateHoverMessage, boolean wasUpdated, final Map placeholders) throws GithubResourcepackManagerException { + sendUpdateMessage(updateMessage, updateHoverMessage, wasUpdated, placeholders, false); + } + + private static void sendUpdateMessage(final String updateMessage, @Nullable final String updateHoverMessage, boolean wasUpdated, final Map placeholders, boolean adminsOnly) throws GithubResourcepackManagerException { if (!wasUpdated) { LOGGER.info("Not sending chat message because pack was not updated."); return; } - PlatformText.INSTANCE.sendUpdateMessage(placeholders); + PlatformText.INSTANCE.sendUpdateMessage(updateMessage, updateHoverMessage, placeholders, adminsOnly); } private static String getOldResourcePackName() { diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java index 1b12dea..645ad7f 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java @@ -78,6 +78,12 @@ public static class ResourcePackProvider { public String getRootLocation() { return rootLocation.startsWith("/") ? rootLocation.substring(1) : rootLocation; } + public Path getPackRoot() { + return GIT_FOLDER.resolve(getRootLocation()); + } + public Path getPackPacksDir() { + return getPackRoot().resolve("packs"); + } } public static class DataPackProvider { @@ -87,7 +93,7 @@ public static class DataPackProvider { public String rootLocation = "/datapacks"; @Comment("Message sent TO ADMINS in chat when pack has been updated. May be 'null' to disable.") - public String updateMessage = "Server datapacks has been updated!\nNew packs (if any) have been enabled automatically.\nPlease run '/reload' or restart the server to reload datapacks."; + public String updateMessage = "Server datapacks has been updated!\nNew packs (if any) have to be enabled with the '/datapack enable` command.\nPlease run '/reload' or restart the server to reload datapacks."; @Comment("Message shown when hovering over the 'updateMessage' text. May be 'null' to disable.") public String updateMessageHoverMessage = "{longDescription}"; @@ -99,6 +105,12 @@ public static class DataPackProvider { public String getRootLocation() { return rootLocation.startsWith("/") ? rootLocation.substring(1) : rootLocation; } + public Path getPackRoot() { + return GIT_FOLDER.resolve(getRootLocation()); + } + public Path getPackPacksDir() { + return getPackRoot().resolve("packs"); + } } public static class WebhookInfo { @@ -252,13 +264,6 @@ public URI getWebhookUrl() { return URI.create(resourcePackProvider.successWebhook.url); } - public Path getResourcePackRoot() { - return GIT_FOLDER.resolve(config.resourcePackProvider.getRootLocation()); - } - public Path getResourcePackPacksDir() { - return getResourcePackRoot().resolve("packs"); - } - public String getGithubRef() { return "refs/heads/" + repositoryInfo.branch; } diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/DataPackHandler.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/DataPackHandler.java new file mode 100644 index 0000000..4ec7adc --- /dev/null +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/DataPackHandler.java @@ -0,0 +1,209 @@ +package top.offsetmonkey538.githubresourcepackmanager.handler; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import org.apache.commons.io.file.PathUtils; +import top.offsetmonkey538.githubresourcepackmanager.config.ModConfig; +import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; +import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformServerProperties; +import top.offsetmonkey538.githubresourcepackmanager.utils.MyFileUtils; +import top.offsetmonkey538.githubresourcepackmanager.utils.StringUtils; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; +import java.util.stream.Stream; + +import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.*; +import static top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging.LOGGER; + +public class DataPackHandler { + private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); + + private static final Path STATE_FILE = DATAPACK_FOLDER.resolve("state.json"); + + public void generatePack() throws GithubResourcepackManagerException { + final Path datapacks = PlatformServerProperties.INSTANCE.getDatapacksDir(); + + + // Delete existing stuff + final State existingPacks; + try { + existingPacks = readStateFile(); + } catch (IOException e) { + throw new GithubResourcepackManagerException("Failed to read state file!", e); + } + + Arrays.stream(existingPacks.packs) + .map(datapacks::resolve) + .forEach(path -> { + if (Files.notExists(path)) { + LOGGER.warn("Not deleting pack at '%s' as it doesn't exist!", path.toAbsolutePath()); + return; + } + + try { + PathUtils.delete(path); + LOGGER.info("Deleted pack at '%s'!", path.toAbsolutePath()); + } catch (IOException e) { + LOGGER.error("Failed to delete pack at '%s'!", e, path.toAbsolutePath()); + } + }); + + + // Get new packs + final List sourcePacks; + + try { + sourcePacks = gatherSourcePacks(); + } catch (GithubResourcepackManagerException e) { + throw new GithubResourcepackManagerException("Failed to gather source packs!", e); + } + + // Delete ones with same name from datapacks + for (final Path sourcePack : sourcePacks) { + final Path path = datapacks.resolve(sourcePack.getFileName()); + if (Files.notExists(path)) continue; + + LOGGER.info("Deleting pack at '%s' to replace it...", path.toAbsolutePath()); + try { + PathUtils.delete(path); + LOGGER.info("Deleted pack at '%s'!", path.toAbsolutePath()); + } catch (IOException e) { + throw new GithubResourcepackManagerException("Failed to delete pack at '%s'!", e, path.toAbsolutePath()); + } + } + + // Finally copy over the new ones + for (final Path path : sourcePacks) { + try { + final Path destination = datapacks.resolve(path.getFileName()); + + if (Files.isDirectory(path)) PathUtils.copyDirectory(path, destination); + else Files.copy(path, destination); + + LOGGER.info("Copied pack from '%s' to '%s'.", path.toAbsolutePath(), destination.toAbsolutePath()); + } catch (IOException e) { + throw new GithubResourcepackManagerException("Failed to copy pack at '%s' to datapacks directory at '%s'!", e, path.toAbsolutePath(), datapacks.toAbsolutePath()); + } + } + + try { + writeStateFile(sourcePacks); + } catch (IOException e) { + throw new GithubResourcepackManagerException("Failed to write state file!", e); + } + } + + private void writeStateFile(List files) throws IOException { + Files.createDirectories(STATE_FILE.getParent()); + + final String json = GSON.toJson(new State(files.stream().map(Path::getFileName).map(Path::toString).toArray(String[]::new))); + + Files.writeString(STATE_FILE, json); + } + + private State readStateFile() throws IOException { + if (Files.notExists(STATE_FILE)) { + LOGGER.warn("State file '%s' not found! No datapacks will be deleted!"); + return new State(new String[]{}); + } + + return GSON.fromJson(Files.readString(STATE_FILE), State.class); + } + + private void copyPacksToDirectory(List sourcePacks, Path datapacksDir) throws GithubResourcepackManagerException { + for (Path sourcePack : sourcePacks) { + if (Files.isDirectory(sourcePack)) { + try { + PathUtils.copyDirectory(sourcePack, datapacksDir); + } catch (IOException e) { + throw new GithubResourcepackManagerException("Failed to copy pack '%s' into directory '%s'!", e, sourcePack, datapacksDir); + } + continue; + } + if (sourcePack.getFileName().endsWith(".zip")) { + try { + Files.copy(sourcePack, datapacksDir); + } catch (IOException e) { + throw new GithubResourcepackManagerException("Failed to copy pack '%s' into directory '%s'!", e, sourcePack, datapacksDir); + } + continue; + } + + LOGGER.error("'%s' is not a valid pack! Ignoring...", sourcePack); + sourcePacks.remove(sourcePack); + } + } + + /** + * Gather list of {@link File}s to construct the final pack from. + *

+ * If there's a {@code pack.mcmeta} file in the {@link ModConfig#getResourcePackRoot() resource pack root directory}, the root directory will be the only file. + *

+ *

+ * If there's a {@link ModConfig#getResourcePackPacksDir() packs directory} in the pack root, all files directly in the packs directory will be returned. + *

+ *

+ * If there's neither a {@code pack.mcmeta} file or {@link ModConfig#getResourcePackPacksDir() packs directory} in the pack root, all files directly in the pack root will be returned. + *

+ * + * @return A list of {@link File}s to construct the final pack from. May include directories and .zip files. + * @throws GithubResourcepackManagerException when the source packs could not be determined. + */ + private List gatherSourcePacks() throws GithubResourcepackManagerException { + LOGGER.info("Checking for 'pack.mcmeta' in data pack root..."); + final boolean hasPackMcmeta = Files.exists(config.dataPackProvider.getPackRoot().resolve("pack.mcmeta")); + LOGGER.info("%sFound!", hasPackMcmeta ? "" : "Not "); + + LOGGER.info("Checking for 'packs' directory in data pack root..."); + Path packsDir = config.dataPackProvider.getPackPacksDir(); + final boolean hasPacksFolder = Files.exists(packsDir) && Files.isDirectory(packsDir); + LOGGER.info("%sFound!", hasPacksFolder ? "" : "Not "); + + if (hasPackMcmeta && hasPacksFolder) { + throw new GithubResourcepackManagerException("Found both 'pack.mcmeta' and the 'packs' directory in data pack root '%s'!", config.dataPackProvider.getPackRoot().toAbsolutePath()); + } + if (!hasPackMcmeta && !hasPacksFolder) { + LOGGER.info("Found neither 'pack.mcmeta' nor the 'packs' directory in data pack root '%s'!", config.dataPackProvider.getPackPacksDir().toAbsolutePath()); + LOGGER.info("Assuming data pack root '%s' as 'packs' directory.", config.dataPackProvider.getPackPacksDir().toAbsolutePath()); + packsDir = config.dataPackProvider.getPackRoot(); + } + + + if (hasPackMcmeta) { + LOGGER.info("Using data pack root as data pack."); + return List.of(config.dataPackProvider.getPackRoot()); + } + + LOGGER.info("Using 'packs' directory for data packs."); + return gatherSourcePacksFrom(packsDir); + } + + private List gatherSourcePacksFrom(final Path packsDir) throws GithubResourcepackManagerException { + try (final Stream sourcePacks = Files.list(packsDir)) { + final List result = sourcePacks + .filter(path -> { + final boolean hidden = path.getFileName().startsWith("."); + if (!hidden) return true; + LOGGER.warn("Excluding hidden file '%s'", path.toAbsolutePath()); + return false; + }) + .toList(); + + if (result.isEmpty()) + throw new GithubResourcepackManagerException("Repository contains empty 'packs' folder!"); + + return result; + } catch (IOException e) { + throw new GithubResourcepackManagerException("Failed to list files in 'packs' folder'!", e); + } + } + + + private record State(String[] packs) { + + } +} diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/ResourcePackHandler.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/ResourcePackHandler.java index f927434..3ce9137 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/ResourcePackHandler.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/ResourcePackHandler.java @@ -40,7 +40,7 @@ private void generateNewPack() throws GithubResourcepackManagerException { try { sourcePacks = gatherSourcePacks(); } catch (GithubResourcepackManagerException e) { - throw new GithubResourcepackManagerException("Failed to determine pack type!", e); + throw new GithubResourcepackManagerException("Failed to gather source packs!", e); } @@ -156,27 +156,27 @@ private List gatherSourcePacksFrom(final Path packsDir) throws GithubResou */ private List gatherSourcePacks() throws GithubResourcepackManagerException { LOGGER.info("Checking for 'pack.mcmeta' in resource pack root..."); - final boolean hasPackMcmeta = config.getResourcePackRoot().resolve("pack.mcmeta").toFile().exists(); + final boolean hasPackMcmeta = config.resourcePackProvider.getPackRoot().resolve("pack.mcmeta").toFile().exists(); LOGGER.info("%sFound!", hasPackMcmeta ? "" : "Not "); LOGGER.info("Checking for 'packs' directory in resource pack root..."); - Path packsDir = config.getResourcePackPacksDir(); + Path packsDir = config.resourcePackProvider.getPackPacksDir(); final boolean hasPacksFolder = Files.exists(packsDir) && Files.isDirectory(packsDir); LOGGER.info("%sFound!", hasPacksFolder ? "" : "Not "); if (hasPackMcmeta && hasPacksFolder) { - throw new GithubResourcepackManagerException("Found both 'pack.mcmeta' and the 'packs' directory in resource pack root '%s'!", config.getResourcePackPacksDir().toAbsolutePath()); + throw new GithubResourcepackManagerException("Found both 'pack.mcmeta' and the 'packs' directory in resource pack root '%s'!", config.resourcePackProvider.getPackPacksDir().toAbsolutePath()); } if (!hasPackMcmeta && !hasPacksFolder) { - LOGGER.info("Found neither 'pack.mcmeta' nor the 'packs' directory in resource pack root '%s'!", config.getResourcePackPacksDir().toAbsolutePath()); - LOGGER.info("Assuming resource pack root '%s' as 'packs' directory.", config.getResourcePackPacksDir().toAbsolutePath()); - packsDir = config.getResourcePackRoot(); + LOGGER.info("Found neither 'pack.mcmeta' nor the 'packs' directory in resource pack root '%s'!", config.resourcePackProvider.getPackPacksDir().toAbsolutePath()); + LOGGER.info("Assuming resource pack root '%s' as 'packs' directory.", config.resourcePackProvider.getPackPacksDir().toAbsolutePath()); + packsDir = config.resourcePackProvider.getPackRoot(); } if (hasPackMcmeta) { LOGGER.info("Using resource pack root as resource pack."); - return List.of(config.getResourcePackRoot().toFile()); + return List.of(config.resourcePackProvider.getPackRoot().toFile()); } LOGGER.info("Using 'packs' directory for resource packs."); diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformServerProperties.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformServerProperties.java index 98a82bd..c683a76 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformServerProperties.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformServerProperties.java @@ -5,6 +5,7 @@ import top.offsetmonkey538.githubresourcepackmanager.handler.ResourcePackHandler; import java.io.IOException; +import java.nio.file.Path; import java.util.Map; import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.*; @@ -16,6 +17,7 @@ public interface PlatformServerProperties { String getResourcePackUrl(); String getServerPort(); + Path getDatapacksDir(); void setProperties(Map properties); void reload() throws GithubResourcepackManagerException; diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformText.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformText.java index 3a7341c..4757534 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformText.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformText.java @@ -1,5 +1,6 @@ package top.offsetmonkey538.githubresourcepackmanager.platform; +import org.jetbrains.annotations.Nullable; import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; import java.util.Map; @@ -9,5 +10,5 @@ public interface PlatformText { PlatformText INSTANCE = load(PlatformText.class); - void sendUpdateMessage(Map placeholders) throws GithubResourcepackManagerException; + void sendUpdateMessage(final String updateMessage, @Nullable final String updateHoverMessage, Map placeholders, boolean adminsOnly) throws GithubResourcepackManagerException; } diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/utils/StringUtils.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/utils/StringUtils.java index 3f8d63b..cf811a3 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/utils/StringUtils.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/utils/StringUtils.java @@ -46,12 +46,25 @@ public static String replacePlaceholders(String string, Map plac } public static int extractPriorityFromFile(File file) { + final int result = extractPriorityFromFileInternal(file); + if (result != -1) return result; + LOGGER.error("File '%s' doesn't start with priority!", file); + return -1; + } + + public static int extractPriorityFromFileLenient(File file) { + final int result = extractPriorityFromFileInternal(file); + if (result != -1) return result; + LOGGER.warn("File '%s' doesn't start with priority!", file); + return 0; + } + + private static int extractPriorityFromFileInternal(File file) { final String filename = file.getName(); final Matcher matcher = RESOURCEPACK_NAME_PATTERN.matcher(filename); if (!matcher.find()) { - LOGGER.error("File '%s' doesn't start with priority!", file); return -1; } diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java index f7f54a6..7f9c7a0 100644 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java +++ b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java @@ -3,9 +3,12 @@ import net.fabricmc.api.DedicatedServerModInitializer; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; import net.fabricmc.loader.api.FabricLoader; +import net.minecraft.resource.ResourcePackManager; +import net.minecraft.resource.ResourcePackProfile; import net.minecraft.server.MinecraftServer; import org.slf4j.LoggerFactory; import top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager; +import top.offsetmonkey538.githubresourcepackmanager.config.ModConfig; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformMain; import java.nio.file.Path; diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformServerProperties.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformServerProperties.java index b0885bf..f4b816f 100644 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformServerProperties.java +++ b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformServerProperties.java @@ -3,12 +3,14 @@ import net.minecraft.server.MinecraftServer; import net.minecraft.server.dedicated.ServerPropertiesHandler; import net.minecraft.server.dedicated.ServerPropertiesLoader; +import net.minecraft.util.WorldSavePath; import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; import top.offsetmonkey538.githubresourcepackmanager.mixin.AbstractPropertiesHandlerAccessor; import top.offsetmonkey538.githubresourcepackmanager.mixin.MinecraftDedicatedServerAccessor; import top.offsetmonkey538.githubresourcepackmanager.mixin.ServerPropertiesLoaderAccessor; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformServerProperties; +import java.nio.file.Path; import java.util.Map; import java.util.Optional; import java.util.Properties; @@ -27,6 +29,11 @@ public String getServerPort() { return String.valueOf(FabricPlatformMain.getServer().getServerPort()); } + @Override + public Path getDatapacksDir() { + return FabricPlatformMain.getServer().getSavePath(WorldSavePath.DATAPACKS); + } + @Override public void setProperties(Map properties) { final ServerPropertiesLoader propertiesLoader = ((MinecraftDedicatedServerAccessor) FabricPlatformMain.getServer()).getPropertiesLoader(); diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java index e31d02e..4148723 100644 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java +++ b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java @@ -1,31 +1,32 @@ package top.offsetmonkey538.githubresourcepackmanager.platform.fabric; +import com.mojang.authlib.GameProfile; import net.minecraft.server.PlayerManager; +import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.*; +import org.jetbrains.annotations.Nullable; import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformText; import top.offsetmonkey538.githubresourcepackmanager.utils.StringUtils; import top.offsetmonkey538.monkeylib538.utils.TextUtils; +import java.io.IOException; import java.util.Map; -import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.config; - public class FabricPlatformText implements PlatformText { @Override - public void sendUpdateMessage(Map placeholders) throws GithubResourcepackManagerException { + public void sendUpdateMessage(final String message, @Nullable final String hoverMessage, Map placeholders, boolean adminsOnly) throws GithubResourcepackManagerException { final PlayerManager playerManager = FabricPlatformMain.getServer().getPlayerManager(); if (playerManager == null) return; - String message = config.resourcePackProvider.updateMessage; final String[] splitMessage = message.split("\n"); final HoverEvent hoverEvent; try { - hoverEvent = config.resourcePackProvider.updateMessageHoverMessage == null ? null : new HoverEvent( + hoverEvent = hoverMessage == null ? null : new HoverEvent( HoverEvent.Action.SHOW_TEXT, TextUtils.INSTANCE.getStyledText( - StringUtils.replacePlaceholders(config.resourcePackProvider.updateMessageHoverMessage, placeholders).replace("\\n", "\n") + StringUtils.replacePlaceholders(hoverMessage, placeholders).replace("\\n", "\n") ) ); } catch (Exception e) { @@ -65,7 +66,16 @@ public void sendUpdateMessage(Map placeholders) throws GithubRes throw new GithubResourcepackManagerException("Failed to style update message at line number '%s'!", e, lineNumber); } - playerManager.broadcast(currentLine, false); + + if (!adminsOnly) { + playerManager.broadcast(currentLine, false); + continue; + } + + for (final ServerPlayerEntity player : playerManager.getPlayerList()) { + if (!playerManager.isOperator(player.getGameProfile())) continue; + player.sendMessage(currentLine); + } } } } diff --git a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformServerProperties.java b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformServerProperties.java index 1021a6c..8a56c04 100644 --- a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformServerProperties.java +++ b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformServerProperties.java @@ -4,6 +4,7 @@ import net.minecraft.server.dedicated.DedicatedServer; import net.minecraft.server.dedicated.DedicatedServerProperties; import net.minecraft.server.dedicated.DedicatedServerSettings; +import net.minecraft.world.level.storage.LevelResource; import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformServerProperties; import xyz.jpenilla.reflectionremapper.ReflectionRemapper; @@ -26,6 +27,11 @@ public String getServerPort() { return String.valueOf(MinecraftServer.getServer().getPort()); } + @Override + public Path getDatapacksDir() { + return MinecraftServer.getServer().getWorldPath(LevelResource.DATAPACK_DIR); + } + @Override public void setProperties(Map properties) { final DedicatedServerSettings settings = ((DedicatedServer) MinecraftServer.getServer()).settings; diff --git a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformText.java b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformText.java index 4e270c6..623645b 100644 --- a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformText.java +++ b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformText.java @@ -3,6 +3,9 @@ import net.minecraft.ChatFormatting; import net.minecraft.network.chat.*; import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.players.PlayerList; +import org.jetbrains.annotations.Nullable; import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformText; import top.offsetmonkey538.githubresourcepackmanager.utils.StringUtils; @@ -10,20 +13,17 @@ import java.util.List; import java.util.Map; -import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.config; - public class PaperPlatformText implements PlatformText { @Override - public void sendUpdateMessage(Map placeholders) throws GithubResourcepackManagerException { - String message = config.resourcePackProvider.updateMessage; + public void sendUpdateMessage(final String message, @Nullable final String hoverMessage, Map placeholders, boolean adminsOnly) throws GithubResourcepackManagerException { final String[] splitMessage = message.split("\n"); final HoverEvent hoverEvent; try { - hoverEvent = config.resourcePackProvider.updateMessageHoverMessage == null ? null : new HoverEvent( + hoverEvent = hoverMessage == null ? null : new HoverEvent( HoverEvent.Action.SHOW_TEXT, getStyledText( - StringUtils.replacePlaceholders(config.resourcePackProvider.updateMessageHoverMessage, placeholders).replace("\\n", "\n") + StringUtils.replacePlaceholders(hoverMessage, placeholders).replace("\\n", "\n") ) ); } catch (Exception e) { @@ -63,7 +63,17 @@ public void sendUpdateMessage(Map placeholders) throws GithubRes throw new GithubResourcepackManagerException("Failed to style update message at line number '%s'!", e, lineNumber); } - MinecraftServer.getServer().getPlayerList().broadcastSystemMessage(currentLine, false); + + final PlayerList players = MinecraftServer.getServer().getPlayerList(); + if (!adminsOnly) { + players.broadcastSystemMessage(currentLine, false); + continue; + } + + for (final ServerPlayer player : players.players) { + if (!players.isOp(player.getGameProfile())) continue; + player.sendSystemMessage(currentLine); + } } } From e2715f23ad92cab3ffe03529736bfd471eaeaca8 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Mon, 16 Jun 2025 12:31:28 +0300 Subject: [PATCH 12/52] Cleanup --- .../GithubResourcepackManager.java | 1 - .../githubresourcepackmanager/handler/DataPackHandler.java | 2 -- .../githubresourcepackmanager/utils/StringUtils.java | 7 ------- .../platform/fabric/FabricPlatformMain.java | 3 --- .../platform/fabric/FabricPlatformText.java | 2 -- 5 files changed, 15 deletions(-) diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java index bee1ed4..a6f9563 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java @@ -1,7 +1,6 @@ package top.offsetmonkey538.githubresourcepackmanager; import org.apache.commons.io.FileUtils; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import top.offsetmonkey538.githubresourcepackmanager.config.ModConfig; import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/DataPackHandler.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/DataPackHandler.java index 4ec7adc..3a1b62e 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/DataPackHandler.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/DataPackHandler.java @@ -6,8 +6,6 @@ import top.offsetmonkey538.githubresourcepackmanager.config.ModConfig; import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformServerProperties; -import top.offsetmonkey538.githubresourcepackmanager.utils.MyFileUtils; -import top.offsetmonkey538.githubresourcepackmanager.utils.StringUtils; import java.io.File; import java.io.IOException; diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/utils/StringUtils.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/utils/StringUtils.java index cf811a3..fdbd903 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/utils/StringUtils.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/utils/StringUtils.java @@ -52,13 +52,6 @@ public static int extractPriorityFromFile(File file) { return -1; } - public static int extractPriorityFromFileLenient(File file) { - final int result = extractPriorityFromFileInternal(file); - if (result != -1) return result; - LOGGER.warn("File '%s' doesn't start with priority!", file); - return 0; - } - private static int extractPriorityFromFileInternal(File file) { final String filename = file.getName(); diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java index 7f9c7a0..f7f54a6 100644 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java +++ b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java @@ -3,12 +3,9 @@ import net.fabricmc.api.DedicatedServerModInitializer; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; import net.fabricmc.loader.api.FabricLoader; -import net.minecraft.resource.ResourcePackManager; -import net.minecraft.resource.ResourcePackProfile; import net.minecraft.server.MinecraftServer; import org.slf4j.LoggerFactory; import top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager; -import top.offsetmonkey538.githubresourcepackmanager.config.ModConfig; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformMain; import java.nio.file.Path; diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java index 4148723..e7e93e1 100644 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java +++ b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java @@ -1,6 +1,5 @@ package top.offsetmonkey538.githubresourcepackmanager.platform.fabric; -import com.mojang.authlib.GameProfile; import net.minecraft.server.PlayerManager; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.*; @@ -10,7 +9,6 @@ import top.offsetmonkey538.githubresourcepackmanager.utils.StringUtils; import top.offsetmonkey538.monkeylib538.utils.TextUtils; -import java.io.IOException; import java.util.Map; public class FabricPlatformText implements PlatformText { From 3b4772b586735964c82bc36204e8ec1b6085fc78 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Mon, 16 Jun 2025 12:37:31 +0300 Subject: [PATCH 13/52] Automatically reload datapack list after datapack update --- .../GithubResourcepackManager.java | 3 +++ .../githubresourcepackmanager/platform/PlatformMain.java | 7 +++++++ .../platform/fabric/FabricPlatformMain.java | 7 +++++-- .../platform/paper/PaperPlatformMain.java | 6 ++++++ 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java index a6f9563..4c82286 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java @@ -203,6 +203,9 @@ private static void updateDataPack(final GitHandler gitHandler, final UpdateType updateFailed = true; } + // Refresh datapack list + PlatformMain.INSTANCE.refreshDatapacks(); + // Generate placeholder map final Map placeholders = new HashMap<>(); diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformMain.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformMain.java index 07b9820..deaf7d8 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformMain.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformMain.java @@ -23,4 +23,11 @@ public interface PlatformMain { * @param work the stuff to run */ void runOnServerStart(Runnable work); + + /** + * Tell the server to refresh the list of available datapacks. + *

+ * This way the user won't have to run {@code /datapack list} before being able to enable them + */ + void refreshDatapacks(); } diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java index f7f54a6..bdd846d 100644 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java +++ b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java @@ -13,8 +13,6 @@ import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.MOD_ID; public class FabricPlatformMain implements PlatformMain, DedicatedServerModInitializer { - public static final FabricPlatformMain INSTANCE = (FabricPlatformMain) PlatformMain.INSTANCE; - private static MinecraftServer minecraftServer; @Override @@ -40,4 +38,9 @@ public Path getConfigDir() { public void runOnServerStart(Runnable work) { ServerLifecycleEvents.SERVER_STARTED.register(minecraftServer1 -> work.run()); } + + @Override + public void refreshDatapacks() { + getServer().getDataPackManager().scanPacks(); + } } diff --git a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java index 07068ae..df65219 100644 --- a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java +++ b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java @@ -1,5 +1,6 @@ package top.offsetmonkey538.githubresourcepackmanager.platform.paper; +import net.minecraft.server.MinecraftServer; import org.bukkit.plugin.java.JavaPlugin; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformMain; @@ -18,6 +19,11 @@ public void runOnServerStart(Runnable work) { work.run(); } + @Override + public void refreshDatapacks() { + MinecraftServer.getServer().getPackRepository().reload(); + } + public static void setPlugin(JavaPlugin plugin) { PaperPlatformMain.plugin = plugin; } From 389bf9de427d56a8c09be38aa1ea07b48e462e34 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Thu, 19 Jun 2025 23:19:35 +0300 Subject: [PATCH 14/52] Only request-pack command needs to be executed by player. Update triggering should be able to be done in console --- .../platform/fabric/FabricPlatformCommand.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformCommand.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformCommand.java index 3e58206..a6e46a6 100644 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformCommand.java +++ b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformCommand.java @@ -27,8 +27,8 @@ public void registerGithubRpManagerCommand() { public static void register(CommandDispatcher dispatcher, CommandRegistryAccess commandRegistryAccess, CommandManager.RegistrationEnvironment registrationEnvironment) { dispatcher.register(literal("gh-rp-manager") - .requires(ServerCommandSource::isExecutedByPlayer) .then(literal("request-pack") + .requires(ServerCommandSource::isExecutedByPlayer) .executes( context -> { final ServerPlayerEntity player = context.getSource().getPlayerOrThrow(); From 004d441c79ce44225ddc67ac559f7da4321cb4b1 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Thu, 19 Jun 2025 23:34:57 +0300 Subject: [PATCH 15/52] Fix webhooks always sending using resource pack success url --- .../githubresourcepackmanager/config/ModConfig.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java index 645ad7f..33f3910 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java @@ -139,12 +139,17 @@ public void trigger(final boolean updateSucceeded, final Map pla String webhookBody = Files.readString(getBodyPath()); webhookBody = StringUtils.replacePlaceholders(webhookBody, placeholders, true); - WebhookSender.send(webhookBody, config.getWebhookUrl(), updateType, updateSucceeded); + WebhookSender.send(webhookBody, getWebhookUrl(), updateType, updateSucceeded); } catch (IOException e) { throw new GithubResourcepackManagerException("Failed to read content of webhook body file '%s'!", e, config.resourcePackProvider.successWebhook.body); } } + public @Nullable URI getWebhookUrl() { + if (url == null) return null; + return URI.create(url); + } + public @Nullable Path getBodyPath() { return body == null ? null : CURRENT_CONFIG_FILE_PATH.getParent().resolve(body); } @@ -259,11 +264,6 @@ public String getPackUrl(String outputFileName) { ); } - public URI getWebhookUrl() { - if (resourcePackProvider.successWebhook.url == null) return null; - return URI.create(resourcePackProvider.successWebhook.url); - } - public String getGithubRef() { return "refs/heads/" + repositoryInfo.branch; } From 205c2bf3511a49c1365d8453bceb5e9505fb3b08 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Fri, 4 Jul 2025 17:42:44 +0300 Subject: [PATCH 16/52] Fix crash on older minecraft versions --- .../platform/fabric/FabricPlatformText.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java index e7e93e1..9631a18 100644 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java +++ b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java @@ -72,7 +72,7 @@ public void sendUpdateMessage(final String message, @Nullable final String hover for (final ServerPlayerEntity player : playerManager.getPlayerList()) { if (!playerManager.isOperator(player.getGameProfile())) continue; - player.sendMessage(currentLine); + player.sendMessageToClient(currentLine, false); } } } From 1b40c65e417acf512db50080a989e432d2cc6b14 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Thu, 10 Jul 2025 00:10:32 +0300 Subject: [PATCH 17/52] Allow specifying protocol for public ip Should work for when the http server is behind a proxy that handles ssl? ill test it rn --- .../githubresourcepackmanager/config/ModConfig.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java index 33f3910..5753ead 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java @@ -37,10 +37,11 @@ public class ModConfig { public static class ServerInfo { - @Comment("The public ip of your server (\"123.45.67.89\" or \"play.coolserver.net\")") + @SuppressWarnings("HttpUrlsUsage") + @Comment("The public ip of your server, may also specify the protocol (\"123.45.67.89\" or \"http://play.coolserver.net\")") public String publicIp = null; - @Comment("If set, this port will be used in the server.properties file instead of the Minecraft server port. HTTP server will still be hosted on the Minecraft port. Only useful when running the server behind a proxy like nginx, traefik, cloudflare tunnel, etc.") + @Comment("If set, this port will be used in the server.properties file instead of the Minecraft server port. The HTTP server will still be hosted on the Minecraft port. Only useful when running the server behind a proxy like nginx, traefik, cloudflare tunnel, etc.") public String proxyPort = null; } @@ -254,9 +255,11 @@ public void createDefaultWebhooks() { } } + @SuppressWarnings("HttpUrlsUsage") public String getPackUrl(String outputFileName) { return String.format( - "http://%s:%s/%s/%s", + "%s%s:%s/%s/%s", + (serverInfo.publicIp.startsWith("http://") || serverInfo.publicIp.startsWith("https://") ? "" : "http://"), serverInfo.publicIp, serverInfo.proxyPort == null ? PlatformServerProperties.INSTANCE.getServerPort() : serverInfo.proxyPort, MOD_URI, From e69ce9c747f1c4eb6ca456b5f73a9b454321b7ff Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Tue, 22 Jul 2025 14:35:42 +0300 Subject: [PATCH 18/52] Cleanup --- .../GithubResourcepackManager.java | 7 ++----- .../handler/DataPackHandler.java | 17 ----------------- .../handler/ResourcePackHandler.java | 16 ---------------- .../platform/PlatformLogging.java | 2 -- .../platform/fabric/FabricPlatformCommand.java | 1 - .../platform/paper/PaperPlatformLogging.java | 2 -- 6 files changed, 2 insertions(+), 43 deletions(-) diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java index c155133..3fd4800 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java @@ -10,9 +10,6 @@ import top.offsetmonkey538.githubresourcepackmanager.networking.MainHttpHandler; import top.offsetmonkey538.githubresourcepackmanager.platform.*; import top.offsetmonkey538.githubresourcepackmanager.config.ConfigManager; -import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformMain; -import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformServerProperties; -import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformText; import top.offsetmonkey538.meshlib.api.HttpHandlerRegistry; import java.io.IOException; @@ -214,12 +211,12 @@ private static void updateDataPack(final GitHandler gitHandler, final UpdateType if (gitHandler.getCommitProperties() != null) placeholders.putAll(gitHandler.getCommitProperties().toPlaceholdersMap()); placeholders.put("{packType}", "data"); placeholders.put("{updateType}", updateType.name()); - placeholders.put("{wasUpdated}", String.valueOf(wasUpdated)); + placeholders.put("{wasUpdated}", String.valueOf(true)); LOGGER.info("Placeholders: %s", placeholders); // Send chat message try { - sendUpdateMessage(config.dataPackProvider.updateMessage, config.dataPackProvider.updateMessageHoverMessage, wasUpdated, placeholders, true); + sendUpdateMessage(config.dataPackProvider.updateMessage, config.dataPackProvider.updateMessageHoverMessage, true, placeholders, true); } catch (GithubResourcepackManagerException e) { LOGGER.error("Failed to send update message in chat!", e); } diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/DataPackHandler.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/DataPackHandler.java index 3a1b62e..344aa2f 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/DataPackHandler.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/DataPackHandler.java @@ -3,11 +3,9 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import org.apache.commons.io.file.PathUtils; -import top.offsetmonkey538.githubresourcepackmanager.config.ModConfig; import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformServerProperties; -import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -136,21 +134,6 @@ private void copyPacksToDirectory(List sourcePacks, Path datapacksDir) thr } } - /** - * Gather list of {@link File}s to construct the final pack from. - *

- * If there's a {@code pack.mcmeta} file in the {@link ModConfig#getResourcePackRoot() resource pack root directory}, the root directory will be the only file. - *

- *

- * If there's a {@link ModConfig#getResourcePackPacksDir() packs directory} in the pack root, all files directly in the packs directory will be returned. - *

- *

- * If there's neither a {@code pack.mcmeta} file or {@link ModConfig#getResourcePackPacksDir() packs directory} in the pack root, all files directly in the pack root will be returned. - *

- * - * @return A list of {@link File}s to construct the final pack from. May include directories and .zip files. - * @throws GithubResourcepackManagerException when the source packs could not be determined. - */ private List gatherSourcePacks() throws GithubResourcepackManagerException { LOGGER.info("Checking for 'pack.mcmeta' in data pack root..."); final boolean hasPackMcmeta = Files.exists(config.dataPackProvider.getPackRoot().resolve("pack.mcmeta")); diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/ResourcePackHandler.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/ResourcePackHandler.java index 3ce9137..49052c3 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/ResourcePackHandler.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/ResourcePackHandler.java @@ -3,7 +3,6 @@ import org.apache.commons.io.FileUtils; import org.apache.commons.io.filefilter.HiddenFileFilter; import org.jetbrains.annotations.Nullable; -import top.offsetmonkey538.githubresourcepackmanager.config.ModConfig; import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; import top.offsetmonkey538.githubresourcepackmanager.utils.MyFileUtils; import top.offsetmonkey538.githubresourcepackmanager.utils.StringUtils; @@ -139,21 +138,6 @@ private List gatherSourcePacksFrom(final Path packsDir) throws GithubResou .toList(); } - /** - * Gather list of {@link File}s to construct the final pack from. - *

- * If there's a {@code pack.mcmeta} file in the {@link ModConfig#getResourcePackRoot() resource pack root directory}, the root directory will be the only file. - *

- *

- * If there's a {@link ModConfig#getResourcePackPacksDir() packs directory} in the pack root, all files directly in the packs directory will be returned. - *

- *

- * If there's neither a {@code pack.mcmeta} file or {@link ModConfig#getResourcePackPacksDir() packs directory} in the pack root, all files directly in the pack root will be returned. - *

- * - * @return A list of {@link File}s to construct the final pack from. May include directories and .zip files. - * @throws GithubResourcepackManagerException when the source packs could not be determined. - */ private List gatherSourcePacks() throws GithubResourcepackManagerException { LOGGER.info("Checking for 'pack.mcmeta' in resource pack root..."); final boolean hasPackMcmeta = config.resourcePackProvider.getPackRoot().resolve("pack.mcmeta").toFile().exists(); diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformLogging.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformLogging.java index 8a84d63..c92931e 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformLogging.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformLogging.java @@ -2,8 +2,6 @@ import org.jetbrains.annotations.Nullable; -import java.util.function.BiConsumer; - import static top.offsetmonkey538.githubresourcepackmanager.platform.ServiceLoader.load; public interface PlatformLogging { diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformCommand.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformCommand.java index b991932..8fb9622 100644 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformCommand.java +++ b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformCommand.java @@ -14,7 +14,6 @@ import net.minecraft.text.MutableText; import net.minecraft.text.Style; import net.minecraft.text.Text; -import net.minecraft.util.Colors; import net.minecraft.util.Formatting; import org.apache.commons.lang3.exception.ExceptionUtils; import top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager; diff --git a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformLogging.java b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformLogging.java index 737d7b1..82bba96 100644 --- a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformLogging.java +++ b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformLogging.java @@ -1,13 +1,11 @@ package top.offsetmonkey538.githubresourcepackmanager.platform.paper; -import org.jetbrains.annotations.Nullable; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.function.BiConsumer; import java.util.logging.Level; import java.util.logging.Logger; From c469ddea7162ef4375af92bd3d7a3136409f4595 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Tue, 22 Jul 2025 19:01:49 +0300 Subject: [PATCH 19/52] Really? I just... delete a bunch of code and all of a sudden the mod becomes better? Support literally every git host that can send webhooks --- .../networking/WebhookHttpHandler.java | 47 ------------------- 1 file changed, 47 deletions(-) diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/WebhookHttpHandler.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/WebhookHttpHandler.java index 51f0f7d..7a754fd 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/WebhookHttpHandler.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/WebhookHttpHandler.java @@ -1,64 +1,17 @@ package top.offsetmonkey538.githubresourcepackmanager.networking; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonObject; import io.netty.channel.*; import io.netty.handler.codec.http.*; import top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager; -import top.offsetmonkey538.meshlib.api.HttpHandler; - -import java.nio.charset.StandardCharsets; import static io.netty.handler.codec.http.HttpResponseStatus.*; import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; -import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.config; -import static top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging.LOGGER; public final class WebhookHttpHandler { private WebhookHttpHandler() {} - private static final Gson GSON = new GsonBuilder().create(); - public static void handleRequest(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception { - if (!"application/json".contentEquals(HttpUtil.getMimeType(request))) { - LOGGER.warn(String.format("Bad request: POST request made with incorrect mime type '%s', expected 'application/json'", HttpUtil.getMimeType(request))); - HttpHandler.sendError(ctx, BAD_REQUEST); - return; - } - - - // Get the event header - final String githubEvent = request.headers().get("x-github-event"); - - if (githubEvent == null || githubEvent.isBlank()) { - HttpHandler.sendError(ctx, BAD_REQUEST, "Request headers don't contain 'x-github-event'"); - return; - } - - if (!githubEvent.contains("push")) { - HttpHandler.sendError(ctx, BAD_REQUEST, "Request isn't for push event"); - return; - } - LOGGER.debug("Received github push event"); - - // Get payload - final JsonObject payload = GSON.fromJson(request.content().toString(StandardCharsets.UTF_8), JsonObject.class); - if (payload == null) { - HttpHandler.sendError(ctx, BAD_REQUEST, "Request doesn't contain content"); - return; - } - - // Respond with "yeh bro everythins alright" ctx.writeAndFlush(new DefaultFullHttpResponse(HTTP_1_1, OK)).addListener(ChannelFutureListener.CLOSE); - - // Check which branch was pushed to - final String ref = payload.get("ref").getAsString(); - LOGGER.debug("Ref: %s", ref); - - if (!config.getGithubRef().equals(ref)) return; - - LOGGER.debug("Tracked branch has been updated, updating local pack..."); GithubResourcepackManager.updatePack(GithubResourcepackManager.UpdateType.WEBHOOK); } } From 26141e13dbbcc81c564b0345d58ec5b2d9810da4 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Tue, 22 Jul 2025 19:22:31 +0300 Subject: [PATCH 20/52] Fix crash introduced with #16 --- .../GithubResourcepackManager.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java index 3fd4800..75c1466 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java @@ -44,8 +44,6 @@ private GithubResourcepackManager() { public static ResourcePackHandler resourcePackHandler; public static void initialize() { - PlatformMain.INSTANCE.registerLogToAdminListener(); - PlatformCommand.INSTANCE.registerGithubRpManagerCommand(); ConfigManager.loadConfig(); @@ -58,7 +56,10 @@ public static void initialize() { HttpHandlerRegistry.INSTANCE.register(MOD_URI, new MainHttpHandler()); - PlatformMain.INSTANCE.runOnServerStart(() -> updatePack(UpdateType.RESTART)); + PlatformMain.INSTANCE.runOnServerStart(() -> { + PlatformMain.INSTANCE.registerLogToAdminListener(); + updatePack(UpdateType.RESTART); + }); } private static void createFolderStructure() throws GithubResourcepackManagerException { From fd18edd3797c4abd41e4640a27b3ddd1a0d080e3 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Sat, 26 Jul 2025 17:33:01 +0300 Subject: [PATCH 21/52] Update to monkeylib538 v3 --- common/build.gradle | 12 +- common/gradle.properties | 5 +- .../GithubResourcepackManager.java | 35 ++-- .../config/ConfigHandler.java | 99 ++++++++++ .../config/ConfigManager.java | 176 ------------------ .../config/ModConfig.java | 75 ++++---- .../handler/DataPackHandler.java | 40 +--- .../handler/GitHandler.java | 12 +- .../handler/ResourcePackHandler.java | 16 +- .../networking/FileHttpHandler.java | 2 +- .../networking/MainHttpHandler.java | 4 +- .../platform/PlatformLogging.java | 50 ----- .../platform/PlatformServerProperties.java | 4 +- .../utils/StringUtils.java | 2 +- fabric/build.gradle | 11 +- fabric/gradle.properties | 2 - .../fabric/FabricPlatformCommand.java | 25 ++- .../fabric/FabricPlatformLogging.java | 70 ------- .../platform/fabric/FabricPlatformMain.java | 9 +- .../platform/fabric/FabricPlatformText.java | 89 ++++++++- gradle.properties | 4 +- paper/build.gradle | 9 + .../platform/paper/PaperPlatformCommand.java | 14 +- .../platform/paper/PaperPlatformLogging.java | 71 ------- .../platform/paper/PaperPlatformMain.java | 6 +- .../platform/paper/PaperPlugin.java | 1 - 26 files changed, 329 insertions(+), 514 deletions(-) create mode 100644 common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ConfigHandler.java delete mode 100644 common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ConfigManager.java delete mode 100644 common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformLogging.java delete mode 100644 fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformLogging.java delete mode 100644 paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformLogging.java diff --git a/common/build.gradle b/common/build.gradle index 91a5e77..86dea47 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -4,18 +4,28 @@ plugins { repositories { mavenCentral() + maven { + name = "Mojang Libraries" + url = "https://libraries.minecraft.net" + content { + includeGroup "com.mojang" + } + } maven { name = "OffsetMods538" url = "https://maven.offsetmonkey538.top/releases" content { includeGroup "top.offsetmonkey538.meshlib" + includeGroup "top.offsetmonkey538.offsetconfig538" + includeGroup "top.offsetmonkey538.monkeylib538" } } } dependencies { implementation "org.eclipse.jgit:org.eclipse.jgit:${project.jgit_version}" - implementation "blue.endless:jankson:${project.jankson_version}" + implementation "top.offsetmonkey538.offsetconfig538:offsetconfig538:${project.offsetconfig538_version}" + shadow implementation("top.offsetmonkey538.monkeylib538:monkeylib538-common:${project.monkeylib538_version}") shadow compileOnly("top.offsetmonkey538.meshlib:mesh-lib-api:${project.meshlib_version}") diff --git a/common/gradle.properties b/common/gradle.properties index 8f66902..e8e57e3 100644 --- a/common/gradle.properties +++ b/common/gradle.properties @@ -2,7 +2,8 @@ jgit_version = 6.9.0.202403050737-r guava_version = 33.4.0-jre commons_version = 2.18.0 gson_version = 2.11.0 -# Jankson, check at https://github.com/falkreon/Jankson -jankson_version = 1.2.3 + +## OffsetConfig538, check at https://github.com/OffsetMods538/OffsetConfig538 +offsetconfig538_version = 1.0.0-2f5cd46 nameSuffix = api \ No newline at end of file diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java index 75c1466..2f8f0c0 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java @@ -3,14 +3,18 @@ import org.apache.commons.io.FileUtils; import org.jetbrains.annotations.Nullable; import top.offsetmonkey538.githubresourcepackmanager.config.ModConfig; +import top.offsetmonkey538.githubresourcepackmanager.config.ConfigHandler; import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; import top.offsetmonkey538.githubresourcepackmanager.handler.DataPackHandler; import top.offsetmonkey538.githubresourcepackmanager.handler.GitHandler; import top.offsetmonkey538.githubresourcepackmanager.handler.ResourcePackHandler; import top.offsetmonkey538.githubresourcepackmanager.networking.MainHttpHandler; import top.offsetmonkey538.githubresourcepackmanager.platform.*; -import top.offsetmonkey538.githubresourcepackmanager.config.ConfigManager; import top.offsetmonkey538.meshlib.api.HttpHandlerRegistry; +import top.offsetmonkey538.monkeylib538.api.log.PlatformLogger; +import top.offsetmonkey538.monkeylib538.api.log.PlatformLoggerProvider; +import top.offsetmonkey538.offsetconfig538.api.config.ConfigHolder; +import top.offsetmonkey538.offsetconfig538.api.config.ConfigManager; import java.io.IOException; import java.nio.file.Files; @@ -18,14 +22,13 @@ import java.util.*; import java.util.regex.Pattern; -import static top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging.LOGGER; - public final class GithubResourcepackManager { private GithubResourcepackManager() { } public static final String MOD_ID = "github-resourcepack-manager"; + public static final PlatformLogger LOGGER = PlatformLoggerProvider.INSTANCE.createLogger(MOD_ID); public static final String MOD_URI = "gh-rp-manager"; public static final Path DATA_FOLDER = PlatformMain.INSTANCE.getConfigDir().resolve(".packs"); @@ -39,14 +42,14 @@ private GithubResourcepackManager() { public static final Pattern RESOURCEPACK_NAME_PATTERN = Pattern.compile("\\d+-"); public static final UUID RESOURCEPACK_UUID = UUID.fromString("60ab8dc7-08d1-4f5f-a9a8-9a01d048b7b9"); - public static ModConfig config; + public static ConfigHolder config = ConfigManager.INSTANCE.init(ConfigHolder.create(ModConfig::new, LOGGER::error)); public static ResourcePackHandler resourcePackHandler; public static void initialize() { PlatformCommand.INSTANCE.registerGithubRpManagerCommand(); - ConfigManager.loadConfig(); + ConfigHandler.handleConfig(); try { createFolderStructure(); @@ -104,13 +107,13 @@ public static void updatePack(final UpdateType updateType) { } if (!failed) LOGGER.info("Successfully updated git repository!"); - if (config.resourcePackProvider.enabled) { + if (config.get().resourcePackProvider.enabled) { LOGGER.info(""); LOGGER.info("Updating resource pack..."); updateResourcePack(gitHandler, updateType, failed); LOGGER.info("Resource pack updated!"); } - if (config.dataPackProvider.enabled) { + if (config.get().dataPackProvider.enabled) { LOGGER.info(""); LOGGER.info("Updating data pack..."); updateDataPack(gitHandler, updateType, failed); @@ -125,7 +128,7 @@ private static void updateResourcePack(final GitHandler gitHandler, final Update // Check if pack was updated final boolean wasUpdated = - gitHandler.getChangedFiles().map(changes -> changes.stream().anyMatch(it -> it.startsWith(config.resourcePackProvider.getRootLocation()))).orElse(true) + gitHandler.getChangedFiles().map(changes -> changes.stream().anyMatch(it -> it.startsWith(config.get().resourcePackProvider.getRootLocation()))).orElse(true) || oldResourcePackPath == null || !oldResourcePackPath.toFile().exists(); if (!wasUpdated) LOGGER.info("Pack hasn't changed since last update. Skipping new pack generation."); @@ -155,14 +158,14 @@ private static void updateResourcePack(final GitHandler gitHandler, final Update final Map placeholders = new HashMap<>(); if (gitHandler.getCommitProperties() != null) placeholders.putAll(gitHandler.getCommitProperties().toPlaceholdersMap()); placeholders.put("{packType}", "resource"); - placeholders.put("{downloadUrl}", config.getPackUrl(resourcePackHandler.getOutputPackName())); + placeholders.put("{downloadUrl}", config.get().getPackUrl(resourcePackHandler.getOutputPackName())); placeholders.put("{updateType}", updateType.name()); placeholders.put("{wasUpdated}", String.valueOf(wasUpdated)); LOGGER.info("Placeholders: %s", placeholders); // Send chat message try { - if (!failed) sendUpdateMessage(config.resourcePackProvider.updateMessage, config.resourcePackProvider.updateMessageHoverMessage, wasUpdated, placeholders); + if (!failed) sendUpdateMessage(config.get().resourcePackProvider.updateMessage, config.get().resourcePackProvider.updateMessageHoverMessage, wasUpdated, placeholders); } catch (GithubResourcepackManagerException e) { LOGGER.error("Failed to send update message in chat!", e); } @@ -173,12 +176,12 @@ private static void updateResourcePack(final GitHandler gitHandler, final Update return; } if (!updateFailed) try { - config.resourcePackProvider.successWebhook.trigger(true, placeholders, updateType); + config.get().resourcePackProvider.successWebhook.trigger(true, placeholders, updateType); } catch (GithubResourcepackManagerException e) { LOGGER.error("Failed to trigger success webhook!", e); } else try { - config.resourcePackProvider.failWebhook.trigger(false, placeholders, updateType); + config.get().resourcePackProvider.failWebhook.trigger(false, placeholders, updateType); } catch (GithubResourcepackManagerException e) { LOGGER.error("Failed to trigger fail webhook!", e); } @@ -186,7 +189,7 @@ private static void updateResourcePack(final GitHandler gitHandler, final Update private static void updateDataPack(final GitHandler gitHandler, final UpdateType updateType, boolean updateFailed) { // Check if pack was updated - final boolean wasUpdated = gitHandler.getChangedFiles().map(changes -> changes.stream().anyMatch(it -> it.startsWith(config.dataPackProvider.getRootLocation()))).orElse(true); + final boolean wasUpdated = gitHandler.getChangedFiles().map(changes -> changes.stream().anyMatch(it -> it.startsWith(config.get().dataPackProvider.getRootLocation()))).orElse(true); if (!wasUpdated) { LOGGER.info("Pack hasn't changed since last update. Datapack processing will be skipped."); return; @@ -217,19 +220,19 @@ private static void updateDataPack(final GitHandler gitHandler, final UpdateType // Send chat message try { - sendUpdateMessage(config.dataPackProvider.updateMessage, config.dataPackProvider.updateMessageHoverMessage, true, placeholders, true); + sendUpdateMessage(config.get().dataPackProvider.updateMessage, config.get().dataPackProvider.updateMessageHoverMessage, true, placeholders, true); } catch (GithubResourcepackManagerException e) { LOGGER.error("Failed to send update message in chat!", e); } // Trigger webhooks if (!updateFailed) try { - config.dataPackProvider.successWebhook.trigger(true, placeholders, updateType); + config.get().dataPackProvider.successWebhook.trigger(true, placeholders, updateType); } catch (GithubResourcepackManagerException e) { LOGGER.error("Failed to trigger success webhook!", e); } else try { - config.dataPackProvider.failWebhook.trigger(false, placeholders, updateType); + config.get().dataPackProvider.failWebhook.trigger(false, placeholders, updateType); } catch (GithubResourcepackManagerException e) { LOGGER.error("Failed to trigger fail webhook!", e); } diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ConfigHandler.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ConfigHandler.java new file mode 100644 index 0000000..78bbea3 --- /dev/null +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ConfigHandler.java @@ -0,0 +1,99 @@ +package top.offsetmonkey538.githubresourcepackmanager.config; + +import blue.endless.jankson.*; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.githubresourcepackmanager.config.webhook.BasicWebhook; +import top.offsetmonkey538.githubresourcepackmanager.config.webhook.DefaultWebhookBody; +import top.offsetmonkey538.githubresourcepackmanager.config.webhook.discord.basic.BasicFailMessage; +import top.offsetmonkey538.githubresourcepackmanager.config.webhook.discord.basic.BasicSuccessMessage; +import top.offsetmonkey538.githubresourcepackmanager.config.webhook.discord.embed.EmbedFailMessage; +import top.offsetmonkey538.githubresourcepackmanager.config.webhook.discord.embed.EmbedSuccessMessage; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.*; +import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.LOGGER; + +public final class ConfigHandler { + private ConfigHandler() { + + } + + public static void handleConfig() { + LOGGER.info("Writing default webhook bodies"); + createDefaultWebhooks(); + + // Checking if config is valid + final List errors = checkConfigErrors(); + + // Return when nothing's wrong + if (errors.isEmpty()) return; + + // There were errors, time to log em. + LOGGER.error("There were problems with the config for GitHub Resourcepack Manager, see below for more details!"); + errors.stream().map(string -> "\t" + string).forEach(LOGGER::error); + LOGGER.error("There were problems with the config for Github Resourcepack Manager, see above for more details!"); + + throw new RuntimeException("There were problems with the config for Github Resourcepack Manager, see above for more details!"); + } + + private static void createDefaultWebhooks() { + final Jankson jankson = new Jankson.Builder().build(); + final List webhookBodies = List.of( + new BasicWebhook(), + + new BasicSuccessMessage(), + new BasicFailMessage(), + new EmbedSuccessMessage(), + new EmbedFailMessage() + ); + + for (DefaultWebhookBody webhook : webhookBodies) { + final Path location = config.get().getFilePath().getParent().resolve(webhook.getName()); + + if (Files.exists(location)) continue; + + try { + Files.createDirectories(location.getParent()); + Files.writeString(location, jankson.toJson(webhook).toJson(JsonGrammar.STRICT)); + } catch (IOException e) { + LOGGER.error("Failed to write default webhook body '%s'!", webhook.getName(), e); + } + } + } + + private static @NotNull List checkConfigErrors() { + final List errors = new ArrayList<>(); + + if (config.get().serverInfo.publicIp == null) errors.add("Field 'serverInfo.publicIp' not set!"); + if (config.get().repositoryInfo.url == null) errors.add("Field 'repositoryInfo.url' not set!"); + if (config.get().repositoryInfo.isPrivate) { + if (config.get().repositoryInfo.username == null) errors.add("Field 'repositoryInfo.username' not set, but 'repositoryInfo.isPrivate' is true!"); + if (config.get().repositoryInfo.token == null) errors.add("Field 'repositoryInfo.token' not set, but 'repositoryInfo.isPrivate' is true!"); + } else { + if (config.get().repositoryInfo.username != null) errors.add("Field 'repositoryInfo.username' set, but 'repositoryInfo.isPrivate' is false! Can be unset"); + if (config.get().repositoryInfo.token != null) errors.add("Field 'repositoryInfo.token' set, but 'repositoryInfo.isPrivate' is false! Can be unset"); + } + + checkWebhookErrors(errors, config.get().resourcePackProvider.successWebhook, "resourcePackProvider.successWebhook"); + checkWebhookErrors(errors, config.get().resourcePackProvider.failWebhook, "resourcePackProvider.failWebhook"); + checkWebhookErrors(errors, config.get().dataPackProvider.successWebhook, "dataPackProvider.successWebhook"); + checkWebhookErrors(errors, config.get().dataPackProvider.failWebhook, "dataPackProvider.failWebhook"); + + return errors; + } + + private static void checkWebhookErrors(final List errors, final ModConfig.WebhookInfo webhook, final String webhookPath) { + if (!webhook.enabled) return; + + if (webhook.url == null) errors.add("Field '%s.url' not set, but '%s.enabled' is true!".formatted(webhookPath, webhookPath)); + if (webhook.body == null) errors.add("Field '%s.body' not set, but '%s.enabled' is true!".formatted(webhookPath, webhookPath)); + + final Path bodyPath = webhook.getBodyPath(); + if (bodyPath != null && !Files.exists(bodyPath)) errors.add("Field '%s.body' points to file '%s', which doesn't exist!".formatted(webhookPath, bodyPath.toAbsolutePath())); + } +} diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ConfigManager.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ConfigManager.java deleted file mode 100644 index 3e6650b..0000000 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ConfigManager.java +++ /dev/null @@ -1,176 +0,0 @@ -package top.offsetmonkey538.githubresourcepackmanager.config; - -import blue.endless.jankson.Jankson; -import blue.endless.jankson.JsonElement; -import blue.endless.jankson.JsonObject; -import blue.endless.jankson.JsonPrimitive; -import blue.endless.jankson.api.SyntaxError; -import org.jetbrains.annotations.NotNull; -import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformMain; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.List; - -import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.*; -import static top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging.LOGGER; - -public final class ConfigManager { - private ConfigManager() { - - } - - public static final String VERSION_KEY = "!!!version"; - - public static final Path OLD_CONFIG_FILE_PATH = PlatformMain.INSTANCE.getConfigDir().getParent().resolve(MOD_ID + ".json"); - public static final Path NEW_CONFIG_FILE_PATH = PlatformMain.INSTANCE.getConfigDir().resolve(MOD_ID + ".json"); - public static final Path CURRENT_CONFIG_FILE_PATH = PlatformMain.INSTANCE.getConfigDir().resolve("config").resolve("main.json"); - - - public static void loadConfig() { - if (Files.exists(OLD_CONFIG_FILE_PATH)) { - try { - Files.createDirectories(NEW_CONFIG_FILE_PATH.getParent()); - Files.move(OLD_CONFIG_FILE_PATH, NEW_CONFIG_FILE_PATH); - } catch (IOException e) { - throw new RuntimeException("Failed to move config file to new location!", e); - } - } - if (Files.exists(NEW_CONFIG_FILE_PATH)) { - try { - Files.createDirectories(CURRENT_CONFIG_FILE_PATH.getParent()); - Files.move(NEW_CONFIG_FILE_PATH, CURRENT_CONFIG_FILE_PATH); - } catch (IOException e) { - throw new RuntimeException("Failed to move config file to new location!", e); - } - } - - config = new ModConfig(); - if (CURRENT_CONFIG_FILE_PATH.toFile().exists()) config = load(); - save(config); - - LOGGER.info("Writing default webhook bodies"); - config.createDefaultWebhooks(); - - // Checking if config is valid - final List errors = checkConfigErrors(); - - // Return when nothing's wrong - if (errors.isEmpty()) return; - - // There were errors, time to log em. - LOGGER.error("There were problems with the config for GitHub Resourcepack Manager, see below for more details!"); - errors.stream().map(string -> "\t" + string).forEach(LOGGER::error); - LOGGER.error("There were problems with the config for Github Resourcepack Manager, see above for more details!"); - - throw new RuntimeException("There were problems with the config for Github Resourcepack Manager, see above for more details!"); - } - - private static @NotNull List checkConfigErrors() { - final List errors = new ArrayList<>(); - - if (config.serverInfo.publicIp == null) errors.add("Field 'serverInfo.publicIp' not set!"); - if (config.repositoryInfo.url == null) errors.add("Field 'repositoryInfo.url' not set!"); - if (config.repositoryInfo.isPrivate) { - if (config.repositoryInfo.username == null) errors.add("Field 'repositoryInfo.username' not set, but 'repositoryInfo.isPrivate' is true!"); - if (config.repositoryInfo.token == null) errors.add("Field 'repositoryInfo.token' not set, but 'repositoryInfo.isPrivate' is true!"); - } else { - if (config.repositoryInfo.username != null) errors.add("Field 'repositoryInfo.username' set, but 'repositoryInfo.isPrivate' is false! Can be unset"); - if (config.repositoryInfo.token != null) errors.add("Field 'repositoryInfo.token' set, but 'repositoryInfo.isPrivate' is false! Can be unset"); - } - - checkWebhookErrors(errors, config.resourcePackProvider.successWebhook, "resourcePackProvider.successWebhook"); - checkWebhookErrors(errors, config.resourcePackProvider.failWebhook, "resourcePackProvider.failWebhook"); - checkWebhookErrors(errors, config.dataPackProvider.successWebhook, "dataPackProvider.successWebhook"); - checkWebhookErrors(errors, config.dataPackProvider.failWebhook, "dataPackProvider.failWebhook"); - - return errors; - } - - private static void checkWebhookErrors(final List errors, final ModConfig.WebhookInfo webhook, final String webhookPath) { - if (!webhook.enabled) return; - - if (webhook.url == null) errors.add("Field '%s.url' not set, but '%s.enabled' is true!".formatted(webhookPath, webhookPath)); - if (webhook.body == null) errors.add("Field '%s.body' not set, but '%s.enabled' is true!".formatted(webhookPath, webhookPath)); - - final Path bodyPath = webhook.getBodyPath(); - if (bodyPath != null && !Files.exists(bodyPath)) errors.add("Field '%s.body' points to file '%s', which doesn't exist!".formatted(webhookPath, bodyPath.toAbsolutePath())); - } - - private static ModConfig load() { - final Jankson jankson = Jankson.builder().build(); - final File configFile = CURRENT_CONFIG_FILE_PATH.toFile(); - - // Load the config from disk - final JsonObject json; - try { - json = jankson.load(configFile); - } catch (IOException e) { - LOGGER.error("Config file '%s' could not be read!", CURRENT_CONFIG_FILE_PATH, e); - return config; - } catch (SyntaxError e) { - LOGGER.error("Config file '%s' is formatted incorrectly!", CURRENT_CONFIG_FILE_PATH, e); - return config; - } - - // Check if version matches the latest version - applyDatafixers(config, configFile.toPath(), json, jankson); - - // Load config class from the final json - return jankson.fromJson(json, config.getClass()); - } - - private static void applyDatafixers(ModConfig config, Path configFile, JsonObject json, Jankson jankson) { - int last_version = json.getInt(VERSION_KEY, 0); - int current_version = config.getConfigVersion(); - if (last_version < current_version) try { - final Path backupPath = configFile.resolveSibling(String.format("%s-%s-backup.json", configFile.getFileName(), LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy_MM_dd-HH_mm_ss")))); - Files.copy(configFile, backupPath); - - for (Datafixer datafixer : config.getDatafixers().subList(last_version, current_version)) { - datafixer.apply(json, jankson); - } - save(config); - } catch (IOException e) { - LOGGER.error("Unable to create backup of config file '%s'! Continuing anyway cause I don't care if your config gets messed up and I can't think of a reason for this even happening cause like the initial config file has to be there so I'd imagine that the directory is writeable so like why wouldn't it be possible to write the backup of the file?", configFile, e); - } - - if (last_version > current_version) { - throw new IllegalStateException(String.format("Config file '%s' is for a newer version of the mod, please update! Expected config version '%s', found '%s'!", CURRENT_CONFIG_FILE_PATH, current_version, last_version)); - } - } - - private static void save(ModConfig config) { - final Jankson jankson = Jankson.builder().build(); - - // Convert config to json - final JsonElement jsonAsElement = jankson.toJson(config); - if (!(jsonAsElement instanceof final JsonObject json)) { - LOGGER.error("Could not cast '%s' to 'JsonObject'! Config will not be saved!", jsonAsElement.getClass().getName()); - return; - } - - // Write config version - json.put(VERSION_KEY, new JsonPrimitive(config.getConfigVersion()), "!!!!! DO NOT MODIFY THIS VALUE !!!!"); - - // Convert final json to string - final String result = json.toJson(true, true); - - try { - Files.createDirectories(CURRENT_CONFIG_FILE_PATH.getParent()); - Files.writeString(CURRENT_CONFIG_FILE_PATH, result); - } catch (IOException e) { - LOGGER.error("Config file '%s' could not be written to!", CURRENT_CONFIG_FILE_PATH, e); - } - } - - @FunctionalInterface - public interface Datafixer { - void apply(JsonObject original, Jankson jankson); - } -} diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java index 5753ead..7242aa3 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java @@ -1,33 +1,26 @@ package top.offsetmonkey538.githubresourcepackmanager.config; import blue.endless.jankson.Comment; -import blue.endless.jankson.Jankson; -import blue.endless.jankson.JsonGrammar; import blue.endless.jankson.api.Marshaller; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import top.offsetmonkey538.githubresourcepackmanager.config.webhook.BasicWebhook; -import top.offsetmonkey538.githubresourcepackmanager.config.webhook.DefaultWebhookBody; -import top.offsetmonkey538.githubresourcepackmanager.config.webhook.discord.basic.BasicFailMessage; -import top.offsetmonkey538.githubresourcepackmanager.config.webhook.discord.basic.BasicSuccessMessage; -import top.offsetmonkey538.githubresourcepackmanager.config.webhook.discord.embed.EmbedFailMessage; -import top.offsetmonkey538.githubresourcepackmanager.config.webhook.discord.embed.EmbedSuccessMessage; import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; +import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformMain; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformServerProperties; import top.offsetmonkey538.githubresourcepackmanager.utils.StringUtils; import top.offsetmonkey538.githubresourcepackmanager.utils.WebhookSender; +import top.offsetmonkey538.offsetconfig538.api.config.Config; +import top.offsetmonkey538.offsetconfig538.api.config.Datafixer; import java.io.IOException; import java.net.URI; import java.nio.file.Files; import java.nio.file.Path; -import java.util.List; import java.util.Map; import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.*; -import static top.offsetmonkey538.githubresourcepackmanager.config.ConfigManager.CURRENT_CONFIG_FILE_PATH; -import static top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging.LOGGER; -public class ModConfig { +public class ModConfig implements Config { @Comment("!!!!Please check the wiki for how to set up the mod. It is linked on both the Modrinth and GitHub pages!!!!") public ServerInfo serverInfo = new ServerInfo(); @@ -142,7 +135,7 @@ public void trigger(final boolean updateSucceeded, final Map pla WebhookSender.send(webhookBody, getWebhookUrl(), updateType, updateSucceeded); } catch (IOException e) { - throw new GithubResourcepackManagerException("Failed to read content of webhook body file '%s'!", e, config.resourcePackProvider.successWebhook.body); + throw new GithubResourcepackManagerException("Failed to read content of webhook body file '%s'!", e, config.get().resourcePackProvider.successWebhook.body); } } @@ -152,20 +145,13 @@ public void trigger(final boolean updateSucceeded, final Map pla } public @Nullable Path getBodyPath() { - return body == null ? null : CURRENT_CONFIG_FILE_PATH.getParent().resolve(body); + return body == null ? null : config.get().getFilePath().getParent().resolve(body); } } - protected String getName() { - return MOD_ID + "/" + MOD_ID; - } - - protected int getConfigVersion() { - return 3; - } - - protected List getDatafixers() { - return List.of( + @Override + public @NotNull Datafixer[] getDatafixers() { + return new Datafixer[]{ (original, jankson) -> { // 0 -> 1 original.put("branch", jankson.toJson(jankson.getMarshaller().marshall(String.class, original.get("githubRef")).replace("refs/heads/", ""))); @@ -227,30 +213,35 @@ protected List getDatafixers() { original.put("resourcePackProvider", jankson.toJson(resourcePackProvider)); } - ); + }; } - public void createDefaultWebhooks() { - final Jankson jankson = new Jankson.Builder().build(); - final List webhookBodies = List.of( - new BasicWebhook(), - - new BasicSuccessMessage(), - new BasicFailMessage(), - new EmbedSuccessMessage(), - new EmbedFailMessage() - ); - - for (DefaultWebhookBody webhook : webhookBodies) { - final Path location = CURRENT_CONFIG_FILE_PATH.getParent().resolve(webhook.getName()); + @Override + public int getConfigVersion() { + return 3; + } - if (Files.exists(location)) continue; + @Override + public @NotNull Path getFilePath() { + return PlatformMain.INSTANCE.getConfigDir().resolve("config").resolve("main.json"); + } + @Override + public void beforeLoadStart() { + if (Files.exists(PlatformMain.INSTANCE.getConfigDir().getParent().resolve(MOD_ID + ".json"))) { + try { + Files.createDirectories(PlatformMain.INSTANCE.getConfigDir().resolve(MOD_ID + ".json").getParent()); + Files.move(PlatformMain.INSTANCE.getConfigDir().getParent().resolve(MOD_ID + ".json"), PlatformMain.INSTANCE.getConfigDir().resolve(MOD_ID + ".json")); + } catch (IOException e) { + throw new RuntimeException("Failed to move config file to new location!", e); + } + } + if (Files.exists(PlatformMain.INSTANCE.getConfigDir().resolve(MOD_ID + ".json"))) { try { - Files.createDirectories(location.getParent()); - Files.writeString(location, jankson.toJson(webhook).toJson(JsonGrammar.STRICT)); + Files.createDirectories(getFilePath().getParent()); + Files.move(PlatformMain.INSTANCE.getConfigDir().resolve(MOD_ID + ".json"), getFilePath()); } catch (IOException e) { - LOGGER.error("Failed to write default webhook body '%s'!", webhook.getName(), e); + throw new RuntimeException("Failed to move config file to new location!", e); } } } diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/DataPackHandler.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/DataPackHandler.java index 344aa2f..177a535 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/DataPackHandler.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/DataPackHandler.java @@ -13,7 +13,7 @@ import java.util.stream.Stream; import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.*; -import static top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging.LOGGER; +import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.LOGGER; public class DataPackHandler { private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); @@ -110,53 +110,29 @@ private State readStateFile() throws IOException { return GSON.fromJson(Files.readString(STATE_FILE), State.class); } - private void copyPacksToDirectory(List sourcePacks, Path datapacksDir) throws GithubResourcepackManagerException { - for (Path sourcePack : sourcePacks) { - if (Files.isDirectory(sourcePack)) { - try { - PathUtils.copyDirectory(sourcePack, datapacksDir); - } catch (IOException e) { - throw new GithubResourcepackManagerException("Failed to copy pack '%s' into directory '%s'!", e, sourcePack, datapacksDir); - } - continue; - } - if (sourcePack.getFileName().endsWith(".zip")) { - try { - Files.copy(sourcePack, datapacksDir); - } catch (IOException e) { - throw new GithubResourcepackManagerException("Failed to copy pack '%s' into directory '%s'!", e, sourcePack, datapacksDir); - } - continue; - } - - LOGGER.error("'%s' is not a valid pack! Ignoring...", sourcePack); - sourcePacks.remove(sourcePack); - } - } - private List gatherSourcePacks() throws GithubResourcepackManagerException { LOGGER.info("Checking for 'pack.mcmeta' in data pack root..."); - final boolean hasPackMcmeta = Files.exists(config.dataPackProvider.getPackRoot().resolve("pack.mcmeta")); + final boolean hasPackMcmeta = Files.exists(config.get().dataPackProvider.getPackRoot().resolve("pack.mcmeta")); LOGGER.info("%sFound!", hasPackMcmeta ? "" : "Not "); LOGGER.info("Checking for 'packs' directory in data pack root..."); - Path packsDir = config.dataPackProvider.getPackPacksDir(); + Path packsDir = config.get().dataPackProvider.getPackPacksDir(); final boolean hasPacksFolder = Files.exists(packsDir) && Files.isDirectory(packsDir); LOGGER.info("%sFound!", hasPacksFolder ? "" : "Not "); if (hasPackMcmeta && hasPacksFolder) { - throw new GithubResourcepackManagerException("Found both 'pack.mcmeta' and the 'packs' directory in data pack root '%s'!", config.dataPackProvider.getPackRoot().toAbsolutePath()); + throw new GithubResourcepackManagerException("Found both 'pack.mcmeta' and the 'packs' directory in data pack root '%s'!", config.get().dataPackProvider.getPackRoot().toAbsolutePath()); } if (!hasPackMcmeta && !hasPacksFolder) { - LOGGER.info("Found neither 'pack.mcmeta' nor the 'packs' directory in data pack root '%s'!", config.dataPackProvider.getPackPacksDir().toAbsolutePath()); - LOGGER.info("Assuming data pack root '%s' as 'packs' directory.", config.dataPackProvider.getPackPacksDir().toAbsolutePath()); - packsDir = config.dataPackProvider.getPackRoot(); + LOGGER.info("Found neither 'pack.mcmeta' nor the 'packs' directory in data pack root '%s'!", config.get().dataPackProvider.getPackPacksDir().toAbsolutePath()); + LOGGER.info("Assuming data pack root '%s' as 'packs' directory.", config.get().dataPackProvider.getPackPacksDir().toAbsolutePath()); + packsDir = config.get().dataPackProvider.getPackRoot(); } if (hasPackMcmeta) { LOGGER.info("Using data pack root as data pack."); - return List.of(config.dataPackProvider.getPackRoot()); + return List.of(config.get().dataPackProvider.getPackRoot()); } LOGGER.info("Using 'packs' directory for data packs."); diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/GitHandler.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/GitHandler.java index 5dd0112..018250b 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/GitHandler.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/GitHandler.java @@ -25,7 +25,7 @@ import java.util.Optional; import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.*; -import static top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging.LOGGER; +import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.LOGGER; public class GitHandler { @@ -81,8 +81,8 @@ private static CommitProperties getLatestCommitProperties(String lastCommitHash, private static void updateRepository(boolean retry) throws GithubResourcepackManagerException { // Create credentials provider if repository is private CredentialsProvider credentialsProvider = null; - if (config.repositoryInfo.isPrivate) - credentialsProvider = new UsernamePasswordCredentialsProvider(config.repositoryInfo.username, config.repositoryInfo.token); + if (config.get().repositoryInfo.isPrivate) + credentialsProvider = new UsernamePasswordCredentialsProvider(config.get().repositoryInfo.username, config.get().repositoryInfo.token); // If the repo folder doesn't exist, clone the repository. if (!GIT_FOLDER.toFile().exists()) cloneRepository(credentialsProvider); @@ -94,7 +94,7 @@ private static void updateRepository(boolean retry) throws GithubResourcepackMan .setCredentialsProvider(credentialsProvider) .setContentMergeStrategy(ContentMergeStrategy.THEIRS) .setStrategy(MergeStrategy.THEIRS) - .setRemoteBranchName(config.getGithubRef()) + .setRemoteBranchName(config.get().getGithubRef()) .call(); // Handle errors @@ -132,9 +132,9 @@ private static void updateRepository(boolean retry) throws GithubResourcepackMan private static void cloneRepository(CredentialsProvider credentialsProvider) throws GithubResourcepackManagerException { try { Git git = Git.cloneRepository() - .setURI(config.repositoryInfo.url) + .setURI(config.get().repositoryInfo.url) .setDirectory(GIT_FOLDER.toFile()) - .setBranch(config.getGithubRef()) + .setBranch(config.get().getGithubRef()) .setCredentialsProvider(credentialsProvider) .call(); git.close(); diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/ResourcePackHandler.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/ResourcePackHandler.java index 49052c3..bf0e027 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/ResourcePackHandler.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/ResourcePackHandler.java @@ -18,7 +18,7 @@ import java.util.stream.Stream; import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.*; -import static top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging.LOGGER; +import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.LOGGER; public class ResourcePackHandler { private Path outputPackPath; @@ -140,27 +140,27 @@ private List gatherSourcePacksFrom(final Path packsDir) throws GithubResou private List gatherSourcePacks() throws GithubResourcepackManagerException { LOGGER.info("Checking for 'pack.mcmeta' in resource pack root..."); - final boolean hasPackMcmeta = config.resourcePackProvider.getPackRoot().resolve("pack.mcmeta").toFile().exists(); + final boolean hasPackMcmeta = config.get().resourcePackProvider.getPackRoot().resolve("pack.mcmeta").toFile().exists(); LOGGER.info("%sFound!", hasPackMcmeta ? "" : "Not "); LOGGER.info("Checking for 'packs' directory in resource pack root..."); - Path packsDir = config.resourcePackProvider.getPackPacksDir(); + Path packsDir = config.get().resourcePackProvider.getPackPacksDir(); final boolean hasPacksFolder = Files.exists(packsDir) && Files.isDirectory(packsDir); LOGGER.info("%sFound!", hasPacksFolder ? "" : "Not "); if (hasPackMcmeta && hasPacksFolder) { - throw new GithubResourcepackManagerException("Found both 'pack.mcmeta' and the 'packs' directory in resource pack root '%s'!", config.resourcePackProvider.getPackPacksDir().toAbsolutePath()); + throw new GithubResourcepackManagerException("Found both 'pack.mcmeta' and the 'packs' directory in resource pack root '%s'!", config.get().resourcePackProvider.getPackPacksDir().toAbsolutePath()); } if (!hasPackMcmeta && !hasPacksFolder) { - LOGGER.info("Found neither 'pack.mcmeta' nor the 'packs' directory in resource pack root '%s'!", config.resourcePackProvider.getPackPacksDir().toAbsolutePath()); - LOGGER.info("Assuming resource pack root '%s' as 'packs' directory.", config.resourcePackProvider.getPackPacksDir().toAbsolutePath()); - packsDir = config.resourcePackProvider.getPackRoot(); + LOGGER.info("Found neither 'pack.mcmeta' nor the 'packs' directory in resource pack root '%s'!", config.get().resourcePackProvider.getPackPacksDir().toAbsolutePath()); + LOGGER.info("Assuming resource pack root '%s' as 'packs' directory.", config.get().resourcePackProvider.getPackPacksDir().toAbsolutePath()); + packsDir = config.get().resourcePackProvider.getPackRoot(); } if (hasPackMcmeta) { LOGGER.info("Using resource pack root as resource pack."); - return List.of(config.resourcePackProvider.getPackRoot().toFile()); + return List.of(config.get().resourcePackProvider.getPackRoot().toFile()); } LOGGER.info("Using 'packs' directory for resource packs."); diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/FileHttpHandler.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/FileHttpHandler.java index 3ce42b4..a534078 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/FileHttpHandler.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/FileHttpHandler.java @@ -11,7 +11,7 @@ import static io.netty.handler.codec.http.HttpResponseStatus.OK; import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.resourcePackHandler; -import static top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging.LOGGER; +import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.LOGGER; public final class FileHttpHandler { private FileHttpHandler() {} diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/MainHttpHandler.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/MainHttpHandler.java index 4771df9..4d1b182 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/MainHttpHandler.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/MainHttpHandler.java @@ -7,7 +7,7 @@ import top.offsetmonkey538.meshlib.api.HttpHandler; import static io.netty.handler.codec.http.HttpResponseStatus.*; -import static top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging.LOGGER; +import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.LOGGER; public class MainHttpHandler implements HttpHandler { @@ -18,7 +18,7 @@ public void handleRequest(@NotNull ChannelHandlerContext ctx, @NotNull FullHttpR return; } - LOGGER.debug("Received Request: " + request); + LOGGER.debug("Received Request: &s", request); final HttpMethod method = request.method(); diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformLogging.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformLogging.java deleted file mode 100644 index c92931e..0000000 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformLogging.java +++ /dev/null @@ -1,50 +0,0 @@ -package top.offsetmonkey538.githubresourcepackmanager.platform; - -import org.jetbrains.annotations.Nullable; - -import static top.offsetmonkey538.githubresourcepackmanager.platform.ServiceLoader.load; - -public interface PlatformLogging { - PlatformLogging LOGGER = load(PlatformLogging.class); - - default void debug(String message, Object... args) { - debug(String.format(message, args)); - } - default void info(String message, Object... args) { - info(String.format(message, args)); - } - default void warn(String message, Object... args) { - warn(String.format(message, args)); - } - default void warn(String message, Throwable error, Object... args) { - warn(String.format(message, args), error); - } - default void error(String message, Object... args) { - error(String.format(message, args)); - } - default void error(String message, Throwable error, Object... args) { - error(String.format(message, args), error); - } - - void debug(String message); - void info(String message); - void warn(String message); - void warn(String message, Throwable error); - void error(String message); - void error(String message, Throwable error); - - void addListener(LogLevel level, LogListener listener); - void removeListener(LogLevel level, LogListener listener); - - enum LogLevel { - DEBUG, - INFO, - WARN, - ERROR - } - - @FunctionalInterface - interface LogListener { - void accept(String message, @Nullable Throwable error); - } -} diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformServerProperties.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformServerProperties.java index c683a76..5621e7c 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformServerProperties.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformServerProperties.java @@ -9,7 +9,7 @@ import java.util.Map; import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.*; -import static top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging.LOGGER; +import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.LOGGER; import static top.offsetmonkey538.githubresourcepackmanager.platform.ServiceLoader.load; public interface PlatformServerProperties { @@ -22,7 +22,7 @@ public interface PlatformServerProperties { void reload() throws GithubResourcepackManagerException; default void updatePackProperties(ResourcePackHandler packHandler) throws GithubResourcepackManagerException { - final String resourcePackUrl = config.getPackUrl(packHandler.getOutputPackName()); + final String resourcePackUrl = config.get().getPackUrl(packHandler.getOutputPackName()); final String resourcePackSha1; try { // Ignore the fact that sha1 hashing is deprecated as Minecraft uses it for validating server resource packs. diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/utils/StringUtils.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/utils/StringUtils.java index fdbd903..10153cf 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/utils/StringUtils.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/utils/StringUtils.java @@ -7,7 +7,7 @@ import java.util.regex.Matcher; import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.RESOURCEPACK_NAME_PATTERN; -import static top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging.LOGGER; +import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.LOGGER; public final class StringUtils { private StringUtils() { diff --git a/fabric/build.gradle b/fabric/build.gradle index f671054..87ce2ff 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -59,8 +59,9 @@ repositories { name = "OffsetMods538" url = "https://maven.offsetmonkey538.top/releases" content { - includeGroup "top.offsetmonkey538.monkeylib538" includeGroup "top.offsetmonkey538.meshlib" + includeGroup "top.offsetmonkey538.monkeylib538" + includeGroup "top.offsetmonkey538.offsetconfig538" } } } @@ -91,9 +92,13 @@ dependencies { modRuntimeOnly "me.djtheredstoner:DevAuth-fabric:${devauth_version}" - modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fapi_version}" + compileOnly "top.offsetmonkey538.monkeylib538:monkeylib538-common:${project.monkeylib538_version}" + modRuntimeOnly("top.offsetmonkey538.monkeylib538:monkeylib538-fabric:${project.monkeylib538_version}+1.21.5") { + exclude(group: "net.fabricmc.fabric-api") + exclude(group: "top.offsetmonkey538.monkeylib538:monkeylib538-fabric") + } - modImplementation "top.offsetmonkey538.monkeylib538:monkeylib538:${project.monkeylib538_version}" + modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fapi_version}" include runtimeOnly("top.offsetmonkey538.meshlib:mesh-lib-fabric:${rootProject.meshlib_version}") diff --git a/fabric/gradle.properties b/fabric/gradle.properties index fccff0f..94117ba 100644 --- a/fabric/gradle.properties +++ b/fabric/gradle.properties @@ -10,6 +10,4 @@ fapi_version = 0.119.3+1.21.4 ## DevAuth, check at https://github.com/DJtheRedstoner/DevAuth devauth_version = 1.2.1 -monkeylib538_version = 2.0.2+1.21 - nameSuffix = fabric diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformCommand.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformCommand.java index 8fb9622..6e1a971 100644 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformCommand.java +++ b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformCommand.java @@ -1,6 +1,8 @@ package top.offsetmonkey538.githubresourcepackmanager.platform.fabric; import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.builder.ArgumentBuilder; +import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; import net.minecraft.command.CommandRegistryAccess; @@ -18,7 +20,8 @@ import org.apache.commons.lang3.exception.ExceptionUtils; import top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformCommand; -import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging; +import top.offsetmonkey538.monkeylib538.api.ConfigCommandApi; +import top.offsetmonkey538.monkeylib538.api.log.PlatformLogger; import java.util.Optional; @@ -27,6 +30,7 @@ import static net.minecraft.server.command.CommandManager.argument; import static net.minecraft.server.command.CommandManager.literal; import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.MOD_ID; +import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.config; public class FabricPlatformCommand implements PlatformCommand { @Override @@ -35,7 +39,12 @@ public void registerGithubRpManagerCommand() { } public static void register(CommandDispatcher dispatcher, CommandRegistryAccess commandRegistryAccess, CommandManager.RegistrationEnvironment registrationEnvironment) { - dispatcher.register(literal("gh-rp-manager") + final LiteralArgumentBuilder command = literal("gh-rp-manager"); + + //noinspection unchecked + command.then((ArgumentBuilder) ConfigCommandApi.INSTANCE.createConfigCommand("config", config)); + + dispatcher.register(command .then(literal("request-pack") .requires(ServerCommandSource::isExecutedByPlayer) .executes( @@ -83,10 +92,10 @@ public static void register(CommandDispatcher dispatcher, C } private static void runTriggerUpdate(CommandContext context, boolean force) { - final PlatformLogging.LogListener infoListener = (message, error) -> { + final PlatformLogger.LogListener infoListener = (message, error) -> { context.getSource().sendMessage(Text.literal(String.format("[%s] %s", MOD_ID, message))); }; - final PlatformLogging.LogListener warnListener = (message, error) -> { + final PlatformLogger.LogListener warnListener = (message, error) -> { MutableText text = Text .literal(String.format("[%s] %s", MOD_ID, message)) .setStyle(Style.EMPTY.withColor(Formatting.YELLOW)); @@ -100,12 +109,12 @@ private static void runTriggerUpdate(CommandContext context context.getSource().sendMessage(text); }; - PlatformLogging.LOGGER.addListener(PlatformLogging.LogLevel.INFO, infoListener); - PlatformLogging.LOGGER.addListener(PlatformLogging.LogLevel.WARN, warnListener); + GithubResourcepackManager.LOGGER.addListener(PlatformLogger.LogLevel.INFO, infoListener); + GithubResourcepackManager.LOGGER.addListener(PlatformLogger.LogLevel.WARN, warnListener); GithubResourcepackManager.updatePack(force ? GithubResourcepackManager.UpdateType.COMMAND_FORCE : GithubResourcepackManager.UpdateType.COMMAND); - PlatformLogging.LOGGER.removeListener(PlatformLogging.LogLevel.INFO, infoListener); - PlatformLogging.LOGGER.removeListener(PlatformLogging.LogLevel.WARN, warnListener); + GithubResourcepackManager.LOGGER.removeListener(PlatformLogger.LogLevel.INFO, infoListener); + GithubResourcepackManager.LOGGER.removeListener(PlatformLogger.LogLevel.WARN, warnListener); } } diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformLogging.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformLogging.java deleted file mode 100644 index 45cb414..0000000 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformLogging.java +++ /dev/null @@ -1,70 +0,0 @@ -package top.offsetmonkey538.githubresourcepackmanager.platform.fabric; - -import org.slf4j.Logger; -import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class FabricPlatformLogging implements PlatformLogging { - - private static Logger logger; - private static final Map> listeners = new HashMap<>(Map.of( - LogLevel.DEBUG, new ArrayList<>(), - LogLevel.INFO, new ArrayList<>(), - LogLevel.WARN, new ArrayList<>(), - LogLevel.ERROR, new ArrayList<>() - )); - - @Override - public void debug(String message) { - listeners.get(LogLevel.DEBUG).forEach(consumer -> consumer.accept(message, null)); - logger.debug(message); - } - - @Override - public void info(String message) { - listeners.get(LogLevel.INFO).forEach(consumer -> consumer.accept(message, null)); - logger.info(message); - } - - @Override - public void warn(String message) { - listeners.get(LogLevel.WARN).forEach(consumer -> consumer.accept(message, null)); - logger.warn(message); - } - - @Override - public void warn(String message, Throwable error) { - listeners.get(LogLevel.WARN).forEach(consumer -> consumer.accept(message, error)); - logger.warn(message, error); - } - - @Override - public void error(String message) { - listeners.get(LogLevel.ERROR).forEach(consumer -> consumer.accept(message, null)); - logger.error(message); - } - - @Override - public void error(String message, Throwable error) { - listeners.get(LogLevel.ERROR).forEach(consumer -> consumer.accept(message, error)); - logger.error(message, error); - } - - @Override - public void addListener(LogLevel level, LogListener listener) { - listeners.get(level).add(listener); - } - - @Override - public void removeListener(LogLevel level, LogListener listener) { - listeners.get(level).remove(listener); - } - - public static void setLogger(Logger logger) { - FabricPlatformLogging.logger = logger; - } -} diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java index 06d059c..b60c462 100644 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java +++ b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java @@ -12,16 +12,15 @@ import net.minecraft.text.Text; import net.minecraft.util.Formatting; import org.apache.commons.lang3.exception.ExceptionUtils; -import org.slf4j.LoggerFactory; import top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager; -import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformMain; +import top.offsetmonkey538.monkeylib538.api.log.PlatformLogger; import java.nio.file.Path; import java.util.LinkedList; import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.MOD_ID; -import static top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging.LOGGER; +import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.LOGGER; public class FabricPlatformMain implements PlatformMain, DedicatedServerModInitializer { private static MinecraftServer minecraftServer; @@ -29,8 +28,6 @@ public class FabricPlatformMain implements PlatformMain, DedicatedServerModIniti @Override public void onInitializeServer() { - FabricPlatformLogging.setLogger(LoggerFactory.getLogger(MOD_ID)); - GithubResourcepackManager.initialize(); ServerLifecycleEvents.SERVER_STARTING.register(minecraftServer1 -> minecraftServer = minecraftServer1); @@ -60,7 +57,7 @@ public void runOnServerStart(Runnable work) { @Override public void registerLogToAdminListener() { - LOGGER.addListener(PlatformLogging.LogLevel.ERROR, (message, error) -> { + LOGGER.addListener(PlatformLogger.LogLevel.ERROR, (message, error) -> { MutableText text = Text .literal(String.format("[%s] %s", MOD_ID, message)) .setStyle(Style.EMPTY.withColor(Formatting.RED)); diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java index 9631a18..7a7a199 100644 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java +++ b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java @@ -3,15 +3,19 @@ import net.minecraft.server.PlayerManager; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.*; +import net.minecraft.util.Formatting; import org.jetbrains.annotations.Nullable; import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformText; import top.offsetmonkey538.githubresourcepackmanager.utils.StringUtils; -import top.offsetmonkey538.monkeylib538.utils.TextUtils; +import java.util.List; import java.util.Map; public class FabricPlatformText implements PlatformText { + Style DEFAULT_STYLE = Style.EMPTY.withItalic(false).withColor(Formatting.WHITE); + + @Override public void sendUpdateMessage(final String message, @Nullable final String hoverMessage, Map placeholders, boolean adminsOnly) throws GithubResourcepackManagerException { final PlayerManager playerManager = FabricPlatformMain.getServer().getPlayerManager(); @@ -23,7 +27,7 @@ public void sendUpdateMessage(final String message, @Nullable final String hover try { hoverEvent = hoverMessage == null ? null : new HoverEvent( HoverEvent.Action.SHOW_TEXT, - TextUtils.INSTANCE.getStyledText( + getStyledText( StringUtils.replacePlaceholders(hoverMessage, placeholders).replace("\\n", "\n") ) ); @@ -35,7 +39,7 @@ public void sendUpdateMessage(final String message, @Nullable final String hover final String currentLineString = StringUtils.replacePlaceholders(splitMessage[lineNumber], placeholders).replace("\\n", "\n"); final MutableText currentLine = Text.empty(); try { - for (Text currentLineSibling : TextUtils.INSTANCE.getStyledText(currentLineString).getSiblings()) { + for (Text currentLineSibling : getStyledText(currentLineString).getSiblings()) { final MutableText sibling = currentLineSibling.copy(); if (hoverEvent != null) sibling.setStyle(sibling.getStyle().withHoverEvent(hoverEvent)); @@ -76,4 +80,83 @@ public void sendUpdateMessage(final String message, @Nullable final String hover } } } + + private MutableText getStyledText(String text) throws Exception { + final MutableText result = Text.empty(); + Style style = DEFAULT_STYLE; + + boolean isFormattingCode = false; + boolean isEscaped = false; + char[] characters = text.toCharArray(); + for (int characterIndex = 0; characterIndex < characters.length; characterIndex++) { + char currentChar = characters[characterIndex]; + + if (isFormattingCode) { + // Hex color + if (currentChar == '#') { + if (characterIndex + 7 >= characters.length) throw new Exception("Unfinished hex code starting at character number '" + characterIndex + "'!"); + + try { + style = style.withColor(parseHexColor(text.substring(characterIndex, characterIndex + 7))); + } catch (Exception e) { + throw new Exception("Failed to parse hex color starting at character number '" + characterIndex + "'!", e); + } + + // Move pointer 6 characters ahead as we already read the whole hex code + characterIndex += 6; + isFormattingCode = false; + continue; + } + + style = getStyleForFormattingCode(currentChar, style); + + if (style == null) throw new Exception("Invalid formatting code at character number '" + characterIndex + "'!"); + + isFormattingCode = false; + continue; + } + + if (!isEscaped){ + switch (currentChar){ + case '&': + isFormattingCode = true; + continue; + case '\\': + isEscaped = true; + continue; + } + } + isEscaped = false; + + + final List siblings = result.getSiblings(); + final int lastSiblingIndex = siblings.size() - 1; + final Text lastSibling = siblings.isEmpty() ? Text.empty() : siblings.get(lastSiblingIndex); + + // Check if the style of the last sibling is the same as the current one + if (!siblings.isEmpty() && lastSibling.getStyle().equals(style)) { + // If so, set the last sibling to itself plus the new character + siblings.set(lastSiblingIndex, Text.literal(lastSibling.getString() + currentChar).setStyle(style)); + } else { + // Otherwise, just append a new sibling to the result + result.append(Text.literal(String.valueOf(currentChar)).setStyle(style)); + } + } + + return result; + } + + + private Style getStyleForFormattingCode(char formattingCode, Style currentStyle) { + if (formattingCode == 'r') return DEFAULT_STYLE; + + final Formatting formatting = Formatting.byCode(formattingCode); + if (formatting == null) return null; + + return currentStyle.withFormatting(formatting); + } + + public TextColor parseHexColor(String hexColor) throws Exception { + return TextColor.parse(hexColor).getOrThrow(Exception::new); + } } diff --git a/gradle.properties b/gradle.properties index 512ef06..212b7aa 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,9 @@ minecraft_version = 1.21.4 # MESH Lib, check at https://github.com/OffsetMods538/MESH-Lib meshlib_version = 1.0.5+1.21.4 +# MonkeyLib538, check at https://github.com/OffsetMods538/MonkeyLib538 +monkeylib538_version = 3.0.0-1753538770838 # Mod Properties -mod_version = 4.0.1 +mod_version = 5.0.0 supported_minecraft_versions = >=1.19.2 diff --git a/paper/build.gradle b/paper/build.gradle index 540f806..66104b1 100644 --- a/paper/build.gradle +++ b/paper/build.gradle @@ -18,11 +18,20 @@ repositories { includeGroup "io.papermc.paper" } } + maven { + name = "Mojang Libraries" + url = "https://libraries.minecraft.net" + content { + includeGroup "com.mojang" + } + } maven { name = "OffsetMods538" url = "https://maven.offsetmonkey538.top/releases" content { includeGroup "top.offsetmonkey538.meshlib" + includeGroup "top.offsetmonkey538.monkeylib538" + includeGroup "top.offsetmonkey538.offsetconfig538" } } } diff --git a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformCommand.java b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformCommand.java index f95974a..4597476 100644 --- a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformCommand.java +++ b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformCommand.java @@ -15,7 +15,7 @@ import org.bukkit.entity.Player; import top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformCommand; -import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging; +import top.offsetmonkey538.monkeylib538.api.log.PlatformLogger; import java.net.URI; @@ -84,10 +84,10 @@ public void registerGithubRpManagerCommand() { @SuppressWarnings("UnstableApiUsage") private void runTriggerUpdate(CommandContext context, boolean force) { - final PlatformLogging.LogListener infoListener = (message, error) -> { + final PlatformLogger.LogListener infoListener = (message, error) -> { context.getSource().getSender().sendMessage(Component.text(String.format("[%s] %s", MOD_ID, message))); }; - final PlatformLogging.LogListener warnListener = (message, error) -> { + final PlatformLogger.LogListener warnListener = (message, error) -> { Component text = Component .text(String.format("[%s] %s", MOD_ID, message)) .color(NamedTextColor.YELLOW); @@ -100,12 +100,12 @@ private void runTriggerUpdate(CommandContext context, boolea context.getSource().getSender().sendMessage(text); }; - PlatformLogging.LOGGER.addListener(PlatformLogging.LogLevel.INFO, infoListener); - PlatformLogging.LOGGER.addListener(PlatformLogging.LogLevel.WARN, warnListener); + GithubResourcepackManager.LOGGER.addListener(PlatformLogger.LogLevel.INFO, infoListener); + GithubResourcepackManager.LOGGER.addListener(PlatformLogger.LogLevel.WARN, warnListener); GithubResourcepackManager.updatePack(force ? GithubResourcepackManager.UpdateType.COMMAND_FORCE : GithubResourcepackManager.UpdateType.COMMAND); - PlatformLogging.LOGGER.removeListener(PlatformLogging.LogLevel.INFO, infoListener); - PlatformLogging.LOGGER.removeListener(PlatformLogging.LogLevel.WARN, warnListener); + GithubResourcepackManager.LOGGER.removeListener(PlatformLogger.LogLevel.INFO, infoListener); + GithubResourcepackManager.LOGGER.removeListener(PlatformLogger.LogLevel.WARN, warnListener); } } diff --git a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformLogging.java b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformLogging.java deleted file mode 100644 index 82bba96..0000000 --- a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformLogging.java +++ /dev/null @@ -1,71 +0,0 @@ -package top.offsetmonkey538.githubresourcepackmanager.platform.paper; - -import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; - -public class PaperPlatformLogging implements PlatformLogging { - - private static Logger logger; - private static final Map> listeners = new HashMap<>(Map.of( - LogLevel.DEBUG, new ArrayList<>(), - LogLevel.INFO, new ArrayList<>(), - LogLevel.WARN, new ArrayList<>(), - LogLevel.ERROR, new ArrayList<>() - )); - - @Override - public void debug(String message) { - listeners.get(LogLevel.DEBUG).forEach(consumer -> consumer.accept(message, null)); - logger.log(Level.FINE, message); - } - - @Override - public void info(String message) { - listeners.get(LogLevel.INFO).forEach(consumer -> consumer.accept(message, null)); - logger.log(Level.INFO, message); - } - - @Override - public void warn(String message) { - listeners.get(LogLevel.WARN).forEach(consumer -> consumer.accept(message, null)); - logger.log(Level.WARNING, message); - } - - @Override - public void warn(String message, Throwable error) { - listeners.get(LogLevel.WARN).forEach(consumer -> consumer.accept(message, error)); - logger.log(Level.WARNING, message, error); - } - - @Override - public void error(String message) { - listeners.get(LogLevel.ERROR).forEach(consumer -> consumer.accept(message, null)); - logger.log(Level.SEVERE, message); - } - - @Override - public void error(String message, Throwable error) { - listeners.get(LogLevel.ERROR).forEach(consumer -> consumer.accept(message, error)); - logger.log(Level.SEVERE, message, error); - } - - @Override - public void addListener(LogLevel level, LogListener listener) { - listeners.get(level).add(listener); - } - - @Override - public void removeListener(LogLevel level, LogListener listener) { - listeners.get(level).remove(listener); - } - - public static void setLogger(Logger logger) { - PaperPlatformLogging.logger = logger; - } -} diff --git a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java index 45c5b85..0487b87 100644 --- a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java +++ b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java @@ -6,13 +6,13 @@ import net.kyori.adventure.text.format.NamedTextColor; import org.apache.commons.lang.exception.ExceptionUtils; import org.bukkit.OfflinePlayer; -import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformMain; +import top.offsetmonkey538.monkeylib538.api.log.PlatformLogger; import java.nio.file.Path; import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.MOD_ID; -import static top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging.LOGGER; +import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.LOGGER; public class PaperPlatformMain implements PlatformMain { private static PaperPlugin plugin; @@ -34,7 +34,7 @@ public void refreshDatapacks() { @Override public void registerLogToAdminListener() { - LOGGER.addListener(PlatformLogging.LogLevel.ERROR, (message, error) -> { + LOGGER.addListener(PlatformLogger.LogLevel.ERROR, (message, error) -> { Component text = Component .text(String.format("[%s] %s", MOD_ID, message)) .color(NamedTextColor.RED); diff --git a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlugin.java b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlugin.java index 3428ea3..5dc7a85 100644 --- a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlugin.java +++ b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlugin.java @@ -21,7 +21,6 @@ public void onEnable() { MeshLib.initialize(); PaperPlatformMain.setPlugin(this); - PaperPlatformLogging.setLogger(getLogger()); GithubResourcepackManager.initialize(); } From 40ba893a589eba4e9651bd53b9bf172900e950ec Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Mon, 4 Aug 2025 01:30:34 +0200 Subject: [PATCH 22/52] Update monke to 992e97c --- .../GithubResourcepackManager.java | 14 ++++++++ .../config/ModConfig.java | 5 +++ fabric/build.gradle | 2 +- fabric/gradle.properties | 2 +- .../fabric/FabricPlatformCommand.java | 36 +++++++++++++++++-- gradle.properties | 4 +-- 6 files changed, 57 insertions(+), 6 deletions(-) diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java index 2f8f0c0..3703439 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java @@ -11,6 +11,8 @@ import top.offsetmonkey538.githubresourcepackmanager.networking.MainHttpHandler; import top.offsetmonkey538.githubresourcepackmanager.platform.*; import top.offsetmonkey538.meshlib.api.HttpHandlerRegistry; +import top.offsetmonkey538.monkeylib538.api.command.CommandRegistrationApi; +import top.offsetmonkey538.monkeylib538.api.command.ConfigCommandApi; import top.offsetmonkey538.monkeylib538.api.log.PlatformLogger; import top.offsetmonkey538.monkeylib538.api.log.PlatformLoggerProvider; import top.offsetmonkey538.offsetconfig538.api.config.ConfigHolder; @@ -63,6 +65,18 @@ public static void initialize() { PlatformMain.INSTANCE.registerLogToAdminListener(); updatePack(UpdateType.RESTART); }); + + + ConfigCommandApi.INSTANCE.registerConfigCommand(config, "test1"); + ConfigCommandApi.INSTANCE.registerConfigCommand(config, "test2", "sub1"); + ConfigCommandApi.INSTANCE.registerConfigCommand(config, "test3", "sub1", "sub2"); + ConfigCommandApi.INSTANCE.registerConfigCommand(config, "test4", "sub1", "sub2", "sub3"); + ConfigCommandApi.INSTANCE.registerConfigCommand(config, "test5", "sub1", "sub2", "sub3", "sub4"); + ConfigCommandApi.INSTANCE.registerConfigCommand(config, "test6", "sub1", "sub2", "sub3", "sub4", "sub5"); + ConfigCommandApi.INSTANCE.registerConfigCommand(config, "test7", "sub1", "sub2", "sub3", "sub4", "sub5", "sub6"); + ConfigCommandApi.INSTANCE.registerConfigCommand(config, "test8", "sub1", "sub2", "sub3", "sub4", "sub5", "sub6", "sub7"); + ConfigCommandApi.INSTANCE.registerConfigCommand(config, "test9", "sub1", "sub2", "sub3", "sub4", "sub5", "sub6", "sub7", "sub8"); + ConfigCommandApi.INSTANCE.registerConfigCommand(config, "test10", "sub1", "sub2", "sub3", "sub4", "sub5", "sub6", "sub7", "sub8", "sub9"); } private static void createFolderStructure() throws GithubResourcepackManagerException { diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java index 7242aa3..d92d3a5 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java @@ -226,6 +226,11 @@ public int getConfigVersion() { return PlatformMain.INSTANCE.getConfigDir().resolve("config").resolve("main.json"); } + @Override + public @NotNull String getId() { + return MOD_ID + "/main"; + } + @Override public void beforeLoadStart() { if (Files.exists(PlatformMain.INSTANCE.getConfigDir().getParent().resolve(MOD_ID + ".json"))) { diff --git a/fabric/build.gradle b/fabric/build.gradle index 87ce2ff..28b205a 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -93,7 +93,7 @@ dependencies { modRuntimeOnly "me.djtheredstoner:DevAuth-fabric:${devauth_version}" compileOnly "top.offsetmonkey538.monkeylib538:monkeylib538-common:${project.monkeylib538_version}" - modRuntimeOnly("top.offsetmonkey538.monkeylib538:monkeylib538-fabric:${project.monkeylib538_version}+1.21.5") { + modImplementation("top.offsetmonkey538.monkeylib538:monkeylib538-fabric:${project.monkeylib538_version}+1.21.5") { exclude(group: "net.fabricmc.fabric-api") exclude(group: "top.offsetmonkey538.monkeylib538:monkeylib538-fabric") } diff --git a/fabric/gradle.properties b/fabric/gradle.properties index 94117ba..c0ef59c 100644 --- a/fabric/gradle.properties +++ b/fabric/gradle.properties @@ -3,7 +3,7 @@ # These should be automatically updated, unless the environment # variable "DISABLE_PROPERTIES_UPDATE" is set. yarn_version = 1.21.4+build.8 -loader_version = 0.16.14 +loader_version = 0.17.0 fapi_version = 0.119.3+1.21.4 # Dependencies diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformCommand.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformCommand.java index 6e1a971..453a01a 100644 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformCommand.java +++ b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformCommand.java @@ -4,6 +4,8 @@ import com.mojang.brigadier.builder.ArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.tree.CommandNode; +import com.mojang.brigadier.tree.LiteralCommandNode; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; import net.minecraft.command.CommandRegistryAccess; import net.minecraft.command.ControlFlowAware; @@ -20,7 +22,7 @@ import org.apache.commons.lang3.exception.ExceptionUtils; import top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformCommand; -import top.offsetmonkey538.monkeylib538.api.ConfigCommandApi; +import top.offsetmonkey538.monkeylib538.api.command.ConfigCommandApi; import top.offsetmonkey538.monkeylib538.api.log.PlatformLogger; import java.util.Optional; @@ -38,11 +40,41 @@ public void registerGithubRpManagerCommand() { CommandRegistrationCallback.EVENT.register(FabricPlatformCommand::register); } + /* + private static class TestArgumentBuilder extends LiteralArgumentBuilder { + + protected TestArgumentBuilder(String literal) { + super(literal); + } + + @Override + public LiteralCommandNode build() { + final LiteralCommandNode result = new LiteralCommandNode<>(getLiteral(), getCommand(), getRequirement(), getRedirect(), getRedirectModifier(), isFork()); + + for (final CommandNode argument : getArguments()) { + result.addChild(argument); + } + + return result; + } + } + */ + public static void register(CommandDispatcher dispatcher, CommandRegistryAccess commandRegistryAccess, CommandManager.RegistrationEnvironment registrationEnvironment) { final LiteralArgumentBuilder command = literal("gh-rp-manager"); //noinspection unchecked - command.then((ArgumentBuilder) ConfigCommandApi.INSTANCE.createConfigCommand("config", config)); + + + + //final LiteralArgumentBuilder configCommandImpl = (LiteralArgumentBuilder) ConfigCommandApi.INSTANCE.createConfigCommand("config", config); + //noinspection unchecked + //command.then((LiteralArgumentBuilder) (LiteralArgumentBuilder) configCommandImpl); + + //noinspection unchecked + command.then((LiteralArgumentBuilder) ConfigCommandApi.INSTANCE.createConfigCommand("config", config)); + + //command.then((LiteralArgumentBuilder) ); dispatcher.register(command .then(literal("request-pack") diff --git a/gradle.properties b/gradle.properties index 212b7aa..532872c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,9 +5,9 @@ org.gradle.parallel = true minecraft_version = 1.21.4 # MESH Lib, check at https://github.com/OffsetMods538/MESH-Lib -meshlib_version = 1.0.5+1.21.4 +meshlib_version = 1.0.5+1.21.4 # MonkeyLib538, check at https://github.com/OffsetMods538/MonkeyLib538 -monkeylib538_version = 3.0.0-1753538770838 +monkeylib538_version = 3.0.0-992e97c # Mod Properties mod_version = 5.0.0 From 588c8fe2c5cf197c00df4b5027074eda9143489a Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Mon, 4 Aug 2025 18:59:10 +0200 Subject: [PATCH 23/52] Update monke to eb3e2e1 Also fixes #21 --- common/gradle.properties | 2 +- .../GithubResourcepackManager.java | 38 +++++++------- .../config/ConfigHandler.java | 12 ++--- .../fabric/FabricPlatformCommand.java | 49 +++---------------- .../platform/fabric/FabricPlatformMain.java | 11 +++-- gradle.properties | 2 +- .../platform/paper/PaperPlatformCommand.java | 14 +++--- .../platform/paper/PaperPlatformMain.java | 4 +- 8 files changed, 46 insertions(+), 86 deletions(-) diff --git a/common/gradle.properties b/common/gradle.properties index e8e57e3..41b5d26 100644 --- a/common/gradle.properties +++ b/common/gradle.properties @@ -4,6 +4,6 @@ commons_version = 2.18.0 gson_version = 2.11.0 ## OffsetConfig538, check at https://github.com/OffsetMods538/OffsetConfig538 -offsetconfig538_version = 1.0.0-2f5cd46 +offsetconfig538_version = 1.0.0-946f4f7 nameSuffix = api \ No newline at end of file diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java index 3703439..90cbd8c 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java @@ -11,10 +11,8 @@ import top.offsetmonkey538.githubresourcepackmanager.networking.MainHttpHandler; import top.offsetmonkey538.githubresourcepackmanager.platform.*; import top.offsetmonkey538.meshlib.api.HttpHandlerRegistry; -import top.offsetmonkey538.monkeylib538.api.command.CommandRegistrationApi; import top.offsetmonkey538.monkeylib538.api.command.ConfigCommandApi; -import top.offsetmonkey538.monkeylib538.api.log.PlatformLogger; -import top.offsetmonkey538.monkeylib538.api.log.PlatformLoggerProvider; +import top.offsetmonkey538.monkeylib538.api.log.MonkeyLibLogger; import top.offsetmonkey538.offsetconfig538.api.config.ConfigHolder; import top.offsetmonkey538.offsetconfig538.api.config.ConfigManager; @@ -30,7 +28,7 @@ private GithubResourcepackManager() { } public static final String MOD_ID = "github-resourcepack-manager"; - public static final PlatformLogger LOGGER = PlatformLoggerProvider.INSTANCE.createLogger(MOD_ID); + public static final MonkeyLibLogger LOGGER = MonkeyLibLogger.create(MOD_ID); public static final String MOD_URI = "gh-rp-manager"; public static final Path DATA_FOLDER = PlatformMain.INSTANCE.getConfigDir().resolve(".packs"); @@ -48,10 +46,18 @@ private GithubResourcepackManager() { public static ResourcePackHandler resourcePackHandler; + private static boolean disabled; + public static void initialize() { + PlatformMain.INSTANCE.registerLogToAdminListener(); PlatformCommand.INSTANCE.registerGithubRpManagerCommand(); + ConfigCommandApi.registerConfigCommand( + config, + () -> disabled = ConfigHandler.handleConfig(), + "gh-rp-manager", "config" + ); - ConfigHandler.handleConfig(); + disabled = ConfigHandler.handleConfig(); try { createFolderStructure(); @@ -61,22 +67,7 @@ public static void initialize() { HttpHandlerRegistry.INSTANCE.register(MOD_URI, new MainHttpHandler()); - PlatformMain.INSTANCE.runOnServerStart(() -> { - PlatformMain.INSTANCE.registerLogToAdminListener(); - updatePack(UpdateType.RESTART); - }); - - - ConfigCommandApi.INSTANCE.registerConfigCommand(config, "test1"); - ConfigCommandApi.INSTANCE.registerConfigCommand(config, "test2", "sub1"); - ConfigCommandApi.INSTANCE.registerConfigCommand(config, "test3", "sub1", "sub2"); - ConfigCommandApi.INSTANCE.registerConfigCommand(config, "test4", "sub1", "sub2", "sub3"); - ConfigCommandApi.INSTANCE.registerConfigCommand(config, "test5", "sub1", "sub2", "sub3", "sub4"); - ConfigCommandApi.INSTANCE.registerConfigCommand(config, "test6", "sub1", "sub2", "sub3", "sub4", "sub5"); - ConfigCommandApi.INSTANCE.registerConfigCommand(config, "test7", "sub1", "sub2", "sub3", "sub4", "sub5", "sub6"); - ConfigCommandApi.INSTANCE.registerConfigCommand(config, "test8", "sub1", "sub2", "sub3", "sub4", "sub5", "sub6", "sub7"); - ConfigCommandApi.INSTANCE.registerConfigCommand(config, "test9", "sub1", "sub2", "sub3", "sub4", "sub5", "sub6", "sub7", "sub8"); - ConfigCommandApi.INSTANCE.registerConfigCommand(config, "test10", "sub1", "sub2", "sub3", "sub4", "sub5", "sub6", "sub7", "sub8", "sub9"); + PlatformMain.INSTANCE.runOnServerStart(() -> updatePack(UpdateType.RESTART)); } private static void createFolderStructure() throws GithubResourcepackManagerException { @@ -90,6 +81,11 @@ private static void createFolderStructure() throws GithubResourcepackManagerExce } public static void updatePack(final UpdateType updateType) { + if (disabled) { + LOGGER.warn("Skipping pack updating because config was invalid!"); + return; + } + LOGGER.info("Updating packs..."); if (updateType == UpdateType.COMMAND_FORCE) { diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ConfigHandler.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ConfigHandler.java index 78bbea3..e7b24fc 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ConfigHandler.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ConfigHandler.java @@ -23,22 +23,20 @@ private ConfigHandler() { } - public static void handleConfig() { + public static boolean handleConfig() { LOGGER.info("Writing default webhook bodies"); createDefaultWebhooks(); // Checking if config is valid final List errors = checkConfigErrors(); - // Return when nothing's wrong - if (errors.isEmpty()) return; + // Return false when nothing's wrong (disables the mod) + if (errors.isEmpty()) return false; // There were errors, time to log em. LOGGER.error("There were problems with the config for GitHub Resourcepack Manager, see below for more details!"); - errors.stream().map(string -> "\t" + string).forEach(LOGGER::error); - LOGGER.error("There were problems with the config for Github Resourcepack Manager, see above for more details!"); - - throw new RuntimeException("There were problems with the config for Github Resourcepack Manager, see above for more details!"); + errors.stream().map(string -> " " + string).forEach(LOGGER::error); + return true; } private static void createDefaultWebhooks() { diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformCommand.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformCommand.java index 453a01a..97743ac 100644 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformCommand.java +++ b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformCommand.java @@ -22,8 +22,7 @@ import org.apache.commons.lang3.exception.ExceptionUtils; import top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformCommand; -import top.offsetmonkey538.monkeylib538.api.command.ConfigCommandApi; -import top.offsetmonkey538.monkeylib538.api.log.PlatformLogger; +import top.offsetmonkey538.monkeylib538.api.log.MonkeyLibLogger; import java.util.Optional; @@ -32,7 +31,6 @@ import static net.minecraft.server.command.CommandManager.argument; import static net.minecraft.server.command.CommandManager.literal; import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.MOD_ID; -import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.config; public class FabricPlatformCommand implements PlatformCommand { @Override @@ -40,42 +38,9 @@ public void registerGithubRpManagerCommand() { CommandRegistrationCallback.EVENT.register(FabricPlatformCommand::register); } - /* - private static class TestArgumentBuilder extends LiteralArgumentBuilder { - - protected TestArgumentBuilder(String literal) { - super(literal); - } - - @Override - public LiteralCommandNode build() { - final LiteralCommandNode result = new LiteralCommandNode<>(getLiteral(), getCommand(), getRequirement(), getRedirect(), getRedirectModifier(), isFork()); - - for (final CommandNode argument : getArguments()) { - result.addChild(argument); - } - - return result; - } - } - */ - public static void register(CommandDispatcher dispatcher, CommandRegistryAccess commandRegistryAccess, CommandManager.RegistrationEnvironment registrationEnvironment) { final LiteralArgumentBuilder command = literal("gh-rp-manager"); - //noinspection unchecked - - - - //final LiteralArgumentBuilder configCommandImpl = (LiteralArgumentBuilder) ConfigCommandApi.INSTANCE.createConfigCommand("config", config); - //noinspection unchecked - //command.then((LiteralArgumentBuilder) (LiteralArgumentBuilder) configCommandImpl); - - //noinspection unchecked - command.then((LiteralArgumentBuilder) ConfigCommandApi.INSTANCE.createConfigCommand("config", config)); - - //command.then((LiteralArgumentBuilder) ); - dispatcher.register(command .then(literal("request-pack") .requires(ServerCommandSource::isExecutedByPlayer) @@ -124,10 +89,10 @@ public static void register(CommandDispatcher dispatcher, C } private static void runTriggerUpdate(CommandContext context, boolean force) { - final PlatformLogger.LogListener infoListener = (message, error) -> { + final MonkeyLibLogger.LogListener infoListener = (message, error) -> { context.getSource().sendMessage(Text.literal(String.format("[%s] %s", MOD_ID, message))); }; - final PlatformLogger.LogListener warnListener = (message, error) -> { + final MonkeyLibLogger.LogListener warnListener = (message, error) -> { MutableText text = Text .literal(String.format("[%s] %s", MOD_ID, message)) .setStyle(Style.EMPTY.withColor(Formatting.YELLOW)); @@ -141,12 +106,12 @@ private static void runTriggerUpdate(CommandContext context context.getSource().sendMessage(text); }; - GithubResourcepackManager.LOGGER.addListener(PlatformLogger.LogLevel.INFO, infoListener); - GithubResourcepackManager.LOGGER.addListener(PlatformLogger.LogLevel.WARN, warnListener); + GithubResourcepackManager.LOGGER.addListener(MonkeyLibLogger.LogLevel.INFO, infoListener); + GithubResourcepackManager.LOGGER.addListener(MonkeyLibLogger.LogLevel.WARN, warnListener); GithubResourcepackManager.updatePack(force ? GithubResourcepackManager.UpdateType.COMMAND_FORCE : GithubResourcepackManager.UpdateType.COMMAND); - GithubResourcepackManager.LOGGER.removeListener(PlatformLogger.LogLevel.INFO, infoListener); - GithubResourcepackManager.LOGGER.removeListener(PlatformLogger.LogLevel.WARN, warnListener); + GithubResourcepackManager.LOGGER.removeListener(MonkeyLibLogger.LogLevel.INFO, infoListener); + GithubResourcepackManager.LOGGER.removeListener(MonkeyLibLogger.LogLevel.WARN, warnListener); } } diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java index b60c462..501db61 100644 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java +++ b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java @@ -12,9 +12,10 @@ import net.minecraft.text.Text; import net.minecraft.util.Formatting; import org.apache.commons.lang3.exception.ExceptionUtils; +import org.jetbrains.annotations.Nullable; import top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformMain; -import top.offsetmonkey538.monkeylib538.api.log.PlatformLogger; +import top.offsetmonkey538.monkeylib538.api.log.MonkeyLibLogger; import java.nio.file.Path; import java.util.LinkedList; @@ -23,7 +24,7 @@ import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.LOGGER; public class FabricPlatformMain implements PlatformMain, DedicatedServerModInitializer { - private static MinecraftServer minecraftServer; + private static @Nullable MinecraftServer minecraftServer = null; private static final LinkedList messageQueue = new LinkedList<>(); @Override @@ -40,7 +41,7 @@ public void onInitializeServer() { }); } - public static MinecraftServer getServer() { + public static @Nullable MinecraftServer getServer() { return minecraftServer; } @@ -57,7 +58,7 @@ public void runOnServerStart(Runnable work) { @Override public void registerLogToAdminListener() { - LOGGER.addListener(PlatformLogger.LogLevel.ERROR, (message, error) -> { + LOGGER.addListener(MonkeyLibLogger.LogLevel.ERROR, (message, error) -> { MutableText text = Text .literal(String.format("[%s] %s", MOD_ID, message)) .setStyle(Style.EMPTY.withColor(Formatting.RED)); @@ -69,7 +70,7 @@ public void registerLogToAdminListener() { )); boolean sent = false; - for (final PlayerEntity player : getServer().getPlayerManager().getPlayerList()) { + if (getServer() != null) for (final PlayerEntity player : getServer().getPlayerManager().getPlayerList()) { if (!getServer().getPlayerManager().isOperator(player.getGameProfile())) continue; player.sendMessage(text, false); sent = true; diff --git a/gradle.properties b/gradle.properties index 532872c..6d2e36c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,7 +7,7 @@ minecraft_version = 1.21.4 # MESH Lib, check at https://github.com/OffsetMods538/MESH-Lib meshlib_version = 1.0.5+1.21.4 # MonkeyLib538, check at https://github.com/OffsetMods538/MonkeyLib538 -monkeylib538_version = 3.0.0-992e97c +monkeylib538_version = 3.0.0-eb3e2e1 # Mod Properties mod_version = 5.0.0 diff --git a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformCommand.java b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformCommand.java index 4597476..4c2b685 100644 --- a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformCommand.java +++ b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformCommand.java @@ -15,7 +15,7 @@ import org.bukkit.entity.Player; import top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformCommand; -import top.offsetmonkey538.monkeylib538.api.log.PlatformLogger; +import top.offsetmonkey538.monkeylib538.api.log.MonkeyLibLogger; import java.net.URI; @@ -84,10 +84,10 @@ public void registerGithubRpManagerCommand() { @SuppressWarnings("UnstableApiUsage") private void runTriggerUpdate(CommandContext context, boolean force) { - final PlatformLogger.LogListener infoListener = (message, error) -> { + final MonkeyLibLogger.LogListener infoListener = (message, error) -> { context.getSource().getSender().sendMessage(Component.text(String.format("[%s] %s", MOD_ID, message))); }; - final PlatformLogger.LogListener warnListener = (message, error) -> { + final MonkeyLibLogger.LogListener warnListener = (message, error) -> { Component text = Component .text(String.format("[%s] %s", MOD_ID, message)) .color(NamedTextColor.YELLOW); @@ -100,12 +100,12 @@ private void runTriggerUpdate(CommandContext context, boolea context.getSource().getSender().sendMessage(text); }; - GithubResourcepackManager.LOGGER.addListener(PlatformLogger.LogLevel.INFO, infoListener); - GithubResourcepackManager.LOGGER.addListener(PlatformLogger.LogLevel.WARN, warnListener); + GithubResourcepackManager.LOGGER.addListener(MonkeyLibLogger.LogLevel.INFO, infoListener); + GithubResourcepackManager.LOGGER.addListener(MonkeyLibLogger.LogLevel.WARN, warnListener); GithubResourcepackManager.updatePack(force ? GithubResourcepackManager.UpdateType.COMMAND_FORCE : GithubResourcepackManager.UpdateType.COMMAND); - GithubResourcepackManager.LOGGER.removeListener(PlatformLogger.LogLevel.INFO, infoListener); - GithubResourcepackManager.LOGGER.removeListener(PlatformLogger.LogLevel.WARN, warnListener); + GithubResourcepackManager.LOGGER.removeListener(MonkeyLibLogger.LogLevel.INFO, infoListener); + GithubResourcepackManager.LOGGER.removeListener(MonkeyLibLogger.LogLevel.WARN, warnListener); } } diff --git a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java index 0487b87..43195f7 100644 --- a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java +++ b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java @@ -7,7 +7,7 @@ import org.apache.commons.lang.exception.ExceptionUtils; import org.bukkit.OfflinePlayer; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformMain; -import top.offsetmonkey538.monkeylib538.api.log.PlatformLogger; +import top.offsetmonkey538.monkeylib538.api.log.MonkeyLibLogger; import java.nio.file.Path; @@ -34,7 +34,7 @@ public void refreshDatapacks() { @Override public void registerLogToAdminListener() { - LOGGER.addListener(PlatformLogger.LogLevel.ERROR, (message, error) -> { + LOGGER.addListener(MonkeyLibLogger.LogLevel.ERROR, (message, error) -> { Component text = Component .text(String.format("[%s] %s", MOD_ID, message)) .color(NamedTextColor.RED); From c640319eb27d3ecc670bb8dfe189f32372b605be Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Wed, 6 Aug 2025 16:36:35 +0200 Subject: [PATCH 24/52] Update monke to 30682cc Use mankeee for styling text. Hover message and update command not supported there yet --- build.gradle | 11 +- common/build.gradle | 16 +- common/gradle.properties | 3 - .../GithubResourcepackManager.java | 8 +- .../platform/PlatformText.java | 6 +- fabric/build.gradle | 23 ++- fabric/gradle.properties | 2 +- .../platform/fabric/FabricPlatformText.java | 146 +----------------- gradle.properties | 4 +- paper/build.gradle | 9 -- .../platform/paper/PaperPlatformText.java | 26 +++- 11 files changed, 64 insertions(+), 190 deletions(-) diff --git a/build.gradle b/build.gradle index d0be512..780ac0c 100644 --- a/build.gradle +++ b/build.gradle @@ -11,13 +11,22 @@ allprojects { repositories { mavenLocal() + maven { + name = "OffsetMods538" + url = "https://maven.offsetmonkey538.top/releases" + content { + includeGroup "top.offsetmonkey538.meshlib" + includeGroup "top.offsetmonkey538.monkeylib538" + includeGroup "top.offsetmonkey538.offsetconfig538" + } + } } } subprojects { apply plugin: "java" - archivesBaseName = "github-resourcepack-manager-${project.nameSuffix}" + base.archivesName = "github-resourcepack-manager-${project.nameSuffix}" version = "${project.mod_version}+${project.minecraft_version}" java { diff --git a/common/build.gradle b/common/build.gradle index 86dea47..fb89666 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -1,4 +1,5 @@ plugins { + id 'java-library' id 'com.gradleup.shadow' version '9.0.0-beta4' } @@ -11,25 +12,16 @@ repositories { includeGroup "com.mojang" } } - maven { - name = "OffsetMods538" - url = "https://maven.offsetmonkey538.top/releases" - content { - includeGroup "top.offsetmonkey538.meshlib" - includeGroup "top.offsetmonkey538.offsetconfig538" - includeGroup "top.offsetmonkey538.monkeylib538" - } - } } dependencies { implementation "org.eclipse.jgit:org.eclipse.jgit:${project.jgit_version}" - implementation "top.offsetmonkey538.offsetconfig538:offsetconfig538:${project.offsetconfig538_version}" - shadow implementation("top.offsetmonkey538.monkeylib538:monkeylib538-common:${project.monkeylib538_version}") + + shadow compileOnlyApi("top.offsetmonkey538.monkeylib538:monkeylib538-common:${project.monkeylib538_version}") shadow compileOnly("top.offsetmonkey538.meshlib:mesh-lib-api:${project.meshlib_version}") - // should be bundeled with minecraft + // should be bundled with minecraft shadow compileOnly("com.google.guava:guava:${project.guava_version}") shadow compileOnly("commons-io:commons-io:${project.commons_version}") shadow compileOnly("com.google.code.gson:gson:${project.gson_version}") diff --git a/common/gradle.properties b/common/gradle.properties index 41b5d26..0f283af 100644 --- a/common/gradle.properties +++ b/common/gradle.properties @@ -3,7 +3,4 @@ guava_version = 33.4.0-jre commons_version = 2.18.0 gson_version = 2.11.0 -## OffsetConfig538, check at https://github.com/OffsetMods538/OffsetConfig538 -offsetconfig538_version = 1.0.0-946f4f7 - nameSuffix = api \ No newline at end of file diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java index 90cbd8c..c8fad89 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java @@ -10,9 +10,11 @@ import top.offsetmonkey538.githubresourcepackmanager.handler.ResourcePackHandler; import top.offsetmonkey538.githubresourcepackmanager.networking.MainHttpHandler; import top.offsetmonkey538.githubresourcepackmanager.platform.*; +import top.offsetmonkey538.githubresourcepackmanager.utils.StringUtils; import top.offsetmonkey538.meshlib.api.HttpHandlerRegistry; import top.offsetmonkey538.monkeylib538.api.command.ConfigCommandApi; import top.offsetmonkey538.monkeylib538.api.log.MonkeyLibLogger; +import top.offsetmonkey538.monkeylib538.api.text.TextFormattingApi; import top.offsetmonkey538.offsetconfig538.api.config.ConfigHolder; import top.offsetmonkey538.offsetconfig538.api.config.ConfigManager; @@ -258,7 +260,11 @@ private static void sendUpdateMessage(final String updateMessage, @Nullable fina return; } - PlatformText.INSTANCE.sendUpdateMessage(updateMessage, updateHoverMessage, placeholders, adminsOnly); + try { + PlatformText.INSTANCE.sendUpdateMessage(TextFormattingApi.styleTextMultiline(StringUtils.replacePlaceholders(updateMessage, placeholders)), adminsOnly); + } catch (Exception e) { + throw new GithubResourcepackManagerException("Failed to style update message!", e); + } } private static String getOldResourcePackName() { diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformText.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformText.java index 4757534..405dd71 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformText.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformText.java @@ -1,14 +1,12 @@ package top.offsetmonkey538.githubresourcepackmanager.platform; -import org.jetbrains.annotations.Nullable; import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; - -import java.util.Map; +import top.offsetmonkey538.monkeylib538.api.text.MonkeyLibText; import static top.offsetmonkey538.githubresourcepackmanager.platform.ServiceLoader.load; public interface PlatformText { PlatformText INSTANCE = load(PlatformText.class); - void sendUpdateMessage(final String updateMessage, @Nullable final String updateHoverMessage, Map placeholders, boolean adminsOnly) throws GithubResourcepackManagerException; + void sendUpdateMessage(final MonkeyLibText[] updateMessage, boolean adminsOnly); } diff --git a/fabric/build.gradle b/fabric/build.gradle index 28b205a..52d5ead 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -55,15 +55,6 @@ repositories { includeGroup "me.djtheredstoner" } } - maven { - name = "OffsetMods538" - url = "https://maven.offsetmonkey538.top/releases" - content { - includeGroup "top.offsetmonkey538.meshlib" - includeGroup "top.offsetmonkey538.monkeylib538" - includeGroup "top.offsetmonkey538.offsetconfig538" - } - } } configurations { @@ -90,13 +81,21 @@ dependencies { mappings "net.fabricmc:yarn:${project.yarn_version}:v2" modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" - modRuntimeOnly "me.djtheredstoner:DevAuth-fabric:${devauth_version}" + modRuntimeOnly "me.djtheredstoner:DevAuth-fabric:${project.devauth_version}" - compileOnly "top.offsetmonkey538.monkeylib538:monkeylib538-common:${project.monkeylib538_version}" - modImplementation("top.offsetmonkey538.monkeylib538:monkeylib538-fabric:${project.monkeylib538_version}+1.21.5") { + modRuntimeOnly("top.offsetmonkey538.monkeylib538:monkeylib538-fabric-1.20.5:${project.monkeylib538_version}+1.20.5") { exclude(group: "net.fabricmc.fabric-api") exclude(group: "top.offsetmonkey538.monkeylib538:monkeylib538-fabric") } + modCompileOnly("top.offsetmonkey538.monkeylib538:monkeylib538-fabric:${project.monkeylib538_version}") { + exclude(group: "net.fabricmc.fabric-api") + } + //modRuntimeOnly("top.offsetmonkey538.monkeylib538:monkeylib538-fabric:${project.monkeylib538_version}") { + // exclude(group: "net.fabricmc.fabric-api") + //} + //modCompileOnly("top.offsetmonkey538.monkeylib538:monkeylib538-fabric:${project.monkeylib538_version}") { + // exclude(group: "net.fabricmc.fabric-api") + //} modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fapi_version}" diff --git a/fabric/gradle.properties b/fabric/gradle.properties index c0ef59c..94ec4f1 100644 --- a/fabric/gradle.properties +++ b/fabric/gradle.properties @@ -3,7 +3,7 @@ # These should be automatically updated, unless the environment # variable "DISABLE_PROPERTIES_UPDATE" is set. yarn_version = 1.21.4+build.8 -loader_version = 0.17.0 +loader_version = 0.17.1 fapi_version = 0.119.3+1.21.4 # Dependencies diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java index 7a7a199..5e852a5 100644 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java +++ b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java @@ -3,160 +3,28 @@ import net.minecraft.server.PlayerManager; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.*; -import net.minecraft.util.Formatting; -import org.jetbrains.annotations.Nullable; import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformText; -import top.offsetmonkey538.githubresourcepackmanager.utils.StringUtils; - -import java.util.List; -import java.util.Map; +import top.offsetmonkey538.monkeylib538.api.text.MonkeyLibText; +import top.offsetmonkey538.monkeylib538.fabric.api.text.FabricMonkeyLibText; public class FabricPlatformText implements PlatformText { - Style DEFAULT_STYLE = Style.EMPTY.withItalic(false).withColor(Formatting.WHITE); - - @Override - public void sendUpdateMessage(final String message, @Nullable final String hoverMessage, Map placeholders, boolean adminsOnly) throws GithubResourcepackManagerException { + public void sendUpdateMessage(MonkeyLibText[] updateMessage, boolean adminsOnly) { + assert FabricPlatformMain.getServer() != null : "Literally HOW?? Why are we trying to send the update message when the server hasn't started yet!??!?!?"; final PlayerManager playerManager = FabricPlatformMain.getServer().getPlayerManager(); if (playerManager == null) return; - final String[] splitMessage = message.split("\n"); - - final HoverEvent hoverEvent; - try { - hoverEvent = hoverMessage == null ? null : new HoverEvent( - HoverEvent.Action.SHOW_TEXT, - getStyledText( - StringUtils.replacePlaceholders(hoverMessage, placeholders).replace("\\n", "\n") - ) - ); - } catch (Exception e) { - throw new GithubResourcepackManagerException("Failed to style update hover message!", e); - } - - for (int lineNumber = 0; lineNumber < splitMessage.length; lineNumber++) { - final String currentLineString = StringUtils.replacePlaceholders(splitMessage[lineNumber], placeholders).replace("\\n", "\n"); - final MutableText currentLine = Text.empty(); - try { - for (Text currentLineSibling : getStyledText(currentLineString).getSiblings()) { - final MutableText sibling = currentLineSibling.copy(); - - if (hoverEvent != null) sibling.setStyle(sibling.getStyle().withHoverEvent(hoverEvent)); - - final String siblingString = sibling.getString(); - if (!siblingString.contains("{packUpdateCommand}")) { - currentLine.append(sibling); - continue; - } - - final Style siblingStyle = sibling.getStyle(); - final String[] splitSibling = siblingString.split("\\{packUpdateCommand}"); - - if (splitSibling.length > 0) - currentLine.append(Text.literal(splitSibling[0]).setStyle(siblingStyle)); - - currentLine.append(Text.literal("[HERE]").setStyle(siblingStyle - .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Text.of("Click to update pack"))) - .withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/gh-rp-manager request-pack")) - )); - - if (splitSibling.length > 1) - currentLine.append(Text.literal(splitSibling[1]).setStyle(siblingStyle)); - } - } catch (Exception e) { - throw new GithubResourcepackManagerException("Failed to style update message at line number '%s'!", e, lineNumber); - } - - + for (final MonkeyLibText currentLine : updateMessage) { if (!adminsOnly) { - playerManager.broadcast(currentLine, false); + playerManager.broadcast(FabricMonkeyLibText.of(currentLine).getText(), false); continue; } for (final ServerPlayerEntity player : playerManager.getPlayerList()) { if (!playerManager.isOperator(player.getGameProfile())) continue; - player.sendMessageToClient(currentLine, false); + player.sendMessageToClient(FabricMonkeyLibText.of(currentLine).getText(), false); } } } - - private MutableText getStyledText(String text) throws Exception { - final MutableText result = Text.empty(); - Style style = DEFAULT_STYLE; - - boolean isFormattingCode = false; - boolean isEscaped = false; - char[] characters = text.toCharArray(); - for (int characterIndex = 0; characterIndex < characters.length; characterIndex++) { - char currentChar = characters[characterIndex]; - - if (isFormattingCode) { - // Hex color - if (currentChar == '#') { - if (characterIndex + 7 >= characters.length) throw new Exception("Unfinished hex code starting at character number '" + characterIndex + "'!"); - - try { - style = style.withColor(parseHexColor(text.substring(characterIndex, characterIndex + 7))); - } catch (Exception e) { - throw new Exception("Failed to parse hex color starting at character number '" + characterIndex + "'!", e); - } - - // Move pointer 6 characters ahead as we already read the whole hex code - characterIndex += 6; - isFormattingCode = false; - continue; - } - - style = getStyleForFormattingCode(currentChar, style); - - if (style == null) throw new Exception("Invalid formatting code at character number '" + characterIndex + "'!"); - - isFormattingCode = false; - continue; - } - - if (!isEscaped){ - switch (currentChar){ - case '&': - isFormattingCode = true; - continue; - case '\\': - isEscaped = true; - continue; - } - } - isEscaped = false; - - - final List siblings = result.getSiblings(); - final int lastSiblingIndex = siblings.size() - 1; - final Text lastSibling = siblings.isEmpty() ? Text.empty() : siblings.get(lastSiblingIndex); - - // Check if the style of the last sibling is the same as the current one - if (!siblings.isEmpty() && lastSibling.getStyle().equals(style)) { - // If so, set the last sibling to itself plus the new character - siblings.set(lastSiblingIndex, Text.literal(lastSibling.getString() + currentChar).setStyle(style)); - } else { - // Otherwise, just append a new sibling to the result - result.append(Text.literal(String.valueOf(currentChar)).setStyle(style)); - } - } - - return result; - } - - - private Style getStyleForFormattingCode(char formattingCode, Style currentStyle) { - if (formattingCode == 'r') return DEFAULT_STYLE; - - final Formatting formatting = Formatting.byCode(formattingCode); - if (formatting == null) return null; - - return currentStyle.withFormatting(formatting); - } - - public TextColor parseHexColor(String hexColor) throws Exception { - return TextColor.parse(hexColor).getOrThrow(Exception::new); - } } diff --git a/gradle.properties b/gradle.properties index 6d2e36c..e0a5b25 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,8 +7,8 @@ minecraft_version = 1.21.4 # MESH Lib, check at https://github.com/OffsetMods538/MESH-Lib meshlib_version = 1.0.5+1.21.4 # MonkeyLib538, check at https://github.com/OffsetMods538/MonkeyLib538 -monkeylib538_version = 3.0.0-eb3e2e1 +monkeylib538_version = 3.0.0-30682cc # Mod Properties mod_version = 5.0.0 -supported_minecraft_versions = >=1.19.2 +supported_minecraft_versions = >=1.20.5 diff --git a/paper/build.gradle b/paper/build.gradle index 66104b1..b49d520 100644 --- a/paper/build.gradle +++ b/paper/build.gradle @@ -25,15 +25,6 @@ repositories { includeGroup "com.mojang" } } - maven { - name = "OffsetMods538" - url = "https://maven.offsetmonkey538.top/releases" - content { - includeGroup "top.offsetmonkey538.meshlib" - includeGroup "top.offsetmonkey538.monkeylib538" - includeGroup "top.offsetmonkey538.offsetconfig538" - } - } } configurations { diff --git a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformText.java b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformText.java index 623645b..e9b1953 100644 --- a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformText.java +++ b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformText.java @@ -1,20 +1,33 @@ package top.offsetmonkey538.githubresourcepackmanager.platform.paper; -import net.minecraft.ChatFormatting; import net.minecraft.network.chat.*; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.players.PlayerList; -import org.jetbrains.annotations.Nullable; import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformText; -import top.offsetmonkey538.githubresourcepackmanager.utils.StringUtils; - -import java.util.List; -import java.util.Map; +import top.offsetmonkey538.monkeylib538.api.text.MonkeyLibText; public class PaperPlatformText implements PlatformText { @Override + public void sendUpdateMessage(MonkeyLibText[] updateMessage, boolean adminsOnly) { + for (final MonkeyLibText currentLine : updateMessage) { + final PlayerList players = MinecraftServer.getServer().getPlayerList(); + if (!adminsOnly) { + players.broadcastSystemMessage(Component.empty(), false); + // TODO: once I implement paper version of monke: players.broadcastSystemMessage(PaperMonkeyLibText.of(currentLine).getText(), false); + continue; + } + + for (final ServerPlayer player : players.players) { + if (!players.isOp(player.getGameProfile())) continue; + player.sendSystemMessage(Component.empty()); + // TODO: once I implement paper version of monke: player.sendSystemMessage(PaperMonkeyLibText.of(currentLine).getText()); + } + } + } + + /* public void sendUpdateMessage(final String message, @Nullable final String hoverMessage, Map placeholders, boolean adminsOnly) throws GithubResourcepackManagerException { final String[] splitMessage = message.split("\n"); @@ -155,4 +168,5 @@ private static Style getStyleForFormattingCode(char formattingCode, Style curren return currentStyle.applyFormat(formatting); } + */ } From 71c06a44ecc34b39e6e5e5a0ce6ed59f3d8adb9b Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Wed, 6 Aug 2025 16:37:48 +0200 Subject: [PATCH 25/52] Unused import --- .../githubresourcepackmanager/platform/PlatformText.java | 1 - .../platform/fabric/FabricPlatformText.java | 1 - .../platform/paper/PaperPlatformText.java | 1 - 3 files changed, 3 deletions(-) diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformText.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformText.java index 405dd71..3ad7527 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformText.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformText.java @@ -1,6 +1,5 @@ package top.offsetmonkey538.githubresourcepackmanager.platform; -import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; import top.offsetmonkey538.monkeylib538.api.text.MonkeyLibText; import static top.offsetmonkey538.githubresourcepackmanager.platform.ServiceLoader.load; diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java index 5e852a5..2b3c0d7 100644 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java +++ b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java @@ -3,7 +3,6 @@ import net.minecraft.server.PlayerManager; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.*; -import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformText; import top.offsetmonkey538.monkeylib538.api.text.MonkeyLibText; import top.offsetmonkey538.monkeylib538.fabric.api.text.FabricMonkeyLibText; diff --git a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformText.java b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformText.java index e9b1953..b6356e7 100644 --- a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformText.java +++ b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformText.java @@ -4,7 +4,6 @@ import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.players.PlayerList; -import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformText; import top.offsetmonkey538.monkeylib538.api.text.MonkeyLibText; From a07de908b1a785c818367b3504cc16f9e537848b Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Wed, 6 Aug 2025 16:43:49 +0200 Subject: [PATCH 26/52] Use gray text for info logging in chat from trigger update command --- .../platform/fabric/FabricPlatformCommand.java | 2 +- .../platform/paper/PaperPlatformCommand.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformCommand.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformCommand.java index 97743ac..be4c826 100644 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformCommand.java +++ b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformCommand.java @@ -90,7 +90,7 @@ public static void register(CommandDispatcher dispatcher, C private static void runTriggerUpdate(CommandContext context, boolean force) { final MonkeyLibLogger.LogListener infoListener = (message, error) -> { - context.getSource().sendMessage(Text.literal(String.format("[%s] %s", MOD_ID, message))); + context.getSource().sendMessage(Text.literal(String.format("[%s] %s", MOD_ID, message)).formatted(Formatting.GRAY)); }; final MonkeyLibLogger.LogListener warnListener = (message, error) -> { MutableText text = Text diff --git a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformCommand.java b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformCommand.java index 4c2b685..c5d60d6 100644 --- a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformCommand.java +++ b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformCommand.java @@ -85,7 +85,7 @@ public void registerGithubRpManagerCommand() { @SuppressWarnings("UnstableApiUsage") private void runTriggerUpdate(CommandContext context, boolean force) { final MonkeyLibLogger.LogListener infoListener = (message, error) -> { - context.getSource().getSender().sendMessage(Component.text(String.format("[%s] %s", MOD_ID, message))); + context.getSource().getSender().sendMessage(Component.text(String.format("[%s] %s", MOD_ID, message)).color(NamedTextColor.GRAY)); }; final MonkeyLibLogger.LogListener warnListener = (message, error) -> { Component text = Component From a3071e5a22743b62945cb0e6a898be9169a3c0a2 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Wed, 6 Aug 2025 20:06:04 +0200 Subject: [PATCH 27/52] Update monke to 7adde5c Use maonyk for command --- common/build.gradle | 3 +- common/gradle.properties | 3 +- .../GithubResourcepackManager.java | 3 +- .../command/GitPackManagerCommand.java | 74 +++++++++++ .../platform/PlatformCommand.java | 6 +- .../fabric/FabricPlatformCommand.java | 120 ++++------------- gradle.properties | 2 +- .../platform/paper/PaperPlatformCommand.java | 121 ++++-------------- 8 files changed, 136 insertions(+), 196 deletions(-) create mode 100644 common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/command/GitPackManagerCommand.java diff --git a/common/build.gradle b/common/build.gradle index fb89666..da64765 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -23,7 +23,8 @@ dependencies { // should be bundled with minecraft shadow compileOnly("com.google.guava:guava:${project.guava_version}") - shadow compileOnly("commons-io:commons-io:${project.commons_version}") + shadow compileOnly("commons-io:commons-io:${project.commonsio_version}") + shadow compileOnly("org.apache.commons:commons-lang3:${project.commonslang_version}") shadow compileOnly("com.google.code.gson:gson:${project.gson_version}") // Jetbrains annotations for gud code diff --git a/common/gradle.properties b/common/gradle.properties index 0f283af..9cd2a8d 100644 --- a/common/gradle.properties +++ b/common/gradle.properties @@ -1,6 +1,7 @@ jgit_version = 6.9.0.202403050737-r guava_version = 33.4.0-jre -commons_version = 2.18.0 +commonsio_version = 2.18.0 +commonslang_version = 3.18.0 gson_version = 2.11.0 nameSuffix = api \ No newline at end of file diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java index c8fad89..830b655 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java @@ -2,6 +2,7 @@ import org.apache.commons.io.FileUtils; import org.jetbrains.annotations.Nullable; +import top.offsetmonkey538.githubresourcepackmanager.command.GitPackManagerCommand; import top.offsetmonkey538.githubresourcepackmanager.config.ModConfig; import top.offsetmonkey538.githubresourcepackmanager.config.ConfigHandler; import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; @@ -52,7 +53,7 @@ private GithubResourcepackManager() { public static void initialize() { PlatformMain.INSTANCE.registerLogToAdminListener(); - PlatformCommand.INSTANCE.registerGithubRpManagerCommand(); + GitPackManagerCommand.register(); ConfigCommandApi.registerConfigCommand( config, () -> disabled = ConfigHandler.handleConfig(), diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/command/GitPackManagerCommand.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/command/GitPackManagerCommand.java new file mode 100644 index 0000000..c8d083b --- /dev/null +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/command/GitPackManagerCommand.java @@ -0,0 +1,74 @@ +package top.offsetmonkey538.githubresourcepackmanager.command; + +import com.mojang.brigadier.builder.LiteralArgumentBuilder; +import com.mojang.brigadier.context.CommandContext; +import org.apache.commons.lang3.exception.ExceptionUtils; +import top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager; +import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformCommand; +import top.offsetmonkey538.monkeylib538.api.command.CommandAbstractionApi; +import top.offsetmonkey538.monkeylib538.api.command.CommandRegistrationApi; +import top.offsetmonkey538.monkeylib538.api.log.MonkeyLibLogger; +import top.offsetmonkey538.monkeylib538.api.text.MonkeyLibStyle; +import top.offsetmonkey538.monkeylib538.api.text.MonkeyLibText; + +import static com.mojang.brigadier.arguments.BoolArgumentType.bool; +import static com.mojang.brigadier.arguments.BoolArgumentType.getBool; +import static com.mojang.brigadier.builder.RequiredArgumentBuilder.argument; +import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.MOD_ID; +import static top.offsetmonkey538.monkeylib538.api.command.CommandAbstractionApi.literal; +import static top.offsetmonkey538.monkeylib538.api.command.CommandAbstractionApi.sendText; + +public final class GitPackManagerCommand { + + public static void register() { + CommandRegistrationApi.registerCommand(createCommand()); + } + + private static LiteralArgumentBuilder createCommand() { + return literal("gh-rp-manager") + .then(literal("request-pack") + .requires(CommandAbstractionApi::executedByPlayer) + .executes(PlatformCommand.INSTANCE::executeRequestPackCommand) + ) + + .then(literal("trigger-update") + .requires(CommandAbstractionApi::isOp) + .executes( + context -> { + runTriggerUpdate(context, false); + return 1; + } + ) + .then(argument("force", bool()) + .executes( + context -> { + runTriggerUpdate(context, getBool(context, "force")); + return 1; + } + ) + ) + ); + } + + private static void runTriggerUpdate(CommandContext context, boolean force) { + final MonkeyLibLogger.LogListener infoListener = (message, error) -> { + sendText(context, MonkeyLibText.of(String.format("[%s] %s", MOD_ID, message)).applyStyle(style -> style.withColor(MonkeyLibStyle.Color.GRAY))); + }; + final MonkeyLibLogger.LogListener warnListener = (message, error) -> { + final MonkeyLibText text = MonkeyLibText + .of("[%s] %s".formatted(MOD_ID, message)) + .applyStyle(style -> style.withColor(MonkeyLibStyle.Color.YELLOW)); + + if (error != null) text.applyStyle(style -> style.withShowText(MonkeyLibText.of(ExceptionUtils.getRootCauseMessage(error)))); + + sendText(context, text); + }; + GithubResourcepackManager.LOGGER.addListener(MonkeyLibLogger.LogLevel.INFO, infoListener); + GithubResourcepackManager.LOGGER.addListener(MonkeyLibLogger.LogLevel.WARN, warnListener); + + GithubResourcepackManager.updatePack(force ? GithubResourcepackManager.UpdateType.COMMAND_FORCE : GithubResourcepackManager.UpdateType.COMMAND); + + GithubResourcepackManager.LOGGER.removeListener(MonkeyLibLogger.LogLevel.INFO, infoListener); + GithubResourcepackManager.LOGGER.removeListener(MonkeyLibLogger.LogLevel.WARN, warnListener); + } +} diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformCommand.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformCommand.java index 425d1ff..5050000 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformCommand.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformCommand.java @@ -1,9 +1,13 @@ package top.offsetmonkey538.githubresourcepackmanager.platform; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import org.jetbrains.annotations.NotNull; + import static top.offsetmonkey538.githubresourcepackmanager.platform.ServiceLoader.load; public interface PlatformCommand { PlatformCommand INSTANCE = load(PlatformCommand.class); - void registerGithubRpManagerCommand(); + int executeRequestPackCommand(final @NotNull CommandContext ctx) throws CommandSyntaxException; } diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformCommand.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformCommand.java index be4c826..b309e01 100644 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformCommand.java +++ b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformCommand.java @@ -1,117 +1,41 @@ package top.offsetmonkey538.githubresourcepackmanager.platform.fabric; -import com.mojang.brigadier.CommandDispatcher; -import com.mojang.brigadier.builder.ArgumentBuilder; -import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.context.CommandContext; -import com.mojang.brigadier.tree.CommandNode; -import com.mojang.brigadier.tree.LiteralCommandNode; -import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; -import net.minecraft.command.CommandRegistryAccess; +import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.command.ControlFlowAware; import net.minecraft.network.packet.s2c.common.ResourcePackSendS2CPacket; import net.minecraft.server.MinecraftServer; -import net.minecraft.server.command.CommandManager; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.text.HoverEvent; -import net.minecraft.text.MutableText; -import net.minecraft.text.Style; import net.minecraft.text.Text; -import net.minecraft.util.Formatting; -import org.apache.commons.lang3.exception.ExceptionUtils; -import top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager; +import org.jetbrains.annotations.NotNull; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformCommand; -import top.offsetmonkey538.monkeylib538.api.log.MonkeyLibLogger; +import top.offsetmonkey538.monkeylib538.fabric.api.command.FabricCommandAbstractionApi; import java.util.Optional; -import static com.mojang.brigadier.arguments.BoolArgumentType.bool; -import static com.mojang.brigadier.arguments.BoolArgumentType.getBool; -import static net.minecraft.server.command.CommandManager.argument; -import static net.minecraft.server.command.CommandManager.literal; -import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.MOD_ID; - public class FabricPlatformCommand implements PlatformCommand { @Override - public void registerGithubRpManagerCommand() { - CommandRegistrationCallback.EVENT.register(FabricPlatformCommand::register); - } - - public static void register(CommandDispatcher dispatcher, CommandRegistryAccess commandRegistryAccess, CommandManager.RegistrationEnvironment registrationEnvironment) { - final LiteralArgumentBuilder command = literal("gh-rp-manager"); - - dispatcher.register(command - .then(literal("request-pack") - .requires(ServerCommandSource::isExecutedByPlayer) - .executes( - context -> { - final ServerPlayerEntity player = context.getSource().getPlayerOrThrow(); - final MinecraftServer.ServerResourcePackProperties resourcePackProperties = context.getSource().getServer().getResourcePackProperties().orElse(null); - if (resourcePackProperties == null) { - context.getSource().sendFeedback(() -> Text.literal("Failed to send pack update packet to client!"), true); - return 0; - } - - player.networkHandler.send( - new ResourcePackSendS2CPacket( - resourcePackProperties.id(), - resourcePackProperties.url(), - resourcePackProperties.hash(), - resourcePackProperties.isRequired(), - Optional.ofNullable(resourcePackProperties.prompt()) - ), - null - ); - - return ControlFlowAware.Command.SINGLE_SUCCESS; - }) - ) - - .then(literal("trigger-update") - .requires(source -> source.hasPermissionLevel(2)) - .executes( - context -> { - runTriggerUpdate(context, false); - return 1; - } - ) - .then(argument("force", bool()) - .executes( - context -> { - runTriggerUpdate(context, getBool(context, "force")); - return 1; - } - ) - ) - ) + public int executeRequestPackCommand(@NotNull CommandContext ctx) throws CommandSyntaxException { + final ServerCommandSource source = FabricCommandAbstractionApi.get(ctx); + final ServerPlayerEntity player = source.getPlayerOrThrow(); + final MinecraftServer.ServerResourcePackProperties resourcePackProperties = source.getServer().getResourcePackProperties().orElse(null); + if (resourcePackProperties == null) { + source.sendFeedback(() -> Text.literal("Failed to send pack update packet to client!"), true); + return 0; + } + + player.networkHandler.send( + new ResourcePackSendS2CPacket( + resourcePackProperties.id(), + resourcePackProperties.url(), + resourcePackProperties.hash(), + resourcePackProperties.isRequired(), + Optional.ofNullable(resourcePackProperties.prompt()) + ), + null ); - } - - private static void runTriggerUpdate(CommandContext context, boolean force) { - final MonkeyLibLogger.LogListener infoListener = (message, error) -> { - context.getSource().sendMessage(Text.literal(String.format("[%s] %s", MOD_ID, message)).formatted(Formatting.GRAY)); - }; - final MonkeyLibLogger.LogListener warnListener = (message, error) -> { - MutableText text = Text - .literal(String.format("[%s] %s", MOD_ID, message)) - .setStyle(Style.EMPTY.withColor(Formatting.YELLOW)); - - if (error != null) text.setStyle(Style.EMPTY.withColor(Formatting.YELLOW).withHoverEvent( - new HoverEvent( - HoverEvent.Action.SHOW_TEXT, - Text.literal(ExceptionUtils.getRootCauseMessage(error)) - ) - )); - - context.getSource().sendMessage(text); - }; - GithubResourcepackManager.LOGGER.addListener(MonkeyLibLogger.LogLevel.INFO, infoListener); - GithubResourcepackManager.LOGGER.addListener(MonkeyLibLogger.LogLevel.WARN, warnListener); - - GithubResourcepackManager.updatePack(force ? GithubResourcepackManager.UpdateType.COMMAND_FORCE : GithubResourcepackManager.UpdateType.COMMAND); - GithubResourcepackManager.LOGGER.removeListener(MonkeyLibLogger.LogLevel.INFO, infoListener); - GithubResourcepackManager.LOGGER.removeListener(MonkeyLibLogger.LogLevel.WARN, warnListener); + return ControlFlowAware.Command.SINGLE_SUCCESS; } } diff --git a/gradle.properties b/gradle.properties index e0a5b25..af2a025 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,7 +7,7 @@ minecraft_version = 1.21.4 # MESH Lib, check at https://github.com/OffsetMods538/MESH-Lib meshlib_version = 1.0.5+1.21.4 # MonkeyLib538, check at https://github.com/OffsetMods538/MonkeyLib538 -monkeylib538_version = 3.0.0-30682cc +monkeylib538_version = 3.0.0-7adde5c # Mod Properties mod_version = 5.0.0 diff --git a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformCommand.java b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformCommand.java index c5d60d6..5e64904 100644 --- a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformCommand.java +++ b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformCommand.java @@ -2,110 +2,45 @@ import com.mojang.brigadier.Command; import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; import io.papermc.paper.command.brigadier.CommandSourceStack; -import io.papermc.paper.command.brigadier.Commands; -import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents; import net.kyori.adventure.resource.ResourcePackInfo; import net.kyori.adventure.resource.ResourcePackRequest; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.event.HoverEvent; -import net.kyori.adventure.text.format.NamedTextColor; import net.minecraft.server.MinecraftServer; -import org.apache.commons.lang.exception.ExceptionUtils; import org.bukkit.entity.Player; -import top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager; +import org.jetbrains.annotations.NotNull; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformCommand; -import top.offsetmonkey538.monkeylib538.api.log.MonkeyLibLogger; import java.net.URI; -import static com.mojang.brigadier.arguments.BoolArgumentType.bool; -import static com.mojang.brigadier.arguments.BoolArgumentType.getBool; -import static io.papermc.paper.command.brigadier.Commands.argument; -import static io.papermc.paper.command.brigadier.Commands.literal; -import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.MOD_ID; - public class PaperPlatformCommand implements PlatformCommand { @SuppressWarnings("UnstableApiUsage") @Override - public void registerGithubRpManagerCommand() { - PaperPlatformMain.getPlugin().getLifecycleManager().registerEventHandler(LifecycleEvents.COMMANDS, event -> { - final Commands commands = event. registrar(); - - commands.register(literal("gh-rp-manager") - .requires(commandSourceStack -> commandSourceStack.getExecutor() instanceof Player) - .then(literal("request-pack").executes( - context -> { - final Player player = (Player) context.getSource().getExecutor(); - if (player == null) return 0; - - final MinecraftServer.ServerResourcePackInfo resourcePackProperties = MinecraftServer.getServer().getServerResourcePack().orElse(null); - if (resourcePackProperties == null) { - context.getSource().getSender().sendMessage("Failed to send pack update packet to client!"); - return 0; - } - player.sendResourcePacks( - ResourcePackRequest.resourcePackRequest() - .packs( - ResourcePackInfo.resourcePackInfo( - resourcePackProperties.id(), - URI.create(resourcePackProperties.url()), - resourcePackProperties.hash() - ) - ) - .replace(true) - .required(resourcePackProperties.isRequired()) - .asResourcePackRequest() - ); - return Command.SINGLE_SUCCESS; - }) - ) - .then(literal("trigger-update") - .requires(source -> source.getSender().isOp()) - .executes( - context -> { - runTriggerUpdate(context, false); - return 1; - } - ) - .then(argument("force", bool()) - .executes( - context -> { - runTriggerUpdate(context, getBool(context, "force")); - return 1; - } - ) - ) - ) - .build() - ); - }); - } - - @SuppressWarnings("UnstableApiUsage") - private void runTriggerUpdate(CommandContext context, boolean force) { - final MonkeyLibLogger.LogListener infoListener = (message, error) -> { - context.getSource().getSender().sendMessage(Component.text(String.format("[%s] %s", MOD_ID, message)).color(NamedTextColor.GRAY)); - }; - final MonkeyLibLogger.LogListener warnListener = (message, error) -> { - Component text = Component - .text(String.format("[%s] %s", MOD_ID, message)) - .color(NamedTextColor.YELLOW); - - if (error != null) text = text.hoverEvent( - HoverEvent.showText( - Component.text(ExceptionUtils.getRootCauseMessage(error)) - ) - ); - - context.getSource().getSender().sendMessage(text); - }; - GithubResourcepackManager.LOGGER.addListener(MonkeyLibLogger.LogLevel.INFO, infoListener); - GithubResourcepackManager.LOGGER.addListener(MonkeyLibLogger.LogLevel.WARN, warnListener); - - GithubResourcepackManager.updatePack(force ? GithubResourcepackManager.UpdateType.COMMAND_FORCE : GithubResourcepackManager.UpdateType.COMMAND); - - GithubResourcepackManager.LOGGER.removeListener(MonkeyLibLogger.LogLevel.INFO, infoListener); - GithubResourcepackManager.LOGGER.removeListener(MonkeyLibLogger.LogLevel.WARN, warnListener); + public int executeRequestPackCommand(@NotNull CommandContext ctx) throws CommandSyntaxException { + // TODO: once monke has papier: final CommandSourceStack source = PaperCommandAbstractionApi.get(ctx); + final CommandSourceStack source = null; + if (source == null) return 0; + final Player player = (Player) source.getExecutor(); + if (player == null) return 0; + + final MinecraftServer.ServerResourcePackInfo resourcePackProperties = MinecraftServer.getServer().getServerResourcePack().orElse(null); + if (resourcePackProperties == null) { + source.getSender().sendMessage("Failed to send pack update packet to client!"); + return 0; + } + player.sendResourcePacks( + ResourcePackRequest.resourcePackRequest() + .packs( + ResourcePackInfo.resourcePackInfo( + resourcePackProperties.id(), + URI.create(resourcePackProperties.url()), + resourcePackProperties.hash() + ) + ) + .replace(true) + .required(resourcePackProperties.isRequired()) + .asResourcePackRequest() + ); + return Command.SINGLE_SUCCESS; } } From 0852c88b3acb7d13b8e7bd22e5ae9ea9a63c9105 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Sun, 10 Aug 2025 18:32:58 +0200 Subject: [PATCH 28/52] Update monki to 6345c7b Implement hover message and request pack command again Fix #13 Add test command for update message I think stuff is working? Gotta get this stuff committed so it won't be secretly kept on this laptop forever --- .../GithubResourcepackManager.java | 63 +++++--- .../command/GitPackManagerCommand.java | 35 ++++- .../config/ModConfig.java | 136 ++++++++++++------ .../platform/PlatformText.java | 2 +- .../utils/StringUtils.java | 29 ++-- fabric/gradle.properties | 4 +- .../platform/fabric/FabricPlatformText.java | 18 ++- gradle.properties | 2 +- .../platform/paper/PaperPlatformText.java | 24 ++-- 9 files changed, 198 insertions(+), 115 deletions(-) diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java index 830b655..c160100 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java @@ -15,6 +15,7 @@ import top.offsetmonkey538.meshlib.api.HttpHandlerRegistry; import top.offsetmonkey538.monkeylib538.api.command.ConfigCommandApi; import top.offsetmonkey538.monkeylib538.api.log.MonkeyLibLogger; +import top.offsetmonkey538.monkeylib538.api.text.MonkeyLibText; import top.offsetmonkey538.monkeylib538.api.text.TextFormattingApi; import top.offsetmonkey538.offsetconfig538.api.config.ConfigHolder; import top.offsetmonkey538.offsetconfig538.api.config.ConfigManager; @@ -45,6 +46,8 @@ private GithubResourcepackManager() { public static final Pattern RESOURCEPACK_NAME_PATTERN = Pattern.compile("\\d+-"); public static final UUID RESOURCEPACK_UUID = UUID.fromString("60ab8dc7-08d1-4f5f-a9a8-9a01d048b7b9"); + public static final Map STATIC_PLACEHOLDERS = Map.of("{packUpdateCommand}", "/gh-rp-manager request-pack"); + public static ConfigHolder config = ConfigManager.INSTANCE.init(ConfigHolder.create(ModConfig::new, LOGGER::error)); public static ResourcePackHandler resourcePackHandler; @@ -168,17 +171,11 @@ private static void updateResourcePack(final GitHandler gitHandler, final Update } // Generate placeholder map - final Map placeholders = new HashMap<>(); - if (gitHandler.getCommitProperties() != null) placeholders.putAll(gitHandler.getCommitProperties().toPlaceholdersMap()); - placeholders.put("{packType}", "resource"); - placeholders.put("{downloadUrl}", config.get().getPackUrl(resourcePackHandler.getOutputPackName())); - placeholders.put("{updateType}", updateType.name()); - placeholders.put("{wasUpdated}", String.valueOf(wasUpdated)); - LOGGER.info("Placeholders: %s", placeholders); + final Map placeholders = generatePlaceholders(gitHandler, resourcePackHandler, updateType, "resource", wasUpdated); // Send chat message try { - if (!failed) sendUpdateMessage(config.get().resourcePackProvider.updateMessage, config.get().resourcePackProvider.updateMessageHoverMessage, wasUpdated, placeholders); + if (!failed) sendUpdateMessage(config.get().resourcePackProvider.updateMessage, wasUpdated, placeholders); } catch (GithubResourcepackManagerException e) { LOGGER.error("Failed to send update message in chat!", e); } @@ -224,16 +221,11 @@ private static void updateDataPack(final GitHandler gitHandler, final UpdateType // Generate placeholder map - final Map placeholders = new HashMap<>(); - if (gitHandler.getCommitProperties() != null) placeholders.putAll(gitHandler.getCommitProperties().toPlaceholdersMap()); - placeholders.put("{packType}", "data"); - placeholders.put("{updateType}", updateType.name()); - placeholders.put("{wasUpdated}", String.valueOf(true)); - LOGGER.info("Placeholders: %s", placeholders); + final Map placeholders = generatePlaceholders(gitHandler, null, updateType, "data", true); // Send chat message try { - sendUpdateMessage(config.get().dataPackProvider.updateMessage, config.get().dataPackProvider.updateMessageHoverMessage, true, placeholders, true); + sendUpdateMessage(config.get().dataPackProvider.updateMessage, true, placeholders, true); } catch (GithubResourcepackManagerException e) { LOGGER.error("Failed to send update message in chat!", e); } @@ -251,21 +243,48 @@ private static void updateDataPack(final GitHandler gitHandler, final UpdateType } } - private static void sendUpdateMessage(final String updateMessage, @Nullable final String updateHoverMessage, boolean wasUpdated, final Map placeholders) throws GithubResourcepackManagerException { - sendUpdateMessage(updateMessage, updateHoverMessage, wasUpdated, placeholders, false); + private static void sendUpdateMessage(final String[] updateMessage, boolean wasUpdated, final Map placeholders) throws GithubResourcepackManagerException { + sendUpdateMessage(updateMessage, wasUpdated, placeholders, false); } - private static void sendUpdateMessage(final String updateMessage, @Nullable final String updateHoverMessage, boolean wasUpdated, final Map placeholders, boolean adminsOnly) throws GithubResourcepackManagerException { + private static void sendUpdateMessage(final String[] updateMessage, boolean wasUpdated, final Map placeholders, boolean adminsOnly) throws GithubResourcepackManagerException { if (!wasUpdated) { LOGGER.info("Not sending chat message because pack was not updated."); return; } - try { - PlatformText.INSTANCE.sendUpdateMessage(TextFormattingApi.styleTextMultiline(StringUtils.replacePlaceholders(updateMessage, placeholders)), adminsOnly); - } catch (Exception e) { - throw new GithubResourcepackManagerException("Failed to style update message!", e); + for (final MonkeyLibText line : createUpdateMessage(updateMessage, placeholders)) + PlatformText.INSTANCE.sendUpdateMessage(line, adminsOnly); + } + + public static MonkeyLibText[] createUpdateMessage(final String[] updateMessage, final Map placeholders) throws GithubResourcepackManagerException { + final MonkeyLibText[] result = new MonkeyLibText[updateMessage.length]; + + for (int lineIndex = 0; lineIndex < updateMessage.length; lineIndex++) { + final String line = StringUtils.replacePlaceholders(updateMessage[lineIndex], placeholders, true, false); + + try { + result[lineIndex] = TextFormattingApi.styleText(line); + } catch (Exception e) { + throw new GithubResourcepackManagerException("Failed to style update message at line %s!", e, lineIndex); + } } + + return result; + } + + public static Map generatePlaceholders(final GitHandler gitHandler, final @Nullable ResourcePackHandler resourcePackHandler, final UpdateType updateType, final String packType, final boolean wasUpdated) { + final Map placeholders = new HashMap<>(); + + if (gitHandler.getCommitProperties() != null) placeholders.putAll(gitHandler.getCommitProperties().toPlaceholdersMap()); + if (resourcePackHandler != null) placeholders.put("{downloadUrl}", config.get().getPackUrl(resourcePackHandler.getOutputPackName())); + placeholders.putAll(STATIC_PLACEHOLDERS); + placeholders.put("{packType}", packType); + placeholders.put("{updateType}", updateType.name()); + placeholders.put("{wasUpdated}", String.valueOf(wasUpdated)); + LOGGER.info("Placeholders: %s", placeholders); + + return placeholders; } private static String getOldResourcePackName() { diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/command/GitPackManagerCommand.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/command/GitPackManagerCommand.java index c8d083b..0605219 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/command/GitPackManagerCommand.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/command/GitPackManagerCommand.java @@ -4,6 +4,8 @@ import com.mojang.brigadier.context.CommandContext; import org.apache.commons.lang3.exception.ExceptionUtils; import top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager; +import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; +import top.offsetmonkey538.githubresourcepackmanager.handler.GitHandler; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformCommand; import top.offsetmonkey538.monkeylib538.api.command.CommandAbstractionApi; import top.offsetmonkey538.monkeylib538.api.command.CommandRegistrationApi; @@ -11,10 +13,12 @@ import top.offsetmonkey538.monkeylib538.api.text.MonkeyLibStyle; import top.offsetmonkey538.monkeylib538.api.text.MonkeyLibText; +import java.util.Map; + import static com.mojang.brigadier.arguments.BoolArgumentType.bool; import static com.mojang.brigadier.arguments.BoolArgumentType.getBool; import static com.mojang.brigadier.builder.RequiredArgumentBuilder.argument; -import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.MOD_ID; +import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.*; import static top.offsetmonkey538.monkeylib538.api.command.CommandAbstractionApi.literal; import static top.offsetmonkey538.monkeylib538.api.command.CommandAbstractionApi.sendText; @@ -47,6 +51,12 @@ private static LiteralArgumentBuilder createCommand() { } ) ) + ) + + .then(literal("test-update-message") + .requires(CommandAbstractionApi::isOp) + .then(literal("resource").executes(context -> runTestUpdateMessage(context, true))) + .then(literal("data").executes(context -> runTestUpdateMessage(context, false))) ); } @@ -71,4 +81,27 @@ private static void runTriggerUpdate(CommandContext context, boolean for GithubResourcepackManager.LOGGER.removeListener(MonkeyLibLogger.LogLevel.INFO, infoListener); GithubResourcepackManager.LOGGER.removeListener(MonkeyLibLogger.LogLevel.WARN, warnListener); } + + private static int runTestUpdateMessage(CommandContext context, boolean isResource) { + final GitHandler gitHandler = new GitHandler(); + try { + gitHandler.updateRepositoryAndGenerateCommitProperties(); + } catch (GithubResourcepackManagerException e) { + CommandAbstractionApi.sendError(context, "Failed to update repository, git related placeholders will not be replaced!"); + CommandAbstractionApi.sendError(context, "Cause:\n%s\n", e); + } + final Map placeholders = generatePlaceholders(gitHandler, isResource ? resourcePackHandler : null, UpdateType.COMMAND, isResource ? "resource" : "data", true); + + final MonkeyLibText[] text; + try { + text = createUpdateMessage(config.get().resourcePackProvider.updateMessage, placeholders); + } catch (GithubResourcepackManagerException e) { + CommandAbstractionApi.sendError(context, "Failed to create update message!"); + CommandAbstractionApi.sendError(context, "Cause:\n%s\n", e); + return 0; + } + + for (final MonkeyLibText line : text) CommandAbstractionApi.sendText(context, line); + return 1; + } } diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java index d92d3a5..ef35760 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java @@ -1,6 +1,6 @@ package top.offsetmonkey538.githubresourcepackmanager.config; -import blue.endless.jankson.Comment; +import blue.endless.jankson.*; import blue.endless.jankson.api.Marshaller; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -59,10 +59,11 @@ public static class ResourcePackProvider { @Comment("Where the mod will search for resource packs in the cloned repository. MUST NOT be same as or child of the 'rootLocation' of the datapack provider") public String rootLocation = "/resourcepacks"; - @Comment("Message sent in chat when pack has been updated. May be 'null' to disable.") - public String updateMessage = "Server resourcepack has been updated!\nPlease click {packUpdateCommand} to get the most up to date pack."; - @Comment("Message shown when hovering over the 'updateMessage' text. May be 'null' to disable.") - public String updateMessageHoverMessage = "{longDescription}"; + @Comment("Messages sent in chat when pack has been updated. Each entry will be on a new line. May be 'null' or empty to disable.") + public String[] updateMessage = new String[] { + "&{hoverText,'{longDescription}','Server resourcepack has been updated!'}", + "&{hoverText,'{longDescription}','Please click &{hoverText,'Click to update pack','&{runCommand,'{packUpdateCommand}','[HERE]'}'} to get the most up to date pack.'}" + }; @Comment("Webhook to be sent when pack updating succeeded") public WebhookInfo successWebhook = new WebhookInfo(); @@ -85,11 +86,12 @@ public static class DataPackProvider { public boolean enabled = false; @Comment("Where the mod will search for data packs in the cloned repository. MUST NOT be same as or child of the 'rootLocation' of the resourcepack provider") public String rootLocation = "/datapacks"; - - @Comment("Message sent TO ADMINS in chat when pack has been updated. May be 'null' to disable.") - public String updateMessage = "Server datapacks has been updated!\nNew packs (if any) have to be enabled with the '/datapack enable` command.\nPlease run '/reload' or restart the server to reload datapacks."; - @Comment("Message shown when hovering over the 'updateMessage' text. May be 'null' to disable.") - public String updateMessageHoverMessage = "{longDescription}"; + @Comment("Messages sent TO ADMINS in chat when pack has been updated. Each entry will be on a new line. May be 'null' or empty to disable.") + public String[] updateMessage = new String[] { + "&{hoverText,'{longDescription}','Server datapacks have been updated!'}", + "&{hoverText,'{longDescription}','New packs (if any) will need to be enabled with the &{hoverText,'Click to suggest','&{suggestCommand,'/datapack enable','&n/datapack enable'}'} command.'}", + "&{hoverText,'{longDescription}','Please run &{hoverText,'Click to suggest','&{suggestCommand,'/reload','&n/reload'}'} or restart the server to reload datapacks.'}" + }; @Comment("Webhook to be sent when pack updating succeeded") public WebhookInfo successWebhook = new WebhookInfo(); @@ -111,11 +113,6 @@ public static class WebhookInfo { public WebhookInfo() { } - public WebhookInfo(boolean enabled, String url, String body) { - this.enabled = enabled; - this.url = url; - this.body = body; - } @Comment("Whether or not this webhook is enabled") public boolean enabled = false; @@ -131,7 +128,7 @@ public void trigger(final boolean updateSucceeded, final Map pla try { //noinspection DataFlowIssue: Only returns null when `body` is null, which we have already checked String webhookBody = Files.readString(getBodyPath()); - webhookBody = StringUtils.replacePlaceholders(webhookBody, placeholders, true); + webhookBody = StringUtils.replacePlaceholders(webhookBody, placeholders, false, true); WebhookSender.send(webhookBody, getWebhookUrl(), updateType, updateSucceeded); } catch (IOException e) { @@ -155,8 +152,8 @@ public void trigger(final boolean updateSucceeded, final Map pla (original, jankson) -> { // 0 -> 1 original.put("branch", jankson.toJson(jankson.getMarshaller().marshall(String.class, original.get("githubRef")).replace("refs/heads/", ""))); - original.put("repoUrl", original.get("githubUrl")); - original.put("isRepoPrivate", original.get("isPrivate")); + datafixField(original, "githubUrl", "repoUrl"); + datafixField(original, "isPrivate", "isRepoPrivate"); }, (original, jankson) -> { // 1 -> 2 @@ -168,57 +165,102 @@ public void trigger(final boolean updateSucceeded, final Map pla final Marshaller marsh = jankson.getMarshaller(); // Server Info - final ServerInfo serverInfo = new ServerInfo(); + final JsonObject serverInfo = new JsonObject(); - serverInfo.publicIp = marsh.marshall(String.class, original.get("serverPublicIp")); - serverInfo.proxyPort = marsh.marshall(String.class, original.get("proxyPort")); + datafixField(original, "serverPublicIp", serverInfo, "publicIp"); + datafixField(original, "proxyPort", serverInfo, "proxyPort"); - original.put("serverInfo", jankson.toJson(serverInfo)); + original.put("serverInfo", serverInfo); // Repository Info - final RepositoryInfo repositoryInfo = new RepositoryInfo(); + final JsonObject repositoryInfo = new JsonObject(); + + datafixField(original, "branch", repositoryInfo, "branch"); + datafixField(original, "repoUrl", repositoryInfo, "url"); + + datafixField(original, "isRepoPrivate", repositoryInfo, "isPrivate"); - repositoryInfo.branch = marsh.marshall(String.class, original.get("branch")); - repositoryInfo.url = marsh.marshall(String.class, original.get("repoUrl")); - repositoryInfo.isPrivate = marsh.marshall(Boolean.class, original.get("isRepoPrivate")); - repositoryInfo.username = marsh.marshall(String.class, original.get("githubUsername")); - repositoryInfo.token = marsh.marshall(String.class, original.get("githubToken")); + datafixField(original, "githubUsername", repositoryInfo, "username"); + datafixField(original, "githubToken", repositoryInfo, "token"); - original.put("repositoryInfo", jankson.toJson(repositoryInfo)); + original.put("repositoryInfo", repositoryInfo); // Resource Pack Provider - final ResourcePackProvider resourcePackProvider = new ResourcePackProvider(); + final JsonObject resourcePackProvider = new JsonObject(); - resourcePackProvider.enabled = true; - resourcePackProvider.rootLocation = marsh.marshall(String.class, original.get("resourcePackRoot")); - resourcePackProvider.updateMessage = marsh.marshall(String.class, original.get("packUpdateMessage")); - resourcePackProvider.updateMessageHoverMessage = marsh.marshall(String.class, original.get("packUpdateMessageHoverMessage")); + datafixField(original, "resourcePackRoot", serverInfo, "rootLocation"); - final String webhookUrl = marsh.marshall(String.class, original.get("webhookUrl")); - final String webhookBody = marsh.marshall(String.class, original.get("webhookBody")); + datafixField(original, "packUpdateMessage", serverInfo, "updateMessage"); + datafixField(original, "packUpdateMessageHoverMessage", serverInfo, "updateMessageHoverMessage"); + final String webhookUrl = datafixGetAndRemove(marsh, String.class, original, "webhookUrl"); + final String webhookBody = datafixGetAndRemove(marsh, String.class, original, "webhookBody"); if (webhookUrl != null && webhookBody != null) { - final WebhookInfo webhookInfo = new WebhookInfo( - true, - webhookUrl, - webhookBody - ); + final JsonObject webhookInfo = new JsonObject(); + webhookInfo.put("enabled", jankson.toJson(true)); + webhookInfo.put("url", jankson.toJson(webhookUrl)); + webhookInfo.put("body", jankson.toJson(webhookBody)); + + resourcePackProvider.put("successWebhook", webhookInfo); + if (!webhookBody.contains("discord")) resourcePackProvider.put("failWebhook", webhookInfo); + } + + original.put("resourcePackProvider", resourcePackProvider); + }, + (original, jankson) -> { + // 3 -> 4 + final Marshaller marsh = jankson.getMarshaller(); - resourcePackProvider.successWebhook = webhookInfo; + final JsonObject resourcePackProvider = (JsonObject) original.get("resourcePackProvider"); + assert resourcePackProvider != null; - if (!webhookBody.contains("discord")) { - resourcePackProvider.failWebhook = webhookInfo; - } + final String updateMessage = datafixGetAndRemove(marsh, String.class, resourcePackProvider, "updateMessage"); + if (updateMessage == null) { + original.put("updateMessage", JsonNull.INSTANCE); + return; } - original.put("resourcePackProvider", jankson.toJson(resourcePackProvider)); + final String updateMessageHoverMessage = datafixGetAndRemove(marsh, String.class, resourcePackProvider, "updateMessageHoverMessage"); + resourcePackProvider.remove("updateMessageHoverMessage"); + + final String[] newUpdateMessage = updateMessage.split("\\\\n"); + for (int i = 0; i < newUpdateMessage.length; i++) { + newUpdateMessage[i] = newUpdateMessage[i].replaceAll("\\{packUpdateCommand}", "&{hoverText,'Click to update pack','&{runCommand,'{packUpdateCommand}','[HERE]'}'}"); + if (updateMessageHoverMessage != null) newUpdateMessage[i] = "&{hoverText,'%s','%s'}".formatted(updateMessageHoverMessage, newUpdateMessage[i]); + } + resourcePackProvider.put("updateMessage", new JsonArray(newUpdateMessage, marsh)); } }; } + private static void datafixField(final @NotNull JsonObject originalObject, final @NotNull String originalKey, final @NotNull String newKey) { + datafixField(originalObject, originalKey, originalObject, newKey); + } + private static void datafixField(final @NotNull JsonObject originalObject, final @NotNull String originalKey, final @NotNull JsonObject newObject, final @NotNull String newKey) { + final JsonElement originalValue = originalObject.get(originalKey); + if (originalValue == null) { + LOGGER.warn("JSON of current config doesn't contain value with key '%s', new value with key '%s' will be reset to default!", originalKey, newKey); + return; + } + + newObject.put(newKey, originalValue); + originalObject.remove(originalKey); + } + + @Nullable + private static T datafixGetAndRemove(final @NotNull Marshaller marsh, final @NotNull Class type, final @NotNull JsonObject object, final @NotNull String key) { + final JsonElement jsonValue = object.get(key); + if (jsonValue == null) { + LOGGER.warn("JSON of current config doesn't contain value with key '%s'!", key); + return null; + } + object.remove(key); + return marsh.marshall(type, jsonValue); + } + @Override public int getConfigVersion() { - return 3; + return 4; } @Override diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformText.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformText.java index 3ad7527..84272d1 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformText.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformText.java @@ -7,5 +7,5 @@ public interface PlatformText { PlatformText INSTANCE = load(PlatformText.class); - void sendUpdateMessage(final MonkeyLibText[] updateMessage, boolean adminsOnly); + void sendUpdateMessage(final MonkeyLibText updateMessage, boolean adminsOnly); } diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/utils/StringUtils.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/utils/StringUtils.java index 10153cf..7468c7b 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/utils/StringUtils.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/utils/StringUtils.java @@ -17,30 +17,23 @@ private StringUtils() { /** * Replaces all instances of the keys in the placeholders map with their values. *

- * Same as calling {@link StringUtils#replacePlaceholders(String, Map, boolean)} with {@code escapeQuotes} false. + * Replaces {@code '} with {@code \'} in the placeholders if {@code escapeSingleQuotes} is true. + *
+ * Replaces {@code "} with {@code \"} in the placeholders if {@code escapeDoubleQuotes} is true. * * @param string The string to replace placeholders in. * @param placeholders The placeholders to replace. + * @param escapeSingleQuotes Whether single quotes (') should be escaped. + * @param escapeDoubleQuotes Whether double quotes (") should be escaped. * @return The original string with all instances of the keys in the placeholders map replaced with their values. */ - public static String replacePlaceholders(String string, Map placeholders) { - return replacePlaceholders(string, placeholders, false); - } - - /** - * Replaces all instances of the keys in the placeholders map with their values. - *

- * Replaces {@code "} with {@code \"} in the placeholders if {@code escapeQuotes} is true. - * - * @param string The string to replace placeholders in. - * @param placeholders The placeholders to replace. - * @param escapeQuotes Whether quotes should be escaped. - * @return The original string with all instances of the keys in the placeholders map replaced with their values. - */ - public static String replacePlaceholders(String string, Map placeholders, boolean escapeQuotes) { + public static String replacePlaceholders(String string, Map placeholders, boolean escapeSingleQuotes, boolean escapeDoubleQuotes) { for (Map.Entry entry : placeholders.entrySet()) { - // I love strings. "\"" matches " and "\\\"" matches \" - string = string.replace(entry.getKey(), escapeQuotes ? entry.getValue().replace("\"", "\\\"") : entry.getValue()); + String placeholderValue = entry.getValue(); + if (escapeSingleQuotes) placeholderValue = placeholderValue.replace("'", "\\'"); + if (escapeDoubleQuotes) placeholderValue = placeholderValue.replace("\"", "\\\""); + + string = string.replace(entry.getKey(), placeholderValue); } return string; } diff --git a/fabric/gradle.properties b/fabric/gradle.properties index 94ec4f1..7695676 100644 --- a/fabric/gradle.properties +++ b/fabric/gradle.properties @@ -3,8 +3,8 @@ # These should be automatically updated, unless the environment # variable "DISABLE_PROPERTIES_UPDATE" is set. yarn_version = 1.21.4+build.8 -loader_version = 0.17.1 -fapi_version = 0.119.3+1.21.4 +loader_version = 0.17.2 +fapi_version = 0.119.4+1.21.4 # Dependencies ## DevAuth, check at https://github.com/DJtheRedstoner/DevAuth diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java index 2b3c0d7..a1e847b 100644 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java +++ b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java @@ -9,21 +9,19 @@ public class FabricPlatformText implements PlatformText { @Override - public void sendUpdateMessage(MonkeyLibText[] updateMessage, boolean adminsOnly) { + public void sendUpdateMessage(MonkeyLibText updateMessage, boolean adminsOnly) { assert FabricPlatformMain.getServer() != null : "Literally HOW?? Why are we trying to send the update message when the server hasn't started yet!??!?!?"; final PlayerManager playerManager = FabricPlatformMain.getServer().getPlayerManager(); if (playerManager == null) return; - for (final MonkeyLibText currentLine : updateMessage) { - if (!adminsOnly) { - playerManager.broadcast(FabricMonkeyLibText.of(currentLine).getText(), false); - continue; - } + if (!adminsOnly) { + playerManager.broadcast(FabricMonkeyLibText.of(updateMessage).getText(), false); + return; + } - for (final ServerPlayerEntity player : playerManager.getPlayerList()) { - if (!playerManager.isOperator(player.getGameProfile())) continue; - player.sendMessageToClient(FabricMonkeyLibText.of(currentLine).getText(), false); - } + for (final ServerPlayerEntity player : playerManager.getPlayerList()) { + if (!playerManager.isOperator(player.getGameProfile())) continue; + player.sendMessageToClient(FabricMonkeyLibText.of(updateMessage).getText(), false); } } } diff --git a/gradle.properties b/gradle.properties index af2a025..8c2a0ce 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,7 +7,7 @@ minecraft_version = 1.21.4 # MESH Lib, check at https://github.com/OffsetMods538/MESH-Lib meshlib_version = 1.0.5+1.21.4 # MonkeyLib538, check at https://github.com/OffsetMods538/MonkeyLib538 -monkeylib538_version = 3.0.0-7adde5c +monkeylib538_version = 3.0.0-6345c7b # Mod Properties mod_version = 5.0.0 diff --git a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformText.java b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformText.java index b6356e7..2bfbef4 100644 --- a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformText.java +++ b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformText.java @@ -9,20 +9,18 @@ public class PaperPlatformText implements PlatformText { @Override - public void sendUpdateMessage(MonkeyLibText[] updateMessage, boolean adminsOnly) { - for (final MonkeyLibText currentLine : updateMessage) { - final PlayerList players = MinecraftServer.getServer().getPlayerList(); - if (!adminsOnly) { - players.broadcastSystemMessage(Component.empty(), false); - // TODO: once I implement paper version of monke: players.broadcastSystemMessage(PaperMonkeyLibText.of(currentLine).getText(), false); - continue; - } + public void sendUpdateMessage(MonkeyLibText updateMessage, boolean adminsOnly) { + final PlayerList players = MinecraftServer.getServer().getPlayerList(); + if (!adminsOnly) { + players.broadcastSystemMessage(Component.empty(), false); + // TODO: once I implement paper version of monke: players.broadcastSystemMessage(PaperMonkeyLibText.of(updateMessage).getText(), false); + return; + } - for (final ServerPlayer player : players.players) { - if (!players.isOp(player.getGameProfile())) continue; - player.sendSystemMessage(Component.empty()); - // TODO: once I implement paper version of monke: player.sendSystemMessage(PaperMonkeyLibText.of(currentLine).getText()); - } + for (final ServerPlayer player : players.players) { + if (!players.isOp(player.getGameProfile())) continue; + player.sendSystemMessage(Component.empty()); + // TODO: once I implement paper version of monke: player.sendSystemMessage(PaperMonkeyLibText.of(updateMessage).getText()); } } From df5ef9ef6cf67ef8717f15b8d82863e83fbb737b Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Sat, 30 Aug 2025 18:37:50 +0300 Subject: [PATCH 29/52] fix config fixing --- .../config/ModConfig.java | 56 ++++++++++++++----- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java index ef35760..c2f58b4 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java @@ -211,24 +211,52 @@ public void trigger(final boolean updateSucceeded, final Map pla // 3 -> 4 final Marshaller marsh = jankson.getMarshaller(); - final JsonObject resourcePackProvider = (JsonObject) original.get("resourcePackProvider"); - assert resourcePackProvider != null; - - final String updateMessage = datafixGetAndRemove(marsh, String.class, resourcePackProvider, "updateMessage"); - if (updateMessage == null) { - original.put("updateMessage", JsonNull.INSTANCE); - return; + { + final JsonObject resourcePackProvider = (JsonObject) original.get("resourcePackProvider"); + assert resourcePackProvider != null; + + final String updateMessage = datafixGetAndRemove(marsh, String.class, resourcePackProvider, "updateMessage"); + if (updateMessage == null) { + original.put("updateMessage", JsonNull.INSTANCE); + return; + } + + final String updateMessageHoverMessage = datafixGetAndRemove(marsh, String.class, resourcePackProvider, "updateMessageHoverMessage"); + resourcePackProvider.remove("updateMessageHoverMessage"); + + final String[] newUpdateMessage = updateMessage.split("\\n"); + for (int i = 0; i < newUpdateMessage.length; i++) { + newUpdateMessage[i] = newUpdateMessage[i].replaceAll("\\{packUpdateCommand}", "&{hoverText,'Click to update pack','&{runCommand,'{packUpdateCommand}','[HERE]'}'}"); + if (updateMessageHoverMessage != null) + newUpdateMessage[i] = "&{hoverText,'%s','%s'}".formatted(updateMessageHoverMessage, newUpdateMessage[i]); + } + resourcePackProvider.put("updateMessage", new JsonArray(newUpdateMessage, marsh)); + + original.put("resourcePackProvider", resourcePackProvider); } + { + + final JsonObject dataPackProvider = (JsonObject) original.get("dataPackProvider"); + assert dataPackProvider != null; + + final String updateMessage = datafixGetAndRemove(marsh, String.class, dataPackProvider, "updateMessage"); + if (updateMessage == null) { + original.put("updateMessage", JsonNull.INSTANCE); + return; + } + + final String updateMessageHoverMessage = datafixGetAndRemove(marsh, String.class, dataPackProvider, "updateMessageHoverMessage"); + dataPackProvider.remove("updateMessageHoverMessage"); - final String updateMessageHoverMessage = datafixGetAndRemove(marsh, String.class, resourcePackProvider, "updateMessageHoverMessage"); - resourcePackProvider.remove("updateMessageHoverMessage"); + final String[] newUpdateMessage = updateMessage.split("\\n"); + for (int i = 0; i < newUpdateMessage.length; i++) { + newUpdateMessage[i] = newUpdateMessage[i].replaceAll("\\{packUpdateCommand}", "&{hoverText,'Click to update pack','&{runCommand,'{packUpdateCommand}','[HERE]'}'}"); + if (updateMessageHoverMessage != null) newUpdateMessage[i] = "&{hoverText,'%s','%s'}".formatted(updateMessageHoverMessage, newUpdateMessage[i]); + } + dataPackProvider.put("updateMessage", new JsonArray(newUpdateMessage, marsh)); - final String[] newUpdateMessage = updateMessage.split("\\\\n"); - for (int i = 0; i < newUpdateMessage.length; i++) { - newUpdateMessage[i] = newUpdateMessage[i].replaceAll("\\{packUpdateCommand}", "&{hoverText,'Click to update pack','&{runCommand,'{packUpdateCommand}','[HERE]'}'}"); - if (updateMessageHoverMessage != null) newUpdateMessage[i] = "&{hoverText,'%s','%s'}".formatted(updateMessageHoverMessage, newUpdateMessage[i]); + original.put("dataPackProvider", dataPackProvider); } - resourcePackProvider.put("updateMessage", new JsonArray(newUpdateMessage, marsh)); } }; } From 62fff023b408c2711572e965edd38e5f781868da Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Thu, 4 Sep 2025 16:45:35 +0300 Subject: [PATCH 30/52] fix update message test command only sending resource pack test, even when data is selected --- .../command/GitPackManagerCommand.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/command/GitPackManagerCommand.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/command/GitPackManagerCommand.java index 0605219..3bf655e 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/command/GitPackManagerCommand.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/command/GitPackManagerCommand.java @@ -89,15 +89,17 @@ private static int runTestUpdateMessage(CommandContext context, boolean } catch (GithubResourcepackManagerException e) { CommandAbstractionApi.sendError(context, "Failed to update repository, git related placeholders will not be replaced!"); CommandAbstractionApi.sendError(context, "Cause:\n%s\n", e); + LOGGER.error("Failed to update repository, git related placeholders will not be replaced!", e); } final Map placeholders = generatePlaceholders(gitHandler, isResource ? resourcePackHandler : null, UpdateType.COMMAND, isResource ? "resource" : "data", true); final MonkeyLibText[] text; try { - text = createUpdateMessage(config.get().resourcePackProvider.updateMessage, placeholders); + text = createUpdateMessage(isResource ? config.get().resourcePackProvider.updateMessage : config.get().dataPackProvider.updateMessage, placeholders); } catch (GithubResourcepackManagerException e) { CommandAbstractionApi.sendError(context, "Failed to create update message!"); CommandAbstractionApi.sendError(context, "Cause:\n%s\n", e); + LOGGER.error("Failed to create update message for %spack!", e, isResource ? "resource" : "data"); return 0; } From f5b888d6509b221677ca6b47c61a7b85b6bb2f2e Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Thu, 4 Sep 2025 21:20:27 +0300 Subject: [PATCH 31/52] fix fixing again --- .../config/ModConfig.java | 68 ++++++------------- 1 file changed, 22 insertions(+), 46 deletions(-) diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java index c2f58b4..a63b787 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java @@ -211,52 +211,8 @@ public void trigger(final boolean updateSucceeded, final Map pla // 3 -> 4 final Marshaller marsh = jankson.getMarshaller(); - { - final JsonObject resourcePackProvider = (JsonObject) original.get("resourcePackProvider"); - assert resourcePackProvider != null; - - final String updateMessage = datafixGetAndRemove(marsh, String.class, resourcePackProvider, "updateMessage"); - if (updateMessage == null) { - original.put("updateMessage", JsonNull.INSTANCE); - return; - } - - final String updateMessageHoverMessage = datafixGetAndRemove(marsh, String.class, resourcePackProvider, "updateMessageHoverMessage"); - resourcePackProvider.remove("updateMessageHoverMessage"); - - final String[] newUpdateMessage = updateMessage.split("\\n"); - for (int i = 0; i < newUpdateMessage.length; i++) { - newUpdateMessage[i] = newUpdateMessage[i].replaceAll("\\{packUpdateCommand}", "&{hoverText,'Click to update pack','&{runCommand,'{packUpdateCommand}','[HERE]'}'}"); - if (updateMessageHoverMessage != null) - newUpdateMessage[i] = "&{hoverText,'%s','%s'}".formatted(updateMessageHoverMessage, newUpdateMessage[i]); - } - resourcePackProvider.put("updateMessage", new JsonArray(newUpdateMessage, marsh)); - - original.put("resourcePackProvider", resourcePackProvider); - } - { - - final JsonObject dataPackProvider = (JsonObject) original.get("dataPackProvider"); - assert dataPackProvider != null; - - final String updateMessage = datafixGetAndRemove(marsh, String.class, dataPackProvider, "updateMessage"); - if (updateMessage == null) { - original.put("updateMessage", JsonNull.INSTANCE); - return; - } - - final String updateMessageHoverMessage = datafixGetAndRemove(marsh, String.class, dataPackProvider, "updateMessageHoverMessage"); - dataPackProvider.remove("updateMessageHoverMessage"); - - final String[] newUpdateMessage = updateMessage.split("\\n"); - for (int i = 0; i < newUpdateMessage.length; i++) { - newUpdateMessage[i] = newUpdateMessage[i].replaceAll("\\{packUpdateCommand}", "&{hoverText,'Click to update pack','&{runCommand,'{packUpdateCommand}','[HERE]'}'}"); - if (updateMessageHoverMessage != null) newUpdateMessage[i] = "&{hoverText,'%s','%s'}".formatted(updateMessageHoverMessage, newUpdateMessage[i]); - } - dataPackProvider.put("updateMessage", new JsonArray(newUpdateMessage, marsh)); - - original.put("dataPackProvider", dataPackProvider); - } + original.put("resourcePackProvider", datafix3to4UpdateMessage((JsonObject) original.get("resourcePackProvider"), marsh)); + original.put("dataPackProvider", datafix3to4UpdateMessage((JsonObject) original.get("dataPackProvider"), marsh)); } }; } @@ -286,6 +242,26 @@ private static T datafixGetAndRemove(final @NotNull Marshaller marsh, final return marsh.marshall(type, jsonValue); } + private static JsonObject datafix3to4UpdateMessage(final @NotNull JsonObject originalJson, final @NotNull Marshaller marsh) { + final String updateMessage = datafixGetAndRemove(marsh, String.class, originalJson, "updateMessage"); + if (updateMessage == null) { + originalJson.put("updateMessage", JsonNull.INSTANCE); + return originalJson; + } + + final String updateMessageHoverMessage = datafixGetAndRemove(marsh, String.class, originalJson, "updateMessageHoverMessage"); + + final String[] newUpdateMessage = updateMessage.split("\\n"); + for (int i = 0; i < newUpdateMessage.length; i++) { + newUpdateMessage[i] = newUpdateMessage[i].replaceAll("\\{packUpdateCommand}", "&{hoverText,'Click to update pack','&{runCommand,'{packUpdateCommand}','[HERE]'}'}"); + if (updateMessageHoverMessage != null) + newUpdateMessage[i] = "&{hoverText,'%s','%s'}".formatted(updateMessageHoverMessage, newUpdateMessage[i].replace("'", "\\'")); + } + originalJson.put("updateMessage", new JsonArray(newUpdateMessage, marsh)); + + return originalJson; + } + @Override public int getConfigVersion() { return 4; From 6bb77b114bec82ff04d19dcdf867d5fe87aeb495 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Thu, 4 Sep 2025 21:36:59 +0300 Subject: [PATCH 32/52] correct handling of admin message queue --- .../GithubResourcepackManager.java | 42 +++++++++++++++++-- .../platform/PlatformMain.java | 16 ++++++- .../platform/fabric/FabricPlatformMain.java | 42 +++++++------------ .../platform/paper/PaperPlatformMain.java | 33 ++++++++++++++- .../platform/paper/PaperPlugin.java | 22 +--------- 5 files changed, 100 insertions(+), 55 deletions(-) diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java index c160100..b33cb71 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java @@ -1,6 +1,8 @@ package top.offsetmonkey538.githubresourcepackmanager; import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import top.offsetmonkey538.githubresourcepackmanager.command.GitPackManagerCommand; import top.offsetmonkey538.githubresourcepackmanager.config.ModConfig; @@ -15,6 +17,7 @@ import top.offsetmonkey538.meshlib.api.HttpHandlerRegistry; import top.offsetmonkey538.monkeylib538.api.command.ConfigCommandApi; import top.offsetmonkey538.monkeylib538.api.log.MonkeyLibLogger; +import top.offsetmonkey538.monkeylib538.api.text.MonkeyLibStyle; import top.offsetmonkey538.monkeylib538.api.text.MonkeyLibText; import top.offsetmonkey538.monkeylib538.api.text.TextFormattingApi; import top.offsetmonkey538.offsetconfig538.api.config.ConfigHolder; @@ -48,14 +51,22 @@ private GithubResourcepackManager() { public static final Map STATIC_PLACEHOLDERS = Map.of("{packUpdateCommand}", "/gh-rp-manager request-pack"); - public static ConfigHolder config = ConfigManager.INSTANCE.init(ConfigHolder.create(ModConfig::new, LOGGER::error)); + private static final List MESSAGE_QUEUE = new ArrayList<>(); + + @SuppressWarnings("NotNullFieldNotInitialized") + public static @NotNull ConfigHolder config; public static ResourcePackHandler resourcePackHandler; private static boolean disabled; public static void initialize() { - PlatformMain.INSTANCE.registerLogToAdminListener(); + addLogToAdminListeners(); + PlatformMain.INSTANCE.registerSendMessageQueueOnAdminJoin(MESSAGE_QUEUE); + + // config should be initialized after the error listeners + config = ConfigManager.INSTANCE.init(ConfigHolder.create(ModConfig::new, LOGGER::error)); + GitPackManagerCommand.register(); ConfigCommandApi.registerConfigCommand( config, @@ -76,6 +87,24 @@ public static void initialize() { PlatformMain.INSTANCE.runOnServerStart(() -> updatePack(UpdateType.RESTART)); } + private static void addLogToAdminListeners() { + LOGGER.addListener(MonkeyLibLogger.LogLevel.ERROR, createLogToAdminListener(MonkeyLibLogger.LogLevel.ERROR, MonkeyLibStyle.Color.RED)); + LOGGER.addListener(MonkeyLibLogger.LogLevel.WARN, createLogToAdminListener(MonkeyLibLogger.LogLevel.WARN, MonkeyLibStyle.Color.YELLOW)); + } + + private static MonkeyLibLogger.LogListener createLogToAdminListener(MonkeyLibLogger.LogLevel logLevel, int textColor) { + return (message, error) -> { + final MonkeyLibText text = MonkeyLibText + .of("[%s] %s".formatted(MOD_ID, message)) + .applyStyle(style -> style.withColor(textColor)); + + if (error != null) text.applyStyle(style -> style.withShowText(MonkeyLibText.of(ExceptionUtils.getRootCauseMessage(error)))); + + PlatformMain.INSTANCE.sendMessageToAdmins(text); + MESSAGE_QUEUE.addLast(text); + }; + } + private static void createFolderStructure() throws GithubResourcepackManagerException { try { Files.createDirectories(RESOURCEPACK_OUTPUT_FOLDER); @@ -87,6 +116,11 @@ private static void createFolderStructure() throws GithubResourcepackManagerExce } public static void updatePack(final UpdateType updateType) { + if (updateType != UpdateType.RESTART) { + LOGGER.debug("Clearing admin message queue before updating after a restart..."); + MESSAGE_QUEUE.clear(); + } + if (disabled) { LOGGER.warn("Skipping pack updating because config was invalid!"); return; @@ -177,7 +211,7 @@ private static void updateResourcePack(final GitHandler gitHandler, final Update try { if (!failed) sendUpdateMessage(config.get().resourcePackProvider.updateMessage, wasUpdated, placeholders); } catch (GithubResourcepackManagerException e) { - LOGGER.error("Failed to send update message in chat!", e); + LOGGER.error("Failed to send update message for resource pack in chat!", e); } // Trigger webhooks @@ -227,7 +261,7 @@ private static void updateDataPack(final GitHandler gitHandler, final UpdateType try { sendUpdateMessage(config.get().dataPackProvider.updateMessage, true, placeholders, true); } catch (GithubResourcepackManagerException e) { - LOGGER.error("Failed to send update message in chat!", e); + LOGGER.error("Failed to send update message for datapack in chat!", e); } // Trigger webhooks diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformMain.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformMain.java index cdef7a5..b6ea4be 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformMain.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformMain.java @@ -1,6 +1,9 @@ package top.offsetmonkey538.githubresourcepackmanager.platform; +import top.offsetmonkey538.monkeylib538.api.text.MonkeyLibText; + import java.nio.file.Path; +import java.util.List; import static top.offsetmonkey538.githubresourcepackmanager.platform.ServiceLoader.load; @@ -32,7 +35,16 @@ public interface PlatformMain { void refreshDatapacks(); /** - * Registers log listeners for reporting errors to admins + * Sends provided message to currently online admins + * + * @param message the message to send + */ + void sendMessageToAdmins(final MonkeyLibText message); + + /** + * Sends messages contained in the provided list to admins when they join + * + * @param messageQueue list containing messages to send to admins when they join. Contents can be changed after calling this method. */ - void registerLogToAdminListener(); + void registerSendMessageQueueOnAdminJoin(final List messageQueue); } diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java index 501db61..1424619 100644 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java +++ b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java @@ -16,29 +16,24 @@ import top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformMain; import top.offsetmonkey538.monkeylib538.api.log.MonkeyLibLogger; +import top.offsetmonkey538.monkeylib538.api.text.MonkeyLibText; +import top.offsetmonkey538.monkeylib538.fabric.api.text.FabricMonkeyLibText; import java.nio.file.Path; import java.util.LinkedList; +import java.util.List; import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.MOD_ID; import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.LOGGER; public class FabricPlatformMain implements PlatformMain, DedicatedServerModInitializer { private static @Nullable MinecraftServer minecraftServer = null; - private static final LinkedList messageQueue = new LinkedList<>(); @Override public void onInitializeServer() { GithubResourcepackManager.initialize(); ServerLifecycleEvents.SERVER_STARTING.register(minecraftServer1 -> minecraftServer = minecraftServer1); - ServerPlayConnectionEvents.JOIN.register((serverPlayNetworkHandler, packetSender, minecraftServer1) -> { - if (!minecraftServer1.getPlayerManager().isOperator(serverPlayNetworkHandler.player.getGameProfile())) return; - - for (Text text : messageQueue) { - serverPlayNetworkHandler.player.sendMessage(text); - } - }); } public static @Nullable MinecraftServer getServer() { @@ -57,27 +52,20 @@ public void runOnServerStart(Runnable work) { } @Override - public void registerLogToAdminListener() { - LOGGER.addListener(MonkeyLibLogger.LogLevel.ERROR, (message, error) -> { - MutableText text = Text - .literal(String.format("[%s] %s", MOD_ID, message)) - .setStyle(Style.EMPTY.withColor(Formatting.RED)); - if (error != null) text = text.setStyle(Style.EMPTY.withColor(Formatting.RED).withHoverEvent( - new HoverEvent( - HoverEvent.Action.SHOW_TEXT, - Text.literal(ExceptionUtils.getRootCauseMessage(error)) - ) - )); + public void sendMessageToAdmins(MonkeyLibText message) { + if (getServer() == null) return; + for (final PlayerEntity player : getServer().getPlayerManager().getPlayerList()) { + if (!getServer().getPlayerManager().isOperator(player.getGameProfile())) continue; + player.sendMessage(FabricMonkeyLibText.of(message).getText(), false); + } + } - boolean sent = false; - if (getServer() != null) for (final PlayerEntity player : getServer().getPlayerManager().getPlayerList()) { - if (!getServer().getPlayerManager().isOperator(player.getGameProfile())) continue; - player.sendMessage(text, false); - sent = true; - } - if (sent) return; + @Override + public void registerSendMessageQueueOnAdminJoin(List messageQueue) { + ServerPlayConnectionEvents.JOIN.register((serverPlayNetworkHandler, packetSender, minecraftServer1) -> { + if (!minecraftServer1.getPlayerManager().isOperator(serverPlayNetworkHandler.player.getGameProfile())) return; - messageQueue.addLast(text); + for (MonkeyLibText text : messageQueue) serverPlayNetworkHandler.player.sendMessage(FabricMonkeyLibText.of(text).getText()); }); } diff --git a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java index 43195f7..7027c19 100644 --- a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java +++ b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java @@ -1,15 +1,23 @@ package top.offsetmonkey538.githubresourcepackmanager.platform.paper; +import net.kyori.adventure.text.ComponentLike; import net.minecraft.server.MinecraftServer; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.event.HoverEvent; import net.kyori.adventure.text.format.NamedTextColor; import org.apache.commons.lang.exception.ExceptionUtils; +import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformMain; import top.offsetmonkey538.monkeylib538.api.log.MonkeyLibLogger; +import top.offsetmonkey538.monkeylib538.api.text.MonkeyLibText; import java.nio.file.Path; +import java.util.List; import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.MOD_ID; import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.LOGGER; @@ -33,7 +41,30 @@ public void refreshDatapacks() { } @Override - public void registerLogToAdminListener() { + public void sendMessageToAdmins(MonkeyLibText message) { + for (final OfflinePlayer operator : plugin.getServer().getOperators()) { + if (operator.getPlayer() == null) continue; + // todo: once monkeylib has da paper: operator.getPlayer().sendMessage(PaperMonkeyLibText.of(text)); + } + } + + @Override + public void registerSendMessageQueueOnAdminJoin(List messageQueue) { + record AdminMessageQueueEventHandler(List messageQueue) implements Listener { + @EventHandler(priority = EventPriority.MONITOR) + public void onPlayerJoin(PlayerJoinEvent event) { + if (!event.getPlayer().isOp()) return; + + for (MonkeyLibText text : messageQueue) { + // todo: once monkeylib has paper support: event.getPlayer().sendMessage(PaperMonkeyLibText.of(text)); + } + } + } + + Bukkit.getPluginManager().registerEvents(new AdminMessageQueueEventHandler(messageQueue), getPlugin()); + } + + public void sendMessageToAdmins() { LOGGER.addListener(MonkeyLibLogger.LogLevel.ERROR, (message, error) -> { Component text = Component .text(String.format("[%s] %s", MOD_ID, message)) diff --git a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlugin.java b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlugin.java index 5dc7a85..55777cc 100644 --- a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlugin.java +++ b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlugin.java @@ -1,37 +1,17 @@ package top.offsetmonkey538.githubresourcepackmanager.platform.paper; -import net.kyori.adventure.text.Component; -import org.bukkit.Bukkit; -import org.bukkit.event.EventHandler; -import org.bukkit.event.EventPriority; -import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.plugin.java.JavaPlugin; import top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager; import top.offsetmonkey538.meshlib.MeshLib; -import java.util.LinkedList; - -public class PaperPlugin extends JavaPlugin implements Listener { - public final LinkedList messageQueue = new LinkedList<>(); +public class PaperPlugin extends JavaPlugin { @Override public void onEnable() { - Bukkit.getPluginManager().registerEvents(this, this); MeshLib.initialize(); PaperPlatformMain.setPlugin(this); GithubResourcepackManager.initialize(); } - - @EventHandler(priority = EventPriority.MONITOR) - public void onPlayerJoin(PlayerJoinEvent event) { - if (!event.getPlayer().isOp()) return; - - for (Component text : messageQueue) { - event.getPlayer().sendMessage(text); - } - messageQueue.clear(); - } } From aa88371a77191bf1903944b9c993a89d862240d7 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Thu, 4 Sep 2025 21:37:31 +0300 Subject: [PATCH 33/52] typo --- .../githubresourcepackmanager/handler/DataPackHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/DataPackHandler.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/DataPackHandler.java index 177a535..b209fa8 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/DataPackHandler.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/DataPackHandler.java @@ -103,7 +103,7 @@ private void writeStateFile(List files) throws IOException { private State readStateFile() throws IOException { if (Files.notExists(STATE_FILE)) { - LOGGER.warn("State file '%s' not found! No datapacks will be deleted!"); + LOGGER.warn("State file '%s' not found! No datapacks will be deleted!".formatted(STATE_FILE)); return new State(new String[]{}); } From 15e6fb5a5908fc40622ac9651157860913b8de06 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Thu, 4 Sep 2025 21:42:14 +0300 Subject: [PATCH 34/52] cleanup also the paper version probably builds now? still won't work though ofc --- .../config/ConfigHandler.java | 1 - .../handler/DataPackHandler.java | 1 - .../handler/GitHandler.java | 1 - .../handler/ResourcePackHandler.java | 1 - .../platform/PlatformServerProperties.java | 1 - .../platform/fabric/FabricPlatformMain.java | 9 -- .../platform/fabric/FabricPlatformText.java | 1 - .../platform/paper/PaperPlatformMain.java | 35 ----- .../platform/paper/PaperPlatformText.java | 143 ------------------ 9 files changed, 193 deletions(-) diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ConfigHandler.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ConfigHandler.java index e7b24fc..0b2fc06 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ConfigHandler.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ConfigHandler.java @@ -16,7 +16,6 @@ import java.util.List; import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.*; -import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.LOGGER; public final class ConfigHandler { private ConfigHandler() { diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/DataPackHandler.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/DataPackHandler.java index b209fa8..4b81956 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/DataPackHandler.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/DataPackHandler.java @@ -13,7 +13,6 @@ import java.util.stream.Stream; import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.*; -import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.LOGGER; public class DataPackHandler { private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/GitHandler.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/GitHandler.java index 018250b..fdac1e4 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/GitHandler.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/GitHandler.java @@ -25,7 +25,6 @@ import java.util.Optional; import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.*; -import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.LOGGER; public class GitHandler { diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/ResourcePackHandler.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/ResourcePackHandler.java index bf0e027..7c4ac35 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/ResourcePackHandler.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/handler/ResourcePackHandler.java @@ -18,7 +18,6 @@ import java.util.stream.Stream; import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.*; -import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.LOGGER; public class ResourcePackHandler { private Path outputPackPath; diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformServerProperties.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformServerProperties.java index 5621e7c..5685317 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformServerProperties.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformServerProperties.java @@ -9,7 +9,6 @@ import java.util.Map; import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.*; -import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.LOGGER; import static top.offsetmonkey538.githubresourcepackmanager.platform.ServiceLoader.load; public interface PlatformServerProperties { diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java index 1424619..4bea390 100644 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java +++ b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java @@ -6,25 +6,16 @@ import net.fabricmc.loader.api.FabricLoader; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.server.MinecraftServer; -import net.minecraft.text.HoverEvent; -import net.minecraft.text.MutableText; -import net.minecraft.text.Style; -import net.minecraft.text.Text; -import net.minecraft.util.Formatting; -import org.apache.commons.lang3.exception.ExceptionUtils; import org.jetbrains.annotations.Nullable; import top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformMain; -import top.offsetmonkey538.monkeylib538.api.log.MonkeyLibLogger; import top.offsetmonkey538.monkeylib538.api.text.MonkeyLibText; import top.offsetmonkey538.monkeylib538.fabric.api.text.FabricMonkeyLibText; import java.nio.file.Path; -import java.util.LinkedList; import java.util.List; import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.MOD_ID; -import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.LOGGER; public class FabricPlatformMain implements PlatformMain, DedicatedServerModInitializer { private static @Nullable MinecraftServer minecraftServer = null; diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java index a1e847b..f7335eb 100644 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java +++ b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java @@ -2,7 +2,6 @@ import net.minecraft.server.PlayerManager; import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.text.*; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformText; import top.offsetmonkey538.monkeylib538.api.text.MonkeyLibText; import top.offsetmonkey538.monkeylib538.fabric.api.text.FabricMonkeyLibText; diff --git a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java index 7027c19..91c7cea 100644 --- a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java +++ b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java @@ -1,11 +1,6 @@ package top.offsetmonkey538.githubresourcepackmanager.platform.paper; -import net.kyori.adventure.text.ComponentLike; import net.minecraft.server.MinecraftServer; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.event.HoverEvent; -import net.kyori.adventure.text.format.NamedTextColor; -import org.apache.commons.lang.exception.ExceptionUtils; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.event.EventHandler; @@ -13,15 +8,11 @@ import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerJoinEvent; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformMain; -import top.offsetmonkey538.monkeylib538.api.log.MonkeyLibLogger; import top.offsetmonkey538.monkeylib538.api.text.MonkeyLibText; import java.nio.file.Path; import java.util.List; -import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.MOD_ID; -import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.LOGGER; - public class PaperPlatformMain implements PlatformMain { private static PaperPlugin plugin; @@ -64,32 +55,6 @@ public void onPlayerJoin(PlayerJoinEvent event) { Bukkit.getPluginManager().registerEvents(new AdminMessageQueueEventHandler(messageQueue), getPlugin()); } - public void sendMessageToAdmins() { - LOGGER.addListener(MonkeyLibLogger.LogLevel.ERROR, (message, error) -> { - Component text = Component - .text(String.format("[%s] %s", MOD_ID, message)) - .color(NamedTextColor.RED); - - if (error != null) text = text.hoverEvent( - HoverEvent.showText( - Component.text(ExceptionUtils.getRootCauseMessage(error)) - ) - ); - - - boolean sent = false; - for (final OfflinePlayer operator : plugin.getServer().getOperators()) { - if (operator.getPlayer() == null) continue; - operator.getPlayer().sendMessage(text); - sent = true; - } - - if (sent) return; - - plugin.messageQueue.addLast(text); - }); - } - public static void setPlugin(PaperPlugin plugin) { PaperPlatformMain.plugin = plugin; } diff --git a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformText.java b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformText.java index 2bfbef4..50e3ab6 100644 --- a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformText.java +++ b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformText.java @@ -23,147 +23,4 @@ public void sendUpdateMessage(MonkeyLibText updateMessage, boolean adminsOnly) { // TODO: once I implement paper version of monke: player.sendSystemMessage(PaperMonkeyLibText.of(updateMessage).getText()); } } - - /* - public void sendUpdateMessage(final String message, @Nullable final String hoverMessage, Map placeholders, boolean adminsOnly) throws GithubResourcepackManagerException { - final String[] splitMessage = message.split("\n"); - - final HoverEvent hoverEvent; - try { - hoverEvent = hoverMessage == null ? null : new HoverEvent( - HoverEvent.Action.SHOW_TEXT, - getStyledText( - StringUtils.replacePlaceholders(hoverMessage, placeholders).replace("\\n", "\n") - ) - ); - } catch (Exception e) { - throw new GithubResourcepackManagerException("Failed to style update hover message!", e); - } - - for (int lineNumber = 0; lineNumber < splitMessage.length; lineNumber++) { - final String currentLineString = StringUtils.replacePlaceholders(splitMessage[lineNumber], placeholders).replace("\\n", "\n"); - final MutableComponent currentLine = Component.empty(); - try { - for (Component currentLineSibling : getStyledText(currentLineString).getSiblings()) { - final MutableComponent sibling = currentLineSibling.copy(); - - if (hoverEvent != null) sibling.setStyle(sibling.getStyle().withHoverEvent(hoverEvent)); - - final String siblingString = sibling.getString(); - if (!siblingString.contains("{packUpdateCommand}")) { - currentLine.append(sibling); - continue; - } - - final Style siblingStyle = sibling.getStyle(); - final String[] splitSibling = siblingString.split("\\{packUpdateCommand}"); - - if (splitSibling.length > 0) - currentLine.append(Component.literal(splitSibling[0]).setStyle(siblingStyle)); - - currentLine.append(Component.literal("[HERE]").setStyle(siblingStyle - .withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, Component.literal("Click to update pack"))) - .withClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, "/gh-rp-manager request-pack")) - )); - - if (splitSibling.length > 1) - currentLine.append(Component.literal(splitSibling[1]).setStyle(siblingStyle)); - } - } catch (Exception e) { - throw new GithubResourcepackManagerException("Failed to style update message at line number '%s'!", e, lineNumber); - } - - - final PlayerList players = MinecraftServer.getServer().getPlayerList(); - if (!adminsOnly) { - players.broadcastSystemMessage(currentLine, false); - continue; - } - - for (final ServerPlayer player : players.players) { - if (!players.isOp(player.getGameProfile())) continue; - player.sendSystemMessage(currentLine); - } - } - } - - - private static final Style DEFAULT_STYLE = Style.EMPTY.withItalic(false).withColor(ChatFormatting.WHITE); - - private static MutableComponent getStyledText(String text) throws Exception { - final MutableComponent result = Component.empty(); - Style style = DEFAULT_STYLE; - - boolean isFormattingCode = false; - boolean isEscaped = false; - char[] characters = text.toCharArray(); - for (int characterIndex = 0; characterIndex < characters.length; characterIndex++) { - char currentChar = characters[characterIndex]; - - if (isFormattingCode) { - // Hex color - if (currentChar == '#') { - if (characterIndex + 7 >= characters.length) - throw new Exception("Unfinished hex code starting at character number '" + characterIndex + "'!"); - - try { - style = style.withColor(TextColor.parseColor(text.substring(characterIndex, characterIndex + 7)).getOrThrow(Exception::new)); - } catch (Exception e) { - throw new Exception("Failed to parse hex color starting at character number '" + characterIndex + "'!", e); - } - - // Move pointer 6 characters ahead as we already read the whole hex code - characterIndex += 6; - isFormattingCode = false; - continue; - } - - style = getStyleForFormattingCode(currentChar, style); - - if (style == null) - throw new Exception("Invalid formatting code at character number '" + characterIndex + "'!"); - - isFormattingCode = false; - continue; - } - - if (!isEscaped) { - switch (currentChar) { - case '&': - isFormattingCode = true; - continue; - case '\\': - isEscaped = true; - continue; - } - } - isEscaped = false; - - - final List siblings = result.getSiblings(); - final int lastSiblingIndex = siblings.size() - 1; - final Component lastSibling = siblings.isEmpty() ? Component.empty() : siblings.get(lastSiblingIndex); - - // Check if the style of the last sibling is the same as the current one - if (!siblings.isEmpty() && lastSibling.getStyle().equals(style)) { - // If so, set the last sibling to itself plus the new character - siblings.set(lastSiblingIndex, Component.literal(lastSibling.getString() + currentChar).setStyle(style)); - } else { - // Otherwise, just append a new sibling to the result - result.append(Component.literal(String.valueOf(currentChar)).setStyle(style)); - } - } - - return result; - } - - private static Style getStyleForFormattingCode(char formattingCode, Style currentStyle) { - if (formattingCode == 'r') return DEFAULT_STYLE; - - final ChatFormatting formatting = ChatFormatting.getByCode(formattingCode); - if (formatting == null) return null; - - return currentStyle.applyFormat(formatting); - } - */ } From 57d723a196a2d53636c372645ff7414b5c71b2e9 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Sat, 6 Sep 2025 22:03:37 +0300 Subject: [PATCH 35/52] remove unused logLevel parameter for createLogToAdminListener --- .../GithubResourcepackManager.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java index b33cb71..e0440a8 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java @@ -88,11 +88,11 @@ public static void initialize() { } private static void addLogToAdminListeners() { - LOGGER.addListener(MonkeyLibLogger.LogLevel.ERROR, createLogToAdminListener(MonkeyLibLogger.LogLevel.ERROR, MonkeyLibStyle.Color.RED)); - LOGGER.addListener(MonkeyLibLogger.LogLevel.WARN, createLogToAdminListener(MonkeyLibLogger.LogLevel.WARN, MonkeyLibStyle.Color.YELLOW)); + LOGGER.addListener(MonkeyLibLogger.LogLevel.ERROR, createLogToAdminListener(MonkeyLibStyle.Color.RED)); + LOGGER.addListener(MonkeyLibLogger.LogLevel.WARN, createLogToAdminListener(MonkeyLibStyle.Color.YELLOW)); } - private static MonkeyLibLogger.LogListener createLogToAdminListener(MonkeyLibLogger.LogLevel logLevel, int textColor) { + private static MonkeyLibLogger.LogListener createLogToAdminListener(final int textColor) { return (message, error) -> { final MonkeyLibText text = MonkeyLibText .of("[%s] %s".formatted(MOD_ID, message)) From a7934bee809478ed58e68c1dfa3813059a1a8b1f Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Tue, 7 Oct 2025 20:20:17 +0300 Subject: [PATCH 36/52] neo? :3 I think it's possible that this could perhaps work? --- common/build.gradle | 17 ++- common/gradle.properties | 1 - .../GithubResourcepackManager.java | 27 ++++- .../networking/MainHttpHandler.java | 6 +- .../resources/META-INF/neoforge.mods.toml | 14 +++ fabric/build.gradle | 4 +- ...sourcepackmanager.platform.PlatformLogging | 1 - gradle.properties | 6 +- neoforge/build.gradle | 100 ++++++++++++++++++ neoforge/gradle.properties | 12 +++ .../mixin/DedicatedServerAccessor.java | 13 +++ .../DedicatedServerSettingsAccessor.java | 20 ++++ .../mixin/SettingsAccessor.java | 14 +++ .../neoforge/NeoforgePlatformCommand.java | 42 ++++++++ .../neoforge/NeoforgePlatformInitializer.java | 22 ++++ .../neoforge/NeoforgePlatformMain.java | 66 ++++++++++++ .../NeoforgePlatformServerProperties.java | 57 ++++++++++ .../neoforge/NeoforgePlatformText.java | 25 +++++ .../resources/META-INF/neoforge.mods.toml | 39 +++++++ ...sourcepackmanager.platform.PlatformCommand | 1 + ...bresourcepackmanager.platform.PlatformMain | 1 + ...kmanager.platform.PlatformServerProperties | 1 + ...bresourcepackmanager.platform.PlatformText | 1 + .../github-resourcepack-manager/icon.png | Bin 0 -> 1761 bytes .../github-resourcepack-manager.mixins.json | 13 +++ paper/build.gradle | 4 +- .../platform/paper/PaperPlugin.java | 3 - settings.gradle | 1 + 28 files changed, 497 insertions(+), 14 deletions(-) create mode 100644 common/src/main/resources/META-INF/neoforge.mods.toml delete mode 100644 fabric/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging create mode 100644 neoforge/build.gradle create mode 100644 neoforge/gradle.properties create mode 100644 neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/mixin/DedicatedServerAccessor.java create mode 100644 neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/mixin/DedicatedServerSettingsAccessor.java create mode 100644 neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/mixin/SettingsAccessor.java create mode 100644 neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/neoforge/NeoforgePlatformCommand.java create mode 100644 neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/neoforge/NeoforgePlatformInitializer.java create mode 100644 neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/neoforge/NeoforgePlatformMain.java create mode 100644 neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/neoforge/NeoforgePlatformServerProperties.java create mode 100644 neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/neoforge/NeoforgePlatformText.java create mode 100644 neoforge/src/main/resources/META-INF/neoforge.mods.toml create mode 100644 neoforge/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformCommand create mode 100644 neoforge/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformMain create mode 100644 neoforge/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformServerProperties create mode 100644 neoforge/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformText create mode 100644 neoforge/src/main/resources/assets/github-resourcepack-manager/icon.png create mode 100644 neoforge/src/main/resources/github-resourcepack-manager.mixins.json diff --git a/common/build.gradle b/common/build.gradle index da64765..3f52771 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -15,13 +15,16 @@ repositories { } dependencies { - implementation "org.eclipse.jgit:org.eclipse.jgit:${project.jgit_version}" + implementation "org.eclipse.jgit:org.eclipse.jgit:${rootProject.jgit_version}" shadow compileOnlyApi("top.offsetmonkey538.monkeylib538:monkeylib538-common:${project.monkeylib538_version}") - shadow compileOnly("top.offsetmonkey538.meshlib:mesh-lib-api:${project.meshlib_version}") + shadow compileOnly("top.offsetmonkey538.meshlib:mesh-lib-api:${project.meshlib_version}") { + exclude(group: "top.offsetmonkey538.monkeylib538") + } // should be bundled with minecraft + shadow compileOnly("com.mojang:brigadier:1.3.10") //todo put in properties shadow compileOnly("com.google.guava:guava:${project.guava_version}") shadow compileOnly("commons-io:commons-io:${project.commonsio_version}") shadow compileOnly("org.apache.commons:commons-lang3:${project.commonslang_version}") @@ -31,3 +34,13 @@ dependencies { shadow compileOnly("org.jetbrains:annotations:24.0.0") } tasks.build.dependsOn(shadowJar) + +processResources { + final Map properties = [ + "modVersion": project.version + ] + inputs.properties(properties) + filesMatching("META-INF/neoforge.mods.toml") { + expand(properties) + } +} diff --git a/common/gradle.properties b/common/gradle.properties index 9cd2a8d..f6977dd 100644 --- a/common/gradle.properties +++ b/common/gradle.properties @@ -1,4 +1,3 @@ -jgit_version = 6.9.0.202403050737-r guava_version = 33.4.0-jre commonsio_version = 2.18.0 commonslang_version = 3.18.0 diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java index e0440a8..e0fdc6d 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java @@ -1,5 +1,7 @@ package top.offsetmonkey538.githubresourcepackmanager; +import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.HttpHeaderNames; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.jetbrains.annotations.NotNull; @@ -14,7 +16,10 @@ import top.offsetmonkey538.githubresourcepackmanager.networking.MainHttpHandler; import top.offsetmonkey538.githubresourcepackmanager.platform.*; import top.offsetmonkey538.githubresourcepackmanager.utils.StringUtils; -import top.offsetmonkey538.meshlib.api.HttpHandlerRegistry; +import top.offsetmonkey538.meshlib.api.router.HttpRouter; +import top.offsetmonkey538.meshlib.api.router.HttpRouterRegistry; +import top.offsetmonkey538.meshlib.api.router.rule.HttpRule; +import top.offsetmonkey538.meshlib.impl.router.rule.DomainHttpRule; import top.offsetmonkey538.monkeylib538.api.command.ConfigCommandApi; import top.offsetmonkey538.monkeylib538.api.log.MonkeyLibLogger; import top.offsetmonkey538.monkeylib538.api.text.MonkeyLibStyle; @@ -82,7 +87,25 @@ public static void initialize() { LOGGER.error("Failed to create folder structure!", e); } - HttpHandlerRegistry.INSTANCE.register(MOD_URI, new MainHttpHandler()); + HttpRouterRegistry.INSTANCE.register(MOD_URI, new HttpRouter( + new HttpRule<>() { + @Override + public String getType() { + return "2hartqwf"; + } + + @Override + public Object getData() { + return new Object(); + } + + @Override + public boolean matches(FullHttpRequest httpRequest) { + return MOD_URI.equals(httpRequest.uri().split("/")[1]); + } + }, + new MainHttpHandler(new Object()) + )); PlatformMain.INSTANCE.runOnServerStart(() -> updatePack(UpdateType.RESTART)); } diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/MainHttpHandler.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/MainHttpHandler.java index 4d1b182..6dbb345 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/MainHttpHandler.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/MainHttpHandler.java @@ -9,7 +9,11 @@ import static io.netty.handler.codec.http.HttpResponseStatus.*; import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.LOGGER; -public class MainHttpHandler implements HttpHandler { +public class MainHttpHandler extends HttpHandler { + + public MainHttpHandler(Object data) { + super(data); + } @Override public void handleRequest(@NotNull ChannelHandlerContext ctx, @NotNull FullHttpRequest request) throws Exception { diff --git a/common/src/main/resources/META-INF/neoforge.mods.toml b/common/src/main/resources/META-INF/neoforge.mods.toml new file mode 100644 index 0000000..0a5b461 --- /dev/null +++ b/common/src/main/resources/META-INF/neoforge.mods.toml @@ -0,0 +1,14 @@ +modLoader = "javafml" +loaderVersion = "[1,)" +license="MIT" + +issueTrackerURL="https://github.com/OffsetMods538/Github-Resourcepack-Manager/issues" + + +[[mods]] +modId="git_pack_manager_common" +displayName="Git Pack Manager Common" +version="${modVersion}" +displayURL="https://modrinth.com/mod/github-resourcepack-manager" +authors="OffsetMonkey538" +description="Modify server resourcepack from GitHub" diff --git a/fabric/build.gradle b/fabric/build.gradle index 52d5ead..f0f93aa 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -99,7 +99,9 @@ dependencies { modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fapi_version}" - include runtimeOnly("top.offsetmonkey538.meshlib:mesh-lib-fabric:${rootProject.meshlib_version}") + runtimeOnly("top.offsetmonkey538.meshlib:mesh-lib-fabric:${rootProject.meshlib_version}+1.21.4") { + exclude(group: "top.offsetmonkey538.monkeylib538") + } include project(path: ":common", configuration: "shadow") common project(":common") diff --git a/fabric/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging b/fabric/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging deleted file mode 100644 index 7695764..0000000 --- a/fabric/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformLogging +++ /dev/null @@ -1 +0,0 @@ -top.offsetmonkey538.githubresourcepackmanager.platform.fabric.FabricPlatformLogging \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 8c2a0ce..7c78e61 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,9 +5,11 @@ org.gradle.parallel = true minecraft_version = 1.21.4 # MESH Lib, check at https://github.com/OffsetMods538/MESH-Lib -meshlib_version = 1.0.5+1.21.4 +meshlib_version = 1.0.5-8e52b9d # MonkeyLib538, check at https://github.com/OffsetMods538/MonkeyLib538 -monkeylib538_version = 3.0.0-6345c7b +monkeylib538_version = 3.0.0-66c29d2 +jgit_version = 6.9.0.202403050737-r + # Mod Properties mod_version = 5.0.0 diff --git a/neoforge/build.gradle b/neoforge/build.gradle new file mode 100644 index 0000000..b3f6f3d --- /dev/null +++ b/neoforge/build.gradle @@ -0,0 +1,100 @@ +import dex.plugins.outlet.v2.util.ReleaseType + +plugins { + id 'net.neoforged.moddev' version '2.0.112' + id 'io.github.dexman545.outlet' version '1.6.1' +} + +outlet { + mcVersionRange = rootProject.supported_minecraft_versions + allowedReleaseTypes = Set.of(ReleaseType.RELEASE) +} + +neoForge { + version = project.neoforge_version + + parchment { + mappingsVersion = project.parchment_mappings_version + minecraftVersion = project.minecraft_version + } + + mods { + "${project.name}" { + sourceSet(sourceSets.main) + } + } + + runs { + server { + server() + } + + configureEach { + systemProperty "forge.logging.markers", "REGISTRIES" + } + } +} + +repositories { + mavenCentral() + mavenLocal() + maven { + name = "DevAuth" + url = "https://pkgs.dev.azure.com/djtheredstoner/DevAuth/_packaging/public/maven/v1" + content { + includeGroup "me.djtheredstoner" + } + } +} + +configurations { + common { + canBeResolved = true + canBeConsumed = false + } + api.extendsFrom common + + runtimeClasspath.extendsFrom localRuntime +} + +dependencies { + localRuntime "me.djtheredstoner:DevAuth-neoforge:${project.devauth_version}" + + runtimeOnly "top.offsetmonkey538.monkeylib538:monkeylib538-neoforge-1.20.6:${rootProject.monkeylib538_version}+1.20.6" + compileOnly "top.offsetmonkey538.monkeylib538:monkeylib538-neoforge:${rootProject.monkeylib538_version}" + + + runtimeOnly("top.offsetmonkey538.meshlib:mesh-lib-neoforge:${rootProject.meshlib_version}+1.21.4") { + exclude(group: "top.offsetmonkey538.monkeylib538") + } + + jarJar(api(project(":common"))) + jarJar(additionalRuntimeClasspath("org.eclipse.jgit:org.eclipse.jgit:${rootProject.jgit_version}")) +} + +processResources { + final Map properties = Map.of( + "modVersion", project.mod_version, + "supportedMinecraftVersions", project.minecraft_version_range, + "monkeylib538Version", project.monkeylib538_version, + "meshLibVersion", project.meshlib_version + ) + + inputs.properties(properties) + + filesMatching("META-INF/neoforge.mods.toml") { + expand(properties) + } + + exclude ".cache/**" +} + +modrinth { + loaders = ["neoforge"] + //todo idk:_uploadFile = remapJar.archiveFile + + dependencies { + required.project "monkeylib538" + // todo idk: required.project "fabric-api" + } +} \ No newline at end of file diff --git a/neoforge/gradle.properties b/neoforge/gradle.properties new file mode 100644 index 0000000..406e37f --- /dev/null +++ b/neoforge/gradle.properties @@ -0,0 +1,12 @@ +# Minecraft version, check at https://fabricmc.net/develop +minecraft_version = 1.21.4 +neoforge_version = 21.4.154 +parchment_mappings_version = 2025.03.23 + +# Dependencies +## DevAuth, check at https://github.com/DJtheRedstoner/DevAuth +devauth_version = 1.2.1 + +nameSuffix = neoforge + +minecraft_version_range=[1.20.6,) diff --git a/neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/mixin/DedicatedServerAccessor.java b/neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/mixin/DedicatedServerAccessor.java new file mode 100644 index 0000000..41d4c9a --- /dev/null +++ b/neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/mixin/DedicatedServerAccessor.java @@ -0,0 +1,13 @@ +package top.offsetmonkey538.githubresourcepackmanager.mixin; + +import net.minecraft.server.dedicated.DedicatedServer; +import net.minecraft.server.dedicated.DedicatedServerSettings; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(DedicatedServer.class) +public interface DedicatedServerAccessor { + + @Accessor + DedicatedServerSettings getSettings(); +} diff --git a/neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/mixin/DedicatedServerSettingsAccessor.java b/neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/mixin/DedicatedServerSettingsAccessor.java new file mode 100644 index 0000000..66638ba --- /dev/null +++ b/neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/mixin/DedicatedServerSettingsAccessor.java @@ -0,0 +1,20 @@ +package top.offsetmonkey538.githubresourcepackmanager.mixin; + +import net.minecraft.server.dedicated.DedicatedServerProperties; +import net.minecraft.server.dedicated.DedicatedServerSettings; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.nio.file.Path; + +@Mixin(DedicatedServerSettings.class) +public interface DedicatedServerSettingsAccessor { + + @Accessor + Path getSource(); + + @Mutable + @Accessor + void setProperties(DedicatedServerProperties properties); +} diff --git a/neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/mixin/SettingsAccessor.java b/neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/mixin/SettingsAccessor.java new file mode 100644 index 0000000..565a199 --- /dev/null +++ b/neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/mixin/SettingsAccessor.java @@ -0,0 +1,14 @@ +package top.offsetmonkey538.githubresourcepackmanager.mixin; + +import net.minecraft.server.dedicated.Settings; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Properties; + +@Mixin(Settings.class) +public interface SettingsAccessor { + + @Accessor + Properties getProperties(); +} diff --git a/neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/neoforge/NeoforgePlatformCommand.java b/neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/neoforge/NeoforgePlatformCommand.java new file mode 100644 index 0000000..a41ec2a --- /dev/null +++ b/neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/neoforge/NeoforgePlatformCommand.java @@ -0,0 +1,42 @@ +package top.offsetmonkey538.githubresourcepackmanager.platform.neoforge; + +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.network.chat.Component; +import net.minecraft.network.protocol.common.ClientboundResourcePackPushPacket; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerPlayer; +import org.jetbrains.annotations.NotNull; +import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformCommand; +import top.offsetmonkey538.monkeylib538.neoforge.api.command.NeoforgeCommandAbstractionApi; + +import java.util.Optional; + +import static com.mojang.brigadier.Command.SINGLE_SUCCESS; + +public class NeoforgePlatformCommand implements PlatformCommand { + @Override + public int executeRequestPackCommand(@NotNull CommandContext ctx) throws CommandSyntaxException { + final CommandSourceStack source = NeoforgeCommandAbstractionApi.get(ctx); + final ServerPlayer player = source.getPlayerOrException(); + final MinecraftServer.ServerResourcePackInfo resourcePackProperties = source.getServer().getServerResourcePack().orElse(null); + if (resourcePackProperties == null) { + source.sendSuccess(() -> Component.literal("Failed to send pack update packet to client!"), true); + return 0; + } + + player.connection.send( + new ClientboundResourcePackPushPacket( + resourcePackProperties.id(), + resourcePackProperties.url(), + resourcePackProperties.hash(), + resourcePackProperties.isRequired(), + Optional.ofNullable(resourcePackProperties.prompt()) + ), + null + ); + + return SINGLE_SUCCESS; + } +} diff --git a/neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/neoforge/NeoforgePlatformInitializer.java b/neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/neoforge/NeoforgePlatformInitializer.java new file mode 100644 index 0000000..0d9fd2a --- /dev/null +++ b/neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/neoforge/NeoforgePlatformInitializer.java @@ -0,0 +1,22 @@ +package top.offsetmonkey538.githubresourcepackmanager.platform.neoforge; + +import net.neoforged.api.distmarker.Dist; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.fml.ModContainer; +import net.neoforged.fml.common.Mod; +import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.neoforge.event.server.ServerStartingEvent; +import top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager; + +@Mod( + value = "git_pack_manager", + dist = Dist.DEDICATED_SERVER +) +public class NeoforgePlatformInitializer { + + public NeoforgePlatformInitializer(IEventBus modEventBus, ModContainer modContainer) { + GithubResourcepackManager.initialize(); + + NeoForge.EVENT_BUS.addListener(ServerStartingEvent.class, serverStartingEvent -> NeoforgePlatformMain.setServer(serverStartingEvent.getServer())); + } +} diff --git a/neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/neoforge/NeoforgePlatformMain.java b/neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/neoforge/NeoforgePlatformMain.java new file mode 100644 index 0000000..69024c2 --- /dev/null +++ b/neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/neoforge/NeoforgePlatformMain.java @@ -0,0 +1,66 @@ +package top.offsetmonkey538.githubresourcepackmanager.platform.neoforge; + +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.entity.player.Player; +import net.neoforged.fml.loading.FMLPaths; +import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.neoforge.event.entity.EntityJoinLevelEvent; +import net.neoforged.neoforge.event.entity.player.PlayerEvent; +import net.neoforged.neoforge.event.server.ServerStartedEvent; +import net.neoforged.neoforge.event.server.ServerStartingEvent; +import org.jetbrains.annotations.Nullable; +import top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager; +import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformMain; +import top.offsetmonkey538.monkeylib538.api.text.MonkeyLibText; +import top.offsetmonkey538.monkeylib538.neoforge.api.text.NeoforgeMonkeyLibText; + +import java.nio.file.Path; +import java.util.List; + +import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.MOD_ID; + +public class NeoforgePlatformMain implements PlatformMain { + private static @Nullable MinecraftServer minecraftServer = null; + + public static @Nullable MinecraftServer getServer() { + return minecraftServer; + } + + public static void setServer(MinecraftServer server) { + minecraftServer = server; + } + + + @Override + public Path getConfigDir() { + return FMLPaths.CONFIGDIR.get().resolve(MOD_ID); + } + + @Override + public void runOnServerStart(Runnable work) { + NeoForge.EVENT_BUS.addListener(ServerStartedEvent.class, serverStartedEvent -> work.run()); + } + + @Override + public void sendMessageToAdmins(MonkeyLibText message) { + if (getServer() == null) return; + for (final Player player : getServer().getPlayerList().getPlayers()) { + if (!getServer().getPlayerList().isOp(player.getGameProfile())) continue; + player.displayClientMessage(NeoforgeMonkeyLibText.of(message).getText(), false); + } + } + + @Override + public void registerSendMessageQueueOnAdminJoin(List messageQueue) { + NeoForge.EVENT_BUS.addListener(PlayerEvent.PlayerLoggedInEvent.class, playerLoggedInEvent -> { + if (!playerLoggedInEvent.getEntity().getServer().getPlayerList().isOp(playerLoggedInEvent.getEntity().getGameProfile())) return; + + for (MonkeyLibText text : messageQueue) playerLoggedInEvent.getEntity().displayClientMessage(NeoforgeMonkeyLibText.of(text).getText(), false); + }); + } + + @Override + public void refreshDatapacks() { + getServer().getPackRepository().reload(); + } +} diff --git a/neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/neoforge/NeoforgePlatformServerProperties.java b/neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/neoforge/NeoforgePlatformServerProperties.java new file mode 100644 index 0000000..73d86a6 --- /dev/null +++ b/neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/neoforge/NeoforgePlatformServerProperties.java @@ -0,0 +1,57 @@ +package top.offsetmonkey538.githubresourcepackmanager.platform.neoforge; + +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.dedicated.DedicatedServerProperties; +import net.minecraft.server.dedicated.DedicatedServerSettings; +import net.minecraft.world.level.storage.LevelResource; +import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; +import top.offsetmonkey538.githubresourcepackmanager.mixin.SettingsAccessor; +import top.offsetmonkey538.githubresourcepackmanager.mixin.DedicatedServerAccessor; +import top.offsetmonkey538.githubresourcepackmanager.mixin.DedicatedServerSettingsAccessor; +import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformServerProperties; + +import java.nio.file.Path; +import java.util.Map; +import java.util.Optional; +import java.util.Properties; + +public class NeoforgePlatformServerProperties implements PlatformServerProperties { + + @Override + public String getResourcePackUrl() { + final Optional resourcePackProperties = NeoforgePlatformMain.getServer().getServerResourcePack(); + + return resourcePackProperties.map(MinecraftServer.ServerResourcePackInfo::url).orElse(null); + } + + @Override + public String getServerPort() { + return String.valueOf(NeoforgePlatformMain.getServer().getPort()); + } + + @Override + public Path getDatapacksDir() { + return NeoforgePlatformMain.getServer().getWorldPath(LevelResource.DATAPACK_DIR); + } + + @Override + public void setProperties(Map properties) { + final DedicatedServerSettings propertiesLoader = ((DedicatedServerAccessor) NeoforgePlatformMain.getServer()).getSettings(); + + propertiesLoader.update(propertiesHandler -> { + final Properties serverProperties = ((SettingsAccessor) propertiesHandler).getProperties(); + + properties.forEach(serverProperties::setProperty); + + return propertiesHandler; + }); + } + + @Override + public void reload() throws GithubResourcepackManagerException { + final DedicatedServerSettings propertiesLoader = ((DedicatedServerAccessor) NeoforgePlatformMain.getServer()).getSettings(); + final DedicatedServerSettingsAccessor propertiesLoaderAccess = (DedicatedServerSettingsAccessor) propertiesLoader; + + propertiesLoaderAccess.setProperties(DedicatedServerProperties.fromFile(propertiesLoaderAccess.getSource())); + } +} diff --git a/neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/neoforge/NeoforgePlatformText.java b/neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/neoforge/NeoforgePlatformText.java new file mode 100644 index 0000000..a564e4f --- /dev/null +++ b/neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/neoforge/NeoforgePlatformText.java @@ -0,0 +1,25 @@ +package top.offsetmonkey538.githubresourcepackmanager.platform.neoforge; + +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.players.PlayerList; +import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformText; +import top.offsetmonkey538.monkeylib538.api.text.MonkeyLibText; +import top.offsetmonkey538.monkeylib538.neoforge.api.text.NeoforgeMonkeyLibText; + +public class NeoforgePlatformText implements PlatformText { + @Override + public void sendUpdateMessage(MonkeyLibText updateMessage, boolean adminsOnly) { + assert NeoforgePlatformMain.getServer() != null : "Literally HOW?? Why are we trying to send the update message when the server hasn't started yet!??!?!?"; + final PlayerList playerManager = NeoforgePlatformMain.getServer().getPlayerList(); + + if (!adminsOnly) { + playerManager.broadcastSystemMessage(NeoforgeMonkeyLibText.of(updateMessage).getText(), false); + return; + } + + for (final ServerPlayer player : playerManager.getPlayers()) { + if (!playerManager.isOp(player.getGameProfile())) continue; + player.sendSystemMessage(NeoforgeMonkeyLibText.of(updateMessage).getText(), false); + } + } +} diff --git a/neoforge/src/main/resources/META-INF/neoforge.mods.toml b/neoforge/src/main/resources/META-INF/neoforge.mods.toml new file mode 100644 index 0000000..ea42043 --- /dev/null +++ b/neoforge/src/main/resources/META-INF/neoforge.mods.toml @@ -0,0 +1,39 @@ +modLoader = "javafml" +loaderVersion = "[1,)" +license="MIT" + +issueTrackerURL="https://github.com/OffsetMods538/Github-Resourcepack-manager" + + +[[mods]] +modId="git_pack_manager" +displayName="Git Pack Manager" +version="${modVersion}" +displayURL="https://modrinth.com/mod/github-resourcepack-manager" +authors="OffsetMonkey538" +description="Modify server resourcepack from GitHub" + +[[mixins]] +config="github-resourcepack-manager.mixins.json" + + +[[dependencies.git_pack_manager]] +modId="minecraft" +type="required" +versionRange="${supportedMinecraftVersions}" +ordering="NONE" +side="BOTH" + +[[dependencies.git_pack_manager]] +modId="monkeylib538_common_neoforge" +type="required" +versionRange="[${monkeylib538Version},)" +ordering="NONE" +side="BOTH" + +[[dependencies.git_pack_manager]] +modId="mesh_lib_common" +type="required" +versionRange="[${meshLibVersion},)" +ordering="NONE" +side="BOTH" diff --git a/neoforge/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformCommand b/neoforge/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformCommand new file mode 100644 index 0000000..e14ad3a --- /dev/null +++ b/neoforge/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformCommand @@ -0,0 +1 @@ +top.offsetmonkey538.githubresourcepackmanager.platform.neoforge.NeoforgePlatformCommand \ No newline at end of file diff --git a/neoforge/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformMain b/neoforge/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformMain new file mode 100644 index 0000000..9d0e114 --- /dev/null +++ b/neoforge/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformMain @@ -0,0 +1 @@ +top.offsetmonkey538.githubresourcepackmanager.platform.neoforge.NeoforgePlatformMain \ No newline at end of file diff --git a/neoforge/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformServerProperties b/neoforge/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformServerProperties new file mode 100644 index 0000000..425ca86 --- /dev/null +++ b/neoforge/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformServerProperties @@ -0,0 +1 @@ +top.offsetmonkey538.githubresourcepackmanager.platform.neoforge.NeoforgePlatformServerProperties \ No newline at end of file diff --git a/neoforge/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformText b/neoforge/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformText new file mode 100644 index 0000000..712b8d3 --- /dev/null +++ b/neoforge/src/main/resources/META-INF/services/top.offsetmonkey538.githubresourcepackmanager.platform.PlatformText @@ -0,0 +1 @@ +top.offsetmonkey538.githubresourcepackmanager.platform.neoforge.NeoforgePlatformText \ No newline at end of file diff --git a/neoforge/src/main/resources/assets/github-resourcepack-manager/icon.png b/neoforge/src/main/resources/assets/github-resourcepack-manager/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a6544fae6f35cbb6b9f002277050d34f8bfaeba3 GIT binary patch literal 1761 zcmV<71|Io|P)Px*n@L1LRCt{2oxhG8MG(g8EGK}Il`b<#PLOeg99Rfm#4kZ+C&%K*USERaL*fY> zAd&F~NQi*&fDq%9y})t;(h(f9o|)^JuI{ets_L$vbkgnK%xvF$UsqTEnck!CzW?zO zoNl=xT>wWGH$dmy1oV<9-T?}V|BaR#ZBRU2*20tVQz}E8NxItK~QQQc8 zWEp^4xTWj65$GW`0JT7F1b$EkkQO5J`ECLlOmqWJUOc@fI8~zAJJAR*ztPe)&!Yh? zs|L^}{us?=vjn;c&~}4EO@cQI=`0#xd^0iPO9&7umzy9B@+45g1Rf3Y3FZK)wSRuj zn3_!oRf4$LZ6~ky+g)~Qx- z2u(PhK6(=Uoc(qe6?yo33r)i$2Pmoi)U}+_5V`*srnXx*fm#F1DK$jN{}wZXWfL57 zfYP>~JKLA++U>WyRgo>{1tung#^joS!k}wLz~l`YlgJwYJefZy(t|vyCYZmk$pq#i zz<^d0SYu*8xIzX7l}E%oNs5MvO%)_OmRYibc6AR`GJZ}}=ot(uNq>7z;1 z6xe$K)P~Uz;;o7uVs(QEDyT%$Abx8U%>~qI3M?ge);JhI;|+G<`3XjVlrCP)XYoqV zPzH^#JsOtsux842OYw*@Wzq{s$=K(|dJT*7>*nO}`~Cf?U`gJw*bT(Y`e6)XVVbxG zWAW$jt(hTo-k4G&k?j<^Mv3^;pD)n_%tgGvKZW-`{~>zbPrviWgF^Y@pi535KU(VF-G#FIg_BSP#6rCLah`BT6$8(1=c6e8ss ztxouGQRTUSF)c0gswsY~_UD9u0D<)Yt!Cw13=2KR^Zgj!ynF_?9)2A?&KHDa5~}^7 zRXHKB$s+c8C6F1QrEk;ZW-M&T45`8|g?~&G2(AfCrKfM(9*{`>Le0Qh+mC4ie498! z%-8zmCVpdB4AAnHD$?vPU&Ha)MO1uD?FV3t0Ii!qYMIx^-+n-PJrV=tex0gyYMJ2Q z?a|=}%bS^&#hC0rG~T#;En`K2Ga

Mg(lcX@NEIv4|`Q8$`kg!6g>; z9EDXx*949QXr)$aLET7TjM`tM;I;n>b*q9?nun6>J6EVPz(9^rG1K%SJ~sRVC~5*q z=|%kWS8rGUo+3U#BL?sUG9U8>s5S6ofz;X$(0~DA^df#PrVY?z=%*O{8)Mc09{Q)` zgnt0V-M|uB7Ctlqlq)`fz-%A|OSQkY@REx{1{0Ks4`46?XlnN#Jbq4Qf(MVElUyei za|T#KD(p)MM}@g0FeG%#Z9X?9a{z}9a}mIy#SPFoZh+2l19XlXpmW>+o#O`R95+Dc zxB)uH4bVAmfX;CPbdDRKbKC%(;|Ay)H$dmO0XpYDedHh)PcoBZ00000NkvXXu0mjf DiGDA; literal 0 HcmV?d00001 diff --git a/neoforge/src/main/resources/github-resourcepack-manager.mixins.json b/neoforge/src/main/resources/github-resourcepack-manager.mixins.json new file mode 100644 index 0000000..2262de0 --- /dev/null +++ b/neoforge/src/main/resources/github-resourcepack-manager.mixins.json @@ -0,0 +1,13 @@ +{ + "required": true, + "package": "top.offsetmonkey538.githubresourcepackmanager.mixin", + "compatibilityLevel": "JAVA_17", + "mixins": [ + "SettingsAccessor", + "DedicatedServerAccessor", + "DedicatedServerSettingsAccessor" + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/paper/build.gradle b/paper/build.gradle index b49d520..02b312a 100644 --- a/paper/build.gradle +++ b/paper/build.gradle @@ -41,7 +41,9 @@ dependencies { paperweight.paperDevBundle(project.paper_version) implementation "xyz.jpenilla:reflection-remapper:${project.reflection_remapper_version}" - implementation "top.offsetmonkey538.meshlib:mesh-lib-paper:${rootProject.meshlib_version}" + implementation("top.offsetmonkey538.meshlib:mesh-lib-paper:${rootProject.meshlib_version}+1.21.4") { + exclude(group: "top.offsetmonkey538.monkeylib538") + } common project(path: ":common", configuration: "shadow") } diff --git a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlugin.java b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlugin.java index 55777cc..fc6e210 100644 --- a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlugin.java +++ b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlugin.java @@ -2,14 +2,11 @@ import org.bukkit.plugin.java.JavaPlugin; import top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager; -import top.offsetmonkey538.meshlib.MeshLib; public class PaperPlugin extends JavaPlugin { @Override public void onEnable() { - MeshLib.initialize(); - PaperPlatformMain.setPlugin(this); GithubResourcepackManager.initialize(); diff --git a/settings.gradle b/settings.gradle index e866fb5..66601c4 100644 --- a/settings.gradle +++ b/settings.gradle @@ -15,4 +15,5 @@ rootProject.name = "github-resourcepack-manager" include "common" include "fabric" +include "neoforge" include "paper" From 6aecd24f6d7a24ae4f7e5dcdcd37fe901ba92fc4 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Tue, 7 Oct 2025 20:36:35 +0300 Subject: [PATCH 37/52] depend on newer monkeylib and meshlib --- gradle.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index 7c78e61..bbab9a6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,9 +5,9 @@ org.gradle.parallel = true minecraft_version = 1.21.4 # MESH Lib, check at https://github.com/OffsetMods538/MESH-Lib -meshlib_version = 1.0.5-8e52b9d +meshlib_version = 1.0.5-c33be54 # MonkeyLib538, check at https://github.com/OffsetMods538/MonkeyLib538 -monkeylib538_version = 3.0.0-66c29d2 +monkeylib538_version = 3.0.0-10e70ec jgit_version = 6.9.0.202403050737-r From d5f38be6da7aa54f2a09513626a839c687245b5c Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Tue, 7 Oct 2025 21:05:27 +0300 Subject: [PATCH 38/52] depend on newer monkeylib and meshlib --- .../githubresourcepackmanager/GithubResourcepackManager.java | 2 +- gradle.properties | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java index e0fdc6d..2e848d0 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java @@ -70,7 +70,7 @@ public static void initialize() { PlatformMain.INSTANCE.registerSendMessageQueueOnAdminJoin(MESSAGE_QUEUE); // config should be initialized after the error listeners - config = ConfigManager.INSTANCE.init(ConfigHolder.create(ModConfig::new, LOGGER::error)); + config = ConfigManager.init(ConfigHolder.create(ModConfig::new, LOGGER::error)); GitPackManagerCommand.register(); ConfigCommandApi.registerConfigCommand( diff --git a/gradle.properties b/gradle.properties index bbab9a6..247c39b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,9 +5,9 @@ org.gradle.parallel = true minecraft_version = 1.21.4 # MESH Lib, check at https://github.com/OffsetMods538/MESH-Lib -meshlib_version = 1.0.5-c33be54 +meshlib_version = 1.0.5-1a52522 # MonkeyLib538, check at https://github.com/OffsetMods538/MonkeyLib538 -monkeylib538_version = 3.0.0-10e70ec +monkeylib538_version = 3.0.0-3df936b jgit_version = 6.9.0.202403050737-r From be184b129eae09cfe54ab329c13e251c0a526c46 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Sat, 11 Oct 2025 12:15:42 +0300 Subject: [PATCH 39/52] depend on newer monkeylib and meshlib --- gradle.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index 247c39b..218c8ce 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,9 +5,9 @@ org.gradle.parallel = true minecraft_version = 1.21.4 # MESH Lib, check at https://github.com/OffsetMods538/MESH-Lib -meshlib_version = 1.0.5-1a52522 +meshlib_version = 2.0.0-alpha.0.1760173787064+6126daa # MonkeyLib538, check at https://github.com/OffsetMods538/MonkeyLib538 -monkeylib538_version = 3.0.0-3df936b +monkeylib538_version = 3.0.0-alpha.0.1760173173400+2230010 jgit_version = 6.9.0.202403050737-r From 41f5ac01cc862a350026b9103f442bc3e45a19e1 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Sat, 11 Oct 2025 12:21:57 +0300 Subject: [PATCH 40/52] redo versioning see https://github.com/OffsetMods538/MonkeyLib538/commit/7d04b64b6682b25032762250a91e085c51d4d793 and https://github.com/OffsetMods538/MonkeyLib538/commit/223001036477e9f6f82c135fb929ff8c2276aa02 --- .github/workflows/build_artifacts.yml | 7 ++++++- .github/workflows/publish.yml | 3 +++ build.gradle | 28 +++++++++++++++++++++++++-- fabric/build.gradle | 2 +- gradle.properties | 2 +- neoforge/build.gradle | 2 +- paper/build.gradle | 2 +- 7 files changed, 39 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build_artifacts.yml b/.github/workflows/build_artifacts.yml index 497f54c..7ae91d7 100644 --- a/.github/workflows/build_artifacts.yml +++ b/.github/workflows/build_artifacts.yml @@ -24,10 +24,15 @@ jobs: with: cache-read-only: false + - name: Store short commit hash + run: echo "short_commit_hash=$(git rev-parse --short "$GITHUB_SHA")" >> "$GITHUB_ENV" + - name: Build with Gradle run: ./gradlew build env: + PRESERVE_PRERELEASE_VERSION: true DISABLE_PROPERTIES_UPDATE: true + VERSION_SUFFIX: ${{ env.short_commit_hash }} - name: Delete common libs run: rm -r ./common/build/libs @@ -35,5 +40,5 @@ jobs: - name: Upload build artifacts uses: actions/upload-artifact@v4 with: - name: Artifacts + name: Build Artifacts path: ./*/build/libs/ diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index c75535a..4832d00 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -29,11 +29,14 @@ jobs: - name: Build with Gradle run: ./gradlew build env: + IS_RELEASE: true DISABLE_PROPERTIES_UPDATE: true - name: Upload to Modrinth run: ./gradlew modrinth env: + IS_RELEASE: true + DISABLE_PROPERTIES_UPDATE: true MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }} VERSION_NAME: ${{ github.event.release.name }} VERSION_IS_PRERELEASE: ${{ github.event.release.prerelease }} diff --git a/build.gradle b/build.gradle index 780ac0c..fd9372d 100644 --- a/build.gradle +++ b/build.gradle @@ -1,11 +1,36 @@ import dex.plugins.outlet.v2.util.ReleaseType +import java.nio.file.Files + plugins { id 'fabric-loom' version '1.10-SNAPSHOT' apply false id 'io.github.dexman545.outlet' version '1.6.1' apply false id 'com.modrinth.minotaur' version '2.+' apply false } +ext { + versionPrefix = rootProject.mod_version + if (!Boolean.parseBoolean(System.getenv("IS_RELEASE"))) { + String preReleaseVersion = System.currentTimeMillis() + + if (Boolean.parseBoolean(System.getenv("PRESERVE_PRERELEASE_VERSION"))) { + var preReleaseVersionFile = file("preReleaseVersion.txt").toPath() + if (Files.exists(preReleaseVersionFile)) preReleaseVersion = Files.readString(preReleaseVersionFile) + Files.writeString(preReleaseVersionFile, preReleaseVersion) + } + + var separator = "-" + if (versionPrefix.contains("-")) separator = "." + versionPrefix = "${versionPrefix}${separator}${preReleaseVersion}" + } + final String versionSuffix = System.getenv("VERSION_SUFFIX") + if (versionSuffix != null && !versionSuffix.isEmpty()) { + versionPrefix = "${versionPrefix}+${versionSuffix}" + } + + System.out.println("Version Prefix: " + versionPrefix) +} + allprojects { group = "top.offsetmonkey538.githubresourcepackmanager" @@ -27,7 +52,7 @@ subprojects { apply plugin: "java" base.archivesName = "github-resourcepack-manager-${project.nameSuffix}" - version = "${project.mod_version}+${project.minecraft_version}" + version = "${rootProject.versionPrefix}+${project.minecraft_version}" java { withSourcesJar() @@ -69,5 +94,4 @@ configure(subprojects.findAll { it.name != "common" }) { } } tasks.modrinth.dependsOn(tasks.modrinthSyncBody) - } diff --git a/fabric/build.gradle b/fabric/build.gradle index f0f93aa..8f16b53 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -109,7 +109,7 @@ dependencies { processResources { final Map properties = Map.of( - "modVersion", project.mod_version, + "modVersion", project.version, "supportedMinecraftVersions", project.supported_minecraft_versions, "monkeylib538Version", project.monkeylib538_version ) diff --git a/gradle.properties b/gradle.properties index 218c8ce..91881e6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,5 +12,5 @@ jgit_version = 6.9.0.202403050737-r # Mod Properties -mod_version = 5.0.0 +mod_version = 5.0.0-alpha.0 supported_minecraft_versions = >=1.20.5 diff --git a/neoforge/build.gradle b/neoforge/build.gradle index b3f6f3d..b495727 100644 --- a/neoforge/build.gradle +++ b/neoforge/build.gradle @@ -74,7 +74,7 @@ dependencies { processResources { final Map properties = Map.of( - "modVersion", project.mod_version, + "modVersion", project.version, "supportedMinecraftVersions", project.minecraft_version_range, "monkeylib538Version", project.monkeylib538_version, "meshLibVersion", project.meshlib_version diff --git a/paper/build.gradle b/paper/build.gradle index 02b312a..c6ce13f 100644 --- a/paper/build.gradle +++ b/paper/build.gradle @@ -51,7 +51,7 @@ tasks.build.dependsOn(shadowJar) processResources { final Map properties = Map.of( - "modVersion", project.mod_version, + "modVersion", project.version, "lowestMinecraftVersion", outlet.mcVersions().first() // Hopefully outlet always does stuff in this order ) From 4ee4908da0aeb187514cb2608356abfc7de0e816 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Sun, 12 Oct 2025 10:59:35 +0300 Subject: [PATCH 41/52] Add command for clearing admin message queue --- .../GithubResourcepackManager.java | 18 +++++++++++++++++- .../command/GitPackManagerCommand.java | 9 +++++++++ .../platform/PlatformMain.java | 2 +- .../platform/fabric/FabricPlatformMain.java | 4 +++- .../neoforge/NeoforgePlatformMain.java | 4 +++- .../platform/paper/PaperPlatformMain.java | 6 ++++-- 6 files changed, 37 insertions(+), 6 deletions(-) diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java index 2e848d0..aa05f02 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java @@ -58,6 +58,15 @@ private GithubResourcepackManager() { private static final List MESSAGE_QUEUE = new ArrayList<>(); + private static final MonkeyLibText MESSAGE_QUEUE_EMPTY_MESSAGE; + static { + try { + MESSAGE_QUEUE_EMPTY_MESSAGE = TextFormattingApi.styleText("Admin message queue can be emptied using the &{hoverText,'Click to run','&{runCommand,'/gh-rp-manager reset-admin-message-queue','[/gh-rp-manager reset-admin-message-queue]'}'} command."); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + @SuppressWarnings("NotNullFieldNotInitialized") public static @NotNull ConfigHolder config; @@ -67,7 +76,10 @@ private GithubResourcepackManager() { public static void initialize() { addLogToAdminListeners(); - PlatformMain.INSTANCE.registerSendMessageQueueOnAdminJoin(MESSAGE_QUEUE); + PlatformMain.INSTANCE.registerSendMessageQueueOnAdminJoin( + MESSAGE_QUEUE, + MESSAGE_QUEUE_EMPTY_MESSAGE + ); // config should be initialized after the error listeners config = ConfigManager.init(ConfigHolder.create(ModConfig::new, LOGGER::error)); @@ -344,6 +356,10 @@ public static Map generatePlaceholders(final GitHandler gitHandl return placeholders; } + public static void clearAdminMessageQueue() { + MESSAGE_QUEUE.clear(); + } + private static String getOldResourcePackName() { final String oldPackUrl = PlatformServerProperties.INSTANCE.getResourcePackUrl(); if (oldPackUrl == null) return null; diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/command/GitPackManagerCommand.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/command/GitPackManagerCommand.java index 3bf655e..31ac367 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/command/GitPackManagerCommand.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/command/GitPackManagerCommand.java @@ -57,6 +57,15 @@ private static LiteralArgumentBuilder createCommand() { .requires(CommandAbstractionApi::isOp) .then(literal("resource").executes(context -> runTestUpdateMessage(context, true))) .then(literal("data").executes(context -> runTestUpdateMessage(context, false))) + ) + + .then(literal("reset-admin-message-queue") + .requires(CommandAbstractionApi::isOp) + .executes(context -> { + clearAdminMessageQueue(); + CommandAbstractionApi.sendMessage(context, "Admin message queue cleared!"); + return 1; + }) ); } diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformMain.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformMain.java index b6ea4be..a301a07 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformMain.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformMain.java @@ -46,5 +46,5 @@ public interface PlatformMain { * * @param messageQueue list containing messages to send to admins when they join. Contents can be changed after calling this method. */ - void registerSendMessageQueueOnAdminJoin(final List messageQueue); + void registerSendMessageQueueOnAdminJoin(final List messageQueue, final MonkeyLibText lastMessage); } diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java index 4bea390..464f799 100644 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java +++ b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java @@ -52,11 +52,13 @@ public void sendMessageToAdmins(MonkeyLibText message) { } @Override - public void registerSendMessageQueueOnAdminJoin(List messageQueue) { + public void registerSendMessageQueueOnAdminJoin(List messageQueue, MonkeyLibText lastMessage) { ServerPlayConnectionEvents.JOIN.register((serverPlayNetworkHandler, packetSender, minecraftServer1) -> { + if (messageQueue.isEmpty()) return; if (!minecraftServer1.getPlayerManager().isOperator(serverPlayNetworkHandler.player.getGameProfile())) return; for (MonkeyLibText text : messageQueue) serverPlayNetworkHandler.player.sendMessage(FabricMonkeyLibText.of(text).getText()); + serverPlayNetworkHandler.player.sendMessage(FabricMonkeyLibText.of(lastMessage).getText()); }); } diff --git a/neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/neoforge/NeoforgePlatformMain.java b/neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/neoforge/NeoforgePlatformMain.java index 69024c2..fb76c0a 100644 --- a/neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/neoforge/NeoforgePlatformMain.java +++ b/neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/neoforge/NeoforgePlatformMain.java @@ -51,11 +51,13 @@ public void sendMessageToAdmins(MonkeyLibText message) { } @Override - public void registerSendMessageQueueOnAdminJoin(List messageQueue) { + public void registerSendMessageQueueOnAdminJoin(List messageQueue, MonkeyLibText lastMessage) { NeoForge.EVENT_BUS.addListener(PlayerEvent.PlayerLoggedInEvent.class, playerLoggedInEvent -> { + if (messageQueue.isEmpty()) return; if (!playerLoggedInEvent.getEntity().getServer().getPlayerList().isOp(playerLoggedInEvent.getEntity().getGameProfile())) return; for (MonkeyLibText text : messageQueue) playerLoggedInEvent.getEntity().displayClientMessage(NeoforgeMonkeyLibText.of(text).getText(), false); + playerLoggedInEvent.getEntity().displayClientMessage(NeoforgeMonkeyLibText.of(lastMessage).getText(), false); }); } diff --git a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java index 91c7cea..79273b4 100644 --- a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java +++ b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java @@ -40,15 +40,17 @@ public void sendMessageToAdmins(MonkeyLibText message) { } @Override - public void registerSendMessageQueueOnAdminJoin(List messageQueue) { + public void registerSendMessageQueueOnAdminJoin(List messageQueue, MonkeyLibText lastMessage) { record AdminMessageQueueEventHandler(List messageQueue) implements Listener { @EventHandler(priority = EventPriority.MONITOR) public void onPlayerJoin(PlayerJoinEvent event) { + if (messageQueue.isEmpty()) return; if (!event.getPlayer().isOp()) return; for (MonkeyLibText text : messageQueue) { - // todo: once monkeylib has paper support: event.getPlayer().sendMessage(PaperMonkeyLibText.of(text)); + // todo: once monkeylib has paper support: event.getPlayer().sendMessage(PaperMonkeyLibText.of(text).getText()); } + // todo: once monkeylib has paper support: event.getPlayer().sendMessage(PaperMonkeyLibText.of(lastMessage).getText()); } } From 9534d5d6024cbce34c6284d7b6efa341aa4552e6 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Sun, 12 Oct 2025 11:41:44 +0300 Subject: [PATCH 42/52] Async pack updating no idea if there are any issues caused from this... There shouldn't be I don't think as updates from webhooks were already running on the netty thread and that seemed to work... I think --- .../GithubResourcepackManager.java | 18 ++++++++++++++++-- .../command/GitPackManagerCommand.java | 8 ++++---- .../networking/WebhookHttpHandler.java | 2 +- fabric/gradle.properties | 2 +- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java index aa05f02..8132e71 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java @@ -1,5 +1,6 @@ package top.offsetmonkey538.githubresourcepackmanager; +import com.google.common.util.concurrent.ThreadFactoryBuilder; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.HttpHeaderNames; import org.apache.commons.io.FileUtils; @@ -32,6 +33,10 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; import java.util.regex.Pattern; public final class GithubResourcepackManager { @@ -56,6 +61,8 @@ private GithubResourcepackManager() { public static final Map STATIC_PLACEHOLDERS = Map.of("{packUpdateCommand}", "/gh-rp-manager request-pack"); + private static final Executor EXECUTOR = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat(MOD_ID + "-%d").build()); + private static final List MESSAGE_QUEUE = new ArrayList<>(); private static final MonkeyLibText MESSAGE_QUEUE_EMPTY_MESSAGE; @@ -119,7 +126,7 @@ public boolean matches(FullHttpRequest httpRequest) { new MainHttpHandler(new Object()) )); - PlatformMain.INSTANCE.runOnServerStart(() -> updatePack(UpdateType.RESTART)); + PlatformMain.INSTANCE.runOnServerStart(() -> updatePack(UpdateType.RESTART, true)); } private static void addLogToAdminListeners() { @@ -150,7 +157,14 @@ private static void createFolderStructure() throws GithubResourcepackManagerExce } } - public static void updatePack(final UpdateType updateType) { + public static CompletableFuture updatePack(final UpdateType updateType, final boolean onSameThread) { + if (!onSameThread) return CompletableFuture.runAsync(() -> updatePack(updateType), EXECUTOR); + + updatePack(updateType); + return CompletableFuture.completedFuture(null); + } + + private static void updatePack(final UpdateType updateType) { if (updateType != UpdateType.RESTART) { LOGGER.debug("Clearing admin message queue before updating after a restart..."); MESSAGE_QUEUE.clear(); diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/command/GitPackManagerCommand.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/command/GitPackManagerCommand.java index 31ac367..568d35b 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/command/GitPackManagerCommand.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/command/GitPackManagerCommand.java @@ -85,10 +85,10 @@ private static void runTriggerUpdate(CommandContext context, boolean for GithubResourcepackManager.LOGGER.addListener(MonkeyLibLogger.LogLevel.INFO, infoListener); GithubResourcepackManager.LOGGER.addListener(MonkeyLibLogger.LogLevel.WARN, warnListener); - GithubResourcepackManager.updatePack(force ? GithubResourcepackManager.UpdateType.COMMAND_FORCE : GithubResourcepackManager.UpdateType.COMMAND); - - GithubResourcepackManager.LOGGER.removeListener(MonkeyLibLogger.LogLevel.INFO, infoListener); - GithubResourcepackManager.LOGGER.removeListener(MonkeyLibLogger.LogLevel.WARN, warnListener); + GithubResourcepackManager.updatePack(force ? GithubResourcepackManager.UpdateType.COMMAND_FORCE : GithubResourcepackManager.UpdateType.COMMAND, false).thenRun(() -> { + GithubResourcepackManager.LOGGER.removeListener(MonkeyLibLogger.LogLevel.INFO, infoListener); + GithubResourcepackManager.LOGGER.removeListener(MonkeyLibLogger.LogLevel.WARN, warnListener); + }); } private static int runTestUpdateMessage(CommandContext context, boolean isResource) { diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/WebhookHttpHandler.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/WebhookHttpHandler.java index 7a754fd..b7e4b9d 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/WebhookHttpHandler.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/WebhookHttpHandler.java @@ -12,6 +12,6 @@ private WebhookHttpHandler() {} public static void handleRequest(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception { ctx.writeAndFlush(new DefaultFullHttpResponse(HTTP_1_1, OK)).addListener(ChannelFutureListener.CLOSE); - GithubResourcepackManager.updatePack(GithubResourcepackManager.UpdateType.WEBHOOK); + GithubResourcepackManager.updatePack(GithubResourcepackManager.UpdateType.WEBHOOK, false); } } diff --git a/fabric/gradle.properties b/fabric/gradle.properties index 7695676..de5dfc9 100644 --- a/fabric/gradle.properties +++ b/fabric/gradle.properties @@ -3,7 +3,7 @@ # These should be automatically updated, unless the environment # variable "DISABLE_PROPERTIES_UPDATE" is set. yarn_version = 1.21.4+build.8 -loader_version = 0.17.2 +loader_version = 0.17.3 fapi_version = 0.119.4+1.21.4 # Dependencies From 3a93c23f95122fabd4627d077d246b56575068b2 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Sun, 12 Oct 2025 20:38:14 +0300 Subject: [PATCH 43/52] build against 1.21.10 --- build.gradle | 2 +- fabric/build.gradle | 9 +++++---- fabric/gradle.properties | 6 ++++-- .../platform/fabric/FabricPlatformMain.java | 5 +++-- .../platform/fabric/FabricPlatformText.java | 3 ++- gradle.properties | 2 +- 6 files changed, 16 insertions(+), 11 deletions(-) diff --git a/build.gradle b/build.gradle index fd9372d..81c8cc5 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ import dex.plugins.outlet.v2.util.ReleaseType import java.nio.file.Files plugins { - id 'fabric-loom' version '1.10-SNAPSHOT' apply false + id 'fabric-loom' version '1.11-SNAPSHOT' apply false id 'io.github.dexman545.outlet' version '1.6.1' apply false id 'com.modrinth.minotaur' version '2.+' apply false } diff --git a/fabric/build.gradle b/fabric/build.gradle index 8f16b53..3ce837f 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -1,7 +1,7 @@ import dex.plugins.outlet.v2.util.ReleaseType plugins { - id 'fabric-loom' version '1.10-SNAPSHOT' + id 'fabric-loom' version '1.11-SNAPSHOT' id 'io.github.dexman545.outlet' version '1.6.1' } @@ -10,9 +10,9 @@ outlet { mcVersionRange = rootProject.supported_minecraft_versions allowedReleaseTypes = Set.of(ReleaseType.RELEASE) propertiesData = [ - 'yarn_version': outlet.yarnVersion(rootProject.minecraft_version), + 'yarn_version': outlet.yarnVersion(project.minecraft_version), 'loader_version': outlet.loaderVersion(), - 'fapi_version': outlet.fapiVersion(rootProject.minecraft_version) + 'fapi_version': outlet.fapiVersion(project.minecraft_version) ] } @@ -83,7 +83,7 @@ dependencies { modRuntimeOnly "me.djtheredstoner:DevAuth-fabric:${project.devauth_version}" - modRuntimeOnly("top.offsetmonkey538.monkeylib538:monkeylib538-fabric-1.20.5:${project.monkeylib538_version}+1.20.5") { + modRuntimeOnly("top.offsetmonkey538.monkeylib538:monkeylib538-fabric-1.21.9:${project.monkeylib538_version}+1.21.9") { exclude(group: "net.fabricmc.fabric-api") exclude(group: "top.offsetmonkey538.monkeylib538:monkeylib538-fabric") } @@ -100,6 +100,7 @@ dependencies { modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fapi_version}" runtimeOnly("top.offsetmonkey538.meshlib:mesh-lib-fabric:${rootProject.meshlib_version}+1.21.4") { + exclude(group: "net.fabricmc.fabric-api") exclude(group: "top.offsetmonkey538.monkeylib538") } diff --git a/fabric/gradle.properties b/fabric/gradle.properties index de5dfc9..5048e68 100644 --- a/fabric/gradle.properties +++ b/fabric/gradle.properties @@ -1,10 +1,12 @@ +minecraft_version = 1.21.10 + # Fabric # Check at https://fabricmc.net/develop # These should be automatically updated, unless the environment # variable "DISABLE_PROPERTIES_UPDATE" is set. -yarn_version = 1.21.4+build.8 +yarn_version = 1.21.10+build.2 loader_version = 0.17.3 -fapi_version = 0.119.4+1.21.4 +fapi_version = 0.135.0+1.21.10 # Dependencies ## DevAuth, check at https://github.com/DJtheRedstoner/DevAuth diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java index 464f799..b04a59b 100644 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java +++ b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java @@ -10,6 +10,7 @@ import top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformMain; import top.offsetmonkey538.monkeylib538.api.text.MonkeyLibText; +import top.offsetmonkey538.monkeylib538.fabric.api.player.FabricPlayerApi; import top.offsetmonkey538.monkeylib538.fabric.api.text.FabricMonkeyLibText; import java.nio.file.Path; @@ -46,7 +47,7 @@ public void runOnServerStart(Runnable work) { public void sendMessageToAdmins(MonkeyLibText message) { if (getServer() == null) return; for (final PlayerEntity player : getServer().getPlayerManager().getPlayerList()) { - if (!getServer().getPlayerManager().isOperator(player.getGameProfile())) continue; + if (!FabricPlayerApi.isPlayerOp(getServer().getPlayerManager(), player)) continue; player.sendMessage(FabricMonkeyLibText.of(message).getText(), false); } } @@ -55,7 +56,7 @@ public void sendMessageToAdmins(MonkeyLibText message) { public void registerSendMessageQueueOnAdminJoin(List messageQueue, MonkeyLibText lastMessage) { ServerPlayConnectionEvents.JOIN.register((serverPlayNetworkHandler, packetSender, minecraftServer1) -> { if (messageQueue.isEmpty()) return; - if (!minecraftServer1.getPlayerManager().isOperator(serverPlayNetworkHandler.player.getGameProfile())) return; + if (!FabricPlayerApi.isPlayerOp(minecraftServer1.getPlayerManager(), serverPlayNetworkHandler.player)) return; for (MonkeyLibText text : messageQueue) serverPlayNetworkHandler.player.sendMessage(FabricMonkeyLibText.of(text).getText()); serverPlayNetworkHandler.player.sendMessage(FabricMonkeyLibText.of(lastMessage).getText()); diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java index f7335eb..9fabeaa 100644 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java +++ b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformText.java @@ -4,6 +4,7 @@ import net.minecraft.server.network.ServerPlayerEntity; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformText; import top.offsetmonkey538.monkeylib538.api.text.MonkeyLibText; +import top.offsetmonkey538.monkeylib538.fabric.api.player.FabricPlayerApi; import top.offsetmonkey538.monkeylib538.fabric.api.text.FabricMonkeyLibText; public class FabricPlatformText implements PlatformText { @@ -19,7 +20,7 @@ public void sendUpdateMessage(MonkeyLibText updateMessage, boolean adminsOnly) { } for (final ServerPlayerEntity player : playerManager.getPlayerList()) { - if (!playerManager.isOperator(player.getGameProfile())) continue; + if (!FabricPlayerApi.isPlayerOp(playerManager, player)) continue; player.sendMessageToClient(FabricMonkeyLibText.of(updateMessage).getText(), false); } } diff --git a/gradle.properties b/gradle.properties index 91881e6..577c803 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,7 +7,7 @@ minecraft_version = 1.21.4 # MESH Lib, check at https://github.com/OffsetMods538/MESH-Lib meshlib_version = 2.0.0-alpha.0.1760173787064+6126daa # MonkeyLib538, check at https://github.com/OffsetMods538/MonkeyLib538 -monkeylib538_version = 3.0.0-alpha.0.1760173173400+2230010 +monkeylib538_version = 3.0.0-alpha.0.1760290533497+21bc668 jgit_version = 6.9.0.202403050737-r From ba62165085209949ef7f1117e659e880bfc95163 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Sat, 18 Oct 2025 19:50:42 +0200 Subject: [PATCH 44/52] replace \n with newlines in update message --- .../githubresourcepackmanager/GithubResourcepackManager.java | 2 +- fabric/gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java index 8132e71..336476c 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java @@ -344,7 +344,7 @@ public static MonkeyLibText[] createUpdateMessage(final String[] updateMessage, final MonkeyLibText[] result = new MonkeyLibText[updateMessage.length]; for (int lineIndex = 0; lineIndex < updateMessage.length; lineIndex++) { - final String line = StringUtils.replacePlaceholders(updateMessage[lineIndex], placeholders, true, false); + final String line = StringUtils.replacePlaceholders(updateMessage[lineIndex], placeholders, true, false).replace("\\n", "\n"); try { result[lineIndex] = TextFormattingApi.styleText(line); diff --git a/fabric/gradle.properties b/fabric/gradle.properties index 5048e68..e636565 100644 --- a/fabric/gradle.properties +++ b/fabric/gradle.properties @@ -6,7 +6,7 @@ minecraft_version = 1.21.10 # variable "DISABLE_PROPERTIES_UPDATE" is set. yarn_version = 1.21.10+build.2 loader_version = 0.17.3 -fapi_version = 0.135.0+1.21.10 +fapi_version = 0.136.0+1.21.10 # Dependencies ## DevAuth, check at https://github.com/DJtheRedstoner/DevAuth From de2cf35736767bbb60722504bf4bf84162dc60aa Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Mon, 20 Oct 2025 18:26:28 +0200 Subject: [PATCH 45/52] update gradle to v9.1.0 --- gradle/wrapper/gradle-wrapper.jar | Bin 43764 -> 45457 bytes gradle/wrapper/gradle-wrapper.properties | 4 ++-- gradlew | 5 +---- gradlew.bat | 3 +-- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 1b33c55baabb587c669f562ae36f953de2481846..8bdaf60c75ab801e22807dde59e12a8735a34077 100644 GIT binary patch delta 37256 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 delta 35551 zcmYJZV|bna)5V*{Y~1X)L1WvtZQHhXxMQoaZ98df+je97^#6O#xz79h)jhM;%=fb< zejogP5xmysJ1}Y-zK;P#^eNya^!*RyrWsaa*o?`cG4E0x(uI5*J=Ql{I8pVHbrf*&ViJbv&0$Zx^9HzKJYQ+2@eUCip7Q~vv%wZxh=X(hybkQ-d%4h08A3r-BgR1yDQOhGU!yc)KY_R) z<~z-KN~9P>0@{5up2;>ZO7$o~VmdL?8yt&VFrbN!Ax~@SD^gB(*;lok#cYX1yF0ri zTfoNS4~q_qcA&~muAcevb&3QXO?~0wIJt9T@@k%iwWyg|@`P{EtB0FDW2TTpJ449e zuN$b!Af;6128-YK{g=RgMOrWWfwmiBb%I9~ClxAv$Tv$EFuBIYWT39uPZWMY_)u>-6QS>Dpp%(#NEFIeU zjJN#v$j{|sq!va#kM7Uh3#%b(XnIqbX?K%PlWA%C!0rz)hR9!_CvWd*YWqemcDG<_ ztH|`aB23nP=k&Rwy!(xW{j|Wn?pi2hNM1G%1t1en-wK?TTrRDhBR7g@m1Q#C7R_i_ zL3gbJo7pkkx%%3RHtl+`z|2k&Q(IqCA$2glZe)H(AF@Q`UUFJnn$##p$J+Wg29V06 z^$W;@!nT*;@Fm6WWuq~~ZbeD|5ihjEEcv%uhGHE&8e;#tPwF|FJFRb1H*J)HAb-%_ zATZ3|un`ABE3ffkn8#v4L?T+D&Ath57i3+NL7H6VrjcSx00}9XLCoNTea8^xLS$ul zj~YlyyKT+NZn9!<(nGF`y+z)ulWL?2y{qJxmB*f{ug(}O0}n4IaigLNKcqBbBr*t= zAbGz_({CW|vYA*MC0CMUm#7EfqwiX&)Q#eM9U657>_Z_=xQ_KLM zO%6h`rx~)x-7(vp@br}&k(TFMBXDg~(68W~7Id{DO7>I%!1Is@@Z$NA0*S#kM~}+M zO;#+U>;QsYyR6@9itLyZXt?aMAe&1UyFw@2JH?lLl_gE+<6YSM)@Ls;5 zX&SY^f>-?i>qi@tYFRsQFtCPi5dY~o7hMQ=A%`xA!7Ch4v_2OI`%GK?^Fs@VApw2} zQc^|&han&EY+T$iZ))h?oVJ-iFcS2P_&EdlYjyzUIxot79StR&<&wfumAu}Bs9%YpbNZ+1Q6_U5E>>Jo(Gcc?vo73mT|MU zjZUVk4qN7C;+OIaIiiV369ED#h6Bf;tb$G|3w$vB9@Xu`$R4ZvbCmXCj*}^O+=%@F z?=UU%P|G2nihG9%jS$(?h*>v|@=Mlj^g-^oXqx>TK_|sk=2c$Oy!7?DbCN)O^j5Ja zz{rC@_R^7N3(lv$2dGRhkafdoB)-0To|uCK*;$MQWvw&`~J&*b;AnbCAg8}xm^Q^Ypo+fh_OqPzc* zWPK%OH*$E-|C-La5++UiU(+>1{?~KIM86Uve~<&^=M6CY^aS9WD6nq)uraZ1sL^LQ zf3yG5CeC$~Vv=FGYEP}28=rH_Wqf6pxo_YXK*uDxxt$y!H09AXhZG#cTCTkC-a5{_ z%N+N9-9Ij&2NQD)+FiUmcCVLTBwkJp)>R@`@l}*9Yd2O!N_+zuTc;?ak-CRawvt;k z^zi~^YhZmxD>SpY>PBSc3m2?38$48*!Epy=%tQ!zr8U^!w1IVI>7>_GI=Fd7wc{Y# zVCxmr1UiIe5`EI?@3BbcO$i!mIZXkKBc3HkXM5>}@Sv#ulzG$CRGIiCSrXn0jUO%2 z%qFL7?!3E?^5LSxzZ%b9UbO1!=<`B$bqax(RaPih2k`E=37ylvM0v@1i!}hfFH2}w zvN4&MnPa5&YkDRf!YI&JbZMmYxkFo?CzP#){V*K`yvg4bB12^1P-ArAWn@og8pJ7{ zy>T8}r;g02H$f}sj9NjTvesSpv8>v?J?qC)J#KIT40LBAhIPXy_OX~v?1ArOJy zS?%=pXOb4ddE_iQcSy{>LEg!ldXtnK!TlE;VI+vU8O^`&j4kL8atsZ4XSD~#g`Oy7 zGeqF!ev<8TyfzmZbk;|X0~V2gb_O) z_@8OloSoSzC5RX0@CzBks;Dq5iQ0hyOD%F5+l^6>C-0{ET4N;K8!XeeGZ%@J-Dk7enSJ zxiQ``wpU9n8nmzC5P}3s(FoeBXGkf+k{S-V&gy@9;e{_NBv0L=|T!{Qb zcmbg?KO`F&&H99L0;=@mYUbvJw@i%PP!!X7-kRqpAVkrW}Z(P}X7Kut#HlOn0( z9;4KaiG_OrL*-N#+++{f|Fi@p@qK^}0t`$y5e3H*cP^%2H{CvQuOlDf63e=PD_TZ*Er2A}3kqg z;SOi^KKTtFvm~xW?E-yT+S`VA&i2P9?e^Ep;W8N8{ud%WA#Z!l#p6tFI^TdS?E--m zatLuAurYb^6m)i$f<38)L*6!tRLzz7JyexEo#5zHSdQ;Jcr8?=e>Yx%4t=t`t(49O z(Qdt&vg?Iuu4z5uQP{KpX8?1h82cjLX5+DUWdfiQhQMoZTU_7Ogs() z$Y5@4-O?}G&H*$|%Z)z1Qf_vwu{LA8sm4|TOxMcfxlpwYT~GbXSf$v&PVWDfP*~Bf zBjj&*S2=|F_lS8UgH~Ar&gHZS$3gla3sqMKU1XLSYuBq zC|pj}*|05*nI|HNO3`8=>8mw3s@OgK3kzgS-~- zA4}J0_nB-EjHu~K>{aJWO{7RJ@p(q(?Zof=u+?*Q71nl9MNkhA>8$SNiaF>*kfe9-5ZZw9$5s?X_wRv+66j-AiQFTAX9C6boKn)z=SGf_R zs~dTH*P?QqE2LOcv3qjg9_gq)g*=!pQR~e%#vNv(;L4<1^$%3%xsZbL>dFQTTTB7L zYJX{FIgt1AxOn_SE#tU=ueLfv1x8GC!^TY4aWf6AO2AdhCKRXWJ54saLUsu}9e?UIF{9wu)__c$BjVfHHJV;A zhYVV#cIZ5%7iJAy*D|&hb93@El0wF)$Nce4RlU%4s}FbBKDa0lNj0b?i9*!eliscz zodbJd(Id6B#d8UVh-(`Q;ednhCz)^jlD5p2xStUJkK;xI@Xh<>1S@qFad|%OkqbW8 znVl68ZQ*?W*2Pk+^~|laLAs~x#?dbF3&$%-@9lZgq1rG%{)bP1H0d|CU}c!^Dzb*B zmNfDgX?o{Rf5?QfzwnSI21 zkYHzU9R=B?O7mO6gH7q(FltF9hECeLF~*f%HF(3jjpO8j1^k%VLT4%(f70AKl7vuV zemQmc>s02~G!f*z)z$29iJA93EdehD1_jCx^f<^ub{-T7yt-^~5_>@qTbGwMJx7lP6}LNr(_prpAFt zWd~4xIkP1FMzdYf%d;^c2==XPj+g~5Pf#g-& zLgR>80`CNs$QgV}R+hyjnn!Tn^!A|Gzkt^;Sk(-{c6Ie$(>6cGjhBwRj57B;6MV6U zyBD+W@8+8^8|o~h6Ky`hPWl!mg*{7|`$dUGT&_U?A+-lycI%k=(ck3<-YA_u(K+?` z6GhRf$0LMU#JLrFB1u0M2>KU(LKmH?S;g@*4R76n57qV%1 zSR+cm4zfql_dUk+8De}Do~3@VQP8`qqx@vav-B0=e}nJJ|1xs}8VtkQ-oc40NO4+*oMypQV@`FbPBrinn*))GcdlkzS`|6!Qz~ z=|xUIk$K-iz81%pmo}fF5wuA3zU1}IKF-W`zMR(I27;CL8a&tbeC6NBSvxw*k2E)z zr{Px>re&`;;S;Q7v*^^&j$9##Ukl6(>kT!v`N_ zo;v(qg(sg1qnFN$u!z%@WY=leHXC-yQ_d%dU3&h8Ab(Q!4#hKMUu)`vJOzd+1+D~d z1GFL1{z4#D1;d6N!6+}RhlFAD^OKEb=o9wk89C~RJ#*B#{M|a$oWi^ULxBqZwPtYvb9qofWYm z-n-zqIruA~1uuY#RX?v|oB?YR{DRCPM+~$?ob@BF53nk;>w1POhuK5?hCRzHe&qwM zMXV+PsT6T%4z2MHI8V07A{{rfr4j?zBOSz8P3yxlfoavEL2|fI&TorKhD?!WDIw8t z1oMR*Ex3k3vm{4R@^X#CjyxQWdqw(RqYe1?a?AdEt)%|%wIY}}PD%z;v6i1#0Qh~! zO^SBJX8)#`7iec=sslMBIznn8;Xorm`W%w!8meT$?X*TTFoJx;{w#=;DuNF5=O24^ zgE&m7l$G<&e)7zDa@u-)$|39li!uz@y&E0XdM!vle(iREKZ`2ADwR~FUxO(gy zaI5`|_# z0pHNAj-FHF0G+}T$qxU#SCB|GLd_;1Ae6I)axC>LhcSk&!ID55;6I*#p`(v?jrA51j3d%qd;tN)@r8pvbNX_tH_#~N z5tdENu+KVm=kWn;p}ypq)7i}U^BLwI=oNA`1bm-#febi8rK0G<49$NbP#c5ue&Pu7 z3U!x7=M5eWdkTg~)yy$~Vphfo_zx%}xy7tD@1{-JKC=bGXHb2BK| zo-7D9UqX>ZaO6L)B%_lnHJ?-+HR)fpaLFtR?Ren&uh_ZVli996H3AA|AMSWCx z(%F_pOiH)=nDY;2Bnmey!G4Ggjhn&>*HJ`&5JI%GG$*g%HVdXiP=tA+jsfi%t65SQ zq?8j@cE+Bp9a)o|x@%LWY-}k@^@y9xbBTQ@;wq`faHl|ph<=HXT*CvgeQIn9fN?2% zaEpawYPn71V2!CJwB!yHSs!4SG)S#!H4Q&Pi<3cJFx~KaN@k1S5p^P%5s52rhuHTF zak86IyZ%nd?z;0=;0KE<{D*@T%0noMMfj_;lmuARJFca#WQQIk9MRp(lG+~PWB@`V z+4RgO(x)k=C=3^Un!H2>C|fGO=^QV%dxpB7r^@yI{)&PCy-a8-zEqw7u*N0&MhT66 zEMb$K|H3WCKF!$lf`A7eMEnftQ zO|p_WO>P0~mBVF3!B32v0Sid^A&1v~MkGk1t%ND6K=chQUkS3bjKks1iySv-xud>I z@s|o;A+Q&&EYuH-Fa!|#(@Xey=h)N!$kXid^6L}A|9d6Fv$O9KHF|-vj)W!UleoL%#wE7t;Gp<9x6 zlP(A-RpHA9!+c%*&DDaTw7I)w8i(Oxdr~Jc)^YfG{30!>_gJmt$q4t0wN{w4p`(IB zE9;H8xVP*6{uue&OfU8s`uRl2_Ln zkaBW*#cY7M3ei&`b2Ann*n6F<+kn|pSeiChX8Tq>&TAc-^w3$NL zVYFD*2}8aZH2~m2)l9-}UWDObZ~L+RygAsbUt1|x4!X#at|TrttAK*=jZFZsSUB4) zRU%4i@vTj&!83g04C;0fVZ!elG=`UbQfnxws6c^Jj8ERma2K-1GpNYyuvMWm*e_<4 zFZ*8cHFyuU`W+4*NJb}|{D|QjO3g??e)Hd^q|@S#`u*Pk6aGKM8%ZMoRQx|(lM_ip zP*Os9o#jz~mrOQ=!lVEn_$E>$h59q_|I>9$XNCl9GV(4x2hqbHnEL{%AtHr1;=zOu zv!m$k6=vYqhbN>z(sSR=<>O%O>-PF~E1t-i}gF}=)MYQ*u}$xl{BrHy={Y@&GH zY^eOuJu2KnU|P@SAyt3zwtQgH6T~S?epQugU7ciG^Mg|lw?YKCW-QG4LB3p}Sfdg- z27dlz>5oBeYyKrI!6@OcCmIIm#qu2StheP>>R4nu?I zJX#965ONPvine}|{x#GkJ(VXCU&jpZc#1RD;cL%H2Oy@ntD)gkdXIEdy-(nFwKoA& zKEB<=tRiF#E-caJpS+XqIMj!Hk2aSQ6*il?8sOPCYI4A3=o};dsIC0( zl;d>jysNuE)hP4MbRhdd+hu^uS@@}u%YeU6Dti4f~w4u_y-OdV|-qWIxu4wxJi&zm+Z`*e%3g|;(`+{7XM!8 zI>6wx(N55j-A424OTn?gL$aU6?r{&=juA0SF-}bGgQQs&@?vkfyrVB7^;R1P{`ct5 zSYq8F_%0IAw_iq0m+B!tqZQeI@T!PqYd8Zc+YxT-&$81~?80r}3jq-Kw6m5GQFz^8bHe!Tw8p6A5v?|G&v4YC<_OFj`et8(kd3Zy1t&pix4_hUScI5e=LO z3Ip}sB1(fY?x&!wh;-;Ck><+Zp-m*ID!u3X_UZj1y~m;TX06SdGR*2ICyy+)El$_nQ&f5ED0iBF!_aW8}C03bB zAa-+d`AYlG4icGOUBO7x%i_lRnWIgu!D!?Or+Lh*8!JlH-Nhs#---JNS8Lu9xbyp( zi=3)7GVBc|dDnRrjbHs}eT1<4s=@^xP0O3eFoqkj=Gur3C;jZ*^LU-!G zr&*jKRJ`b)QNDABj-aK1i%9+LYQB-*YE`!mR=!E;-HA5HyAYuMj+w$8Vd$bQI+a`% zBNviFF7}{{4kf%^Ngs?MxJFSRickS!an?y$;TN1* znzYVm@a+xh<%(Q71yt=WF6&CM1l2?@r}UrI}22@E%dS9)9y=L2PL;JFofWk(y`JSpqLDX z8`jpc2kNx@96s@MrU8K6%hFvm5_0s8<170FhOtjByI{uf3{v9os)~n=NJAO_0g1Zh zVABd%%;0+$Tz4F}mq9k)JX0wBgj|4%_~q(CJ#F}89%9Yf=qMtvk%2?vD}Q|%b3zGl zuRRj}rUz--cqt4AEj&XE(cdfb_LxcXJCxE9Q>oZ0+TeqGW4`5SteqNH)ie2OE?)C> zGmdGj{J<(1dsjwkSByP8Qi#9nr;(Di{|6(bzlmkanv_1s{ln8=tZ?++&C+cm2V&O5 z5qnmhLjzB9DDMC$&+!g%fZpeQzOuivZ;UL0o8mz8{0y~V;R6+pC9%{iKNB#edaaM4 z0O6a;t(SwW!?E^?-!0{acYzJtJ+Q0c07uB*-=x8?))4$@F7Xvs$dausbVP~M16O-& z|LGHA!}v^{v?uZN2aQN*0yRKy=)_+8Z=3GlecZ=zBgaY!W2hW@i#*L zG3Vt0S*qV2a*$1-J?jyVvkLZtBa%WSA@W;JSQ831TF zHx5%;G(+9{m^RQELa{DUM!OL-xQAyL#DXlSTQTaf>*qxgf3xC_th+-(&IDA-Fu7b#_o*gJKFMg|~NnuNAh zv~7Qb&ksZTx6lS{m$%8YIk%vQr=fd@?-X;5+UIr21qNe-#=m~Wlewu4Wv=M7{m}Lfct-P!JypG))+PpVMO!;aoe!Ey2G4tIji181H9N%Z5*!>P0%&9)kd z^Hs!}Q*DKeliE$PiF>8T%{C7p38Rv)Q*BDz;;HcPC)3LCvY;AN)^sPbtSn?`2W5v9 zbOb1ejHL1uDHlqHfnn|nmmhW*d6qyWiAXM7L>n4^?n0tzyX65Bw9YCtV$MG$u5fnSPCIzPKdidn!{cKt=OInFY<O_65e(4m6jj>(r+GP9S`_g_21ajkkIIA~ZBwyHSPy2z}M zn-v^#)4X19DfwQOA7nVAW-Zhlih~Yps=Z|=$bhoF%G&98-|oR~g+Won(9v#}up5t z5i8fYQVE~dd_2`s{W<2wHGTIVT98YnqTQKJWg6`Rq!VeYU)UsVI>~b$L;jv3yKkg? ztY0kN-oAMgldw=*G!p_#cg_;zApXv~vrQG@4jOG4gih|S%_sE2zmM`D`h**C=B_#! z23%l_d`385|8cZPLsDtzQaCJP~T z9PjnVf7sCGNU)XXpRw%z3uf^XYq`0BlT!TxD4$E^Wlf)rXN$t$^NkQylaxeJdLu(3 z0(Trc(u%FwC0AwPi5~@h5Ri!}p27H%IA}fYm?oYYwkQ5RO%G%FLsTMkMh&x1lJ`(A z`p=Enzmy+ey--Pm)<$&9E#pj38SO{oTn3Ev+XWsZk#yoYdKMFhX0!RDf<(RpA$Uhm z2ng91dQrV?@2-4n7(j5#se(a7MRjuFm2$>r;wJdhM%`_|)@?*$oR?`+*nlxxH4V|! zwYWcOX8R1yOiUP51^w2R_@Y>v2_r04&U)q?nydYlf6jvNMrTG?zH@KFD7A%p2E4?x zKyd~{KdR6>+4ebG9~x_Syayv0lyEJ+r2S+3$JG(=Kd7%2Fg4zWuMFD)F;yxkj19jz zm%>fxU3Xb9TtCM`S)tpmg-hZrvx;RQkRR4oCsUN2y|7}cAgi*_+(>?H<~EQFT}Eo(2^iFDwC9AkZet# z5#q&Qmt?l+QFxYOt6#!xe7#%SG`XV;8*A;Vz`aJ#Yl%X9^HsR^sZ4YeN&bkonEJ*P6MVr|jJh2uo4C4RRoavA zop>D5G0n?cjd0Eq!X>n=8c|MhZ%a!)4Gz)n`cJxU?l5C;mDuGYOX@iWsgO8D9JF@2 z!hD_J@aFY8h}+A;)lYm9L+n$qEIoTc?1;DNB(a z8>2L)>6rAXg-qsq?TKuWs8Q}vEjPw1XyR4qY?8`HMrCKW!+i?^f6$K^!Gi{oMuFB{ z3sLRPcwGu}dw&7)N1aF%m$ezL5SztBv-fTH(|6vo{1|3W-SI*%5-ILg5L4aQ4$!7U zFWMOO_BkIBCS2lSZC~L2ZkEj76ma41B_qwF?sjU z|04y*)sb?(||E&lT#$>pD6CWnNH!Fw((H;ycad1NT?yqe5d^?Y^y0yDtE z1@Eb@=|QUL6Dg-$Rcs|JcWlKk=gF`nLC9LC7#AOCB@v!OPeeZ@VI^XHFg@!30M@Z& zH}`Aem^%G99V1y?$1UANu5|4Oe(cWypx;HrAm~Pm*U&g^mBo$^c&3efTJQYK0nru& zpE`jk7Qkugl9NO>Qir$>7P%}u?1(1X5lzcIM&-KE#iXjeSgf%mz3Fq1anZ<|vZbjM zoq({xgU*zx4JmaG>2YBMSR{BPFm&x~Pr|^^`MfgdSK}J&%#Rb(Tc$kpMDJHEE2@d2 zKSM{yYa+*vvLgdCy-V1U`hULZA+V^by46N3F{#agLYz4` zUG#=hr0u_hMPfT8T*J+se_{RTmzSh|(WqxzM; zSfBs7)+8`1DDJe-GCROPxx#p;_w=>Pl|mSC{~L-(!^0-=PBN&37@ZApI0@R-6gw)KsEY5($Mcyky-?|xirLHS zW9XR{=TXubo?YMKgF6Qrf($ifB(Mq*<UH0{XTb81#ye;beWBetn$eD6e+qycgClN!mf#Dg z%>N&YA5v93>ibvOg8wQjE-D6O9g4$}+-Y~HC8<&WPF#;R@QqaN-*M2Me{19L#REq} zLq%F0=g(Ur9|$bEpN=~a&lDo--@c)xTDrQbx=v0!5$gAR;~3HnK~7Djhq;eeFHOJ56K3EIa+d&YO$3sACzE^b)+nbAM_Ua^30JqT$TiegvS$OGq^n2tqs%Ie17$;kFs;gc zPESj9ydud2g$?iG9m)8BY8uw=dQCF}(PU_iCIVW{_?VYX(_c$DSzoJ+QRC~Gu6opX zdLa`ulUY2;(_Z5CUd*>hHecxHQV9m?M3j{9tQ3D+zRcJ9Z2z*?g+hcpl-w4d7z_7N z>ZJB`lBv#(d5X8=mr0!s&0=l5LssT$ue`Eup}(dt6n1pnVTTf8s6#ddnp~s*&l}HL z@A+c>6^G!z;_!+q02S@$)i6FU=N76QrKNBwRN@v3Xy9ap5rQiNkkmj)XiH^+qVZ&P zxNk#_=PSEwa`7mg*F*i;9)`&4``PhJO15)D=!wl=EEhTu1sPzIDL(%s*m2B#?9&Z= zf4HjwOS$IkcSk0uRKH5IwX=oWW=oZ=FrLa#n>p_wh~4-Dq<;X{R?vZ$zgCzrOAY;1 zL0wtJa2ays6zZM#oBd6$Z20Y$`k{q7Rpio~XW!V_`CZn^9R-S;r)7LfpSzAe?CI-w zQ5Yf6fauLx-)e}}=nsgyPgp?E7NU`5xb;8aY8Buz7IV-{KDM6l^d^*21HImjY{k3`_gibq~f&{L87;FV|hGZfi1^G{_&M|VK1UbXzE^}wXWXvHo@5ZjI(%@UW2 zNVlHFJC-tYoVeidFa;ByulY32ktG+^p7N^s?c1#ab3NtdKwpc9Eq`w^ z*CYoZNaB|IN|2UvK@((bk8)l|*v5M^s4IQH*fryjZRiDrWA9*EkyGl#I1G$|FDE_i zgH1ug8)VFKX&qrm%XAEK^0n3Hn)9{@xrFcUh1QLx-`CR~$)F+V?N@gzv zmuVq-oA4n}1`4|GlBvK0QGm<*(AMYg&zlEw|2E?0$Xx5apBLGKQ=O!~&H)r-dHlxp zedq0_{0#2zDM+4We*9aoQD6Yiti4@qch$SmuOs$k=dPW6kFEm8o+bO`@5Gov2BgZ^ z>Oa+`F*~9#?BN%$e~0<^ZvGs))DbAz;;?e(~n8zm1*Xb`ObOfp6K&Rm}pt}`QLsK%fjbE z^>4p8_`mb*Z_>iRb)|U)4Bb#|X;^jC0bCq~c_Hm@y-uhB#CrY#-wgj=@8Hb|<4PoY zB?Ly15bnV|N5!Nln&IWR48=Na?Cv!VVvh#jwpXnt{oo|kIrlK~R<7_ya zfT<$dX82?Phi!HT$DCLZWiPAG!)a8N$fq&rg!ea4`L5E`Y_gBVu&st<*6)X~weIV6 zERyq-kgLiSa;ac*^+Zvcno7k;gvGTyA~#&!@zSXBi*1=)PV?G&+CPzqkI2qyN%amx zqyuxVjx4~v91TZ7?b2}tRCKwE%P#SGZ#^pY@i%X?_mNnu6I zx|-<)3UwM0D4#ghZ~0u<3wttP?AT}T0g}Vch{Hw}ytK`&SuwQU-O8ncSnZe=t%Eaq z*;!*5YEmY3vVOd6DC+6B&7k*0eq=xs;v|girvzhi4nCc@x^AQE7IiV|B zmDv%?DdMv-99BR?9kaEuwR`d*6}I?=Wg<01qR7k3FR=O@Ngp%^A+9BB3zC$%+k3!s|8zvD=&uc?5seXWIj_r8qqOLD|z5uV7zRkK9=Xj|w4D zUSkg5YzZA7c-i_!!R;_cfH^ZRu)M2xw_thT#I%gB5mp#H<$I;NSw z@(Ybo(*#Duk{I({!QP#Oe1GOYNNE3tb%7`UUoi59dwP8IFBn0E`u~EFL~I<4L}xjA zpgNono+|cNj|n^XrXA60b3jpJ3{hU2+x$99fKZ|y5e!jAAsy|~=;gRs`evG`85>Np z*H1nF2yt3f#ZIb-HP}rSkz6ZFOk|N85z)anK82fnKYKIwO;YQ>@^|C*Julr)-TS`F zZ(GLG{Lc*jt{meI2RpslLlBq{QZB!(fprnZ5hn(szM?Af#S6hkW$iy?&KTufg2-Eq zoV4(iCJbD{#6u@t<|-|4RM5z3Y9t1OB!6M5ghU0%W-N&<+ZJ|-8OHz_vLsM?@st9s z;SRNQ7CG2eXyq1A?S2)8Gv%g-bp7&oexR-7k70QXNp_Ww>B{9jT6Nsq?=|I_^peapI zNvyZH2QoT6n7h^NwAJK-i@WI?^!P>vc)wfbEj77TIC8yV9B+R0BBUDzo(+}?u?9&u zjE+0i-!b`t2txd6MzOVgt>s+l9D&@3n z9E3$+Q`j}IRYN+r5sJkLjx#!v1Z!se;FEZy48OJ+Y=)Xl4Omj8k86Y4+ftjSr=fll z?8_H**ta6|(ID>D0;GQdV+$V*aQn+cCLC`qL$TKD=3(f6AXM4%>G&fIs&n@jC9MZp z@z^>f@UeBX+9E01l__>?KhIDm%tq6}x0WH^@(DMwu9XxjS)QC*j=xZcGCkiqB6|UT zD9ZFLlq6sz>7kY}yh@NNx}O#w_S=O%8ig)Z;mYa77cCpdYOH1ebrma#2=(^ReQ1&JHOs)BKK?l8&dw+`8|qy)nPosH{NTwW{{1YGuFiRZsibY+9*Xv)wRQ&)qmrJhxUU{rctQ`QrP*?8oHl>91P-P(P7?}mpv3Su``@mVTy^(5Zc3cq z?kz^?E^vdSo$+)zZFsbntf=UNUuN`|7|SBz26IM;z2Id`J(^}Olp6Mf>%n0y%2=g# zx*q%714I3L<^{?Idm^@LxtIOiS>WDSLF?b!f;&dZ{EXAhP(g zcAH&IB^6cHz>*E~1SL;(d;1ofH~nmUFwGKf4K)_cMHzx3&@XXwAG$HJlu44b-v?RE z!iNA?DPeqxNM540_3U)WjIz1jgZrpH2Z=ry0Qgs3qSrN1IaIptQ6@#r5`UC;7e_>_ z0ybQ~t8mw7vv!~F0rIg38Xuk0liu!#u?opCWD^+$@Pxo80Y0(Q+8Eyj!1xSlw&~$1 zjgbc9uo3wdKWe5Xfgu^@awCgNn)%ZhfywLo=Yz>EO~#1AgFe&nme?6zNNDHpp?(!D zlS4OJsXNkNkCG+*?oM26hr5eVg%@e$wEEq>Fz6Vg(Bj~fuZVoqQ?3!adu_+%nTp=& znS-{4Kz42diDx|F+3X+41mjLW60Ul&D2dD2@{#A8YTE=rmz>jXPo_MVgQ?e;V;|jH z_`PCq`mS_EDUQ+;p@$*w?InYuqFz8Y?Y!n>!NMy&0A zWPsg>tA!#h6#RISxT>{9K%c6t<~;4HOo@_9!~8GtMn^BHk>z`LrQHt-c7!#ugH0v= zVquYF5f<4RLOPtOB@W4=PvepS*ax1h&bx-ce^AHxbV%QcwKenN4>boXm!JpCb>v#r3gw^ZjH(-u!CnsbT?%7 zg~XQ2Cqg^T?BfCM>p4Gt&K1F}Xt zh)9g&_GHa&Nti>k+l=lM$yOug%U&WvXGmF{pQ%IZd~?q=K|8B^v_uqtA6=6yB&Z9a zDQ*c6B%o}_BOJHYkh>!Jrf!goWU6D_s%t;}c}?BOjY4yBEhK^@=+A;Q>rr(E!5bV2U!P}6@{1@%8Z zpZ<>Te2DLmXlj2DPV5wX#x@~*e*YpTW85X5mK7tGrTbEWj(z6WeMh;R2JXy~wR}bW z;lCp0QTqEO^gHYudx5Duv^>fpI@}L?r?;MzUiQ?Er`cO{6QVNx9`2o6p!PLi^7ME; zjkZlpGAF3OoUo>*3W00L{JI~G++vzTP&*jnpg{Q<&aR&bmtbg9E1#kum6Xqa|*7kYom2Kwr$%sJGPS@cWkqh z?AW$#+qP|WY<29M{=akT+^ktOYt5Tg>tfb;$9M*JV23Ql9vo_KYkASyx6Rtox9l1L zd@8uEkzyY~iq&8-h3lS*qR-m5Zr&mIS9)c|uQvwKzrFv-E_=lXB9LYcVEJomFcPv%WsO|wTLrX#D#BWQ@(!Pl0 z(OC99`(1v*g7REkKN1HziV&8B$32B8J**q~3V2j*Hd|v~`eTI*8my5<8|kJO3!Wl& zlopfFB6)00Q5crg&J}W%w&Z)NN(K*QnIxuR_@;$ed^X<4g48i;Lct>kJ9V|>-ntn* zI0Mvo{#~kk)1>ogX8ye^u9vs=1uBSBY95Df~Hqz8pjD&ak=m$4H>HI4#_CtJ!h!rpbp6mC@l;-t_vUqeyHI=>R_R7d)J}0!> z|J#s$@|M?s3h94hPPNio(t2V)004yZ#y4#iGJj%eOuVAYOkylHmDcIBY=B{iYtd23 z(A;dwY+^?+eb19~qZ(h>&aUIzW(n<&LeKg6b>S_5)oHks-*7e z)*oJd42G4t`OaLIZx}CG`g2u#b?NDaeg%1BAUI=|4 z*-Hp<&2RHtYhMT6lmjx^ z@w2<0!ln%K8+IEkQAVq3wlsOvVoYQX#VZ}OxlKqtE>jb6PEW}p&;XXa$~ikI;U$^M zPPz0)kx{yfbR~GxGUU;gh&PIiH^r5Mnvh9Mu~MR|l4q<;kL>87AOn8-CeIY!r+2Bk zn{@b%o8oqN@|x$lg4)vPl`WvcCKb3&s0|+WrwiQ1qYstQ7AP#Yq^2ywCa26_7$*B- zYvvnmaZRF1cKEn3L)1fj>(PKVKbunIGm9sy3)pf zgzO6StB^#n$_GPPTc4sPYb+MaC9^%7T7k-z82vsB(gz{c@av9Q(VPRoVm+#?#h*D* zYQLa{c~}-Qd|~9ddXi={b19(N572cliB{8csAg8LWCJ7=GlBZ&$lw{4jq*)8vS<1m zR<-^5*PjThmgz^ZwxM9`@TTzKq3Lstu&(~KQG!WJKb1@y<|aB=Pg3@ZvQXUT6!Kr` z(lv7MP-L?R`w#6l_iP=50=ir#OB9Ktm&QiFj=EG}jUH4JL2Dh3DTWAIL~uL4OE+0e#Eq(~z#-O)uKPtE!u z;nDejaT`8BO^FE9T~*WwE7@aPKnHE84*qK8;qcayJ$~4L47TfoaTLItB!_(~r$2$W z&*Op>w5K1bclDB`EJPrK{D#(DeNsHt3Hjra}({;;pkN3_H2ic~7A%JSZ`pYuF zDjc;;OHp2#AdWbZIoDVsp9Lc~3nxzKf|mY+2T7-MG` z^sZ4^qEaaEEvmG0166~k!qFu;hcDs}j$(x8GmqIcK3GD1PMpAO#rZ*6fuFf%38Eyy z3P9Fi{rk2QUudl{N!I8H5N^$Ep@Ic$0odvw(f1llL8a0;^V@_4IrP=4R6?w+rFoj9 z5Stn%9fzB9L-Tc;Pi-$1VIX4qs#K~}=QF-+pLK*4T2_Gp{yPLOgW41NVg``VpoEDu z6Jrg-cRs;C2n%Y~KUIaXM{c(4f#MCe3wu1SvzEvlaZ=S#KledOwdmf1?@Q%0p z!PQIQ^c-&>mCs!Dq!oM&m@mz-z!1znvjmuN{?fMV6`O^#>x~38a->UZ_VD?!Zq0KZ zKz-s+`t(y{$Y4uWs7`hZDZT;@J0A>mZ*=%;ZojlRY(0KF%`v> ze)U$D>dS~*!FLKwo5^I9v1W{qihO&QMJEF9t5x$-ZlbiC2bL;}iJ1=P2E&toGJGn; zy%-!KE!J^$KS0fobx8q(>gULa88DYGiiH*>gUs|Bnh-eS#;6@ zHNN~v4Dx&7=sv+%anI}u=de7^fKhX|V#oo*}Yv zlo=Ig5JpbsfvKh%YHp2^)aVgCAG%$}5}au^Oly%9ea>n6?snX)vtpuQa&%+Cpuee@ zZg0J7=s9PKL0C1*bs3yExahoh=y{ZfV2%CCjNy@sm_r~(mF&E9w51jsfhnH}x-+sk zg~J3<^92=I8m1#*dm|(aju%-clHL090^u3= z+U8>Y#qJ7$9)Z4{i1lb@n`?oi9dfjD;4-&!r+_i$B^&%IebvNl!3nh9mGI1CQMmNuwpfl88ttWh0JF5r68@ z>H}dY`Ms3a>#&jDy!bIUsri>M`S+_8d!Xq|BsLh>zF&92>1FflX6>DzAhFp_VVH2+ zu1NfK22P@^JPv9w&^k7zFzr(uY}n`4E8a{aWqI`B(j>RM65m)&kPE+8$p0LW5L-g9 zY}S9snvosn5r;;YXPls|3t3JOsI@S+&q_7PXUtQ|Xe+gSyNJ_3DoYSk;Z_uL02d(+?X zV55OIw}}SUL2WjA#cqm2!En8*F`H8|u?Qk`bMRZOCzA!D-OJq`v07CNUXXZ`*9P`R zM=R#IM}r9%cY`4#%;I_yvOo5khrG2)Yqk9OVI<-VEYiA~+eYGSp@igJEU}}2o)Wxn z8}=VV$83+i2Lpv#jNx0ejQ8&*RC_i4h&#>6LGLBRWI%W7|0qAUUT!GUrV|U+XS!_*a zaOH|~G#JTYmnN>0r$bsWddlt=KPWcos_5{SViV$<9cl+>Z#C5tUMrcc#8};=_GnLBtooYi|QZ_gkW!1xjoi?a3y~aFr`l6 zbwU|&Ce8GcshcEr2$B~7GeLmKvt=JZB$&oXHb|sL8B`Jieg>WhePs&)&xv+^Qi$%C^~M^G8Lu5L$uX?{{hXgFiik;j~YENafq6g zAu9sgmwZ0l%yuHCEhZBs@CnmHn_e$Z=0sMuYsu)lLuss`_Cai%eobRe7OPw(IjGzO z@jL{Yb<=H;sq#`CzfBiF0w4Cbh?h?At*<{OgW@uWDC?7-hI$#+1)fgUs6IqgHfzc0 zY>jxssdEtPNu}r?;lL1+bv^>PYB3GhE^QTu8%)T2^fIv(G`WBaQJC{6P$0_%g&@^Y z4u9msMy)77SNI&sH!qP1ir6h@rBW^m&~Y+WhNY0bh$lxo8yq1a&wDhLm|Cw*kqu$B z40LIy4W@vXu1O0MuXPEA4x_b1Qyn!qmy2LB?{Jm0tK?8pb2ikOtPuv1>gnbHc){p2 zO*A>FQI9FOoakZS*!3q*OW|vWd8DmUdFS}0GL_+BKkM3BHH)hE$&At`%V}Ea7C2pg zEVz}7fOsQ$kAg`y1;G&0y(=!A`6`B`cW6T_dUwQLpaM*hLBrv(kSAvOoG%uqG3WuIBy|iIT!O1oJ)03*MIhZGB1s3Fr zbadADOCGwu`F2r^zk@iL#U;v|X1O^eJJ0W$ER!}a$SThxZgg(#bxeyI_!K)O%DEIZ zH-TgaOOWmHV`V)cBTbCz9fh{D|F{lkoMhjmg+?BaWYk>=P9e(|%A=rc?3w(m39 z153$)_r?usuh94dxK!v7e>V5b^ZU_67jhzI)FQS6#5wR~EZw~BODiXbTfsMPTxsUy z^RAy?AiK0SM32mzuJzeFsFz3aj}5BdGRS8O0^rI?-}>{-JEw;#E(YZ69aBY^ zn1@Q_v*9CFW zVh|ffv3|fiEhVmZy@Q8eOE)}PuNTU1@;Sb_r9$D|r6evnUrt%x;v%-3`kw_vOiZDA zHI&7GzhZi|JMZVxy_En*eLC`L4SMCl2yqP>5^J`5Cv0M03V2X5bA^5d08JxPr0TE6 zJ9Q8X3~W!czn$YZ;HsDS#?8O8u0c);b(Pa6@3(+xmy`Dc($=cx;nhA})U%O=@)H70 z!gKe36Zj39%nzrWePz*mFUvH7*c9&&mhfv4qV+HkKF^91Iutoe6m(0eY%X2n1oEfx2Syu zr)+`0y|-9KvbitV)g$Kuq!@Q!w&QX|1$P8Twi_>J8Z~tDNJZJuF=|}}cX%cQjPZlv zfA!zcYVY~X+l^^?3KW!66Zo=6-EnxX#PH?do@lWHgk~lS3h{}K{L#G2tg}=>kd||I z>FHTUBoSlo5Dq>|vTE z!a0fUkIj;o$q~}7_A6DKHpn?q)VZcOcm&Uq%~I$Uvgp*-!hBLyxTS^`Y1SZA`m6!g znSK%FUt1lZ1(s24tLo=SGAqlXArV!9Y=|5dTGY z@tM;>6O=!xIx#7HqCaJ02L2^IU~q!1L?`jr>kOC=f$R2q8Uqq#n29=I%3|7c8#1^UYA zTl^7Mhhs$z5Wox};Hltx!_dL9_6E%v0R3 zEEUgfvPN|S?PG)MbNjKE=vIrH{FIe3;3&WygUORaIo`A15ez?Nt)Ps-8`2)3*^z>| z=maa{GXs@Pb!1-L<~-%O;U#$RQRC53xfQuB8NOAyRat!ka9{JXbFl}upmnW5Ks)*Vvm|Rkw5j^@z+1mSAjW75|q*R@;jajWKYd0_I$vf zHc!TMpiq~|CC+`IR+k2rmI1sHFnLqvJYzr@oT`X>3sYv?+2?;r;_2LRH`c18fUt;?rN)Vs#o3wXCbq-q>HD0ZkXnKV= z4~0ZDvDfpN!tuYM{wJ-Ds)LA8V1R&3(EKN+4?3~{5xjNOF~0v4P5<`sdAI0vlYL%x z#dEP;vkNQgj z780N;EaC!$GQ54N#JHH_TF{&GuQdq`(t+y1T!)jbd#~u<}pFG zqBD9ID8YtV@uUg$yW*lU(5-1U0z1ZZ)LWU)WWi%ADotXbXk4Fc5AG?WKRVomUHR&U zg%qZ-r-SJ-64ysC($s~EiwTy|uAuoZ#rmhfxKt1%YIle|O1&Aq&9EGs-S7Z=$9NQ# z6jn5oC3lTcIFpH8MUPrA@*MA_3BN^66KP2w5T1|F4t_LRX~^a>7SG4WtgD_Q#UV<{ zWQP<20yL2eJ2Pq|3Eu|+Hy#hbi^bnUXUiUGuGFyv zs=_dlRSRfv4U2-NCW4bz*a3wN1SZNIiv zc}k*sE^#t)Yf8e%L@I?j5#UC=T2~+nd>$>c{6KrP?ue02n=)X7*y8A_g>U4bE<>fx zn^XNLS)#YV1BM)C=UfB@c!Hu0lr&BNcLU{eR}L>ns!Dld`s;Cz3ndKC%f=8xov)jU zFksRhA)0Z|wYo+3H=@gUb^;!pP>;pH;H-~-Y8&|@q5cqzkusWkzuo=CB?(hPz`cOPUU@{ z45M()PR?OM;zsDv36}4{XVExZD%+_zU}|UTdxQ`agJey^tjDMu8x|PL4zLu$YN#Gg zac^JT1)9~8(h)Q)vlp23<5n>MMWJSj`F4!8;!U>rBliu1XiR19DW*K3>ssz%XzrlZ z>T(ilVxdTbppRZv!VzCpPZu11FculZqk!-oio3sI2PW~mL@}U{#S>!~Cukrhz)*U< zxCP%sG5j&rFpOtuFI$Ed@FG%oFk7y$u$qAmQi%D5op{MqZbv(24&Lx!*2v}}34c;b-T$3oHSoDKtKWgWd49pek zLt5`4Qs$&G#?tYz)%`$9orWSPjDFtp-FZ21nU^{^iD}BF!L^ne!z=uimewXs-5E|? z@OIlw`dih7KMW-Wc!%tnx$FgKC>@Q;%wH}cxmX@_QCM$Z(K28Kqgp?cY-naQc9=nh zh&|$=)|T=u*mLA3QEGFWmidEUg@_(j=Y!nrpQdoI8&} zLX*#V{^7zuO0pT8o48>(q%b$e)P}PbY>*Ji;Kqtt5wWfSR7VPw!`Kerp#>$FSjVD1 zyEn1oWI_Lk*w111nre0&Xwc?3*tPJUG8mY|^^N`$MR&3;3mkI#(&^#pMMFlQ)u%Wa zI|?GWPmHfMb(FZ)UBqjBU#vbRYNJe7C~-OU2rR540+MH5{S=GhMaBRYB+R5^w2rfc z_FbhFTCtA-i&}46Bsk8qZGvSF(5N{7VKe-!ZAbg9lG!Br{tW+#yyfcRYT=Y=hy9X< zq(6p_U(K ztjidkM$kB>?`bO@Z}U57#IO6Bxt+m99z6_(Jkcw%ZE%=mbvf!T(S=1??l_skWfC!6 z<0npNUtLzRE@7FZ^|E+-+1wC1OL7HFdW!S(De8$!WBaormcH_MW=SlK2|2qJHzJ>q zDq5onP)IK=bZ^YF^t~eAnY5$w`{N=FpK4^T$%kvgIr}1H9wbR zZmn7R{e)BH=}nr+*H|{Eeb+A{h8wz(m#j2nfK~?CQ9K$;{65Zemx)n)zz2|bpvTXvK-q%!c}2fB;1?K4va&bR+O*|=0usSt&VXNHWTOV*m^?9ezvJe$rFiV1}DnC2tXn) z1KE;xekCl(%Bgs@|8SUpW0lLtdWPM%vg{2#t=i~&d)x^iC@b6aw|wMNI@|Qe*%=^6 z;|St;_Wzbqif%vi3Eq^Zl6E)H+9z$EWWKo(lD`fh_p$;9TFS&9pihdDCZ83#eg2e4&ym1V(me zr1td8c?L5=B6giGe^hAtfEZv(0d<+`Fh>8bu7VTh$GvbgeBxhGqz3ruTFnDGZ?4bby{>^hk5gC?Yc3$5#XC@0}(3o=(- zyUzILDQMeTTxKDsEcr=eDla3q z838_;pIx}C*~QLY_)yLWyUwN`yw6O^-5D}u6LG8$sKevXS4>Yk(1ddng?WkG(k~7y z&`UzSKchFWBsJ)3yg2HDl#~2mdYSmZahducZ$*^mE7hDzy{sj_0HfBE2Goe)NzjNyqY%)p zN@1sc8>-w#cZ_e7S*RRtPS9s+k@afCPI(}y*Iek{_pB#EW{OB9?=|QeUUH4Tkaz~K z*Igi;-`}|IP`{H)@11rnJxpg6+Qm)cS3M5ZMUu&(x#!c1mHM~Dw&%qC+st+9CiN_t zx^eC%`M305c>y*59R$uk`u{ulo!_Z+Cl~IX+D4a_n&bgGwFtw{m6zbBxhn^{tI$@D z2=Q>pRODU)rHKmt2L!_%rOX#xo?ep0zlw1njkqA~6c8d^!;yB`0YXtjETdtLYZj7@#K9xF=i2+v$$dNTYGsQ!T&38wBw;Nw0khstDzRxOlfbe&PprTCN@8W( zR@S!sxFjEId`Y!k(%BqXN@!!pW{oR!e^s+WzZUawzNLa+kv3MwZPF|`a;IIz#o5A% zs~_q04~8L{=bi2%FDxmO*yr?1REWKyc)XX5Ret=1s(!j?MfT4tbFUW4AgC%=1CEncd;5chU88@|&4Ln&HFSRj$tr>U-(rdEPNy(THTacB4qxv+? zOu%42c&+mmLtftxwUwG$1Lo$hsIv_=vs}L)0BkLE!T-Me&m2Bb>%?e3B_NCk-l(gu z7zlV<0AfOc$!Xncl7&CF6afm2SPMR3gFH$Bx{9RXcuHztfG*6MsT)>;#j4E4m}N|h zC2DDS(umXcii-|aGytZk@aH*3r|V*o3~_sUlBs*J8$)6^~?WvqIGH{l?F&T>**Cj+Wxqo1m)h$_7E5 zu_NZ)DC@trr{~9MM&}*2X~x(B)tiVj11~i(1O%P?IG-*TXg^Q`l7J|chNX}1(OHZZ z*`~3sG3x-zQumzt=5UzpYkXz`&B>#WLyV^LA~(Rrl;yG3iT`|}*T$o2civkT2WQD< zzzUUhmEy$sb^s{OMO1oYQ&e7bGx+=DBC=j-uKWpXj3eNDIZ@#vrqO_n!*im0ITB%U z*;aMZ)r@2X$`0k}8QEz3B1{P>JrvUiR0;P8U^wxco#NQB~W?;3S{_^?2n+>C|3 z3)+kYw}hxx8B>f7a03!~y_aj}FE3#i5i{5m6IH{g_~E`>v=GxYMfI-qXJ_a(dtR(m z2aH(h*ImwSOP|RNo*xcQ2%K%8q$)Rdequ&)rEUs_(7e0J0o~u7G7g}v5L-2`D4^V- z&fGcztMg!CHHa=sHMoBYS##HrAv`I?ajIsDW}Y&NFsL-`;nGX zB^B8avzBcu-c0p$D5a`2)8FSdR zY0*mkKJyKJJNqG`(<2G~YAHNda*Ic*60(>l`c6$Vc7YvxhRO~mf?EJ)(-RnWPBE?7 zk^y$0W%c!K-D!jm)6_T$wSlEWE){ypTsZ(9$0h;xpfLjTU|VYxr9bJEU&2{W6cOE) zfuOP01)NqKMdzJKv(B|gQ=MevXp>{+aQJ}EbrGHG;gUcms$KV9)}}A#(AewA$m5VA zl5lGf1^OIqkz1G}Bz4uJ{dkXu`n|vD?gjyksLLddFQ8Y4;NIXYbP5->Y9DomPi_p& zpQckVEGOoz6U{d1Th?nGgg}zRt-kQ;vEc^^6 zVCJ&NK~2CiFa$Ap(P9#tFAfkz%$8uspk&Q}%l=Hm#ooP|Ss=H*!ya1XnVb)N0Lvo6 z_X6F=DQDsYmwkjhyLv!O`RtEaQRlj5z;1^(4|b<@$?;#{reg71B4r!tG~`|NQWDYu z02`s}8-KjpdButf$=w{O#dP!&AT7ks{fOBk8b%fy9{S`AddI9~qzjPWQ52f#@D^6` zwnSp6zZ2`aqbWjJtvK!A)m2^2&5NzOl;pAQs`i_pmcmLmdOtI^5nfVaw0ZlB$|J;J zK~cBJcCOVPQ0W|kxWLvmNcl#itO*P<0@@at;*o2y z%1LplUjKo=h9*tsm2;r9%XK-*LIQW2)6?UiS-XBN+mvY_s$$C#YU4l02@vd|Pb4}A<}n(yG-)6}xaE>UQ`6mh{ebJYoH7`hFHRr*e9cq$ z7n3EA$5+*|9}cU37+5A#fx@8}R1cU9+A+^y5UsRKA3b@S72E8u-4da@V}vFMJ2Sz(bh8Z;F$$ z-n`oTS+p+LcIkK}6Us4&v((d6oP1z3ZNn@r@o8H@9H^DwSIR36@bB)C7UJ9=I8^9* z;E-Obx6SLBjxN2nvB(?e=%UbKFEJK;AYPga=!1RoA)Swl#a7FVMIrpnx8JWid7f>k zvtDf4Z|QHn>?$NRh`Vo5LJY>7&W=n%1KK*d?JItMequ0do)#f!4UX*vI8XI9ACc|g zcNk&OB^E{y6@yW5;6$6>zuvS@bv1ls-zDBw5A`>3FvD370UNvkJ0zw#GhZ(1l<+)K z^m=cR0lfy+TA8+A6j|gN>V(Ee0-psi=bbBidnU``vWe38ZGa}~0`02wUivev)*l5@ z@>yq73uFjE9fqG<_-+8I6*^LKPCw9FkMm`GvTaq6y+99HV7Xb%UG71c;k}A>s}3pD0Es!IpL3IFo{|(9*-Septi8N<-q3U@qrBYx;PO3e73Hj2JP8 zIqS2Z*Zc*FfUJNLdK7d%S=GFf<~<5y{mWnJoqJO(o*|LHsbnE?)}ld?5}&7j!;m() zK<*QQ5EZiz_OLg_P01GC9%hQil3t^AYZ-FudTzKGfi8A+ZZ)7j;G%HoKYuf)1AY{fKg2R8|= z4to{$D&xO7DK?22Brl-gHRfa-j-?-3gm)s{e8^qBGcs!C&zE-Dn}60UY@DjY4%aNa zO`-}SH2HI;V1`506%k%FSQJUQ6EZBML>5gc0lgg}t|Kumb*yepD{?zttH(Gt;$;*T zGiz@Cx_Ihz;pG-b$79|+sSRirUBeaq6nk0odFaxV+xF(*#rBNfp+5yJ--30H7#X9*$cN&u@Sw^Zk6e0- z=ihx{bP%W(T3Q&YFsOACnw&dwieB|i`*CNRc29YTOD&(?pnSnHoAWMuX?mw`H!-7R zcZ!={9>m2fZ*Q$Do(uCY7tf?~DOXYX1+=t^2=&fMc_S4Ngs@%=1)N_n*01+sB6&u- z)JO>hJ)YG2X5>7$yaK%cUd*aUb`7@{#@pp&=06vsYJC{D-896xFRzgL+)}rU&V|P2 zJol3rMEn)RQV|n>8;4V($)H`J;C^2(%8gFo&AIg=CEGa-W8zdHBC>o-k83r_2cD?Z z&CYJe0k-@g02TySL(`nZ0?wN;f3h2&06$=eE+2oaU0`@~IlSsgm@}F2TXd2x7&x-` zj@fNow!4d=x32f)ME~Tn2{kr9y%WFl)aN#U+BOJ0EXJDX6R%fman$7D&FPlVR4xBh zYSb!HWV^OwzMeTaScM?IZ(l;b0m3hiMm}V+JwU)@G3nslX#ZWURORZ$QB2N$!2MF(_8v6^r|Nbi(jIJ0lYx9OiI4u z)^1>!dpDWvrGFNAE3=XHRo+E1L~C^2jj>m=31jIsi3*%wga4d9T2dl+4Hk`RIt?$e zS6KY>gQQPsQD~P+GO#a!$PV+dxVos4k$`~+oo}8Vl-p9GiaKH>0`VerZOf2x z&&WL@NR!-K#e^XspgZHXQRhcoZG+^ngaqGy#CIt-<50GEeY^ISYXS8y&7qY7kHn8F z#)zK-tJop;&sf9VdOIQ4!eXtccf;hc0bxq+5)T-|pIB$}91|JBvcTK%gY6&Hc)7TO z8j(KVdKX0{y8oX+fO{`Mhv0yPe}w>$eS8 z&Hgge!-^tDPw#^Z9sutm3a3d`8(d5PQQKuZuN1J%TeHDk9}u-&nC&7YxP^(o)UX?T zzv4SSxbnW;ycC|=kG}37VE(tCTQu1)%ka$O)&B2kP%t|w*t+%2 z>m&BRS1zbQ{_VaEkm0s7>0FQgY`t`z{A}`&IoFPeB%{pxX6QR7Q=>{aM6rAbHYw-5 z^Zu`ml!Y`v_Vr&6hzI_E+Jr?s2e7_RlqN+*xGt~Fw>j99L1ID4_?Ohb{z8rw!^1x= zztw4i1huiO!>tkr_ zr0r#_b3amg@^w1jBJ3daM;%Qs!F%=~81_A+7{|jr8W_k1trDAwDD;c$FM%>#1sL7N zcsZBYF%$E;2DMt&iduLYvoG62t~|)i#majmuPp~?!7=vE4{-xw-Q4VY)(q{?X-3TE%R#`451jj5O$j7WB3@xozn}|((q0-a=%-J|?xJ$Sv zR#;3#_@d13!n`i*j2+VGjmF)I(AHccEYBMJy+9Teq(*5Vy8VGu~Xr<|8-|v~nx<7K>hG?US%2io{O1CsLl;#^^8j@TB26 zIz7S@U6$by>qx4f@=@m7f3xpPm=6g4fBAmG|I4?S<3vil@r6!gPND$He-8n~bA{Jc z>Ey-eQk4F&`x5i0A9~j15^cFM>oQjY*P#9~@WT*#gAmDNg%M^2zrOgsPt(7@K7RcG zF+3+(+M=%eNjp+X|0H}Q=+YOklf6t&?uLpL5z+f&nB-0wMCE00h` zCjVb!3J|S`-kHfXDY*Vvolf7TYm7mW+}Q3P654J;4g0me9>w?pc70;12Uu^VO@2GU z&mk&llq#nKZMi{_Py=_SOrKyL!h~e50#Q%+&I3M@$Hc2{8KzT0fxRC?Uo4w|MIXNt zx8)iv_a`2)+gsIR!YpI6C;4lR$%^_@rdgZl6Q7hvW!X8g(U)h#XG<~Jhy$D?Lr?(s%o1P zf*2B4*7ik7!kQJ{3K^b)pOW<-FdZtiQ5{Z%df!&Zs;fl)mxM)d5RyBIVQNT?(2#4NL_kU*= zUW?W(ZPzSOVIOjZuP6$z{^hLvQhk&VHbEe&;$MQjfmF_3RIXmaME*=L?rNz=c!h^2OB71la2QL2`%{ZHxS!+OsSa@rfm4VOdg$N%2AHGvogv5MhPk` zzq+MUrJ*|}*45%Ah~$#M!HPQwFLbTdx@M1Ze*M1vq1$wk2~BZdk_98tZjX&XHOuudfQb#TY!Rkk9O+&)~NYe*^h>!0;i&i}ZZkoDph|&B)$|RncOvF|_0( z)@Ief?%k^RRWh?xmZ2eH8*qd3R$Am@;!;R|S@w&!yzshTO+1nvc~x}mdop^7syHt& z&`hALB}Tq6;VssVa3Vm4CclbU4)`ePEsc*>F5RG(G81yXr0*d+3QOD6jd<+bQ|=qe zEg)^3(vekM&8t~`7_6&u?JvtM4X!Tq3r+Na`9rvL6*>X(g+Y1njA|~Y@O_=r%c=bm zb7xD!z|M_2UDk#KFv!Qz)f(Nub;S_(_ZH5(k2%xZKNg$NI7_gGQMgwEar<7ypmoq@Xyp^l5ENeZnT>EQJPd zGy}S|R<)6>1>6&zOhaVb3!3f&DF7%r9~+wFB?NhX68cj7Wfn&+5X`wTFyxliNA^aE zn)m>|@%5i>tw;H0{{;4rfcgaa{{y*t^-u}*_=(mTSU{aT4dEoJWbomp0ROl++s!?j7<0K zNWbD!X3_wdslzJbS!l9=YDT)HBn}Sk#R>Qm*AiwcW_XSAczSj1vnh)uc*k~8jKJw| zR~qfYM_|#EGkW8?3r%AXK;YyyIiz4WNV#~N9WkADoYuIbN{0LQj0@Q6!0Xn>fH$MI z*~z{n5i;mkz{;HLWqTDfsIq*jN`k^9tgPN?lfJpvdA2DRM>DA`LU*${lLs`o;u()T zjastG?_pI9*6uk)Vd}|{^2uSyRTSvU7ByNnRp9$;Hb&9L0iK5;=-xIk9hUNsW9c;l zM+9|jZq=Vi67F<_8f*bO==TUDG1y8hvDO?xe4gsyTBk&`HUJ;!bn&f&Lix_@z>$kAsnBnnC@W{OA4LQa}zN`~Z8PGRtJX7&;-g92K*81-14G zw?}^c6?#H)6e5ZLkxwUhwrlC`z0l8A^HLDV)P4|&nBzKJivJPMCwR2Wqv^fTPt0Id*@-!WtqVF=%Ao*Ju~%rebC9~ew+)m|AH_Cvt!HR z^K9sS^e~i)h;`sVv49&&^j9LTDQ0URO>Za(Sp)(C7Q1FJ7;&;NLn+AciH`rGkY#d$ z+Dc2acu>bl2QR8n(!=42F)&;l;Bm&+>|~5mHAaY{jntv*D~i>Wm?S&vX{fUEO}GYn z&wE?nj~uT!1jIrrwDn{2D>GD%zA|d>!T*p~6j$j;Qt~j7OJ&8Wk$mEFI^m8rmzQ_X zPXHRtqgbj%P$y(WJRlP6IW7iUu_n)REU=r}G1H$lxHgnj{d_AqZe^yYw%}2~;?8Km zL@{0{i?Oy+QD9+rnKd(1=R(Dz^gGFH?L!Eqf&)SBvhFas66s|{~4NB0J3VH08}LoC;7pt{?To`2Wj z`tA$Q7yTsRX9CqaC80xNomy>AS`%T`+pMI6cSVTSgLo?}Df>TNoq1Ff*B-}XOj#5H z7KjB#mas1ZPY`5_2LiGNN}E7{00o4SO3+{{V1UT>s9_TZ;)W;+h><0c3If6dMB)Mn z0?I>u8huqGgrz7_+&URO!6E0&ADR2f?|1K=$;{k)?tH)VIO}^qHKNAV^sWyPd|vRx z^PQ$DH*BAJ8f5n|)rfn7hV8vB{gNC}QJ((1_2)EGi*HRnd0-?)KQQ(EJ&T>MvFW}_ z)31p-$TQ z?1>6awB;{splC~gq5Mv}yp%dMY?UvWIOX~f7<*m1&T;5+16_AC!1{;paBQb-#5m&l zW0RasrJ9ljtyp7k(;zw}0bLPIb>qJE;Zz>+CrHXus|yyR1{;F!j@aPJ zbEL=tCb_4i^guP{L+C_J!hvF8+5kQHj%}{f9}Q*m7f*;c7Y&@APWtF>u>`$sFKLd7 z9e3ztUaGm~?D?C>^Hr1&i5=({|92Pj%$}9T?>}C>S{UMzs@S{@^NF3WtTa7!%+5n{ zO+41j+K1jdGGJY=UYm9zn$ElhzvB~z5w+L}5?!EJ%dahDUj4(FtI{RiitxOpbiFQgP& zc=l+yxHpdVlEjI>7ixc|;EEwAqcD&3A$|UHwi`8LpV>9iBRzO^+Vz zTkxY!WNb8vsb~{%-jMA)Gput>7QzzH=Vxi>#?cAFxT}Y;uct1l$TQLu3|h(i2Dw7! zE$(@7l(#A+i|t~ju*pcn@aUtypT&QLTe>5(XV4*|I&x{8xQ+C7|9!gNO#SgBi1`g;_u?vqs!SA8IR|x`u}_qz3xPR zbBM3YP)l3xGqZ3xRuTXH;^fIO0VTJwRlrJ~?6PaZx0CoI9)|r>=5uEcru{iF5<$*u zY9i#D+n*{*;?L%O)ay!8ak_PAb(GW?RqETL zj{;dWUW!~gc7_FgEeCJcxC7`u%ws$>UfTz4|3X3PDYDNJ7A&m=KyMX2@JzF+cH-_P zQWA7GYk`CxjS=7>@JOvYu%|)(csNwv3O(@IBFg>L;6UAKcxfO&W>_wdLb)J7RooX) z9%R+o0bd)ux*|YGT2>j1i)@xP@fJ%skR|1&$W=%iEpVTjf#;v zErH)(z@Zzq%E}5ZH~_2OBy0PeYx4z^E92<`GOGcoOOeN>W;^K2bNdFC$Op4{8faH1 zXa^qb;28m{GU036vgi!H;{^aRiE5|~ZiqHS?t}nsNLAbokf|L*5CH*2xPgx@h5|Ch zT?nv70Odq*Q?mvb>1ibG1?^Q?(Y5J*2ZI`LAiq%oq=IPXtq9057=}8j25{=tHzOdaAq04U3WJGF zHb8)Eu@nl0M?mix5VQrHXwn1Vg*{Np7tn@G>2wf+yn)qeO%zHG5k)Z_0swIEkP2L< z)fp=kN*4i!7Ql64mukSEYkgE#5e4TZ8oL`*D!!E(Nx_UaSv j+6D+geLfC^M|+mQ*Ow$yL@ceNaI6S{mE76Panj42;u diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3735f26..6a38a8c 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=7197a12f450794931532469d4ff21a59ea2c1cd59a3ec3f89c035c3c420a6999 -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip +distributionSha256Sum=a17ddd85a26b6a7f5ddb71ff8b05fc5104c0202c6e64782429790c933686c806 +distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 23d15a9..adff685 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -114,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH="\\\"\\\"" # Determine the Java command to use to start the JVM. @@ -172,7 +171,6 @@ fi # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) @@ -212,7 +210,6 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" diff --git a/gradlew.bat b/gradlew.bat index db3a6ac..c4bdd3a 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -70,11 +70,10 @@ goto fail :execute @rem Setup the command line -set CLASSPATH= @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell From eb2235508887c0948c364c57a2efc8536236e1f2 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Thu, 23 Oct 2025 18:45:40 +0200 Subject: [PATCH 46/52] Update monkeylib to v3.0.0-alpha.0.1761237113556+d3f347d and remove runOnServerStart method --- .../GithubResourcepackManager.java | 3 ++- .../githubresourcepackmanager/platform/PlatformMain.java | 7 ------- .../platform/fabric/FabricPlatformMain.java | 5 ----- gradle.properties | 2 +- .../platform/neoforge/NeoforgePlatformMain.java | 5 ----- .../platform/paper/PaperPlatformMain.java | 5 ----- 6 files changed, 3 insertions(+), 24 deletions(-) diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java index 336476c..f11944a 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java @@ -22,6 +22,7 @@ import top.offsetmonkey538.meshlib.api.router.rule.HttpRule; import top.offsetmonkey538.meshlib.impl.router.rule.DomainHttpRule; import top.offsetmonkey538.monkeylib538.api.command.ConfigCommandApi; +import top.offsetmonkey538.monkeylib538.api.lifecycle.ServerLifecycleApi; import top.offsetmonkey538.monkeylib538.api.log.MonkeyLibLogger; import top.offsetmonkey538.monkeylib538.api.text.MonkeyLibStyle; import top.offsetmonkey538.monkeylib538.api.text.MonkeyLibText; @@ -126,7 +127,7 @@ public boolean matches(FullHttpRequest httpRequest) { new MainHttpHandler(new Object()) )); - PlatformMain.INSTANCE.runOnServerStart(() -> updatePack(UpdateType.RESTART, true)); + ServerLifecycleApi.runOnServerStarted(() -> updatePack(UpdateType.RESTART, true)); } private static void addLogToAdminListeners() { diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformMain.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformMain.java index a301a07..40f60f2 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformMain.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformMain.java @@ -20,13 +20,6 @@ public interface PlatformMain { */ Path getConfigDir(); - /** - * Must be called when initializing - * - * @param work the stuff to run - */ - void runOnServerStart(Runnable work); - /** * Tell the server to refresh the list of available datapacks. *

diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java index b04a59b..03600e7 100644 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java +++ b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java @@ -38,11 +38,6 @@ public Path getConfigDir() { return FabricLoader.getInstance().getConfigDir().resolve(MOD_ID); } - @Override - public void runOnServerStart(Runnable work) { - ServerLifecycleEvents.SERVER_STARTED.register(minecraftServer1 -> work.run()); - } - @Override public void sendMessageToAdmins(MonkeyLibText message) { if (getServer() == null) return; diff --git a/gradle.properties b/gradle.properties index 577c803..9744ca4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,7 +7,7 @@ minecraft_version = 1.21.4 # MESH Lib, check at https://github.com/OffsetMods538/MESH-Lib meshlib_version = 2.0.0-alpha.0.1760173787064+6126daa # MonkeyLib538, check at https://github.com/OffsetMods538/MonkeyLib538 -monkeylib538_version = 3.0.0-alpha.0.1760290533497+21bc668 +monkeylib538_version = 3.0.0-alpha.0.1761237113556+d3f347d jgit_version = 6.9.0.202403050737-r diff --git a/neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/neoforge/NeoforgePlatformMain.java b/neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/neoforge/NeoforgePlatformMain.java index fb76c0a..4474a00 100644 --- a/neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/neoforge/NeoforgePlatformMain.java +++ b/neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/neoforge/NeoforgePlatformMain.java @@ -36,11 +36,6 @@ public Path getConfigDir() { return FMLPaths.CONFIGDIR.get().resolve(MOD_ID); } - @Override - public void runOnServerStart(Runnable work) { - NeoForge.EVENT_BUS.addListener(ServerStartedEvent.class, serverStartedEvent -> work.run()); - } - @Override public void sendMessageToAdmins(MonkeyLibText message) { if (getServer() == null) return; diff --git a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java index 79273b4..e967288 100644 --- a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java +++ b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java @@ -21,11 +21,6 @@ public Path getConfigDir() { return getPlugin().getDataPath(); } - @Override - public void runOnServerStart(Runnable work) { - work.run(); - } - @Override public void refreshDatapacks() { MinecraftServer.getServer().getPackRepository().reload(); From a4743ff8266ef8a081439eb1d0f8a04f7cbea80a Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Thu, 23 Oct 2025 21:56:36 +0200 Subject: [PATCH 47/52] =?UTF-8?q?update=20github=20actions,=20please=20tel?= =?UTF-8?q?l=20me=20this=20fixes=20cache=20not=20working=20=F0=9F=99=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/build_artifacts.yml | 4 ++-- .github/workflows/publish.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build_artifacts.yml b/.github/workflows/build_artifacts.yml index 7ae91d7..7b2ec64 100644 --- a/.github/workflows/build_artifacts.yml +++ b/.github/workflows/build_artifacts.yml @@ -14,13 +14,13 @@ jobs: - uses: actions/checkout@v4 - name: Set up JDK 21 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: '21' distribution: 'temurin' - name: Setup Gradle - uses: gradle/actions/setup-gradle@v4 + uses: gradle/actions/setup-gradle@v5 with: cache-read-only: false diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 4832d00..0df3b3c 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -16,13 +16,13 @@ jobs: - uses: actions/checkout@v4 - name: Set up JDK 21 - uses: actions/setup-java@v4 + uses: actions/setup-java@v5 with: java-version: '21' distribution: 'temurin' - name: Setup Gradle - uses: gradle/actions/setup-gradle@v4 + uses: gradle/actions/setup-gradle@v5 with: cache-read-only: false From e552cfe8ef721b18a2985bf1d3c11f016348429c Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 Date: Thu, 23 Oct 2025 22:39:48 +0200 Subject: [PATCH 48/52] Add files via upload --- .github/dependabot.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..85b8c92 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +# Set update schedule for GitHub Actions + +version: 2 +updates: + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + # Check for updates to GitHub Actions every week + interval: "daily" From 635942d467971d02ab28c74ece227392236ac54c Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Thu, 23 Oct 2025 23:16:02 +0200 Subject: [PATCH 49/52] update meshlib to 2.0.0-alpha.0.1761252973683+89c3c64 --- .../GithubResourcepackManager.java | 31 +++++-------------- .../networking/MainHttpHandler.java | 23 ++++---------- gradle.properties | 2 +- 3 files changed, 14 insertions(+), 42 deletions(-) diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java index f11944a..4e74e34 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java @@ -1,8 +1,6 @@ package top.offsetmonkey538.githubresourcepackmanager; import com.google.common.util.concurrent.ThreadFactoryBuilder; -import io.netty.handler.codec.http.FullHttpRequest; -import io.netty.handler.codec.http.HttpHeaderNames; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.jetbrains.annotations.NotNull; @@ -19,8 +17,7 @@ import top.offsetmonkey538.githubresourcepackmanager.utils.StringUtils; import top.offsetmonkey538.meshlib.api.router.HttpRouter; import top.offsetmonkey538.meshlib.api.router.HttpRouterRegistry; -import top.offsetmonkey538.meshlib.api.router.rule.HttpRule; -import top.offsetmonkey538.meshlib.impl.router.rule.DomainHttpRule; +import top.offsetmonkey538.meshlib.api.rule.rules.PathHttpRule; import top.offsetmonkey538.monkeylib538.api.command.ConfigCommandApi; import top.offsetmonkey538.monkeylib538.api.lifecycle.ServerLifecycleApi; import top.offsetmonkey538.monkeylib538.api.log.MonkeyLibLogger; @@ -37,7 +34,6 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.Executor; import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; import java.util.regex.Pattern; public final class GithubResourcepackManager { @@ -107,25 +103,12 @@ public static void initialize() { LOGGER.error("Failed to create folder structure!", e); } - HttpRouterRegistry.INSTANCE.register(MOD_URI, new HttpRouter( - new HttpRule<>() { - @Override - public String getType() { - return "2hartqwf"; - } - - @Override - public Object getData() { - return new Object(); - } - - @Override - public boolean matches(FullHttpRequest httpRequest) { - return MOD_URI.equals(httpRequest.uri().split("/")[1]); - } - }, - new MainHttpHandler(new Object()) - )); + HttpRouterRegistry.HTTP_ROUTER_REGISTRATION_EVENT.listen(registry -> { + registry.register(MOD_ID, new HttpRouter( + new PathHttpRule(MOD_URI), + new MainHttpHandler() + )); + }); ServerLifecycleApi.runOnServerStarted(() -> updatePack(UpdateType.RESTART, true)); } diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/MainHttpHandler.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/MainHttpHandler.java index 6dbb345..43ae2b2 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/MainHttpHandler.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/networking/MainHttpHandler.java @@ -4,27 +4,16 @@ import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.HttpMethod; import org.jetbrains.annotations.NotNull; -import top.offsetmonkey538.meshlib.api.HttpHandler; +import top.offsetmonkey538.meshlib.api.handler.HttpHandler; +import top.offsetmonkey538.meshlib.api.rule.HttpRule; import static io.netty.handler.codec.http.HttpResponseStatus.*; -import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.LOGGER; +import static top.offsetmonkey538.meshlib.api.util.HttpResponseUtil.sendError; -public class MainHttpHandler extends HttpHandler { - - public MainHttpHandler(Object data) { - super(data); - } +public class MainHttpHandler implements HttpHandler { @Override - public void handleRequest(@NotNull ChannelHandlerContext ctx, @NotNull FullHttpRequest request) throws Exception { - if (!request.decoderResult().isSuccess()) { - HttpHandler.sendError(ctx, BAD_REQUEST); - return; - } - - LOGGER.debug("Received Request: &s", request); - - + public void handleRequest(@NotNull ChannelHandlerContext ctx, @NotNull FullHttpRequest request, @NotNull HttpRule rule) throws Exception { final HttpMethod method = request.method(); // GET request should go to fileserver @@ -40,6 +29,6 @@ public void handleRequest(@NotNull ChannelHandlerContext ctx, @NotNull FullHttpR } // If we reach this point, then the request method isn't supported - HttpHandler.sendError(ctx, METHOD_NOT_ALLOWED); + sendError(ctx, METHOD_NOT_ALLOWED); } } diff --git a/gradle.properties b/gradle.properties index 9744ca4..1ca0efd 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,7 +5,7 @@ org.gradle.parallel = true minecraft_version = 1.21.4 # MESH Lib, check at https://github.com/OffsetMods538/MESH-Lib -meshlib_version = 2.0.0-alpha.0.1760173787064+6126daa +meshlib_version = 2.0.0-alpha.0.1761252973683+89c3c64 # MonkeyLib538, check at https://github.com/OffsetMods538/MonkeyLib538 monkeylib538_version = 3.0.0-alpha.0.1761237113556+d3f347d jgit_version = 6.9.0.202403050737-r From 03492479658327443d3b711f92e879febd96f1b4 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Sat, 25 Oct 2025 20:57:37 +0300 Subject: [PATCH 50/52] Update monkeylib to v3.0.0-alpha.0.1761395863791+bd100da --- .../GithubResourcepackManager.java | 3 ++- .../config/ModConfig.java | 18 +++++++++--------- .../platform/PlatformMain.java | 11 ----------- .../platform/fabric/FabricPlatformMain.java | 9 --------- gradle.properties | 2 +- .../neoforge/NeoforgePlatformMain.java | 13 ------------- .../platform/paper/PaperPlatformMain.java | 5 ----- 7 files changed, 12 insertions(+), 49 deletions(-) diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java index 4e74e34..0075ea6 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java @@ -21,6 +21,7 @@ import top.offsetmonkey538.monkeylib538.api.command.ConfigCommandApi; import top.offsetmonkey538.monkeylib538.api.lifecycle.ServerLifecycleApi; import top.offsetmonkey538.monkeylib538.api.log.MonkeyLibLogger; +import top.offsetmonkey538.monkeylib538.api.platform.PlatformUtil; import top.offsetmonkey538.monkeylib538.api.text.MonkeyLibStyle; import top.offsetmonkey538.monkeylib538.api.text.MonkeyLibText; import top.offsetmonkey538.monkeylib538.api.text.TextFormattingApi; @@ -45,7 +46,7 @@ private GithubResourcepackManager() { public static final MonkeyLibLogger LOGGER = MonkeyLibLogger.create(MOD_ID); public static final String MOD_URI = "gh-rp-manager"; - public static final Path DATA_FOLDER = PlatformMain.INSTANCE.getConfigDir().resolve(".packs"); + public static final Path DATA_FOLDER = PlatformUtil.getConfigDir().resolve(MOD_ID).resolve(".packs"); public static final Path GIT_FOLDER = DATA_FOLDER.resolve("git"); public static final Path RESOURCEPACK_FOLDER = DATA_FOLDER.resolve("resource-pack"); diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java index a63b787..ca142d0 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/config/ModConfig.java @@ -5,10 +5,10 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import top.offsetmonkey538.githubresourcepackmanager.exception.GithubResourcepackManagerException; -import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformMain; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformServerProperties; import top.offsetmonkey538.githubresourcepackmanager.utils.StringUtils; import top.offsetmonkey538.githubresourcepackmanager.utils.WebhookSender; +import top.offsetmonkey538.monkeylib538.api.platform.PlatformUtil; import top.offsetmonkey538.offsetconfig538.api.config.Config; import top.offsetmonkey538.offsetconfig538.api.config.Datafixer; @@ -268,29 +268,29 @@ public int getConfigVersion() { } @Override - public @NotNull Path getFilePath() { - return PlatformMain.INSTANCE.getConfigDir().resolve("config").resolve("main.json"); + public @NotNull Path getConfigDirPath() { + return PlatformUtil.getConfigDir(); } @Override public @NotNull String getId() { - return MOD_ID + "/main"; + return MOD_ID + "config/main"; } @Override public void beforeLoadStart() { - if (Files.exists(PlatformMain.INSTANCE.getConfigDir().getParent().resolve(MOD_ID + ".json"))) { + if (Files.exists(PlatformUtil.getConfigDir().resolve(MOD_ID + ".json"))) { try { - Files.createDirectories(PlatformMain.INSTANCE.getConfigDir().resolve(MOD_ID + ".json").getParent()); - Files.move(PlatformMain.INSTANCE.getConfigDir().getParent().resolve(MOD_ID + ".json"), PlatformMain.INSTANCE.getConfigDir().resolve(MOD_ID + ".json")); + Files.createDirectories(PlatformUtil.getConfigDir().resolve(MOD_ID).resolve(MOD_ID + ".json").getParent()); + Files.move(PlatformUtil.getConfigDir().resolve(MOD_ID + ".json"), PlatformUtil.getConfigDir().resolve(MOD_ID).resolve(MOD_ID + ".json")); } catch (IOException e) { throw new RuntimeException("Failed to move config file to new location!", e); } } - if (Files.exists(PlatformMain.INSTANCE.getConfigDir().resolve(MOD_ID + ".json"))) { + if (Files.exists(PlatformUtil.getConfigDir().resolve(MOD_ID).resolve(MOD_ID + ".json"))) { try { Files.createDirectories(getFilePath().getParent()); - Files.move(PlatformMain.INSTANCE.getConfigDir().resolve(MOD_ID + ".json"), getFilePath()); + Files.move(PlatformUtil.getConfigDir().resolve(MOD_ID).resolve(MOD_ID + ".json"), getFilePath()); } catch (IOException e) { throw new RuntimeException("Failed to move config file to new location!", e); } diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformMain.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformMain.java index 40f60f2..48c6cb3 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformMain.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformMain.java @@ -2,7 +2,6 @@ import top.offsetmonkey538.monkeylib538.api.text.MonkeyLibText; -import java.nio.file.Path; import java.util.List; import static top.offsetmonkey538.githubresourcepackmanager.platform.ServiceLoader.load; @@ -10,16 +9,6 @@ public interface PlatformMain { PlatformMain INSTANCE = load(PlatformMain.class); - /** - * Must already contain the mod id. - *

- * Example: .minecraft/config/github-resourcepack-manager/ - * Example: .minecraft/plugins/Github-Resourcepack-Manager/ - * - * @return config directory for the mod - */ - Path getConfigDir(); - /** * Tell the server to refresh the list of available datapacks. *

diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java index 03600e7..f3289cd 100644 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java +++ b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java @@ -3,7 +3,6 @@ import net.fabricmc.api.DedicatedServerModInitializer; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents; -import net.fabricmc.loader.api.FabricLoader; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.server.MinecraftServer; import org.jetbrains.annotations.Nullable; @@ -13,11 +12,8 @@ import top.offsetmonkey538.monkeylib538.fabric.api.player.FabricPlayerApi; import top.offsetmonkey538.monkeylib538.fabric.api.text.FabricMonkeyLibText; -import java.nio.file.Path; import java.util.List; -import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.MOD_ID; - public class FabricPlatformMain implements PlatformMain, DedicatedServerModInitializer { private static @Nullable MinecraftServer minecraftServer = null; @@ -33,11 +29,6 @@ public void onInitializeServer() { } - @Override - public Path getConfigDir() { - return FabricLoader.getInstance().getConfigDir().resolve(MOD_ID); - } - @Override public void sendMessageToAdmins(MonkeyLibText message) { if (getServer() == null) return; diff --git a/gradle.properties b/gradle.properties index 1ca0efd..ff74d11 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,7 +7,7 @@ minecraft_version = 1.21.4 # MESH Lib, check at https://github.com/OffsetMods538/MESH-Lib meshlib_version = 2.0.0-alpha.0.1761252973683+89c3c64 # MonkeyLib538, check at https://github.com/OffsetMods538/MonkeyLib538 -monkeylib538_version = 3.0.0-alpha.0.1761237113556+d3f347d +monkeylib538_version = 3.0.0-alpha.0.1761395863791+bd100da jgit_version = 6.9.0.202403050737-r diff --git a/neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/neoforge/NeoforgePlatformMain.java b/neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/neoforge/NeoforgePlatformMain.java index 4474a00..f9d83d4 100644 --- a/neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/neoforge/NeoforgePlatformMain.java +++ b/neoforge/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/neoforge/NeoforgePlatformMain.java @@ -2,23 +2,15 @@ import net.minecraft.server.MinecraftServer; import net.minecraft.world.entity.player.Player; -import net.neoforged.fml.loading.FMLPaths; import net.neoforged.neoforge.common.NeoForge; -import net.neoforged.neoforge.event.entity.EntityJoinLevelEvent; import net.neoforged.neoforge.event.entity.player.PlayerEvent; -import net.neoforged.neoforge.event.server.ServerStartedEvent; -import net.neoforged.neoforge.event.server.ServerStartingEvent; import org.jetbrains.annotations.Nullable; -import top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager; import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformMain; import top.offsetmonkey538.monkeylib538.api.text.MonkeyLibText; import top.offsetmonkey538.monkeylib538.neoforge.api.text.NeoforgeMonkeyLibText; -import java.nio.file.Path; import java.util.List; -import static top.offsetmonkey538.githubresourcepackmanager.GithubResourcepackManager.MOD_ID; - public class NeoforgePlatformMain implements PlatformMain { private static @Nullable MinecraftServer minecraftServer = null; @@ -31,11 +23,6 @@ public static void setServer(MinecraftServer server) { } - @Override - public Path getConfigDir() { - return FMLPaths.CONFIGDIR.get().resolve(MOD_ID); - } - @Override public void sendMessageToAdmins(MonkeyLibText message) { if (getServer() == null) return; diff --git a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java index e967288..0046834 100644 --- a/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java +++ b/paper/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/paper/PaperPlatformMain.java @@ -10,16 +10,11 @@ import top.offsetmonkey538.githubresourcepackmanager.platform.PlatformMain; import top.offsetmonkey538.monkeylib538.api.text.MonkeyLibText; -import java.nio.file.Path; import java.util.List; public class PaperPlatformMain implements PlatformMain { private static PaperPlugin plugin; - @Override - public Path getConfigDir() { - return getPlugin().getDataPath(); - } @Override public void refreshDatapacks() { From b265d135b4e55931ddf38c290a25c0e1756b6c2f Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Wed, 17 Dec 2025 18:08:36 +0200 Subject: [PATCH 51/52] Update Gradle to v9.2.0 --- gradle/wrapper/gradle-wrapper.jar | Bin 45457 -> 45633 bytes gradle/wrapper/gradle-wrapper.properties | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 8bdaf60c75ab801e22807dde59e12a8735a34077..f8e1ee3125fe0768e9a76ee977ac089eb657005e 100644 GIT binary patch delta 36466 zcmXVXQ)4CEwrnG|ZQHhO+qRvo*j}-1c5K_WZL@>Z-@f-{{(%~(8dWpl;8)+_uR3dZ zfZQ)egq&Z$i11kM_Up+}Q({I9MU9QZb2tR|yWGN*EITsYQsXf^5qA245#ac}VN7sh zh0J3lJ2M}?KHXivdw;lL^3<*X4-B$3ia;m37Kqtw&i zS44bg*-wcSL! z3SdD4tBX;ArwHhPVinq?S0M$-NDOHL41GA}u(Mv5lJs<>n1t_H{9Dx+iP^!|OZQjg z?r?7~D`^kTzf9h>O*8A}exIMuzXc$pU~b^ya#D9FHgvX-Q%Yzk&SMjmRXXNaWUeur zlkHbC@;ZrS`g8^HFE*ztUGuNoszWPjE*%z7Ig86rushQM?7yOw$~9=Fm8+HQUTzv? zfJk=PC+3tWDRvq{9HGU^Zu%T_j*8tp%>TiVuoXOEUm?=ke1bF@YIBp42=^no_&WYdKw$ss1@Y-se*rkpi&K6u}~U$255Np z?7GN0p@Jp>xLr#KDJ@^^k}45a;HG1THPWPxvaJ=yARI7bHIW%7`oU5K?Zzz`nW5_D zr@AaO1KtwBXLQ3AdtS+tV8RsHYw?WXA}~magC2%dki?8d@kw)G7AS~1Mi{5@cB`Re zhFOH2-A^`}!>EIWtoSO%}~`k7X@ z@Qd#Q?2$C1GC6$WnOnF6&3iLUKO?&*=wT=QJA;RTu^aiHh9T1=UUCc}c#wz@jeYns zvk<0}xrpSWOn(w+ngAey!wL-gr}+QXrU%HlghdPxfNTZ$C0<*Mf_SUTBlzydqQ-|} zB@;h5SoD)PGJxL9yM-6-`Z=S^IvL}Q@C%IJA~4ZnAO2Pxvy>hrm@LM9Z2EHYVRCYM z`s?fA0Nvm85Q7gFw~GeAV71AGFwdq2TWN;^Lw$=~GeXQBUxL3Hcq!!(7#5i2&BId7 zYls^x%g+K(yV!&l?X%!0nZYjEh^X(>p-NVRs2nf-@LG5nMYwi+AhZo}(V+}>Di&T& zQ6M}b^7cHNQ(9{jop|K{8_u%_l{JkpuOY{n6yOvUcicc0op6r22)J>!RHvMWt`W*V zUO7)t$c%B@%MUdS?H7=G8Z8DQ@60bBMWS zjK>B9@LWwT`0;-9+`>2qK`lE(5t43;N7ho7oqWH$L4in9EWoHQ03@#V=2&?;3QW|*ThDm+^W_8PLz=_P?p z7PHSz5MvfwXE@_RYBaN8kHQF*AamlbM=mnH---W3jWF;?Q)W zT?E3Ih|JH}0OS_q`et(vywK<3Y-o&4lySM=>7Bx(T$#uizd|^`c)8!He?5*a1iA-5 za!4*i@&~W#|5sz8{LcJZzkd8^1_2^s;Q&tvNr6#lWB^>XPN7WTZK@5 z8ojIux!<+(H17=O3EubL9sSy$G6!G-3*_?GXnu;+Iy)$Cs@cjafZTlN@%m>du}^ zD4bg?P7|@FjvehnIoA#=$k(Tds}4Fw9Xa||Y5=VZBqW$rV_S9XfbAjM>*n~x8DGNu zp!|Va?Cn!gFjvxP*@V@HP}^5%n3ds!GkhkY*7i8{EDhz^h#EQ{FwuZ$J4_@mOfQ^F zB9~7JyU?e* zHb(HP_@|SQp?06V-{H?Zo3c<$qi%}emH?RjGN>W1aQnQ^YUCw$a$;C6{`9yFy7s0L zQk(B_`F4w9hMC!KLrfAG=IBPJM4R#z8K?N@cB1t5-C{CsM(5sFU467ukNNOAjwRn` zh&T9H`w%qgKAcDwHj7V2Io1eisR&TJ%U%5yxf}bCnQ{n*_(k!ywLHM-^;gfUTzL_f58`f7`3{e;7jHS zS-!BrUgGH%7EF#6z0gy0ZVW53^gTlCC)hWd=68=Cb6@Sy`8(GJlt#7Y{$TFwdg6(> z$6QM`b`8n27K;!=+cQQ7qex-G{?sY8j((iIh-z%iPgQ|Q?1M;9Lb1&$V{UAh!$G66 z5&rxAU&9UP-?1tIVX$lg7y39$n0{a>Ff>4sQP@NawoGiA4g(0)Uo5B$Rwe-~2&cm; zxNh(ahHmD}%+P|?M)vC!U2D|K%X$UnthR+miKDgEx$pTq8-TpeRsP;(D&Of#IvWFJ zp`OXChn$!D&K2jc`?0?Okl8>{&|2MYpOUAw-Eh2IgmRHOS`2&uY-o@pbvm2O7HgFX zi6tq>Su;b(H3VMlg&YYV-9EA$*%`=&nCk-Ko`Dxl;^G40z{1@8{N|=lR;Qb_o*#>G z1_{T3*C9m<5vy5Ia9zwIMmezG%_8b?dDu9nIw3d>hEE+%Gyu>$zUp`vZU_Ymul204 zkXB7DGl#++Q(_08PG3}xpkU2jwiRCm_f}d~soZHQzuRu}9@nNkJ>swCbr4tb=tkr~)d!U2G(jN>q^N6a<5W0DM zNl>jM`Mhz2Nix9+)n&<)*y6mrzC;J&oINA9*NahEal~uUSq;2&LB>lpV9!q3J}4H}MDBui-h@YN0*Dm+l?*gvBH5EC58CVimf z@T8=3Vm$AhDx_gS-GbucQvR7B)3FfX1MyqA9a#dfv0RjZ5Yby^3huOp&REVJZ@bx> zAUvHg?3869Gn%-Vesdst|0$B-Uv)HpOMwSW;Ax@?eXF<||1%OtCxVJ|(fvC|pdU$& zz_jhBdM?6G-lj|BglERPKgg~dSIDV4!x8~j9OT6}dVJ}nGyBn#3;u!N%Tv;|dmMu) zv_e*Z3D}W_tWkGfbUS<1vx7Q(;a8meSjZQ~EltZ3i`*xG2?c{`>p?)U!+!Ig;s%cs zXO2RhwQE`jQ)oIubL7n5I}$hNOEd_oGP;zO()@`UEJMuGGZuSk(1Ze80JyPAf`9z8 z7-w3fN)(ivcANE4ZN`z8MSZd7` zXQkbz+t(QLa&8&ulu+&;BnelNVH4VPLghCuaZ5-s5)}%_-IF2R?wvUkj|^T)?#Noy->*3IRu&vevo)O` zxYU4M`Q!Mplrev2^k&V!oj@_J$-n(MjIve23po1(>v=jb{D`e@5uML4;n+!bpX2tJ zsr(@+^>_kg&y+pQ;yKYhx;UmzjyUv*l&>=Ze?olQ-K4sxqzB?4Hj4|Q(vtv^?N69$ zUKMsLr1+3K`SBsQbLBy9X2?TE#vqKad)V8oOI$F&!-VS}PNdQ6oDo^;b3iX4r}nl( zIWM7S3;`x^;8woQ9FKInmR!-qoNuWVy8Yj6voy~$ME7_ZJ0j8pJX+=osF71jV?11; zCwH?kqAo+DO|>I0;5;}{DWL$c3Vh4fh!OPJzokUxpKr`o^nJ8`?D>Z~5Z8Twb<_vM zL86goRFdY!@g1{=BXDX6=TW}kQTNx!rn(+5vI3gJ^EE7f)?*Vw+OC0w{L1;tew5Zy z@T?0t?Ap9~De!?acVgr6AhWfhZp?qL=~MUe)hwhEROc=7(E21fZc+n`y@;qOOA=FN z{vcH=vvk0`Iv^Y-*YfRdkr6AE2dj82|EbTNj(%rSHPzx@YK9osl>CXA+|<4P$1@t1 zeXu`a^;&C(C-HZiM5vL>XaL2Kxnq6myf17+8q~}|8@$62S$DsE6}!m#3Q>c{8(Oz= zpdtLg3zylQ@7FWWRYxRX7-#exzdJ;BrLI=u+U&k7HYjK=}gBZa1LT_&?FEsD8b%+OIO{}KDC*c zQOqyArw=~kROVy%KV2m+Z9YpQXaV%T=Jm}v$;xrQp3&a{{3shRc&+RCF1VAkc63%B zZEh||$E_|%&)I0e#aqu|(W#{!y@3|L6__2NgNJFLp4`g20b7>}18@hFS@2Fcc+S)r z$9Q$ARc;Yi?O@z<`;HKb&mIlaazR zcH2jiHxpxAdvvQd`E0w1EV*2sx1Y4@G-nn)X8jGgMbBLm6iCeMqIeSTZbH3RpujLM z!x`gcfFz!Ze>Or51$aG3TnqLJLsbs;*h_Q!^g5{XM+i7(8tMSFHq{bBGC~3=QGuf- z3;~v*ofA9FAcCI`%wx`K_T78lo#zggsxAFx&6PvAV4?sFl$fJ?5;^1>{qR7|z*e53 z95AY5h0%SQX+{~(d-}I*#CzgjIaL}>2Ms7XMQ^txg5I$Z082i*^KZmY2OZSZ`uHJI z9Ysex^a3Jl7*2UR52x1+>%t1JP6o#emKb04QH3E_8c{}j1?K(Bu*s1>Q}Oc$!?g?5 zhH0^z*uE{KAE}*35R(cIsO=rmZYzDY2O$Zsur?9(|R^k;-51>qsU8@GwA!@I<)FwtTEAaH_&4uF=D;zY^* zQ_&IB)-u&ed0bS2)Iy>TfgtfcOr~YvPhAITgLd%06I^Z|zEvwp#_JEhq&pkv@nT7L z;9B;L&k1)b=fmHbIQ<`F^}V-mhbFwAic`+M`W&FHVoUCeMHT2*zZdp%G+kIaG!TNN z4I!_gn691F7u-X-Z&Ga3vjJ;+Db$JQ0r_hm0?ZRwK;Xf_!NOqj8TEH|qAB9;wUcdh zb3dbuID{n^SbmsLJ4)2;}VMx+)t4j!u4)3k=w9oE6(%Nk+ISL47B5St22MYts**7K3IbfVNfT_zt)+{Bz zZ-aHKZ;tAcd(Y=N>nv@XXCPC<*6Zr_&K)s$4n>Gp3nQ@J6hAzV76x<<0t@k878k~r zjei=NO2sQVl+k5K?fI2ZUUQRC2|EWs*Kqd&BW2SUV@(_(wn^NxfdbkkQr{;2uq)<( zzh(HCpR1+r@<;SU3h<%IdY6H!&kt|4EKvQ93d7JCPAq>P%_7z8z>^A98BFI{k~}Uk z7N;(=k{Kf~1Vq3yj?XWI66{kB{Xvgn=W52w7r5IntP-&ex zQMqrKl`3F9c@>X{P{SIiNp0)ZuF4`As|3QGA-m&=hU_$$wPNFL5LToHKco=9{l5{YH3W0}7Fot_Y6`aEj&W$nDbqSLm30heN<=az}u~ z0s@7td{-3*3F?HJ_l3F9v7cZ&p!+t3{%&&xi{@e0)nGt1l}g@t z-s;CZmU{l-_LE9NT=VbFdVH73-LkcF{~hA^KcC0i8NQD$Ti7A#(lhZbJ3-z$RaErF=hM9 z-~2_+o};ZfaRhMI)?3TppNYLJ?dVq~4RaOqpeQyZq1xH>Jw4FWg7W_Y)F89Ko z3V_>`R)FN<*41Ct7L{r(OTSDBiQreU@NM>$ZDUwjUyVXdHJZ0-C+U0{p8==;j@rN! zvW(Z?AByK&A;a(tK$+>RwwFzA^XnIU{qCO@1;e*{hlipU)ysRs2@W0WC1#I}6=oi> z_G>(iJnHV9H3WK4+W&T(X)bvC9Qv>#gTpV|2EfRbfC}r*BJTBtTGgBl8&*sh3bi)+ zTUUTBGU8*KUv6waE3G<&WgA)jh!B`c)T`Q+V0E$kQ2jF<>~Q?r3)nn@JgqjvX%5Rp z$)T?HBJ4(my?5-u)-@a@?+FuvlkH5L*ET${E+{k1L)b^!itTmAQ`dApgAgcXC`l@r zFu?ER<%q&GituO%2?S5G29?iDgI;!6mg(qsB5g9fijElDZ%}oWB1Y~g=`{5O%zPeU zu17IU80g~rMmVJQoZ+P&tsakARwfdNHkcrFwN-PCta+`srbtp6a|c{@jL7e8eoPHF zbqUaxFCbpet{WJ2t1y|vPH7v#MQMnf0>Jo=7PV3%isZUNqt_^S855(ca+0Az7P&8e z3*PcH$MKu=MkJRT-S^3(7 zEr$89!koefY#*h9AC84V7{WqTE@1*ato02~TEBk2rgIE^CO^A?VX%u(3SSXtfjXK0 zOy2Q~>PnMD4NxEuPI=fMM<6<1m>fNj*z=DJfkPZ~lwc73+g)bA|91wAq7ej4$sI5t zjEp2TD^MieAg5Zu(^`c$ZwLpVf+I*spc1t2_fAQcl=szBlUD~L@Icu2L0ZD|{D2R^ z2)id$B~s$S|Gu-oynC4VCjVj4j}`Q{qoZLt=HOu$O7+x+TX3bEJ0SM2(7lSq(J+_D zICjz~GD-aiGW)5aLo&?%;Mz67DcxY}Ox$krZ_Oyilv&~2YJt{**Uk>Egk(g~$kq-@ zL1eHHi;!p%Zh>SO`jK`ffKA&jT=>Kd3L{8K9A3shOo%9`oh<cCXmlAJttBvb%DEYlq*hNFxDFEJmN_%PuLU8Ft&3`mSETTBB{igdMa$X6 zE<=uX{m;khOxxco2l~4hqFCljh!p6j7|!&X~B!0N^^)&?HS9iMCTy zm%e6)9w-p39obfe)Nr&wrguNSd0~siw zr`<~2bP2ZOXzo7mpnI95V(i-e*Wu4iEjMpBT~A&lpCrwQ_X_ll*fGjdom0#T1391zQ{(bnp+_& z{?wh4x*Nn|n3cgW2hxlYA3DICpqWox%_1%$6zD?QxWdo{!11+e{Qyq6%^^SC&R9mc zIHe4AOkNrs#+bYqJOApGTIoJcl^Y@zb#mX0-);|XKq3s<@~{9f$P$sK`{89PkFn?E zr!Dx?8S_xF$h2yn+Xy34q>6PjBfi#S3UPFvV*W7$`EZVIbVAHKlGZlD2C4`{_>p$b zGIUzkeZJ`;4^cA241tgo0NJCxdKAK}ZP% zBwzolw?sVS5n5D*YQe*7Q=5@5mpe;*Ywu>u|tqpGWpERNwf48lIB zL0ZOwW|}Dl84wTENc4(ItOi9Qp@2O%Z|E+e#@6BJJ|JGL=i#;G+_^Zh?XjT--JG+o z_fo=N&{J4^bA7K0k#n4NIo?81qAHb2Q*%yKeX7M}twyz}FcNMVsNGyg0)h#E+c|*dsdg?*nx5H4jwCkZ z7k2hDvLeaN95WpbGbdwGRvrLyhOVZ8*KlU!G>b^m6qC0eKulm1;Fh+^X4ODEqfQ++ zv@2k)+J*|7Ej8%r(Q#bELUhEOV6{vfbe=Og=(3~RV?2bmSsZ@y!u*}F3_aE{fFR+r z7ju9J8;A$Y4$d}42@5%6h|i)3f`DVs`5VLJJG;2%?#?dDHgIuL6AnAbpf;O9vNr7b z^zF$_J;!$7_IX>t9&pHsAPoOZ{S%YV>Ugrh;$8vI)UH^`leLXgY<5k?E$oq-ZwjAI z;?qm;b|&;wigdVH=e`N^d{cQJV*Sn#qjVNg5F!Rp!<R+~wS>p60@z zmlis}I)H~d0VCeRePliuUnv5`ThnS8X-O5l@GXh+%>wD06wo|CJJMaud4#KjO^3KL z@=78D>KZy&<`7mfV05|>JC)L=itu9HuONaeIs7S0GNuXBo(Quv=YvbK_i@bV27X`+Z%<)bsWg?-6lAR&h0+b9MiI@lQo5FK^{7pc%|WP9fH;wf`Y`6u7Oy$unp=&D z+@<1kCGBbsJJmb#K@&qOgbDh75ThH0F6{(J?74iBg^ggY-*2#4HcGaDsHd8$PAck^ zgN!S)u1JbQuI#L*++S3nv;CQXHXr`;%A|S$n;5xaoFS{!ME;C6ym8nsk)r*|6P-zs z$RCki*-f!vD6#>m>3ae}#h{ZVNn5E!yeg3W z%g-ChR%t8jXqm$jVRGI*e2>6Y*fdA$B57l$JfvN5e2J=||DML8fR1Etg)uyBBgub& zu|+wtc+9r{(ad8c6Wl^%Dl>;QBl{6>EWo0vBl^f1iz2S>$nu7)<>&twdT&eC2K(7_ z_1IznN)8Oa#~&xAM`#~3k?$3F;1d=*qM5iNA!UG=d;h%%*rTQP>&E*ESOQBkPF8f6mca0dFJyP%yvEAREN0HWN1~W&=+0a&@rcqEwSyh6{@=>K-cG*w zXs4c#^E(y>Hn6_iAri-adBvu%w5#O#g8d|Qp=KbR5oC%^^PMrlA?(9Mp96!>w2}{l z3kUaNFKzFk>VWU~O?|t79{_R}uUeK^F8KkUCN=6u` z_w(gM%gopFG(Eugo7mqH1tW*Y&xicW)Iogg(wP@K3);-7PlCZ1C9VN2ShKJ4*MhRH+Me<`ix4Akmlv&jH|j(D7W{3z1K> zt0>Zfp_%BQs4;o_&td61RnKHADq(pbe`2PFh||9ZZ=r(4Svf1$!zvTtS^N7ml;pk` zVXTOhAc6rd+#!u&km}8XYDUYiyU`6ryby$v`l`9l8=h8SV+uzZJ4r9ev@&;QpRJHd zWEge+(nl`Z?xZjh(*e|{A6B}q?WyXvk?Hr-k}dU)Ge%X95`sZ1$~6j*_6w^~8?Dw; zJaKO#L%TyH1d*PB@(p z{$)}VUHV!76u6Bs6Hrqn?N9}wE`eg#AqHn)ab4bHOBSWZODvdKZK{KEPnrmheVETg2hAyQzeF5O)=$qkeG8yPlT0)52svXin;$dobPo1p zS`Al&rRoVOV2v8$d?5s*Yb6%fSohL;`{Xn+}Gk8I!8r50Vsm&JXAzi z`WMD)^8v&@kCz}5r~yOZsllqp{^bU;99@_cc(O<8Abuda_a*I z1W5c*<3MFkx=$y&}%5Bp@45bE*w^;_cu1MzpoBUS>r z*;5}foXw9f{(d40z*GOCKe$u9J=`9<4VWM0br=ZfxC+%wq^5;VF4jvF9c(9*okz+P zw(N3{*bAaXzQBViYHqwbu1_7^R9*nY>eX&sT{|y>08YS*muXh>AKlWGuqu;A%UH}@ zMTYO`mV>gpvU{T}4*ApxLC#k+Erqz0GeDM^k^j~wB#`khj`t)DE4gHlM#vQMz33kH zsJk&x&vvE~pIN65e+_N%hJ*xp2D~LKl4=GleJ#>M7k?rf9~$eg?Z^KXr&84(suwUd zekcjxLOXYuML7Kcy4R_UFk!)}C{`N6(FUw3`_XI3;CvbPg?CXR_1N-oPcADMf;6_+ zk!yVP-e0|eOjWZt>IYc1_dA0n|1Yss!0_^<@GnhR^}jS>9x^--qLLBds%-7NAcn$= zohiY1T%7WlAQVDCf>^JTsE`F-+n&!exzLD9q0e@&g?4J_wgQP6`VsdI|7aZs9~zMV zIUQ%@MH&hjr^1|~ezNU--PU2g)9L#L?+>bDjA3KDRm1PN>~X75WNSVK$u(VtY$8b? z*%te!F~!7Iuob0#S4sg;H=#;<8o8T)PCl-Aeyb6oS&F`lv|T9*I?~_2xw%PnH2(hg zU5-B%QbAXbTQZjjwonBEvzRddLWt^I)MG32<-Dm`!#yX%047;&IW?daV<<2`Wo#Cn z)PAHtPKnlJ+_*=W@SAL9i#zdzhsW-4KbS?qZ1vl})|vm<@^lBF;Z5+%)*var!bAL! zb;VPtbEGtL!;&^TN-O6L=RN!i*DSB!K*Wmm>`XRv{&BL4s$x~S&+gLHpgZD|C3s%P z>dE_`CrL%9x^oJDhNfawQa8Fz2 z5N0PK_OIuaVMQ@OnD$-Gu;+kZNO7hRPTX?u#1aT9Bvo4Wlo)}TL7}WCrER=FI3oNn z*M#(&HFPU~mfdt2S;<~)&$#LrJb&lR^##Rqre?3Pp+T(KThovMD}M~F`dCQmFhd-E zgrfPBjqoVgn$2GszRB%9nTj%F#$4+N6?+j&?hOSpHH<94)#8LBjS!xGydghgxt=dh zxSpNuZ*$Dfa!FQG@o_n3TW4)Z%CQYm8JXF%1RHY)m^H0X&#ME+*p#$a7(@mas#K^M0VZ4buZ z443qp7a%Ku5IQ)z2O8`?%n*j`6QgE!g}Gn+6fl8nwDTjt8W&@K_s5IQMWvDBL{m5B z{_KxJfZ=#kH-0~+!BA5y>TahsExbE2nS2 z5NG#^TEk7-l+6^^y+P|z}0Tv$#*eBo!=(Af8K=9olaRm-H<||zDH7V*=5(?#uN|Xg6qXrf@ZQGDd zvt2YbS&OTL`$6#cwvmH?$Kt$F7}%gX8o)Pj-*kC0XMDb0Y`Fh0=0SLM@%=QrC(Mv) zBH@mTC1#C-BT6<8Vf(Y#7SrG(7R}8>!pqne&!+~hB&~1C$CV=uI5;Dqn&$wDR$<58 zd9YydnpJ0hROkLn>FuI3@$DzsRL~|Yu$j7uPCL^NlZU~>lix+F&5gi91Z;ciaf;G6 z5(WS_izUwwClv~8C8wo;lj%}2H7Pd9Jb+r6qo)c=j}jD509m!n7^nrbjsAGDOti^S zd1g3uNG8Ri+!>Np!ac>_e~qKevj`vOdgp0?!nxg z-DlTI${yhmuKfUXhu#o0(66Ggkt*WTZ=P@%)-n00B}#y{`Hv?d?X-otP#_6d>%gn?5_p zCciPbXh6C-mD+QgK=}v!XE!wZgXpB8;I5HK^O*Bh!`#vR`i={FRBINnHcRz=EhEU3 z?pAtRhEhcr^;*JMw@QXZ9rXHpXSOx}LNv)i*kGN^Cg?;|?$P(JL3gxg z@koGLPQiudVyi*+#Lok>Ci)yia8>lG5V5kz7fsD6+%x>${Q{ zfZgg_uD$y=sd0+Ly0V|maeS&ED?&|fk^k$%`OEs**nc45{SPEwabrO6I$11XQQzRK zkZ=NyjPel0s1hmH8g4DS=1u$GJp$FEd2Dbwo!uWJISCmEO-dWe39sZIzvUWlXcXq5 z5IW{NZ+IRiJMm{G7tnjY;0*AjVP5RFw5q=hs+^sbJyPl|(rk@~89wc196b+T_1Z49 z18)Ud|3$>nxOr$YaSZ`>p^oeJFs5w_2LLjl`$m{5W67>+Z(>XcSDYSF!emEfieu4Pmr0xGXfC|mPUq$8KO`)dJ z{(18xqR@6|?^!cbZJa8SIb!$d^(DKz5dbj|iY(TT91zJO>=p!g@fYMDQW1@SHJ*tjqyG(1NQ4K$>O-E;uI>WJ4uUPT^cJ1BO_R`MpFB+r3b*&2jg!` z$A3J&f@3=GxeYrinT=Gj3?2LXWYGmhVeyty3MOn@vmp}mJXVB9>aU6645y0BEXvME zKeMQ*KnB!uv*`;xp{sC z8kJSE+Ta&zXk>4aE)d%w-v z7GYQjGhQc!!dq~zHUlERoML$au$3obvQT}vge92!Q zN07SGw7PubZR3lBjQys_?y`S% z$VJWB-bD*~oL41+JwX|-Kxv%vFzzHtH*2-8{PHpIo_w6s6#QX@a(4!!{T{obdfs^P zYgB%#D~wQb2awacVp_MH+!IfERO)s+L|oyFG$K0JABaL)Z|=ZZKcOLO0~E7-*&`z5 z4S3v%e@QFX@Cb;8BF)D0l@_@JMWk}hSj9eExRUise*d3QszvFXAPwA=vIB@h zQ9moHR(!d zN#=_>m`{98vTm~8%mANX@36fDWSHAFHj^cKXHlCvh;nN9%TuIX`s`ambAQ_G+vGZT z%|O?R>p)*XI3G5nGCd7zJc{ttm%TMyT*mt39n4GJv^G(@H9Dw%GK=JDAd?19_~Y z4l@pi3gX8)t}~1~cs96+w^A>RN%`qNX~A#f)_m4to*u?>jCGk!0c|L$yu{$T*0ZT6D`_Vx|VF!d2w?fjm_&lq_Ql zI50BZ9ai1Uy;Z%sscapmY4{`|$mv4$kXUG#?56FzL{oFn>`PKqFs;+WUm+Ztp{@1& zOJA2AcPb>I-2|fiWAutJCQZALm|}s1mmS;&u&KYvz-ky8RmOz+Q~`;gPeR63Roq>z zG%`IE*ENy@tUC53e?5ZC)F!0*5C#%(vAm@qgBAsqI;LhH@G&V{g@I37E;$p7ly6+Z zU5LIZ%M788r~?uUB=8tB4Ue%kH{}>ZzOc3y@Nn0JA*E+R$_vhXzRYDip4}GE_w4c5 z28{*W0+n1^l@=u@#(;I#+3whFE1HG~V&7cEH~-xgG?VS2PElT=$CP>|F24OmZkiz` z(=1vb9zWq9P~wFbALKcv{VPeYB*t6NaxR|{2eWhVP@|?iZPvVqTniig^woZgP^rx; z^39-^dRZQ?o++QeoUO9IK_?KDEE&6T{1!iz%Q}7_u3u9>qQm{SUj`wj^1*)p_#p`T zKQGu>P5}C+R&IWPOJX{uZ6B~(j)tx=5}K-vf{N<;Z(t;7#K|^0P%g3ldBN}PxZC-f z_aw)A8Zgb<64&MO9rQzu8Hgb-25_y?>vB38XKh@zqnv<8`=e>ZA?)>NL)4beC^Kz4 zFp1!vwgGn~`jN6)p&`vO6?Rdp*oW~8veKaRRg8E7Q?B7shR68gj3zE_)r)WD>eb77 za8k=lD#W43hpT@Owmp!SRiM*$CZK%t=I|*zPGVysTv!L#Qf3ePo<0mLM(jI-*d~7X5gy zk5NH#Z#ZfdB;nmY!mC~R!`qlJ#qAK1=4omv<{lR40Z4AqdO>io4A=n4Pa2V7o+LgN z)1EmzB@l%1^66LZc4W(n4O_VDiSX=ZA3W|TgHU@XVKi%hKMkEjnSo}f9%ah%Qt37$qc{g4bM4GONOpDYWv~5Jg>i2=zUC9JavY-HHm4qTZ<7bEYBS}?G=lGMU1KLoyOJL@1DTTfIdZiNxWufy z=ol$8y}qPNOLpmGZT@J9TukfSCRHvHGe6nm?*V5YdRvaCNK%y^+`4?s1Fba%CJRJU zJD9yO`x#}ZLS%wGTBOGfQKphP{Z7c+v?`l2Z`-SWmmxQZ(DtbOCm!V_Ha865)dbrY z#q)nLKA2jmt_d<8Ahyu)2eW@R?g_)J*lKqamlm1>mv3fE){ncp^<=%Y zG6C0CQw7`_3$jxTxH$#$Ma3nZ)VH~Ggkr>mwm_G%l#&b!?BAgEWF=4IHIm_+V7M~W zGPY^q<*yLJul&n0p!md#$~R00BB+|5j+>~xdOih6J_`hSyLM7!juaDYHHqd*ca-}}(Br64mu;i%tv|Dr@x1_B<*T)A0`_Gbzz<4Z9W@6^41RVm!Dw^L$ec?1=* zvT@kn+j|a`MgkD5z-YNkJD3%eK@U_Fn3qPrki|uA_4D}JvwNjMiTCEc(e}YhrFj3HLyq?J(pqM+ z3&jM`z-XmO+^jdovCf3c>w5daCS#!dr@;~jT^ugYdhF*TisPAIIKa6cG$QeFN(8b+ zha=#tXjWm+G`p%Y{Ax?|vNJC{KusU9b4hT{+*5KD{W;8fq~2Tq1frvVA(>qdaX`VyB!Y5PZjbS~JH_=Z#-R=ojNI1=}5ppvv`Wk1XBeK<0&>9iY z5;7**_D_6j1g=6O~;>eb|QAzP@Mx)Fer3!9)1 zPX?^+o)tmNh4-yL{)t1e?@-DAXetO85_kDS_Sgi%IN&F?AcZK8pe`w9J^d8ctvmRF z_U1nSh-^Y=+he7@ivFG-?^~jjk}LwCi${_k|K&}NZy(Dd%6kyy;nssbpX*JS9aZVb zyG1MyEVWBGsBRUGb>Fw+ye97nzf-K(aklVDtf;}r9cFg&L7N$yixS0O{67GzKvcgM zu;E1k=r+RpA^}?k_efMv;&RkTh~G#o0RqB)4g6Cq`SF05c=`z9lQLrkAtSq(l3V=O zP=7D&WRuwQ@G%mmDE|VhOE9_>0SVF@1jslVuDiO zp$5}h6Q7#P?sU6!zjAlCh8X!F{tIJ*MiYO4Kgu{=G)D2kmwRW<%$Yee_kR2S`73}1 zRs`N1A9tE>^aE*)D6sVn-5TiYx)u77>&e6o1H%t(VqB3&GA0fVWo%eOvPN<0$NI)V zdNHl*kz@EWGJo8xMx#-$*B^4OYX0I6QqN-`9!ldryA(KXK&0HjVRTF?=3b09YY%Eo z!=|zRR)Y{IcEeOzwBvdtuo^1IbdoJf;Qa59L^z zo(vpw|KO0UB#&8vrR~}Hl*#yOOVn8SI2Ldw!=L)NC@!XK)-M{z=a9v1~l z4kgc3$nzve0xQ+CO4iOoWn9ALA{Ma7jVH?pYysoHxIZ;>i7U7|5xUHinzhr_YPil` z5}u|CEL2qg+59GMO_;aK&`>B~26qIepYmh#?q`ta4<@)e$1+Pp5hcFwvA2pTwt(d? zn0xgR^OedcES=z5(NwV;+CHkS-D`c_ zm)B>l-R!2M|8wq4l1U~=e~(}E-h0mXedpVkGhe^($YVsbO1Ylt?i+91x%PDx?MCE? zndqnpRzx~0sw*N^yeEGeGtyBj5r|tICf1W?%rsIaHoJ{fAku9{j-)d2R7J4Uh^5Tx zik_}O+~^5JC5=_9R<2yR4zS6!dHl{yEQW;td)xlip@!k`Z%LG-fg_vXWi3T}h)OX4W1{8a;nKX0mp({M>7QwHVQa zN=~M!*BeKT+L)2(s@>jxy%|ZXF}fLc zX%98gCY^ukXtQmT9Y)ejq&?Iu2-MM5n`o;MH$4=x*IGdIK+W|=a*;;c;XO4yNehvc zj+!ZrUIV_8W>3rz5t&RurE80vTZEHh9?(X+gxPj8Ex9xzQk=bl$!qOsvBePz^3Xn7 zCwBHT`A21mNGytYh{ugYM+o@_2q_c#JqVAewCR5x(&;)n3}YFwn00J-;z+_eme}4S zq6IF;aIbcXlmMm!UtJn?!ss^fKnAn|-BujV+YPN*J$8t#DVknF6LV#s28~=#M+7&q zi^^a_tAvw0MzSjthYEJ}_P`mZg~sGBknEuwO30!ln7=_I7|eA;CTgZNI*OG>jgG^_ zsosB7+KdN;p*4Cf(_~2|la9t}L(vpc=j$OAbm(qU;I!0+!rME+QBNVn6scwj?(mPNRY^$c~ zdu>+XD4{~g7NZBpy_4Q0oaEi(2w_{L|AGKgA#kZ#m7}?BuwpU05`f4Z^j@7#(w$7c z%HlA#&)%ie?Q}{Mvd}oLb0F{6={CBDY4xiG!=ie#(@NUm+?Hw2?W^=brp=WXaaw;H zd;M65amvl{^I@GnL?6j#RB5~Bw3#&8MRNNnlNJ?UX)7tD8m*w_+&a|rar#6leT@2; zs$W$q`_Yy*(xCsT{J8}bRr)mYAG(=V^LSb&1O)qM=z&tYpB_Y6O33BH@|E*94}Fdv z5^$e~zs8N;b`vKh)7x_%UNjkl_!)o7>U1CVGbw?9Mh`OuUR98~POzKHM=A}90Jv10 zKdRFg=rPC`2<*RZ!@(+z9>+)ANTV{RJoF{OO%;6^r6HARPvzXZyt27yw*D%e6VOi} z^IC}lY4=@X33ydeZGxjLhhx7+Ul%I-Z>E+1IlfBYKsL*2pKPGL60sl|~aC=JD;Q3{EJo*+bu@?+IOfU-)wKMC$yZ0QnI* zTq{wROXE!S#XRRf<@U$)oZ$9hhSmF6DnWI{j9Z&eDjLNTZ8QZ8Lj4^gGdwouvO`nmJaK`b;#| zA?ok%QG5^pf6(a#(VDnL^PWI~4HOlB0y2>FPxNO&;xEV_Y0KX9&`Y9`d4c{0W1&Yt zTFk50L;s)$gl7IJwGK7*#LP5$hnH>hz5-5S7);XLG8@257_6{yD;psdh-*p!Ps60z&pUBnIi^-<) z9Js&+jpt2}EkzXZ0-=9=3>c&_h6+bdW^BWmOUDy71O+*p7cm^;BxB@ogH&A%eU^80 z1r?PXnUgDdsm^m5qsQc)q?v-}4EKX$i@zwi9P%nTo#F^e|=#v9M-b-W@J9D|wxOz&NpBROx?2L(pZ!WWYBakC-`Y zVYHZIv+74#^T#u>bQCV`w_A@IjW_DNK?K}_98`y8ru+#j4e+C;-$?Y@pn$AHS3cpi zy&HL83;N!WWgW@%sEk2mR%6qqy4|5&hc@r5YYa6V+TGT)^U#*` zt`UuMjclBGi#>m!<~*Ypu6ao*07~D=+eB=HKz8hz8n?p6AZsR$dU%Hoh}mUY?)*_B z4Yx5ZtSp@2CKx?zhKOnOUL-3hxhS* zox^+p&0vx9LRMFoIN&-_boSZ&9oG2}8)9HSjtGPcMnr$Qt79bAiXtR(cP358%}7SH zL);0k7infmQGJZfPWG8moxAvYoC(M8jH0?0?#(;m!N*>u2jhmK88r7~|Bzi!yu>V-(#i;3ap`TXepO--;HbBPv?U_Ke*uP9AfO z#Bbxb3$xq?Bh_krJJJCr*L1VpQF-`Xb`ii_#mIk3n4{E`(P{2ZMw#YMaGNts`VM}t zP%y@+NolLDt)aO&*COy;@&bi1LOEI*uhjVc@W8@Lv=%d!GP=yhXqPF9*$3=2C5@60 zmvbv7PE*BcfS(WXhjsoSV+wNZ-nA*PR%0>vfNXtlT6o`#Vl?;Hd-&u0384fOQ+KAP z137;m6e{=Ei(2(5zE9BnbiU4_yYH^8k9IXD(xyxhp8-;bD7lnYy zgb}lE=3-Xl2-H>EG$A_l{{%l-0;l{M)7*&zZqW*rF>NF-8cc`exw6qyobMM=Y7l{rBQ!$ z`AKvVRub*U5AAgf9kWL$?#v|8$djYmh#zA{YOpe~4%r$+k|sRP-y`qZ{2h5}{!T0D z7i#wl1Nb$@xG@D>*R`jtSSD@Smi?)`ROFxO{G8}Jw1kncL{^p!_Er!7l7A&?^RHbb z{!OXB#zFj@$MaJM)V&jx)Qa1dlsA8&jKN~w@U?8D+ejqLSW23u!TwPB_jv)wfrP-Y z%74HBo;;@UA9a3S6m(r^+XiF0ta8+~a54JO{$Kd70`(<0aXQ&6h>CPH5uVrw(%<HLa91@nK%u5OsM zLsb-{l2ptzdqj5F3UI}#xOBy#xbuB|9^5)6FKZMHkP*1HN73maxT{hME#!V3@+gxq z{P2s)WEeOWZP%2k=m?60K)q$9ku8iK@r#Wd&oRZWP!+GPl!>%c5<@Z(vLgVN7KUQN zhzqTxMWxw4=JH)BS7yN9l$n3|;WPJJhK!mJ$`|yf_{2T2Klj^4$fL{^zke)L#3V8% z&hi)wkX?tnxKTUeo^D)l+FRh|GNnRS{K`TPHQR5Cg)I9Ee}0(&TV?}giLT66Dp9^r zP(<|EMjLx4Wv}AEWlEK>Tr~tqiJy|-S;x}_?9Q&7_&lXXsTJtU;mUuoSWT{1jv1Lm zq#OOZ-TDiAY064rAzg!tS*2Vl^srjPKqr5XXfe~>76iJc2!Z|M2#h9~ zG&{weXst~FwAU#?ol2GU1veIjP^z*4Niv!^Y8^oWT32L{ocLO(TD)GbVm^Gvyt*tv!EUoBpkJJN?l_etC51^eVj72V|M%}I`t>^-j?M$_PxET2E z@EMSIRp-jCe`NZzOPA31$RsRhCme*q zYGVVEIF>pvNG9Xagz1k6KkPwelk(@1nSZO1^mit$xIbcbnC%ccZ^Fa*wldc#wDl&^ z#_>_|wu{@Ji|~KmA$W(cI>m3g$OFifks}x&b_p2NTX9v>nu)iYNx6a)?;=%}Qp6W& z_>Yw?qDhDGR!XFmdtF(YlBM!2&B@Yb;ZT;Ac$a3W_LTZ;)c{@jFd=?IedOIcNOj?s zsw_43)0XPcva0;|HvHG>-QnGtr9J%=&e8$Ggv()R`=#tda?34u@d&a0f62&o}o7l(Ax&-ws6onNVkUrr|CUe zdS5@?-A^AFqz{FIu7JoX;iRPcuCZ(m^##1E=`oKGpk( z5R;}2(ieY#D^Sn&4V}mO7qj%0Q{)4h3gtZMQx9e7e3qUGy84Fh$NqQZevR0lx*BTJCZlmTymH=_dl|7?6G@k^BP5IRffa-6LQ-9-?2~O|t;}YYFV`qtZe8 zZMdKQ_iO-YfDCGo+NWjd`BOAq&=T)?-lhg}w1a;o0;)tpy~*bqq!+@zl0)7X`{|Ea z`fHZ{F80nt=q&x~6qV%#wb{7vH6K1M1YNGxTDj&^v#i{E;f&8!t~n12Ny{}UYEYA8 zwF0s>GZ%sY@o7O2=o>l=N&*eNHgN+5k^(9i)@syq~9d zd$S|#J*YZ-rpp4hwhG}nrS zm8Yo92M4SRy1ZRj4+q_nhbx`82%oS&);d2*GlkpX%aP^vUpA^yw^rTV~;Ki-?m0x$u$lwMr7- z9md9XxHZc^xSLi4ggt2z9?!~@;veMa!UDJYP#OP3gdYDqkN1nS+{!`c0RLt>|2AI6 zzdK>9adA-fsZY@E-0BS19rw_dL4H0g1ObmxG=_?t#g zo)}PIPkozDZSCW7q^MTdcj&X*`WVr7j=o=&RXlJXXhE3*3~{`|k5gU97PYEOnI;4h zLM*M8LR3@_DrMo8er0-fR+$xUIY(2gA9H?~^lHb-mOgTEOZ7Qw8&qb84}<_QC#%eF zaXw1(!j6CHwgF{PR=K=iSvsHuaPo>W<%+Db2D_!q<*TruVqsYuyozC)GNnb@=66|TN13uy7`dcChXG0^0N5rV%9MS}`jvxcM{$2#4UP}xIUXa4kzbd{!0bFr)6pV4Qm%ClIM2!FdFPMB(>>ii$9%%`D}N0%jeDR6KWlvC*1Fr@F(4OilG)Psh#o}Rc{tg zQWSq+J4{+N1ojk_HmJ0aSf#6nXmaiwWQXV@q}m_y?>d!kQ|UgH?k9SgR;yICVAbVA z<|C@jDP_#!-qXj2iOr}98UXsb#lqQPRnf%J*??LPrDa{?g9Zv*%*#_13` zJNNK5*_$@jC|&Sexp7jGeT9|_2LDd;hiHF~P=-E4I=RL`6J0j%Wh%w@5cLgnz&EL! zg7zh(5b~d-zf99v=%P4`*)^7AUc;8gj={BAuuWdX}Ci2pqgzfwwu$X$T*qK$v^ zd>%(5mqZb?atMz6>G@?Eb;h0%FMU}!E_B*PN`7~y z^-5{@->}!{^6c z!SQ=w@_TUz8dv%hDBcg#K8SdHnx=oyGc=WTn#Nx8DxaY;7^ykN~&~L(^BUuTIO6sRn9u9c3w*X=RvA*MyS?#f|ff!Mk}0sw9@$?UEyo|5v_8* zL{~chMXOy*Yg~(Htt&w5T+1owYNqwB?R1qZP8(cDX``!`u6Er;*SH>}I`?#{cdwxa z_np+}zMGodKcr3SCfcmNgSMzoQ?vRkZB_paU9oUO=*sbLP)i30w>@zreii@#T`iLl z8XS|uaY=u%l@VYtItJBJVBIKTgL;?Tws$1CCAry#4^+hA3+3T!h!5~FR8+t&tyG4H z+XNLvK?PCqfiFZ6Q4x{)KVOnH?bfdHUDFnLQY&vFda1yq{LO0#l(wDa43A<%y+8P+J(=F|(jAtbh zi&uXbC{vid-P&QbB&<|l%lX^3+cD2Tg~BmW+e%4!zQTX&cQT8Y)A-2_6|5J!)0~v!tyM@jjMsZI ztf<~}EMK>dOD_sy4yr3rj@lpssFJUFK?L((p&({prhFY8#4OB~uVWPa4RWpq#|B`a zPN9shr~i&>SlTf$hO{nNYJ`Es3N!j_?*dB#nUB`&;=!V&L7adS16YCvg?VEvyF7o% zxwEoPB5tP*GzPFt#2aMSX(jE68sy2zER1Gh)n7=d^CclWalyDU(RF3Uvy%ok z(sOp2_qz75+80nukGk4ck{WEn=1O$oBHr;{%SmL@_IdP!)yI^no-AQ5MpS>KUBXH+ z2}1*0nRElPkYGiqoh+3YN-!x(1)_Grbnd>CU)9)__kVP!HDF)nHKB2}=_ zE4Qm}a;HspnGHBprnIVc^;;`fw{~TdL0m2tZAYSb9Sc>&Kx({4wfY7I=>A?;inr~> z`W23yG_8xJNUTjGBSH3Pt-XJXI%I;_k+*gywHhnpZEhmLbfr_|nsyG5I?GoiDoH#-9zGAB%x(1*?7!F|Zd!6BB_v4D|53x|xe zcg5_1{GIzozW?M)@kri|Z*r|Uqc5L{{Y26>3-%6HoBFBSPVs*-1@SH1FI>J&ZxppU zE8E*^r|tMg+tRJ?JB(U!uRS%8EmWrB_Q7J?EErTmV&J=zgiZIPhXQyI-%}{xylF#t zjfo#9G>#FL@R+izJrckn{7_2T;bVD~3j03_-~fKi3qvmP4i1q)JT5LffS*uoEa7Ij z@-vw$4&aI6jn#i*N<9AaaT}ZZ)9%jvDEJFHrOnMI;&mz4tLv4@RmVHkU&l(uoc|Rs z%_Ny#Of>_)W~4G!ue~|VLb|A7LXDdkrQhOr0sIEfOqeB#MPu4o;tz83{-~guP5hbe zpp!CgDncdyB7M*H3hgO~zlwW)iN8@bH`AQrlw;!W92{Z_imz9MUuhA2v^;-Lj6Xi07VGJJNoGmPR41@`^y*!O z(m@fz;P^hb&dE@p zI=(EOcpck`-fK41dIqYE&uuEvZkG~{zlLq6{S_()%aqxdY}K&+AHD+qrVHAc5?4VCS~+-3VPziA&9g!f@# zsC*w54dJ-EAb)dEkK^;_%%zk`?*8i=N3bN?(Kv|tIV>H)vS?$^5Slc)Jcm`)t^Ak6 zDML7I5DRiRa}QQ8%b{%#nt5g}e+!~HY#2sI^t?e_80|cWioO0>%kD-unQY0y$|2s} z7>$2!B{eDLcMf7RTR<%3uhjZ`${(BD0XWW?~dniZ{;Va?sFHTNLU6b_Z;kPVgR zuuXExU#k=jfTp5qW&5c>?*5KGrP)LD{^X1ZADMlkUA=- z<}+<_YSA8K#1Wn1hKLd3QhoKqJ@nb5A%l(>QHx18q?XW~KPACIG=wT@)QJ(Z>|Pkr zNTokybkW_FIkk+ze5!LVX7jf|7%k_=-0-k!%_$oHJTWsYFnkclh02O_ z;pu9lhDkM`p2kz^3iTch-=}}B)9`xr2@P*i!y0~5J*wfO>M`{UpnjsB(D*azS%JRm zd>`@8R4p0?RM>Zm?=u<~DEO_u&ud(u;J5qsXXx)1lNC%IDG0HmfdSn z_di@@V2Odwmoamcb6>zfxn4L||9AEO?FNS%1&p$aPf5TUjtavVWsRSb#=teCdKI$` z>{e>wD0@ZK)ci;!GX2x>GqhUfbR;)vl2fMF{1OBAjN;lUiChvj1~O8M^0r_$jKMZNjiJ3UQwV37GgxS7JF~=7} zwXY8~zN>JQzYDeg%P`j;$2`A-`Thh#{sAq=ug4Dm{{c`-2MAY7nUlqnzkXeR%Sr<= z6o&s;wOU7Oy=~l$g13%?wTpVejUWn&pdh%6)6;1*b0L}5h1du2EnF1Ag%99EiD#-J zA_sEL`SYDWA$k9JeFIR(g1}R+chPuoZ)9*sans#(gO*!$gCt6omYGUoFG`xkx*<){ z5^uJp^@279ceWE*cef?ArK+2MhF&C7PYpck^;)gA!>noi%(psPvtZlO+v1kuP{l+> z#UZO*GM3L`y|KBy+3=dwbsxtd1WK1l#{_hwGzqLECiFuio0|N4Bh!?Oe-hhFYQ6lO z5A%x^F)T2BE4$LyG7kOon_CL9B1YNoYy3Eg*l+4|z^KH}{r6aCNu@h~hR(=Z88R_* z`s;F;<+o*ObYI0PI}lh}{cG?aUb_+~tgc--a4=Ou5oHHs7$3(7Dh^7R$g5d_;X{5U zRJbYf&kS+J6jzm;;Vd{DO!7L<-69GcKzaT8`UTS)eauw*n0p2WXklPc_ykZ(2MBcE zwK8e}008F!002-+0|XS4nSe)s)p`F;^th6&JZ#5FL;|_7V`58|odgmGg_rFFaI8GA z^FSmeTuayXC6cbty^3RAgEi~{+d5jdHt7nCj+w2jYc`NWQwVGwP&V3i?{184quryW zYsc1Yuz~jf&b_i_$A)gpFF&38o%5aVeCPZA-{bhz_rLf&5nV3s5_I~1o_hwj-Pt^> zrH|?POmm_+J<{CKoHla>BdghlnUCkpjE?!Dp4Bx=$Kse~#nWSY`j}P9SDdY0XH*em21$c|ws{2Pu*(@fkF)h9cq@Eu&^15C$@}rnNt`{wwh52or zmvwH7XY}LEcLzua3JsZmrD9sY&dBP5E;5UwU86-UlhwP%i&~+e7rXlNmaS#83V8)B zyG=W;b!D~uXxHB1+w`=pkYA8LYmScUMM0~R^XyN`#qELu8FM_JHMNYOi|1q9;Vy&q zBK<@wLTDPp-T3(Z1F8a;gtg zm=*`gM=Qb6YN+ZVIe89hbWl7* zG3o?s7Bj2@&aH22KRnwSVcJNWSc}bqmd;sI5ZKf>Bf6)5Sk&a13T+KhH+#CyuyzV{TpgPJjQOU;|Mnl|&cX5>{ZKLIvJCquV7)tx5_AoPrCo9c*> zwdEp2CiC)7>Td=s4k+6n)Rn8ln1lU~twAxaL6s9u9c84fQdjxqc;|^USsXt8n=tefwAVPgXL%H^`UxLA4eht*th-d z7g{7t2k8*2aufKn#&CdcW<)^W_IZraYnnFH)C#+Qq1ceE6_F~|Z&K(ZA-aXp)jr!M zc`e8J!se_q4~%c+lQcoQJ{&82yjJ9^PNPPhXBY097PJC2#Tc2W=EOd?UeH3ys@WBuYWY)Fj zTk|ROSM&1ZvW13QLt`2u zWvG%_g0H?mDuW%DskaScvW-r!Lfl3~y1jB2;tZ7zUHa zx!cu|qM1V)u!|*&)1W20#ZJE1j@ru{C}tCCtA$xtj+!(b6FJkQtCS>ku&G)2j zIHYD;br%jAmSL-7wq39iU2cl(xMZe9kd>R>Jo^^%FVfq4{ z386PUi`-Gl-(eI4t(~`~&g8d$SuGZJblq$eoM!DyA$pFpo}%Y%u8)Euv%0LE`BF|V zO1DF~^YjAqzc@=?4U0m*i%`RBXSZ3@cV_h*q#`nZkK3Cs(@V%hdHsa#F3gSX>B{;F zeTCUyMMb(m>zz<@LZLr^#)#-WD3oFPDo65b^fhFh1^aQ1`Ta$WUrm~S(~NLt{5qOf zOB<0Hfn^E0^he5BQ1XxIPeSxZ^rw6>`apl~7`g}C^)H;1^9|(5suOxkBO{y$_Ll6L zO7d5NR@J&`_Ud1U=neKuWX7|#3@+yWCPc5(-=e#erE_{!P*+=We$!PiGrO+gK5x>u z*tOq=SLQoVp??6}#!+^EVeo(As`iic9sc?s3MjE-3-eEMYKhiw9V0G zHW?Q6q9G)fiN-kvcfr=Zrr?Sum|Uz7w0=l0A~w{bZkLLAB=sBi@81W?Dj>BY|C$*c z<`k={k3IKvMc{#d>a)!QgUK3`IEzWBwnkhW605}}v$rJ`fmh=a6fv%edFQ!@)H`SY z%o-#$iRnF4Y)f&flxB<0z&ONSmdZj_v^?mSsSoER`6g{Gko4I@` z6~Y-ecqqPq&5^x!`s5)K8MvzcOlA;xAg*j%Fh_~qd<8tRmW@gi*lIh&}!=(QA3DeJQ_HDovFFS$C(At4v2#xv0og*2E*bOx>zg~(B_0i3Yi@%H~WbH zEhEB7XdXrpB^JNw_51~Y%bFY7>v!lQS}AK+>3ws5w&VO;#8Gzht+W5Q^uHCPh+)*| zY6plHr(Lr8#&A(xzFc#BrcV zj05X`3`+P1*GeDTtrhk&4`7=#^7@`qZdfnM8LCGKQs62Nz5*1)S3Un-O^#t&j8TCP zm(WrYVgl3#@Ov??j;4-GQNuY}o*I~bc*&) z(kH3ODdI&HXNR!BT)7fwZ-K5>tg5V7FO^m=;2g< z@+Ddtd*1sP1!JDgljIdI*B+6%;E2VNhzby~FTg~L#2xs(lNMKCY7bkwou~p!dGOid zQg!4}XiIf%G5J6#)D0Sinj#hWgW0=X2Epu#!`^yng?gB7ap-ID^$xAAz&DF9lY~d^ zgaT1G+>ed>Fgpfe$KkDyLF^-#odn;1n*he!*i!*tkaQYxy~^thov(JhOx`mMV`nM9 z=NuJM32$tQtomj2r9i?L@v_VAOl|c)LJjeV&(q{D(~nKU-Smkj|Ds3P?{CB--ZGsm z(@$al1K2-B4|=xxntbu+AE(ZElkc*6s@!vy9!e#!`C%BmF5+Fbu}q&j1o4=E`t+fb zJ|N=j9W3Y3)SfauehMnZQe}DqXnQiLuvbz?5?)Vq$Qv8-bqx9ALk+(k@j5PgDg_7q z=6R{bd03!{E`rGk2yT&MJASuJJM7~W^1V+h6uM2Jf=UIQmq2qL{P+-#e-+MCC%tvPqfFmB!$0Sd%|f_JuQb|nIj$sCj?L66 z$2Qt3$1CV+Iqsxga!k^HUO5iZ5jh^AqcAv;hOppqj7&L}=s`L1Z?6Q+r{w5BD9bol z!lC(P>DK4irDkv68V7`k-lbI)+{!BjU|!02m;5`b z;0Qrp2HUXqYeLmHbp-~_#nU<{sTW9 z?)N+RL7bK8eU~+U6pc0W3eWV?c^pQ7gde%X!47iFbqc6@;T1mTc<;l~5hUK}h^A!L z9f*}h&^aDipe#ZV*eJq|Sc#M;4!Ngx;M65J^{iN47Ava?t=_}lMv)OgMpL7RB)lTw z6A8cMTboKKjI2a|fT0DDev?rpT2p`-FvR~;9l(`nFN=;w(Rs8iE`M5VD~l`T?=>%q zo>R0d;(I~#l*NwJRrU1BW1P&o&(Yf{)@sdBMk`EleWU1y+7ZQ3Zc6MrKM(maR$8vEb7kv0L6kbu+xuvJWYs|@3MsMy6)g) z0GAp);t~TL!AvgmW6j?bZ7=3Jafc zX(HUa0bIC zXdb@}`Xqf$(mXQ#c`8eqN2Xt-S0v3N(_g_a(>yZ$Lp*_*=8@_5#3D)a2wHfApJ_QV zy+kaRG>=TL7V9L=T*Bt|5E%_Gydi@PMvBhyccZ^(Pf+#n2E zR)Okh#e^c3Ib~p65$iarkpNm`lZF)z^>M1kdIev#3+@JpNXjLdFH&&=|zLTAHq2}0Wb+NLR7P%|3-%;G{PC|3<<3Za|I}N$+ zbR2>5dZ+wAd?Xq9Fu5~K?cIIP#aUJldq8@f0?!j(N=b{o7s2H^PV#~Gv9uF z`3j(fHHPD(qi5weYMmSV!fmJgPzS?c` z6$bqx;q+F6IZ&BlOn?0Rp{;YK|C`f~4>gpz)LWa>= ze?$lU5%-@LO?<$WV_JFbvb1kxi$2GC=bbM(L~3-!%GY0LjA3&lsQk zgo*c%PX?37c4C$~G3mlbOdKQm0hgCga8(8}thOzGV2qeWTo!i^4k+ z`%bB!NJFRTAW>454mUdvcaWd~Otu|HFQvo$!U=MJd3-18%%i{Mk?&o_+zD3xTLoPp za*@mZzX4E72M7foSXo;F003G9lMxymlh8H~lka{AlQ5JZe*-ZP#wYf#wTgWLC8t)p zU93Hd6xD)kEXPvKWM8U0E40B<=Qhaln3y{1DQ8{uH$Ph%!@#E)9J3{xXiMa@O_;98d47v|G&mVVK};+8+yo(@ z^^h-}qHAqPz+vu^FmV_g3LhO71-bAJ%k6}#AfVzPC zxwg8z5OC1F`3dMe+-yDj?|Ksfm2Pj+h9`;gjkB2LNB&R1sk9e zET&%YWO9-WOlHQpKv`;6eeg%L5Bk{GK83cdWgmR=-QVDE@Wr^#a7lo;G;2*}&c1y6 za=yKP|NG^y04A`(@Z{mcrObD+0@v1sS&U7_v{Eb?)2ynk8{E?^GgY;Ug5-`c1a}1~ zE8Ifje=>f*X#Q6C56|!|Q`~D^0Fi7B5cI8zp0Zp-yVwc*KdA)XDabt^4l zrbc#;zNZ`Md2UZJM4I&qREAF7-A$`KK5{>T2)a~+agw30;X7>^21TaYTm{_>AyKux zEka~C9X}c*f*zbvaT>i0y$w|(PX%Ww>{Z>=z3V~Pdl8(&fQo*cXXtOvaL%-SFXuUe zR~5W}A)_5BFmQcS#UL&)bQl&dGMsA|HTeg&7ZH8Jy;(-^z2j~jpePpOwlLXX%;(gR6iV)@`rlj3f%D}Uz1H{myw!2cY zy^b3y6nw}Kb`Ll;7Q(GV?AtY0@Db73^|<4?E4o)9p2Ux1N+#XL$0}wp%kY}(7u>xR z`r>mfbNB>zq;-~hZO~LnU}DBx1PfS{io0|!%NrPuA1}9M2bPeN*Ro8GL;{9P3&;O| z33U%Ek_ewOobpZD1bRv51w#Y_5RD(<|M7Xak1ymrd`V+_K+MIN2b!?uNGbSwUlBxI zt~l1Fgjetl!}*3{$u~W{Dw-S%`{UBo%Uix~6y@%qNf?@O9LtGrR)iUqvq9>peK3d7 z5{=Nc5K8}Yhrw)BR0!yFD{@bngqiw(kNzLZf{I^rj~JF{5h>fE_`UG93Ju>9 z*{5m$(tzbWTJhA?Wobj#k)`#!;8zR62_&>HPY{#kk7$PwJq#`##?VO`;Fm-Vr=CE0 zj=pC*=+Eb~+jxBkgZcTKN15Sqa^)|6gA{mp4NX$JjYkhvQH6868fth zX!#w8d}6hyyp6RQ%o?oGZ8)ze$zCGu_gKXMqQN_gAv~hghcFzRAK|Cq-3E`OH_;ic z(+X-=FruJep_#{DxKT$3m_my%oc>npXqWmhb!JcsxYV|hfOG`m({qwc$e2R3;Ya$W zp&@A-+W!GiO9u#Kvf8dOlMtF+f9p;YK@`W&K%u2BrCUIds>mYt0;N(4mWvl|DhY~d z1p^@=Lp$9rYi3vrX zu`5Dc&oU^cFqY9w>1Bc{yhq30tg@vc4e2Xkc3J?>A3+(QkN5csrmo zi{PbXd!IzpqXsXnY#Ukw-n*gipjzDG2*gtNI9Ttqu%V31ZX1=?H*5fFTSqf#3i7ot z3UJV+q+R-gcd-p@uIPJ#RWwQ9y zO7ab?x!PovZ(n-Xzy~(UPZu-2ZRm%y{4C@V^n4^GOws+##KRIqFlgl#5PgO?*=HX~VUXGPYG|^p4tWVft zj#GiPMQ5o6_qHo(>&kFKGQ927+q6%(GKFd}g zTQ{cO&~ea$)LDR)ZK^x04x&eowO8DOlcxu4m8sGQDi2-ydys2$UI}X#omgic^#&`tsS!|Ywo?&C!|o7@uZ8(ddXr{1eeXUHckc4ZKLrd zk{C2DLwR>Pg$Ot%{?G@+Y|;@*n#;>}qWLeO1 zV6Paiz&+^s)s0La~v zAOt%&sD#Q|bv#|hi&d(t+EG5u%{iOZ8rs8eA1y=UvRm-s64$|WSfLN`q$#hE3@e4%5jP|M$Iv24-qU zuE3|~0i&r{v`x)Jbk5B7|bIGkisJN<7f+KPrrBcxfg--!@xBuewf|aF`*I0>;j(& zVSvJ4sh+I6+1=K-y;`Fo;nh0fNB1_RI*j{p=oM)2aHv_`3~zLIES?Bch|CQjJP_&T zL-E+S^w>0WibmH&+{eqb_{MbSY;t(cV7UG$ta(Uzq)QkOfVqk^%~$I|GWUtLQ+B-` zB~E?D+~bluCV)kdngcO<_O?AdMv0z*dcY8VI_3O1vXp@2H+|N539B@|67dq3z(j$T z#99M#O7(uAjnbOpr?uajMP`dI(_|W7gStoS;Pa&n9SE-5SFR7UvyRR|PvL^%!>g0d zMpOBTUcOaS(hSnn_>nVYG$=6hY~t3J{+2)zO4zv-OpYx%sNZx-#-YH?^>x_ zd!$tH_n8c?o~F*j(SYA%S6=^6h}sL`AS)hp+z}PQ1-r#lpCtB=6<7r%W#T=A?s`AC z@HJ4dv2O2T`lHJNi+J(6jY5-pOnxBUh9v5}e#ysN&G1i|Q1I>ThihxBH(RKErw-Or zwA|fy8|r&jwZ}{c-LxbP5H9pV%bKXMvWpqE5{I3_yQDdq_&zkki<`I;_=@gg=H>|`Dwpz#Y}0uC^Np2TBu0*x!`@cI{CcMc)Hg96QmP`|PS}S5f$` z)ooqNs#DZW*t=7X&pp@25Nw@vs)|glv&Fl}p3w@aPmgROyDsp$C>;+>$ODihF~Sph z@z^94G2emUu#5fD2TRt*Vl9a|Vq6;ejpJ(CsDSHh`f3d|GsQT^m5Nz_bHrek9MrHf z`AL2{p?d6@sBwY=DXSpi@k|`{j1K)ofMFP>Pb%u#Q|GUZTvYbVCbI{GhejiJXkHhP zg=StSi@vt^x@!fla3Z3I2_g&Xqy zjR)ba%QaB$mnOb9`=t6k;!M=Txs$FvHO`S&nv?L4GRO)LA9{HxDP>|cJNdF-UOjfA zQ|7K(Qq>0_#YdSvBws((Z9A6wMG@COQuS{4#Gl0OwKPLE=RJ7AqVY;g7{+ez_`AD}>9VN!`hGFOoWSm*rCfO~; zPf|C^i`4&4F=!~G3S;aP3rm#a&VR2IwMe=O^Jw{B<<(z}4RNN|Ito#A_4f`=wPY=zhb1)7Nq5ilDbf zgOz52O~cHOgeAA)hr6Y$rl|vv-p21)CCg`w>k5J2?v_F~EhW9h0f8b9#FZ{%uVnDw zX9OgRL_t8lq@5oTxLKr&G|;}J?Ficc1a)wZG1+7H0ck}@;PF0jpx_=KwB-z4#R||V zhOrnQ_-WV!TrE~c{!_DAi>U=}p&j5tu?q5+_AUgX$pjn~+W~TkANk%L;=6w7FzA;l z6aryx^)^5&ae)5N&EzG+SUyTJD)UH5ey@o9rIPsXfWAuq1abq9$r4D08XvfC?E!mH zVsa;5cE=s6tPmr5hLjp`suaeOEw^J#Dpf=NUqfgB6joy%-zdeOK(_QU5yW>vj~H-_ zt^R}S05r;Up$t_DAgT<;LdF1?GI5}`Oaj3m-UlbH`ahH71bi*CK<==;gCXcJh42y! zY?dK_$Z{RT4qrG3I>7{*1>TlBB6g^%LAyMYT?05(VFvv{yjxfSW05lg`0v30Duo?z zHj-zI_Aee%2t=8wGTn3sENaHMgpmKF>=I)FyPy0CmP9i6p1^6j54v-d36y@C0jfKF zm@y@cuW$ftC5)xVkzw$yRDk|SXe$H@DS=?2Ffddpiuh-?6?2o_0N*_m*lFo2}kOpG_cg{q>K$x$J2N?{YLI#onWbA*y+4TfP z&*hj}JeJ7-W>A%pzgCd{9+FNX1LQ*6lTMR`{1-ocl`eV0~V3w delta 36287 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 TbTtHgKIb=}#OPPBcmMwnM9LS( diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 6a38a8c..49ab6fc 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=a17ddd85a26b6a7f5ddb71ff8b05fc5104c0202c6e64782429790c933686c806 -distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip +distributionSha256Sum=df67a32e86e3276d011735facb1535f64d0d88df84fa87521e90becc2d735444 +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From a64c69d93917cf39300f2f259edaeb02d44b81c6 Mon Sep 17 00:00:00 2001 From: OffsetMonkey538 <71213040+OffsetMonkey538@users.noreply.github.com> Date: Fri, 26 Dec 2025 12:07:04 +0200 Subject: [PATCH 52/52] Update monkeylib --- build.gradle | 2 +- .../GithubResourcepackManager.java | 16 ++++++++++++---- .../platform/PlatformMain.java | 7 ------- fabric/build.gradle | 2 +- .../platform/fabric/FabricPlatformMain.java | 11 ----------- gradle.properties | 4 ++-- 6 files changed, 16 insertions(+), 26 deletions(-) diff --git a/build.gradle b/build.gradle index 81c8cc5..1abbcdb 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ import dex.plugins.outlet.v2.util.ReleaseType import java.nio.file.Files plugins { - id 'fabric-loom' version '1.11-SNAPSHOT' apply false + id 'fabric-loom' version '1.14-SNAPSHOT' apply false id 'io.github.dexman545.outlet' version '1.6.1' apply false id 'com.modrinth.minotaur' version '2.+' apply false } diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java index 0075ea6..2222668 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/GithubResourcepackManager.java @@ -22,9 +22,11 @@ import top.offsetmonkey538.monkeylib538.api.lifecycle.ServerLifecycleApi; import top.offsetmonkey538.monkeylib538.api.log.MonkeyLibLogger; import top.offsetmonkey538.monkeylib538.api.platform.PlatformUtil; +import top.offsetmonkey538.monkeylib538.api.telemetry.TelemetryRegistry; import top.offsetmonkey538.monkeylib538.api.text.MonkeyLibStyle; import top.offsetmonkey538.monkeylib538.api.text.MonkeyLibText; import top.offsetmonkey538.monkeylib538.api.text.TextFormattingApi; +import top.offsetmonkey538.monkeylib538.telemetry.TelemetryHandler; import top.offsetmonkey538.offsetconfig538.api.config.ConfigHolder; import top.offsetmonkey538.offsetconfig538.api.config.ConfigManager; @@ -80,11 +82,17 @@ private GithubResourcepackManager() { private static boolean disabled; public static void initialize() { + TelemetryRegistry.register("git-pack-manager"); // TODO: replace with MOD_ID once I do that + addLogToAdminListeners(); - PlatformMain.INSTANCE.registerSendMessageQueueOnAdminJoin( - MESSAGE_QUEUE, - MESSAGE_QUEUE_EMPTY_MESSAGE - ); + PlatformUtil.sendMessagesToAdminsOnJoin(() -> { + if (MESSAGE_QUEUE.isEmpty()) return new MonkeyLibText[] {}; + + final MonkeyLibText[] result = new MonkeyLibText[MESSAGE_QUEUE.size() + 1]; + MESSAGE_QUEUE.toArray(result); + result[result.length - 1] = MESSAGE_QUEUE_EMPTY_MESSAGE; + return result; + }); // config should be initialized after the error listeners config = ConfigManager.init(ConfigHolder.create(ModConfig::new, LOGGER::error)); diff --git a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformMain.java b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformMain.java index 48c6cb3..e37818a 100644 --- a/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformMain.java +++ b/common/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/PlatformMain.java @@ -22,11 +22,4 @@ public interface PlatformMain { * @param message the message to send */ void sendMessageToAdmins(final MonkeyLibText message); - - /** - * Sends messages contained in the provided list to admins when they join - * - * @param messageQueue list containing messages to send to admins when they join. Contents can be changed after calling this method. - */ - void registerSendMessageQueueOnAdminJoin(final List messageQueue, final MonkeyLibText lastMessage); } diff --git a/fabric/build.gradle b/fabric/build.gradle index 3ce837f..6ca899b 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -1,7 +1,7 @@ import dex.plugins.outlet.v2.util.ReleaseType plugins { - id 'fabric-loom' version '1.11-SNAPSHOT' + id 'fabric-loom' version '1.14-SNAPSHOT' id 'io.github.dexman545.outlet' version '1.6.1' } diff --git a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java index f3289cd..4509c87 100644 --- a/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java +++ b/fabric/src/main/java/top/offsetmonkey538/githubresourcepackmanager/platform/fabric/FabricPlatformMain.java @@ -38,17 +38,6 @@ public void sendMessageToAdmins(MonkeyLibText message) { } } - @Override - public void registerSendMessageQueueOnAdminJoin(List messageQueue, MonkeyLibText lastMessage) { - ServerPlayConnectionEvents.JOIN.register((serverPlayNetworkHandler, packetSender, minecraftServer1) -> { - if (messageQueue.isEmpty()) return; - if (!FabricPlayerApi.isPlayerOp(minecraftServer1.getPlayerManager(), serverPlayNetworkHandler.player)) return; - - for (MonkeyLibText text : messageQueue) serverPlayNetworkHandler.player.sendMessage(FabricMonkeyLibText.of(text).getText()); - serverPlayNetworkHandler.player.sendMessage(FabricMonkeyLibText.of(lastMessage).getText()); - }); - } - @Override public void refreshDatapacks() { getServer().getDataPackManager().scanPacks(); diff --git a/gradle.properties b/gradle.properties index ff74d11..728dfcb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,9 +5,9 @@ org.gradle.parallel = true minecraft_version = 1.21.4 # MESH Lib, check at https://github.com/OffsetMods538/MESH-Lib -meshlib_version = 2.0.0-alpha.0.1761252973683+89c3c64 +meshlib_version = 2.0.0-alpha.0.1762972806024+d227635 # MonkeyLib538, check at https://github.com/OffsetMods538/MonkeyLib538 -monkeylib538_version = 3.0.0-alpha.0.1761395863791+bd100da +monkeylib538_version = 3.0.0-alpha.3 jgit_version = 6.9.0.202403050737-r