feat: 增强调度功能,支持Folia调度器并优化任务管理

This commit is contained in:
2026-02-13 21:44:11 +08:00
parent 4f453d4c33
commit 54c720de7d

View File

@@ -6,13 +6,16 @@ import com.ddaodan.MineChatGPT.Main;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.logging.Level; import java.util.logging.Level;
public class RequestCoordinator { public class RequestCoordinator {
@@ -32,6 +35,7 @@ public class RequestCoordinator {
private volatile TokenEstimator tokenEstimator; private volatile TokenEstimator tokenEstimator;
private volatile RequestQueue<RequestJob> queue; private volatile RequestQueue<RequestJob> queue;
private volatile int dispatchTaskId = -1; private volatile int dispatchTaskId = -1;
private volatile Object foliaDispatchTask;
public RequestCoordinator(Main plugin, ConfigManager configManager, ApiService apiService, UserSessionManager sessionManager) { public RequestCoordinator(Main plugin, ConfigManager configManager, ApiService apiService, UserSessionManager sessionManager) {
this.plugin = plugin; this.plugin = plugin;
@@ -57,10 +61,10 @@ public class RequestCoordinator {
} }
public void start() { public void start() {
if (dispatchTaskId != -1) { if (dispatchTaskId != -1 || foliaDispatchTask != null) {
return; return;
} }
dispatchTaskId = Bukkit.getScheduler().runTaskTimer(plugin, () -> { Runnable dispatchRunnable = () -> {
if (!configManager.isQueueEnabled()) { if (!configManager.isQueueEnabled()) {
return; return;
} }
@@ -76,10 +80,30 @@ public class RequestCoordinator {
} }
dispatch(job); dispatch(job);
} }
}, 1L, 1L).getTaskId(); };
if (isFoliaSchedulerAvailable()) {
try {
foliaDispatchTask = runFoliaRepeating(dispatchRunnable, 1L, 1L);
return;
} catch (Exception e) {
plugin.getLogger().log(Level.WARNING, "Failed to schedule Folia repeating task, fallback to Bukkit scheduler.", e);
}
}
dispatchTaskId = Bukkit.getScheduler().runTaskTimer(plugin, dispatchRunnable, 1L, 1L).getTaskId();
} }
public void stop() { public void stop() {
if (foliaDispatchTask != null) {
try {
cancelFoliaTask(foliaDispatchTask);
} catch (Exception e) {
plugin.getLogger().log(Level.WARNING, "Failed to cancel Folia dispatch task.", e);
} finally {
foliaDispatchTask = null;
}
}
if (dispatchTaskId != -1) { if (dispatchTaskId != -1) {
Bukkit.getScheduler().cancelTask(dispatchTaskId); Bukkit.getScheduler().cancelTask(dispatchTaskId);
dispatchTaskId = -1; dispatchTaskId = -1;
@@ -163,7 +187,7 @@ public class RequestCoordinator {
} }
private void handleCompletion(RequestJob job, String characterName, ApiService.ChatCompletionResult result) { private void handleCompletion(RequestJob job, String characterName, ApiService.ChatCompletionResult result) {
Bukkit.getScheduler().runTask(plugin, () -> { runOnMainThread(() -> {
if (!plugin.isEnabled()) { if (!plugin.isEnabled()) {
return; return;
} }
@@ -270,7 +294,7 @@ public class RequestCoordinator {
private CompletableFuture<Void> runSync(Runnable runnable) { private CompletableFuture<Void> runSync(Runnable runnable) {
CompletableFuture<Void> future = new CompletableFuture<>(); CompletableFuture<Void> future = new CompletableFuture<>();
Bukkit.getScheduler().runTask(plugin, () -> { runOnMainThread(() -> {
try { try {
runnable.run(); runnable.run();
future.complete(null); future.complete(null);
@@ -281,6 +305,63 @@ public class RequestCoordinator {
return future; return future;
} }
private void runOnMainThread(Runnable runnable) {
if (isFoliaSchedulerAvailable()) {
try {
runFoliaNow(runnable);
return;
} catch (Exception e) {
plugin.getLogger().log(Level.WARNING, "Failed to execute on Folia scheduler, fallback to Bukkit scheduler.", e);
}
}
Bukkit.getScheduler().runTask(plugin, runnable);
}
private Object runFoliaRepeating(Runnable runnable, long initialDelayTicks, long periodTicks) throws Exception {
Object scheduler = getGlobalRegionScheduler();
Method method = scheduler.getClass().getMethod("runAtFixedRate", Plugin.class, Consumer.class, long.class, long.class);
Consumer<Object> consumer = task -> runnable.run();
return method.invoke(scheduler, plugin, consumer, initialDelayTicks, periodTicks);
}
private void cancelFoliaTask(Object task) throws Exception {
try {
// Prefer invoking through public ScheduledTask interface when available.
Class<?> scheduledTaskInterface = Class.forName("io.papermc.paper.threadedregions.scheduler.ScheduledTask");
Method cancel = scheduledTaskInterface.getMethod("cancel");
cancel.invoke(task);
return;
} catch (ClassNotFoundException ignored) {
// Fall through to reflective invocation on task implementation.
}
Method cancel = task.getClass().getDeclaredMethod("cancel");
if (!cancel.isAccessible()) {
cancel.setAccessible(true);
}
cancel.invoke(task);
}
private void runFoliaNow(Runnable runnable) throws Exception {
Object scheduler = getGlobalRegionScheduler();
Method method = scheduler.getClass().getMethod("execute", Plugin.class, Runnable.class);
method.invoke(scheduler, plugin, runnable);
}
private Object getGlobalRegionScheduler() throws Exception {
Method method = Bukkit.getServer().getClass().getMethod("getGlobalRegionScheduler");
return method.invoke(Bukkit.getServer());
}
private boolean isFoliaSchedulerAvailable() {
try {
Bukkit.getServer().getClass().getMethod("getGlobalRegionScheduler");
return true;
} catch (NoSuchMethodException e) {
return false;
}
}
private void trimContextToBudget(String characterPrompt, String summary, ConversationContext context) { private void trimContextToBudget(String characterPrompt, String summary, ConversationContext context) {
int budget = configManager.getMaxContextTokens(); int budget = configManager.getMaxContextTokens();
if (budget <= 0) { if (budget <= 0) {