Enhance DiscordManager initialization with custom executor and improved thread handling

This commit is contained in:
Xujiayao 2026-01-18 23:23:01 +08:00
parent 6a0e4ed629
commit 4d92192413
2 changed files with 56 additions and 31 deletions

View file

@ -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<Void> 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<Void> 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<CommandData> 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<List<Command>> updateFuture = jda.updateCommands().addCommands(commands).submit();
CompletableFuture<Void> checkFuture = CompletableFuture.runAsync(() -> {
if (!updateFuture.isDone()) {
LOGGER.warn(I18nManager.getDmccTranslation("discord.manager.registering_commands"));
// Blocks until commands are updated
try {
List<CommandData> 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<List<Command>> updateFuture = jda.updateCommands().addCommands(commands).submit();
CompletableFuture<Void> 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;

View file

@ -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.
*