diff --git a/core/src/main/java/com/xujiayao/discord_mc_chat/server/discord/DiscordManager.java b/core/src/main/java/com/xujiayao/discord_mc_chat/server/discord/DiscordManager.java index 6370d1e4..e2df941a 100644 --- a/core/src/main/java/com/xujiayao/discord_mc_chat/server/discord/DiscordManager.java +++ b/core/src/main/java/com/xujiayao/discord_mc_chat/server/discord/DiscordManager.java @@ -1,5 +1,6 @@ package com.xujiayao.discord_mc_chat.server.discord; +import com.xujiayao.discord_mc_chat.utils.ExecutorServiceUtils; import com.xujiayao.discord_mc_chat.utils.config.ConfigManager; import com.xujiayao.discord_mc_chat.utils.config.ModeManager; import com.xujiayao.discord_mc_chat.utils.i18n.I18nManager; @@ -42,9 +43,10 @@ public class DiscordManager { return false; } - try { - // Blocks until JDA is ready - try (ExecutorService executor = Executors.newSingleThreadExecutor(r -> new Thread(r, "DMCC-FutureChecker"))) { + // Use a custom executor with our special ThreadFactory to ensure ClassLoader is correct + try (ExecutorService executor = Executors.newCachedThreadPool(ExecutorServiceUtils.newThreadFactory("DMCC-DiscordInit"))) { + try { + // Blocks until JDA is ready CompletableFuture readyFuture = CompletableFuture.runAsync(() -> { try { jda = JDABuilder.createDefault(token) @@ -60,7 +62,7 @@ public class DiscordManager { } catch (InterruptedException e) { LOGGER.error(I18nManager.getDmccTranslation("discord.manager.init_interrupted"), e); } - }); + }, executor); CompletableFuture checkFuture = CompletableFuture.runAsync(() -> { if (!readyFuture.isDone()) { @@ -70,39 +72,42 @@ public class DiscordManager { readyFuture.join(); checkFuture.cancel(false); + + LOGGER.info(I18nManager.getDmccTranslation("discord.manager.ready", jda.getSelfUser().getAsTag())); + } catch (Exception e) { + LOGGER.error(I18nManager.getDmccTranslation("discord.manager.init_interrupted"), e); } - LOGGER.info(I18nManager.getDmccTranslation("discord.manager.ready", jda.getSelfUser().getAsTag())); - } catch (Exception e) { - LOGGER.error(I18nManager.getDmccTranslation("discord.manager.init_interrupted"), e); - } - - if (jda == null || jda.getStatus() != JDA.Status.CONNECTED) { - return false; - } - - // Blocks until commands are updated - try (ExecutorService executor = Executors.newSingleThreadExecutor(r -> new Thread(r, "DMCC-FutureChecker"))) { - List commands = new ArrayList<>(); - commands.add(Commands.slash("help", I18nManager.getDmccTranslation("commands.help.description"))); - commands.add(Commands.slash("reload", I18nManager.getDmccTranslation("commands.reload.description"))); - if ("standalone".equals(ModeManager.getMode())) { - commands.add(Commands.slash("shutdown", I18nManager.getDmccTranslation("commands.shutdown.description"))); + if (jda == null || jda.getStatus() != JDA.Status.CONNECTED) { + // Don't forget to shut down the temporary executor + executor.shutdown(); + return false; } - CompletableFuture> updateFuture = jda.updateCommands().addCommands(commands).submit(); - CompletableFuture checkFuture = CompletableFuture.runAsync(() -> { - if (!updateFuture.isDone()) { - LOGGER.warn(I18nManager.getDmccTranslation("discord.manager.registering_commands")); + // Blocks until commands are updated + try { + List commands = new ArrayList<>(); + commands.add(Commands.slash("help", I18nManager.getDmccTranslation("commands.help.description"))); + commands.add(Commands.slash("reload", I18nManager.getDmccTranslation("commands.reload.description"))); + if ("standalone".equals(ModeManager.getMode())) { + commands.add(Commands.slash("shutdown", I18nManager.getDmccTranslation("commands.shutdown.description"))); } - }, CompletableFuture.delayedExecutor(5, TimeUnit.SECONDS, executor)); - updateFuture.join(); - checkFuture.cancel(false); - LOGGER.info(I18nManager.getDmccTranslation("discord.manager.commands_success")); - return true; - } catch (Exception e) { - LOGGER.error(I18nManager.getDmccTranslation("discord.manager.commands_failed"), e); + CompletableFuture> updateFuture = jda.updateCommands().addCommands(commands).submit(); + CompletableFuture checkFuture = CompletableFuture.runAsync(() -> { + if (!updateFuture.isDone()) { + LOGGER.warn(I18nManager.getDmccTranslation("discord.manager.registering_commands")); + } + }, CompletableFuture.delayedExecutor(5, TimeUnit.SECONDS, executor)); + + updateFuture.join(); + checkFuture.cancel(false); + + LOGGER.info(I18nManager.getDmccTranslation("discord.manager.commands_success")); + return true; + } catch (Exception e) { + LOGGER.error(I18nManager.getDmccTranslation("discord.manager.commands_failed"), e); + } } return false; diff --git a/core/src/main/java/com/xujiayao/discord_mc_chat/utils/ExecutorServiceUtils.java b/core/src/main/java/com/xujiayao/discord_mc_chat/utils/ExecutorServiceUtils.java index dad169ec..933c367c 100644 --- a/core/src/main/java/com/xujiayao/discord_mc_chat/utils/ExecutorServiceUtils.java +++ b/core/src/main/java/com/xujiayao/discord_mc_chat/utils/ExecutorServiceUtils.java @@ -3,6 +3,7 @@ package com.xujiayao.discord_mc_chat.utils; import com.xujiayao.discord_mc_chat.utils.config.ConfigManager; import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; /** @@ -12,6 +13,25 @@ import java.util.concurrent.TimeUnit; */ public class ExecutorServiceUtils { + /** + * Creates a ThreadFactory that ensures all created threads inherit the current Mod ClassLoader. + * This fixes issues with SLF4J/ServiceLoader not finding resources in async threads within Minecraft. + * + * @param name The thread name. + * @return A ThreadFactory with the correct Context ClassLoader set. + */ + public static ThreadFactory newThreadFactory(String name) { + // Capture the correct ClassLoader NOW (while we are on the main/mod thread) + final ClassLoader modClassLoader = ExecutorServiceUtils.class.getClassLoader(); + + return r -> { + Thread t = new Thread(r, name); + // Force the new thread to use the Mod ClassLoader captured above + t.setContextClassLoader(modClassLoader); + return t; + }; + } + /** * Shuts down the given ExecutorService gracefully based on configuration. *