package github.tartaricacid.bakadanmaku.api.thread; import github.tartaricacid.bakadanmaku.BakaDanmaku; import github.tartaricacid.bakadanmaku.config.BakaDanmakuConfig; import net.minecraft.util.text.TextFormatting; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; public class DanmakuThreadFactory { // 存储弹幕线程的 HashMap private static final HashMap<String, BaseDanmakuThread> danmakuThreads = new HashMap<>(); // 未启动的弹幕线程 private static final HashMap<String, Thread> realDanmakuThreads = new HashMap<>(); // 正在运行的弹幕线程 /** * 向 danmakuThreads 中添加新的 DanmakuThread 类型 * 通常在 Mod 的 init 阶段使用 * * @param name DanmakuThread 的名称 * @param thread DanmakuThread 的实例 */ public static void setDanmakuThread(String name, BaseDanmakuThread thread) { danmakuThreads.put(name, thread); } /** * 获得指定平台的 DanmakuThread * * @param platform 平台名 * @return BaseDanmakuThread 实例 */ public static BaseDanmakuThread getDanmakuThread(String platform) { return danmakuThreads.getOrDefault(platform, null); } /** * 获得当前正在运行的线程 * * @return 当前正在运行的线程 */ public static ArrayList<String> getRunningDanmakuThread() { return new ArrayList<>(realDanmakuThreads.keySet()); } /** * 启动指定平台的 DanmakuThread * * @param platform 平台名 */ public static void runThread(String platform) { // 先获取当前正在运行的弹幕线程 BaseDanmakuThread dmThread = getDanmakuThread(platform); // 如果正在运行的弹幕线程为空 if (dmThread != null) { // 将弹幕开启的指示参数设定为 true dmThread.keepRunning = true; // 而后重新 new 线程,并启动线程 Thread threadToRun = new Thread(dmThread, platform + "DanmakuThread"); threadToRun.start(); // 在存储实际运行的 map 中存入这个线程 realDanmakuThreads.put(platform, threadToRun); } else { // 发送错误信息 BakaDanmaku.logger.error("平台 [" + platform + "] 不存在!请检查配置文件或已安装 Mod!"); BaseDanmakuThread.sendChatMessage(TextFormatting.RED + "弹幕姬错误:"); BaseDanmakuThread.sendChatMessage(TextFormatting.RED + "平台 [" + platform + "] 不存在!请检查配置文件或已安装 Mod!"); } } /** * 停止指定平台的 DanmakuThread * * @param platform 平台名 * @param restart 是否再次启动该线程 */ public static void stopThread(String platform, boolean restart) { // 先获取当前正在运行的弹幕线程 BaseDanmakuThread th = getDanmakuThread(platform); // 如果正在运行的弹幕线程为空 if (th != null) { // 创建新线程,因为关闭弹幕线程是有阻塞的 new Thread(() -> { // 先关闭线程标识符,借此关闭所有弹幕线程 th.keepRunning = false; // 阻塞,等待弹幕线程关闭 while (realDanmakuThreads.get(platform).isAlive()) ; // 在运行线程表中移除该线程 realDanmakuThreads.remove(platform); // 清空线程 th.clear(); // 如果 restart,则再次启动线程 if (restart) runThread(platform); }, "Stop" + platform + "DanmakuThread").start(); } } /** * 停止指定平台的 DanmakuThread * * @param platform 平台名 */ public static void stopThread(String platform) { stopThread(platform, false); } /** * 停止现在正在运行的所有 DanmakuThread * stopThread 会卡住游戏,所以得在单独线程进行关闭操作 */ public static void stopAllThreads() { realDanmakuThreads.forEach((platform, thread) -> stopThread(platform)); } /** * 判断指定 platform 的 DanmakuThread 是否正在运行 * * @param platform 平台名 * @return 指定 platform 的 DanmakuThread 是否正在运行 */ public static boolean isThreadRunning(String platform) { return realDanmakuThreads.containsKey(platform); } /** * 判断指定 platform 的 DanmakuThread 是否存在 * * @param platform 平台名 * @return DanmakuThread 存在与否 */ public static boolean isDanmakuThreadAvailable(String platform) { return danmakuThreads.containsKey(platform); } /** * 重启所有线程 同样可以用于初始化时线程的启动 */ public static void restartThreads() { // 获得所有的 platforms String[] _platforms = BakaDanmakuConfig.livePlatform.platform.split(","); for (int i = 0; i < _platforms.length; i++) { _platforms[i] = _platforms[i].trim(); // 剔除行首行尾空格 } // 获得所有的平台 ArrayList<String> platforms = new ArrayList<>(Arrays.asList(_platforms)); // 获得正在运行的弹幕线程 ArrayList<String> running = getRunningDanmakuThread(); // 创建一个 restart 数据,存入刚刚正在运行的弹幕线程列表 ArrayList<String> restart = new ArrayList<>(running); // 获得两者的交集 restart.retainAll(platforms); // 创建一个 toStop 数据,存入刚刚正在运行的弹幕线程列表 ArrayList<String> toStop = new ArrayList<>(running); // 获得两者的差集 toStop.removeAll(platforms); // 创建一个 toStart 数据,存入所有的平台列表 ArrayList<String> toStart = new ArrayList<>(platforms); // 获得两者的差集 toStart.removeAll((getRunningDanmakuThread())); // restart 部分,依次进行停止、并重启 restart.forEach((platform) -> stopThread(platform, true)); // toStop 部分,依次进行停止 toStop.forEach(DanmakuThreadFactory::stopThread); // toStart 部分,依次进行开启 toStart.forEach(DanmakuThreadFactory::runThread); } }