mirror of
https://github.com/ddaodan/minechatgpt.git
synced 2026-06-21 21:45:28 +08:00
Compare commits
2 Commits
2.8
...
80d1dd0db5
| Author | SHA1 | Date | |
|---|---|---|---|
| 80d1dd0db5 | |||
| 54c720de7d |
@@ -1,8 +1,8 @@
|
||||
package com.ddaodan.MineChatGPT;
|
||||
|
||||
import com.ddaodan.MineChatGPT.service.ApiService;
|
||||
import com.ddaodan.MineChatGPT.service.CommandService;
|
||||
import com.ddaodan.MineChatGPT.service.RequestCoordinator;
|
||||
import com.ddaodan.MineChatGPT.service.UpdateChecker;
|
||||
import com.ddaodan.MineChatGPT.service.UserSessionManager;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
@@ -12,10 +12,10 @@ public class CommandHandler implements CommandExecutor {
|
||||
private final CommandService commandService;
|
||||
private final UserSessionManager sessionManager;
|
||||
|
||||
public CommandHandler(ConfigManager configManager, UserSessionManager sessionManager, RequestCoordinator requestCoordinator) {
|
||||
public CommandHandler(ConfigManager configManager, UserSessionManager sessionManager, RequestCoordinator requestCoordinator, UpdateChecker updateChecker) {
|
||||
this.configManager = configManager;
|
||||
this.sessionManager = sessionManager;
|
||||
this.commandService = new CommandService(configManager, requestCoordinator, sessionManager);
|
||||
this.commandService = new CommandService(configManager, requestCoordinator, sessionManager, updateChecker);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -43,6 +43,8 @@ public class CommandHandler implements CommandExecutor {
|
||||
return commandService.handleCharacterCommand(sender, args, userId);
|
||||
} else if (subCommand.equalsIgnoreCase("stats")) {
|
||||
return commandService.handleStatsCommand(sender, args, userId);
|
||||
} else if (subCommand.equalsIgnoreCase("checkupdate")) {
|
||||
return commandService.handleCheckUpdateCommand(sender);
|
||||
} else {
|
||||
return commandService.handleAskCommand(sender, args, userId);
|
||||
}
|
||||
|
||||
@@ -331,4 +331,29 @@ public class ConfigManager {
|
||||
public int getQueueDispatchPerTick() {
|
||||
return Math.max(1, config.getInt("queue.dispatch_per_tick", 2));
|
||||
}
|
||||
|
||||
public boolean isUpdateCheckerEnabled() {
|
||||
return config.getBoolean("update_checker.enabled", true);
|
||||
}
|
||||
public String getUpdateCheckerSource() {
|
||||
return config.getString("update_checker.source", "github");
|
||||
}
|
||||
public String getHelpCheckUpdateMessage() {
|
||||
return languageManager.getMessage("help_checkupdate");
|
||||
}
|
||||
public String getUpdateCheckingMessage() {
|
||||
return languageManager.getMessage("update_checking", "&eChecking for updates...");
|
||||
}
|
||||
public String getUpdateAvailableMessage() {
|
||||
return languageManager.getMessage("update_available", "&aA new version is available: %s (current: %s)");
|
||||
}
|
||||
public String getUpdateLatestMessage() {
|
||||
return languageManager.getMessage("update_latest", "&aYou are running the latest version.");
|
||||
}
|
||||
public String getUpdateErrorMessage() {
|
||||
return languageManager.getMessage("update_error", "&cFailed to check for updates.");
|
||||
}
|
||||
public String getUpdateDownloadMessage() {
|
||||
return languageManager.getMessage("update_download", "&eDownload: %s");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ public final class Main extends JavaPlugin {
|
||||
private com.ddaodan.MineChatGPT.service.UserSessionManager sessionManager;
|
||||
private com.ddaodan.MineChatGPT.service.ApiService apiService;
|
||||
private com.ddaodan.MineChatGPT.service.RequestCoordinator requestCoordinator;
|
||||
private com.ddaodan.MineChatGPT.service.UpdateChecker updateChecker;
|
||||
private CommandHandler commandHandler;
|
||||
private MineChatGPTTabCompleter tabCompleter;
|
||||
|
||||
@@ -21,10 +22,13 @@ public final class Main extends JavaPlugin {
|
||||
apiService = new com.ddaodan.MineChatGPT.service.ApiService(this, configManager);
|
||||
requestCoordinator = new com.ddaodan.MineChatGPT.service.RequestCoordinator(this, configManager, apiService, sessionManager);
|
||||
requestCoordinator.start();
|
||||
commandHandler = new CommandHandler(configManager, sessionManager, requestCoordinator);
|
||||
updateChecker = new com.ddaodan.MineChatGPT.service.UpdateChecker(this, configManager);
|
||||
commandHandler = new CommandHandler(configManager, sessionManager, requestCoordinator, updateChecker);
|
||||
tabCompleter = new MineChatGPTTabCompleter(configManager);
|
||||
Objects.requireNonNull(getCommand("chatgpt")).setExecutor(commandHandler);
|
||||
Objects.requireNonNull(getCommand("chatgpt")).setTabCompleter(tabCompleter);
|
||||
getServer().getPluginManager().registerEvents(updateChecker, this);
|
||||
updateChecker.checkOnStartup();
|
||||
if (configManager.isDebugMode()) {
|
||||
getLogger().info( "DEBUG MODE IS TRUE!!!!!");
|
||||
}
|
||||
@@ -38,6 +42,9 @@ public final class Main extends JavaPlugin {
|
||||
if (requestCoordinator != null) {
|
||||
requestCoordinator.stop();
|
||||
}
|
||||
if (updateChecker != null) {
|
||||
updateChecker.shutdown();
|
||||
}
|
||||
if (apiService != null) {
|
||||
apiService.shutdown();
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ public class MineChatGPTTabCompleter implements TabCompleter {
|
||||
completions.add("clear");
|
||||
completions.add("character");
|
||||
completions.add("stats");
|
||||
completions.add("checkupdate");
|
||||
} else if (args.length == 2) {
|
||||
String subCommand = args[0];
|
||||
if (subCommand.equalsIgnoreCase("model")) {
|
||||
|
||||
@@ -14,11 +14,13 @@ public class CommandService {
|
||||
private final ConfigManager configManager;
|
||||
private final RequestCoordinator requestCoordinator;
|
||||
private final UserSessionManager sessionManager;
|
||||
private final UpdateChecker updateChecker;
|
||||
|
||||
public CommandService(ConfigManager configManager, RequestCoordinator requestCoordinator, UserSessionManager sessionManager) {
|
||||
public CommandService(ConfigManager configManager, RequestCoordinator requestCoordinator, UserSessionManager sessionManager, UpdateChecker updateChecker) {
|
||||
this.configManager = configManager;
|
||||
this.requestCoordinator = requestCoordinator;
|
||||
this.sessionManager = sessionManager;
|
||||
this.updateChecker = updateChecker;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -199,6 +201,15 @@ public class CommandService {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean handleCheckUpdateCommand(CommandSender sender) {
|
||||
if (!sender.hasPermission("minechatgpt.checkupdate")) {
|
||||
sender.sendMessage(configManager.getNoPermissionMessage().replace("%s", "minechatgpt.checkupdate"));
|
||||
return true;
|
||||
}
|
||||
updateChecker.checkAndNotify(sender);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送帮助信息
|
||||
*
|
||||
@@ -214,5 +225,6 @@ public class CommandService {
|
||||
sender.sendMessage(configManager.getHelpClearMessage());
|
||||
sender.sendMessage(configManager.getHelpCharacterMessage());
|
||||
sender.sendMessage(configManager.getHelpStatsMessage());
|
||||
sender.sendMessage(configManager.getHelpCheckUpdateMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,16 @@ import com.ddaodan.MineChatGPT.Main;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class RequestCoordinator {
|
||||
@@ -32,6 +35,7 @@ public class RequestCoordinator {
|
||||
private volatile TokenEstimator tokenEstimator;
|
||||
private volatile RequestQueue<RequestJob> queue;
|
||||
private volatile int dispatchTaskId = -1;
|
||||
private volatile Object foliaDispatchTask;
|
||||
|
||||
public RequestCoordinator(Main plugin, ConfigManager configManager, ApiService apiService, UserSessionManager sessionManager) {
|
||||
this.plugin = plugin;
|
||||
@@ -57,10 +61,10 @@ public class RequestCoordinator {
|
||||
}
|
||||
|
||||
public void start() {
|
||||
if (dispatchTaskId != -1) {
|
||||
if (dispatchTaskId != -1 || foliaDispatchTask != null) {
|
||||
return;
|
||||
}
|
||||
dispatchTaskId = Bukkit.getScheduler().runTaskTimer(plugin, () -> {
|
||||
Runnable dispatchRunnable = () -> {
|
||||
if (!configManager.isQueueEnabled()) {
|
||||
return;
|
||||
}
|
||||
@@ -76,10 +80,30 @@ public class RequestCoordinator {
|
||||
}
|
||||
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() {
|
||||
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) {
|
||||
Bukkit.getScheduler().cancelTask(dispatchTaskId);
|
||||
dispatchTaskId = -1;
|
||||
@@ -163,7 +187,7 @@ public class RequestCoordinator {
|
||||
}
|
||||
|
||||
private void handleCompletion(RequestJob job, String characterName, ApiService.ChatCompletionResult result) {
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
runOnMainThread(() -> {
|
||||
if (!plugin.isEnabled()) {
|
||||
return;
|
||||
}
|
||||
@@ -270,7 +294,7 @@ public class RequestCoordinator {
|
||||
|
||||
private CompletableFuture<Void> runSync(Runnable runnable) {
|
||||
CompletableFuture<Void> future = new CompletableFuture<>();
|
||||
Bukkit.getScheduler().runTask(plugin, () -> {
|
||||
runOnMainThread(() -> {
|
||||
try {
|
||||
runnable.run();
|
||||
future.complete(null);
|
||||
@@ -281,6 +305,63 @@ public class RequestCoordinator {
|
||||
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) {
|
||||
int budget = configManager.getMaxContextTokens();
|
||||
if (budget <= 0) {
|
||||
|
||||
264
src/main/java/com/ddaodan/MineChatGPT/service/UpdateChecker.java
Normal file
264
src/main/java/com/ddaodan/MineChatGPT/service/UpdateChecker.java
Normal file
@@ -0,0 +1,264 @@
|
||||
package com.ddaodan.MineChatGPT.service;
|
||||
|
||||
import com.ddaodan.MineChatGPT.ConfigManager;
|
||||
import com.ddaodan.MineChatGPT.Main;
|
||||
import jodd.http.HttpRequest;
|
||||
import jodd.http.HttpResponse;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class UpdateChecker implements Listener {
|
||||
private static final String GITHUB_API = "https://api.github.com/repos/ddaodan-minecraft/minechatgpt/releases/latest";
|
||||
private static final String MODRINTH_API = "https://api.modrinth.com/v2/project/minechatgpt/version?limit=1";
|
||||
private static final String GITHUB_URL = "https://github.com/ddaodan-minecraft/minechatgpt/releases/latest";
|
||||
private static final String MODRINTH_URL = "https://modrinth.com/plugin/minechatgpt";
|
||||
|
||||
private final Main plugin;
|
||||
private final ConfigManager configManager;
|
||||
private final ExecutorService executor;
|
||||
private volatile String latestVersion;
|
||||
|
||||
public UpdateChecker(Main plugin, ConfigManager configManager) {
|
||||
this.plugin = plugin;
|
||||
this.configManager = configManager;
|
||||
this.executor = Executors.newSingleThreadExecutor(r -> {
|
||||
Thread t = new Thread(r, "minechatgpt-update-checker");
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
});
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
executor.shutdownNow();
|
||||
}
|
||||
|
||||
public void checkOnStartup() {
|
||||
if (!configManager.isUpdateCheckerEnabled()) {
|
||||
return;
|
||||
}
|
||||
checkAsync().thenAccept(result -> {
|
||||
if (result.error) {
|
||||
plugin.getLogger().warning("Failed to check for updates.");
|
||||
} else if (result.hasUpdate) {
|
||||
plugin.getLogger().info("A new version is available: " + result.latestVersion
|
||||
+ " (current: " + result.currentVersion + ")");
|
||||
plugin.getLogger().info("Download: " + result.downloadUrl);
|
||||
} else {
|
||||
plugin.getLogger().info("You are running the latest version (" + result.currentVersion + ").");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||
if (!configManager.isUpdateCheckerEnabled()) {
|
||||
return;
|
||||
}
|
||||
Player player = event.getPlayer();
|
||||
if (!player.hasPermission("minechatgpt.checkupdate")) {
|
||||
return;
|
||||
}
|
||||
if (latestVersion == null) {
|
||||
return;
|
||||
}
|
||||
String currentVersion = plugin.getDescription().getVersion();
|
||||
if (isNewerVersion(latestVersion, currentVersion)) {
|
||||
String downloadUrl = getDownloadUrl();
|
||||
runOnMainThreadDelayed(() -> {
|
||||
if (player.isOnline()) {
|
||||
player.sendMessage(configManager.getUpdateAvailableMessage()
|
||||
.replaceFirst("%s", latestVersion)
|
||||
.replaceFirst("%s", currentVersion));
|
||||
player.sendMessage(configManager.getUpdateDownloadMessage()
|
||||
.replace("%s", downloadUrl));
|
||||
}
|
||||
}, 40L);
|
||||
}
|
||||
}
|
||||
|
||||
public void checkAndNotify(CommandSender sender) {
|
||||
sender.sendMessage(configManager.getUpdateCheckingMessage());
|
||||
checkAsync().thenAccept(result -> {
|
||||
runOnMainThread(() -> {
|
||||
if (sender instanceof Player && !((Player) sender).isOnline()) {
|
||||
return;
|
||||
}
|
||||
if (result.error) {
|
||||
sender.sendMessage(configManager.getUpdateErrorMessage());
|
||||
return;
|
||||
}
|
||||
if (result.hasUpdate) {
|
||||
sender.sendMessage(configManager.getUpdateAvailableMessage()
|
||||
.replaceFirst("%s", result.latestVersion)
|
||||
.replaceFirst("%s", result.currentVersion));
|
||||
sender.sendMessage(configManager.getUpdateDownloadMessage()
|
||||
.replace("%s", result.downloadUrl));
|
||||
} else {
|
||||
sender.sendMessage(configManager.getUpdateLatestMessage());
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private CompletableFuture<UpdateResult> checkAsync() {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try {
|
||||
String source = configManager.getUpdateCheckerSource();
|
||||
String currentVersion = plugin.getDescription().getVersion();
|
||||
String remote;
|
||||
|
||||
if ("modrinth".equalsIgnoreCase(source)) {
|
||||
remote = fetchModrinthVersion();
|
||||
} else {
|
||||
remote = fetchGitHubVersion();
|
||||
}
|
||||
|
||||
if (remote == null) {
|
||||
return UpdateResult.errorResult();
|
||||
}
|
||||
|
||||
latestVersion = remote;
|
||||
boolean hasUpdate = isNewerVersion(remote, currentVersion);
|
||||
return new UpdateResult(false, hasUpdate, remote, currentVersion, getDownloadUrl());
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().log(Level.WARNING, "Failed to check for updates", e);
|
||||
return UpdateResult.errorResult();
|
||||
}
|
||||
}, executor);
|
||||
}
|
||||
|
||||
private String fetchGitHubVersion() {
|
||||
HttpResponse response = HttpRequest.get(GITHUB_API)
|
||||
.header("Accept", "application/vnd.github.v3+json")
|
||||
.header("User-Agent", "MineChatGPT/" + plugin.getDescription().getVersion())
|
||||
.connectionTimeout(10000)
|
||||
.timeout(15000)
|
||||
.send();
|
||||
if (response.statusCode() != 200) {
|
||||
return null;
|
||||
}
|
||||
JSONObject json = new JSONObject(response.bodyText());
|
||||
String tagName = json.getString("tag_name");
|
||||
return tagName.startsWith("v") ? tagName.substring(1) : tagName;
|
||||
}
|
||||
|
||||
private String fetchModrinthVersion() {
|
||||
HttpResponse response = HttpRequest.get(MODRINTH_API)
|
||||
.header("User-Agent", "MineChatGPT/" + plugin.getDescription().getVersion())
|
||||
.connectionTimeout(10000)
|
||||
.timeout(15000)
|
||||
.send();
|
||||
if (response.statusCode() != 200) {
|
||||
return null;
|
||||
}
|
||||
JSONArray arr = new JSONArray(response.bodyText());
|
||||
if (arr.length() == 0) {
|
||||
return null;
|
||||
}
|
||||
return arr.getJSONObject(0).getString("version_number");
|
||||
}
|
||||
|
||||
private void runOnMainThread(Runnable runnable) {
|
||||
if (isFoliaSchedulerAvailable()) {
|
||||
try {
|
||||
Object scheduler = getGlobalRegionScheduler();
|
||||
Method method = scheduler.getClass().getMethod("execute", Plugin.class, Runnable.class);
|
||||
method.invoke(scheduler, plugin, runnable);
|
||||
return;
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().log(Level.WARNING, "Failed to execute on Folia scheduler, fallback to Bukkit.", e);
|
||||
}
|
||||
}
|
||||
Bukkit.getScheduler().runTask(plugin, runnable);
|
||||
}
|
||||
|
||||
private void runOnMainThreadDelayed(Runnable runnable, long delayTicks) {
|
||||
if (isFoliaSchedulerAvailable()) {
|
||||
try {
|
||||
Object scheduler = getGlobalRegionScheduler();
|
||||
Consumer<Object> consumer = task -> runnable.run();
|
||||
Method method = scheduler.getClass().getMethod("runDelayed", Plugin.class, Consumer.class, long.class);
|
||||
method.invoke(scheduler, plugin, consumer, delayTicks);
|
||||
return;
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().log(Level.WARNING, "Failed to schedule delayed Folia task, fallback to Bukkit.", e);
|
||||
}
|
||||
}
|
||||
Bukkit.getScheduler().runTaskLater(plugin, runnable, delayTicks);
|
||||
}
|
||||
|
||||
private boolean isFoliaSchedulerAvailable() {
|
||||
try {
|
||||
Bukkit.getServer().getClass().getMethod("getGlobalRegionScheduler");
|
||||
return true;
|
||||
} catch (NoSuchMethodException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private Object getGlobalRegionScheduler() throws Exception {
|
||||
Method method = Bukkit.getServer().getClass().getMethod("getGlobalRegionScheduler");
|
||||
return method.invoke(Bukkit.getServer());
|
||||
}
|
||||
|
||||
private String getDownloadUrl() {
|
||||
String source = configManager.getUpdateCheckerSource();
|
||||
return "modrinth".equalsIgnoreCase(source) ? MODRINTH_URL : GITHUB_URL;
|
||||
}
|
||||
|
||||
static boolean isNewerVersion(String remote, String current) {
|
||||
String[] remoteParts = remote.split("\\.");
|
||||
String[] currentParts = current.split("\\.");
|
||||
int length = Math.max(remoteParts.length, currentParts.length);
|
||||
for (int i = 0; i < length; i++) {
|
||||
int r = i < remoteParts.length ? parseIntSafe(remoteParts[i]) : 0;
|
||||
int c = i < currentParts.length ? parseIntSafe(currentParts[i]) : 0;
|
||||
if (r > c) return true;
|
||||
if (r < c) return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static int parseIntSafe(String s) {
|
||||
try {
|
||||
return Integer.parseInt(s.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class UpdateResult {
|
||||
final boolean error;
|
||||
final boolean hasUpdate;
|
||||
final String latestVersion;
|
||||
final String currentVersion;
|
||||
final String downloadUrl;
|
||||
|
||||
UpdateResult(boolean error, boolean hasUpdate, String latestVersion, String currentVersion, String downloadUrl) {
|
||||
this.error = error;
|
||||
this.hasUpdate = hasUpdate;
|
||||
this.latestVersion = latestVersion;
|
||||
this.currentVersion = currentVersion;
|
||||
this.downloadUrl = downloadUrl;
|
||||
}
|
||||
|
||||
static UpdateResult errorResult() {
|
||||
return new UpdateResult(true, false, null, null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -257,6 +257,18 @@ characters:
|
||||
# Example:
|
||||
# Minecraft: "You are a Minecraft expert who helps players with game mechanics and building ideas."
|
||||
|
||||
# ======================================================
|
||||
# Update Checker Settings
|
||||
# 更新检查设置
|
||||
# ======================================================
|
||||
update_checker:
|
||||
# Enable automatic update checking on server start and OP join
|
||||
# 启用服务器启动和OP加入时自动检查更新
|
||||
enabled: true
|
||||
# Update source: "github" or "modrinth"
|
||||
# 更新源:"github" 或 "modrinth"
|
||||
source: "github"
|
||||
|
||||
# ======================================================
|
||||
# Other Settings
|
||||
# 其他设置
|
||||
|
||||
@@ -36,3 +36,9 @@ messages:
|
||||
invalid_model: "&cInvalid model. Use /chatgpt modellist to see available models."
|
||||
available_models: "&eAvailable models:"
|
||||
no_permission: "&cYou do not have permission to use this command. Required permission: %s"
|
||||
help_checkupdate: "&e/chatgpt checkupdate - Check for plugin updates."
|
||||
update_checking: "&eChecking for updates..."
|
||||
update_available: "&aA new version is available: %s (current: %s)"
|
||||
update_latest: "&aYou are running the latest version."
|
||||
update_error: "&cFailed to check for updates."
|
||||
update_download: "&eDownload: %s"
|
||||
|
||||
@@ -36,3 +36,9 @@ messages:
|
||||
invalid_model: "&c模型无效。使用 /chatgpt modellist 查看可用模型。"
|
||||
available_models: "&e可用模型列表:"
|
||||
no_permission: "&c你没有权限使用这个指令。需要的权限:%s"
|
||||
help_checkupdate: "&e/chatgpt checkupdate - 检查插件更新。"
|
||||
update_checking: "&e正在检查更新..."
|
||||
update_available: "&a发现新版本:%s(当前版本:%s)"
|
||||
update_latest: "&a你正在使用最新版本。"
|
||||
update_error: "&c检查更新失败。"
|
||||
update_download: "&e下载地址:%s"
|
||||
|
||||
@@ -34,3 +34,6 @@ permissions:
|
||||
minechatgpt.stats:
|
||||
description: Allows viewing/resetting usage statistics
|
||||
default: op
|
||||
minechatgpt.checkupdate:
|
||||
description: Allows checking for plugin updates
|
||||
default: op
|
||||
|
||||
Reference in New Issue
Block a user