19 Commits

Author SHA1 Message Date
a75853187c style: 移除无效异常信息 2024-07-25 20:23:04 +08:00
2263a74a77 feat: 兼容1.21 2024-07-25 20:22:32 +08:00
be756b0a56 feat: 增强 ConfigSection 2024-07-12 15:19:52 +08:00
be25334cc7 feat: 优化配置文件描述 2024-07-06 14:22:14 +08:00
b6e6641041 feat: 完成 SimpleLogger 2024-07-06 13:31:29 +08:00
a0ae6ec550 feat: 添加 PluginLogger 工具类 2024-07-03 18:02:43 +08:00
738b566a2d feat(bukkit): 支持 1.20.6 版本 2024-06-29 01:42:12 +08:00
271328ef6f feat: 添加 CompletableTask 功能 2024-04-05 16:18:01 +08:00
f3e4becf35 Revert "perf: 优化代码"
This reverts commit 86bd28e134.
2024-03-24 13:41:40 +08:00
e2a8cc236e feat: 1.3.3 稳定版 2024-03-24 13:36:27 +08:00
86bd28e134 perf: 优化代码 2024-03-21 04:44:16 +08:00
03b0d62b19 fix: 修复检测更新时获取版本号错误的问题 2024-03-19 17:57:27 +08:00
98300804fe feat: 优化版本更新检测流程 2024-03-19 16:31:38 +08:00
7285f1b3e2 perf: 简化代码 2024-03-18 17:32:56 +08:00
6f4e40942c perf: 简化代码 2024-03-18 17:32:31 +08:00
7462b99ce4 chore: 添加邮箱API 2024-03-18 17:13:33 +08:00
9bcf8a28dd perf: 简化代码 2024-03-18 17:13:14 +08:00
e6cb7efe77 fix: 修复检查版本更新时的问题 2024-03-18 16:59:37 +08:00
fb75b4d95f fix: 修复检查版本更新时的问题 2024-03-18 16:49:54 +08:00
40 changed files with 804 additions and 426 deletions

View File

@@ -26,9 +26,9 @@ repositories {
dependencies { dependencies {
// 对于 Bukkit 插件 // 对于 Bukkit 插件
compileOnly("cn.hamster3.mc.plugin:core-bukkit:1.3.2") compileOnly("cn.hamster3.mc.plugin:core-bukkit:1.3.4-SNAPSHOT")
// 对于 BungeeCord 插件 // 对于 BungeeCord 插件
compileOnly("cn.hamster3.mc.plugin:core-bungee:1.3.2") compileOnly("cn.hamster3.mc.plugin:core-bungee:1.3.4-SNAPSHOT")
} }
``` ```
@@ -54,13 +54,13 @@ dependencies {
<dependency> <dependency>
<groupId>cn.hamster3.mc.plugin</groupId> <groupId>cn.hamster3.mc.plugin</groupId>
<artifactId>core-bukkit</artifactId> <artifactId>core-bukkit</artifactId>
<version>1.3.2</version> <version>1.3.4-SNAPSHOT</version>
</dependency> </dependency>
<!--对于 BungeeCord 插件--> <!--对于 BungeeCord 插件-->
<dependency> <dependency>
<groupId>cn.hamster3.mc.plugin</groupId> <groupId>cn.hamster3.mc.plugin</groupId>
<artifactId>core-bungee</artifactId> <artifactId>core-bungee</artifactId>
<version>1.3.2</version> <version>1.3.4-SNAPSHOT</version>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>

View File

@@ -5,7 +5,8 @@ plugins {
} }
group = "cn.hamster3.mc.plugin" group = "cn.hamster3.mc.plugin"
version = "1.3.2" version = "1.3.4-SNAPSHOT"
description = "叁只仓鼠的 Minecraft 插件开发通用工具包"
subprojects { subprojects {
apply { apply {
@@ -16,6 +17,7 @@ subprojects {
group = rootProject.group group = rootProject.group
version = rootProject.version version = rootProject.version
description = rootProject.description
repositories { repositories {
maven("https://maven.airgame.net/maven-public") maven("https://maven.airgame.net/maven-public")

View File

@@ -1,12 +1,7 @@
@file:Suppress("VulnerableLibrariesLocal") @file:Suppress("VulnerableLibrariesLocal")
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
evaluationDependsOn(":core-common") evaluationDependsOn(":core-common")
val shade = configurations.create("shade")
val shadeJava8 = configurations.create("shadeJava8")
dependencies { dependencies {
api(project(":core-common")) { isTransitive = false } api(project(":core-common")) { isTransitive = false }
compileOnly("org.spigotmc:spigot-api:1.18.2-R0.1-SNAPSHOT") compileOnly("org.spigotmc:spigot-api:1.18.2-R0.1-SNAPSHOT")
@@ -30,16 +25,15 @@ dependencies {
} }
// https://mvnrepository.com/artifact/org.quartz-scheduler/quartz // https://mvnrepository.com/artifact/org.quartz-scheduler/quartz
api("org.quartz-scheduler:quartz:2.3.2") { isTransitive = false } api("org.quartz-scheduler:quartz:2.3.2") { isTransitive = false }
// https://mvnrepository.com/artifact/com.sun.mail/jakarta.mail
api("com.sun.mail:jakarta.mail:2.0.1")
// https://www.spigotmc.org/resources/nbt-api.7939/ // https://www.spigotmc.org/resources/nbt-api.7939/
implementation("de.tr7zw:item-nbt-api:2.12.2") implementation("de.tr7zw:item-nbt-api:+")
// https://mvnrepository.com/artifact/com.zaxxer/HikariCP // https://mvnrepository.com/artifact/com.zaxxer/HikariCP
implementation("com.zaxxer:HikariCP:4.0.3") { exclude(group = "org.slf4j") } implementation("com.zaxxer:HikariCP:4.0.3") { exclude(group = "org.slf4j") }
// https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-stdlib
shade("org.jetbrains.kotlin:kotlin-stdlib:1.9.23") { exclude(group = "org.jetbrains") }
// https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-stdlib-jdk8 // https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-stdlib-jdk8
shadeJava8("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.23") { exclude(group = "org.jetbrains") } implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.23") { exclude(group = "org.jetbrains") }
} }
tasks { tasks {
@@ -52,23 +46,8 @@ tasks {
archiveBaseName = "HamsterCore-Bukkit" archiveBaseName = "HamsterCore-Bukkit"
} }
shadowJar { shadowJar {
from(shade.map { if (it.isDirectory) it else zipTree(it) })
destinationDirectory = rootProject.layout.buildDirectory destinationDirectory = rootProject.layout.buildDirectory
relocate("de.tr7zw.changeme.nbtapi", "cn.hamster3.mc.plugin.core.lib.de.tr7zw.nbtapi") relocate("de.tr7zw.changeme.nbtapi", "cn.hamster3.mc.plugin.core.lib.de.tr7zw.nbtapi")
relocate("de.tr7zw.annotations", "cn.hamster3.mc.plugin.core.lib.de.tr7zw.nbtapi.annotations") relocate("de.tr7zw.annotations", "cn.hamster3.mc.plugin.core.lib.de.tr7zw.nbtapi.annotations")
} }
val shadowJava8 = register<ShadowJar>("shadowJava8") {
dependsOn(":core-common:build")
archiveClassifier = "Java8"
from(project.configurations.runtimeClasspath.get().map { if (it.isDirectory) it else zipTree(it) })
from(processResources.get().outputs)
from(jar.get().outputs)
from(shadeJava8.map { if (it.isDirectory) it else zipTree(it) })
destinationDirectory = rootProject.layout.buildDirectory
relocate("de.tr7zw.changeme.nbtapi", "cn.hamster3.mc.plugin.core.lib.de.tr7zw.nbtapi")
relocate("de.tr7zw.annotations", "cn.hamster3.mc.plugin.core.lib.de.tr7zw.nbtapi.annotations")
}
build {
dependsOn(shadowJava8)
}
} }

View File

@@ -11,10 +11,13 @@ import cn.hamster3.mc.plugin.core.bukkit.listener.CallbackListener;
import cn.hamster3.mc.plugin.core.bukkit.listener.DebugListener; import cn.hamster3.mc.plugin.core.bukkit.listener.DebugListener;
import cn.hamster3.mc.plugin.core.bukkit.page.handler.PageHandler; import cn.hamster3.mc.plugin.core.bukkit.page.handler.PageHandler;
import cn.hamster3.mc.plugin.core.bukkit.page.listener.PageListener; import cn.hamster3.mc.plugin.core.bukkit.page.listener.PageListener;
import cn.hamster3.mc.plugin.core.bukkit.util.BukkitSimpleLogger;
import cn.hamster3.mc.plugin.core.bukkit.util.MinecraftVersion; import cn.hamster3.mc.plugin.core.bukkit.util.MinecraftVersion;
import cn.hamster3.mc.plugin.core.common.api.CoreAPI; import cn.hamster3.mc.plugin.core.common.api.CoreAPI;
import cn.hamster3.mc.plugin.core.common.config.ConfigSection;
import cn.hamster3.mc.plugin.core.common.config.YamlConfig; import cn.hamster3.mc.plugin.core.common.config.YamlConfig;
import cn.hamster3.mc.plugin.core.common.util.UpdateCheckUtils; import cn.hamster3.mc.plugin.core.common.util.UpdateCheckUtils;
import com.zaxxer.hikari.HikariDataSource;
import lombok.Getter; import lombok.Getter;
import net.kyori.adventure.platform.bukkit.BukkitAudiences; import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@@ -27,19 +30,18 @@ import org.bukkit.scheduler.BukkitTask;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
import java.util.logging.Logger;
@SuppressWarnings("CallToPrintStackTrace")
public class HamsterCorePlugin extends JavaPlugin { public class HamsterCorePlugin extends JavaPlugin {
@Getter @Getter
private static HamsterCorePlugin instance; private static HamsterCorePlugin instance;
@Getter @Getter
private static BukkitSimpleLogger simpleLogger;
@Getter
private BukkitAudiences audienceProvider; private BukkitAudiences audienceProvider;
/** /**
@@ -67,15 +69,15 @@ public class HamsterCorePlugin extends JavaPlugin {
@Override @Override
public void onLoad() { public void onLoad() {
instance = this; instance = this;
simpleLogger = new BukkitSimpleLogger(getInstance());
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
Logger logger = getLogger(); simpleLogger.info("仓鼠核心正在初始化");
logger.info("仓鼠核心正在初始化"); simpleLogger.info("Minecraft 版本: " + MinecraftVersion.getMCVersion());
logger.info("Minecraft 版本: " + MinecraftVersion.getMCVersion()); simpleLogger.info("NMS 版本: " + MinecraftVersion.getNMSVersion());
logger.info("NMS 版本: " + MinecraftVersion.getNMSVersion());
try { try {
File dataFolder = getDataFolder(); File dataFolder = getDataFolder();
if (dataFolder.mkdir()) { if (dataFolder.mkdir()) {
logger.info("已生成插件存档文件夹"); simpleLogger.info("已生成插件存档文件夹");
} }
File configFile = new File(dataFolder, "config.yml"); File configFile = new File(dataFolder, "config.yml");
if (!configFile.exists()) { if (!configFile.exists()) {
@@ -86,68 +88,58 @@ public class HamsterCorePlugin extends JavaPlugin {
} }
} }
CoreBukkitAPI.init(configFile); CoreBukkitAPI.init(configFile);
logger.info("已初始化 CoreAPI"); simpleLogger.info("已初始化 CoreAPI");
} catch (Exception e) { } catch (Exception e) {
logger.warning("初始化 CoreAPI 出错"); simpleLogger.error(e, "初始化 CoreAPI 出错");
e.printStackTrace();
} }
CoreMessage.init(this); CoreMessage.init(this);
logger.info("已初始化语言文本"); simpleLogger.info("已初始化语言文本");
long time = System.currentTimeMillis() - start; long time = System.currentTimeMillis() - start;
logger.info("仓鼠核心初始化完成,总计耗时 " + time + " ms"); simpleLogger.info("仓鼠核心初始化完成,总计耗时 " + time + " ms");
} }
@Override @Override
public void onEnable() { public void onEnable() {
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
Logger logger = getLogger(); simpleLogger.info("仓鼠核心正在启动");
logger.info("仓鼠核心正在启动");
audienceProvider = BukkitAudiences.create(this); audienceProvider = BukkitAudiences.create(this);
logger.info("已创建 AudienceProvider"); simpleLogger.info("已创建 AudienceProvider");
CoreCommand.INSTANCE.register(); CoreCommand.INSTANCE.register();
LoreCommand.INSTANCE.register(); LoreCommand.INSTANCE.register();
NBTCommand.INSTANCE.register(); NBTCommand.INSTANCE.register();
Bukkit.getPluginManager().registerEvents(PageListener.INSTANCE, this); Bukkit.getPluginManager().registerEvents(PageListener.INSTANCE, this);
logger.info("已注册 PageListener"); simpleLogger.info("已注册 PageListener");
Bukkit.getPluginManager().registerEvents(CallbackListener.INSTANCE, this); Bukkit.getPluginManager().registerEvents(CallbackListener.INSTANCE, this);
logger.info("已注册 CallbackListener"); simpleLogger.info("已注册 CallbackListener");
Bukkit.getPluginManager().registerEvents(DebugListener.INSTANCE, this); Bukkit.getPluginManager().registerEvents(DebugListener.INSTANCE, this);
logger.info("已注册 DebugListener"); simpleLogger.info("已注册 DebugListener");
simpleLogger.info("Redis 启用状态: %b", CoreAPI.getInstance().isEnableRedis());
simpleLogger.info("Database 启用状态: %b", CoreAPI.getInstance().isEnableDatabase());
simpleLogger.info("已注册 DebugListener");
long time = System.currentTimeMillis() - start; long time = System.currentTimeMillis() - start;
sync(() -> { sync(() -> {
PointAPI.reloadPlayerPointAPIHook(); PointAPI.reloadPlayerPointAPIHook();
VaultAPI.reloadVaultHook(); VaultAPI.reloadVaultHook();
async(() -> { async(this::checkUpdate);
for (Plugin plugin : Bukkit.getPluginManager().getPlugins()) {
try (InputStream stream = plugin.getResource("update.yml")) {
if (stream == null) {
continue;
}
try (InputStreamReader reader = new InputStreamReader(stream, StandardCharsets.UTF_8)) {
YamlConfig config = YamlConfig.load(reader);
UpdateCheckUtils.showUpdate(plugin.getName(), config, Bukkit.getConsoleSender()::sendMessage);
}
} catch (IOException ignored) {
}
}
});
}); });
logger.info("仓鼠核心启动完成,总计耗时 " + time + " ms"); simpleLogger.info("仓鼠核心启动完成,总计耗时 " + time + " ms");
} }
@Override @Override
public void onDisable() { public void onDisable() {
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
Logger logger = getLogger(); if (CoreAPI.getInstance().isEnableRedis()) {
CoreAPI.getInstance().getJedisPool().close(); CoreAPI.getInstance().getJedisPool().close();
logger.info("已关闭 Redis 连接池"); simpleLogger.info("已关闭 Redis 连接池");
CoreAPI.getInstance().getHikariDataSource().close(); }
logger.info("已关闭数据库连接池"); if (CoreAPI.getInstance().isEnableDatabase()) {
((HikariDataSource) CoreAPI.getInstance().getDataSource()).close();
simpleLogger.info("已关闭数据库连接池");
}
CoreAPI.getInstance().getExecutorService().shutdownNow(); CoreAPI.getInstance().getExecutorService().shutdownNow();
logger.info("已关闭 ExecutorService 线程池"); simpleLogger.info("已关闭 ExecutorService 线程池");
CoreAPI.getInstance().getScheduledService().shutdownNow(); CoreAPI.getInstance().getScheduledService().shutdownNow();
logger.info("已关闭 ScheduledExecutorService 线程池"); simpleLogger.info("已关闭 ScheduledExecutorService 线程池");
for (Player player : Bukkit.getOnlinePlayers()) { for (Player player : Bukkit.getOnlinePlayers()) {
InventoryView view = player.getOpenInventory(); InventoryView view = player.getOpenInventory();
Inventory inventory = view.getTopInventory(); Inventory inventory = view.getTopInventory();
@@ -156,8 +148,27 @@ public class HamsterCorePlugin extends JavaPlugin {
} }
player.closeInventory(); player.closeInventory();
} }
logger.info("已关闭所有玩家的界面"); simpleLogger.info("已关闭所有玩家的界面");
long time = System.currentTimeMillis() - start; long time = System.currentTimeMillis() - start;
logger.info("仓鼠核心已关闭,总计耗时 " + time + " ms"); simpleLogger.info("仓鼠核心已关闭,总计耗时 " + time + " ms");
}
private void checkUpdate() {
for (Plugin plugin : Bukkit.getPluginManager().getPlugins()) {
try (InputStream stream = plugin.getResource("plugin.yml")) {
if (stream == null) {
continue;
}
try (InputStreamReader reader = new InputStreamReader(stream, StandardCharsets.UTF_8)) {
YamlConfig config = YamlConfig.load(reader);
ConfigSection section = config.getSection("UPDATE_CHECKER");
if (section == null) {
continue;
}
UpdateCheckUtils.checkUpdate(plugin.getName(), section);
}
} catch (Exception ignored) {
}
}
} }
} }

View File

@@ -3,6 +3,7 @@ package cn.hamster3.mc.plugin.core.bukkit.api;
import cn.hamster3.mc.plugin.core.bukkit.HamsterCorePlugin; import cn.hamster3.mc.plugin.core.bukkit.HamsterCorePlugin;
import cn.hamster3.mc.plugin.core.bukkit.impl.ItemStackAdapter; import cn.hamster3.mc.plugin.core.bukkit.impl.ItemStackAdapter;
import cn.hamster3.mc.plugin.core.bukkit.impl.PotionEffectAdapter; import cn.hamster3.mc.plugin.core.bukkit.impl.PotionEffectAdapter;
import cn.hamster3.mc.plugin.core.bukkit.util.BukkitSimpleLogger;
import cn.hamster3.mc.plugin.core.bukkit.util.MinecraftVersion; import cn.hamster3.mc.plugin.core.bukkit.util.MinecraftVersion;
import cn.hamster3.mc.plugin.core.common.api.CoreAPI; import cn.hamster3.mc.plugin.core.common.api.CoreAPI;
import cn.hamster3.mc.plugin.core.common.config.ConfigSection; import cn.hamster3.mc.plugin.core.common.config.ConfigSection;
@@ -20,7 +21,6 @@ import org.jetbrains.annotations.NotNull;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.logging.Logger;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public final class CoreBukkitAPI extends CoreAPI { public final class CoreBukkitAPI extends CoreAPI {
@@ -62,8 +62,8 @@ public final class CoreBukkitAPI extends CoreAPI {
} }
@Override @Override
public @NotNull Logger getLogger() { public @NotNull BukkitSimpleLogger getLogger() {
return HamsterCorePlugin.getInstance().getLogger(); return HamsterCorePlugin.getSimpleLogger();
} }
@Override @Override

View File

@@ -78,13 +78,13 @@ public class YamlCommand extends ChildCommand {
config.set("test-item", stack); config.set("test-item", stack);
File dataFolder = new File(HamsterCorePlugin.getInstance().getDataFolder(), "yaml"); File dataFolder = new File(HamsterCorePlugin.getInstance().getDataFolder(), "yaml");
if (dataFolder.mkdirs()) { if (dataFolder.mkdirs()) {
HamsterCorePlugin.getInstance().getLogger().info("创建 yaml 存档文件夹"); HamsterCorePlugin.getSimpleLogger().info("创建 yaml 存档文件夹");
} }
File saveFile = new File(dataFolder, sender.getName() + "_" + System.currentTimeMillis() + ".yml"); File saveFile = new File(dataFolder, sender.getName() + "_" + System.currentTimeMillis() + ".yml");
try { try {
config.save(saveFile); config.save(saveFile);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); HamsterCorePlugin.getSimpleLogger().error(e);
} }
sender.sendMessage("§a信息已保存至文件 " + saveFile.getAbsolutePath()); sender.sendMessage("§a信息已保存至文件 " + saveFile.getAbsolutePath());
return true; return true;

View File

@@ -14,6 +14,7 @@ import org.jetbrains.annotations.NotNull;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.logging.Level;
@Getter @Getter
public enum CoreMessage { public enum CoreMessage {
@@ -70,7 +71,6 @@ public enum CoreMessage {
this.message = new DisplayMessage().setMessage(message); this.message = new DisplayMessage().setMessage(message);
} }
@SuppressWarnings("CallToPrintStackTrace")
public static void init(@NotNull Plugin plugin) { public static void init(@NotNull Plugin plugin) {
File dataFolder = plugin.getDataFolder(); File dataFolder = plugin.getDataFolder();
if (dataFolder.mkdirs()) { if (dataFolder.mkdirs()) {
@@ -82,8 +82,7 @@ public enum CoreMessage {
try { try {
config.load(file); config.load(file);
} catch (Exception e) { } catch (Exception e) {
plugin.getLogger().warning("加载消息配置文件时出现了一个异常:"); plugin.getLogger().log(Level.WARNING, "加载消息配置文件时出现了一个异常:", e);
e.printStackTrace();
} }
} }
for (CoreMessage value : values()) { for (CoreMessage value : values()) {
@@ -96,15 +95,13 @@ public enum CoreMessage {
try { try {
value.message = CoreBukkitUtils.loadDisplayMessage(section); value.message = CoreBukkitUtils.loadDisplayMessage(section);
} catch (Exception e) { } catch (Exception e) {
plugin.getLogger().warning("加载消息设置 " + value.name() + " 时遇到了一个异常: "); plugin.getLogger().log(Level.WARNING, "加载消息设置 " + value.name() + " 时遇到了一个异常: ", e);
e.printStackTrace();
} }
} }
try { try {
config.save(file); config.save(file);
} catch (IOException e) { } catch (IOException e) {
plugin.getLogger().warning("保存消息配置文件时出现了一个异常:"); plugin.getLogger().log(Level.WARNING, "保存消息配置文件时出现了一个异常:", e);
e.printStackTrace();
} }
} }

View File

@@ -20,7 +20,7 @@ public class EconomyAPI {
* @return true代表安装了false代表未安装 * @return true代表安装了false代表未安装
*/ */
public static boolean isSetupEconomy() { public static boolean isSetupEconomy() {
return VaultAPI.isSetupVault() && VaultAPI.getEconomy() != null; return VaultAPI.isVaultEnabled() && VaultAPI.getEconomy() != null;
} }
/** /**

View File

@@ -1,6 +1,7 @@
package cn.hamster3.mc.plugin.core.bukkit.hook; package cn.hamster3.mc.plugin.core.bukkit.hook;
import cn.hamster3.mc.plugin.core.bukkit.HamsterCorePlugin; import cn.hamster3.mc.plugin.core.bukkit.HamsterCorePlugin;
import lombok.Getter;
import org.black_ixx.playerpoints.PlayerPoints; import org.black_ixx.playerpoints.PlayerPoints;
import org.black_ixx.playerpoints.PlayerPointsAPI; import org.black_ixx.playerpoints.PlayerPointsAPI;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
@@ -14,6 +15,7 @@ import java.util.UUID;
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class PointAPI { public class PointAPI {
@Getter
private static PlayerPointsAPI playerPointsAPI; private static PlayerPointsAPI playerPointsAPI;
private PointAPI() { private PointAPI() {
@@ -25,20 +27,11 @@ public class PointAPI {
public static void reloadPlayerPointAPIHook() { public static void reloadPlayerPointAPIHook() {
Plugin plugin = Bukkit.getPluginManager().getPlugin("PlayerPoints"); Plugin plugin = Bukkit.getPluginManager().getPlugin("PlayerPoints");
if (plugin == null) { if (plugin == null) {
HamsterCorePlugin.getInstance().getLogger().warning("未检测到 PlayerPointAPI 插件"); HamsterCorePlugin.getSimpleLogger().warn("未检测到 PlayerPointAPI 插件");
return; return;
} }
playerPointsAPI = ((PlayerPoints) plugin).getAPI(); playerPointsAPI = ((PlayerPoints) plugin).getAPI();
HamsterCorePlugin.getInstance().getLogger().info("PlayerPointAPI 挂接成功"); HamsterCorePlugin.getSimpleLogger().info("PlayerPointAPI 挂接成功");
}
/**
* 获取 PlayerPointsAPI 实例
*
* @return PlayerPointsAPI 实例
*/
public static PlayerPointsAPI getPlayerPointsAPI() {
return playerPointsAPI;
} }
/** /**

View File

@@ -1,22 +1,27 @@
package cn.hamster3.mc.plugin.core.bukkit.hook; package cn.hamster3.mc.plugin.core.bukkit.hook;
import cn.hamster3.mc.plugin.core.bukkit.HamsterCorePlugin; import cn.hamster3.mc.plugin.core.bukkit.HamsterCorePlugin;
import cn.hamster3.mc.plugin.core.bukkit.util.BukkitSimpleLogger;
import lombok.Getter;
import net.milkbowl.vault.chat.Chat; import net.milkbowl.vault.chat.Chat;
import net.milkbowl.vault.economy.Economy; import net.milkbowl.vault.economy.Economy;
import net.milkbowl.vault.permission.Permission; import net.milkbowl.vault.permission.Permission;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.plugin.RegisteredServiceProvider; import org.bukkit.plugin.RegisteredServiceProvider;
import java.util.logging.Logger;
/** /**
* Vault API * Vault API
*/ */
@Getter
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class VaultAPI { public class VaultAPI {
@Getter
private static boolean vaultEnabled; private static boolean vaultEnabled;
@Getter
private static Chat chat; private static Chat chat;
@Getter
private static Economy economy; private static Economy economy;
@Getter
private static Permission permission; private static Permission permission;
private VaultAPI() { private VaultAPI() {
@@ -27,9 +32,9 @@ public class VaultAPI {
economy = null; economy = null;
permission = null; permission = null;
vaultEnabled = Bukkit.getPluginManager().isPluginEnabled("Vault"); vaultEnabled = Bukkit.getPluginManager().isPluginEnabled("Vault");
Logger logger = HamsterCorePlugin.getInstance().getLogger(); BukkitSimpleLogger logger = HamsterCorePlugin.getSimpleLogger();
if (!vaultEnabled) { if (!vaultEnabled) {
logger.warning("未检测到 Vault 插件"); logger.warn("未检测到 Vault 插件");
return; return;
} }
logger.info("已连接 Vault"); logger.info("已连接 Vault");
@@ -40,7 +45,7 @@ public class VaultAPI {
chat = chatProvider.getProvider(); chat = chatProvider.getProvider();
logger.info("聊天系统挂接成功"); logger.info("聊天系统挂接成功");
} else { } else {
logger.warning("未检测到聊天系统"); logger.warn("未检测到聊天系统");
} }
RegisteredServiceProvider<Economy> economyProvider = Bukkit.getServer().getServicesManager().getRegistration(Economy.class); RegisteredServiceProvider<Economy> economyProvider = Bukkit.getServer().getServicesManager().getRegistration(Economy.class);
@@ -48,7 +53,7 @@ public class VaultAPI {
economy = economyProvider.getProvider(); economy = economyProvider.getProvider();
logger.info("经济系统挂接成功"); logger.info("经济系统挂接成功");
} else { } else {
logger.warning("未检测到经济系统"); logger.warn("未检测到经济系统");
} }
RegisteredServiceProvider<Permission> permissionProvider = Bukkit.getServer().getServicesManager().getRegistration(Permission.class); RegisteredServiceProvider<Permission> permissionProvider = Bukkit.getServer().getServicesManager().getRegistration(Permission.class);
@@ -56,45 +61,9 @@ public class VaultAPI {
permission = permissionProvider.getProvider(); permission = permissionProvider.getProvider();
logger.info("权限系统挂接成功"); logger.info("权限系统挂接成功");
} else { } else {
logger.warning("未检测到权限插件"); logger.warn("未检测到权限插件");
} }
logger.info("已完成 VaultAPI 挂载"); logger.info("已完成 VaultAPI 挂载");
} }
/** }
* 返回服务器是否安装了 Vault 插件
*
* @return true 代表服务器已安装
*/
public static boolean isSetupVault() {
return vaultEnabled;
}
/**
* 返回 Vault 的 Chat 前置系统
*
* @return Chat 系统
*/
public static Chat getChat() {
return chat;
}
/**
* 返回 Vault 的 Economy 前置系统
*
* @return Economy 系统
*/
public static Economy getEconomy() {
return economy;
}
/**
* 返回 Vault 的 Permission 前置系统
*
* @return Permission 系统
*/
public static Permission getPermission() {
return permission;
}
}

View File

@@ -188,8 +188,8 @@ public class ButtonGroup {
@Override @Override
public String toString() { public String toString() {
return "ButtonGroup{" + return "ButtonGroup{" +
"name='" + name + '\'' + "name='" + name + '\'' +
", buttonNameMap=" + buttonNameMap + ", buttonNameMap=" + buttonNameMap +
'}'; '}';
} }
} }

View File

@@ -98,8 +98,7 @@ public class PageConfig implements InventoryHolder {
try { try {
buttonSounds.put(key, Sound.valueOf(soundName)); buttonSounds.put(key, Sound.valueOf(soundName));
} catch (Exception e) { } catch (Exception e) {
HamsterCorePlugin.getInstance().getLogger().warning("初始化 PageConfig 时遇到了一个异常:"); HamsterCorePlugin.getSimpleLogger().error(e, "初始化 PageConfig 时遇到了一个异常: ");
e.printStackTrace();
} }
} }
} }
@@ -197,10 +196,10 @@ public class PageConfig implements InventoryHolder {
@Override @Override
public String toString() { public String toString() {
return "PageConfig{" + return "PageConfig{" +
", title='" + title + '\'' + ", title='" + title + '\'' +
", graphic=" + graphic + ", graphic=" + graphic +
", buttonMap=" + buttons + ", buttonMap=" + buttons +
", buttonGroups=" + buttonGroups + ", buttonGroups=" + buttonGroups +
'}'; '}';
} }
} }

View File

@@ -39,8 +39,7 @@ public class PageListener implements Listener {
try { try {
pageHandler.onClick(event); pageHandler.onClick(event);
} catch (Exception e) { } catch (Exception e) {
HamsterCorePlugin.getInstance().getLogger().warning(String.format("执行 %s 的 onClick(event) 时遇到了一个异常: ", pageHandler.getClass().getName())); HamsterCorePlugin.getSimpleLogger().warn(e, "执行 %s 的 onClick(event) 时遇到了一个异常: ", pageHandler.getClass().getName());
e.printStackTrace();
} }
if (event.isCancelled()) { if (event.isCancelled()) {
return; return;
@@ -55,31 +54,29 @@ public class PageListener implements Listener {
try { try {
pageHandler.onClickInside(event); pageHandler.onClickInside(event);
} catch (Exception e) { } catch (Exception e) {
HamsterCorePlugin.getInstance().getLogger().warning(String.format("执行 %s 的 onClickInside(event) 时遇到了一个异常: ", pageHandler.getClass().getName())); HamsterCorePlugin.getSimpleLogger().warn(e, "执行 %s 的 onClickInside(event) 时遇到了一个异常: ", pageHandler.getClass().getName());
e.printStackTrace();
} }
try { try {
pageHandler.onClickInside(event.getClick(), event.getAction(), index); pageHandler.onClickInside(event.getClick(), event.getAction(), index);
} catch (Exception e) { } catch (Exception e) {
HamsterCorePlugin.getInstance().getLogger().warning(String.format( HamsterCorePlugin.getSimpleLogger().warn(e,
"执行 %s 的 onClickInside(%s, %s, %d) 时遇到了一个异常: ", "执行 %s 的 onClickInside(%s, %s, %d) 时遇到了一个异常: ",
pageHandler.getClass().getName(), pageHandler.getClass().getName(),
event.getClick().name(), event.getClick().name(),
event.getAction().name(), event.getAction().name(),
index index
)); );
e.printStackTrace();
} }
try { try {
pageHandler.onPlayButtonSound(event.getClick(), event.getAction(), index); pageHandler.onPlayButtonSound(event.getClick(), event.getAction(), index);
} catch (Exception e) { } catch (Exception e) {
HamsterCorePlugin.getInstance().getLogger().warning(String.format( HamsterCorePlugin.getSimpleLogger().warn(e,
"执行 %s 的 onPlayButtonSound(%s, %s, %d) 时遇到了一个异常: ", "执行 %s 的 onPlayButtonSound(%s, %s, %d) 时遇到了一个异常: ",
pageHandler.getClass().getName(), pageHandler.getClass().getName(),
event.getClick().name(), event.getClick().name(),
event.getAction().name(), event.getAction().name(),
index index
)); );
} }
} }
@@ -93,8 +90,7 @@ public class PageListener implements Listener {
try { try {
pageHandler.onDrag(event); pageHandler.onDrag(event);
} catch (Exception e) { } catch (Exception e) {
HamsterCorePlugin.getInstance().getLogger().warning(String.format("执行 %s 的 onDrag(event) 时遇到了一个异常: ", pageHandler.getClass().getName())); HamsterCorePlugin.getSimpleLogger().warn(e, "执行 %s 的 onDrag(event) 时遇到了一个异常: ", pageHandler.getClass().getName());
e.printStackTrace();
} }
if (event.isCancelled()) { if (event.isCancelled()) {
return; return;

View File

@@ -0,0 +1,38 @@
package cn.hamster3.mc.plugin.core.bukkit.util;
import cn.hamster3.mc.plugin.core.common.util.SimpleLogger;
import lombok.Getter;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import java.util.logging.Level;
import java.util.logging.Logger;
@Getter
@SuppressWarnings("unused")
public class BukkitSimpleLogger extends SimpleLogger {
@NotNull
private final Plugin plugin;
@NotNull
private final Logger logger;
public BukkitSimpleLogger(@NotNull Plugin plugin) {
this.plugin = plugin;
logger = plugin.getLogger();
}
@Override
public void log(@NotNull Level level, @NotNull String msg) {
logger.log(level, msg);
}
@Override
public void log(@NotNull Level level, @NotNull Throwable throwable) {
logger.log(level, "", throwable);
}
@Override
public void log(@NotNull Level level, @NotNull Throwable throwable, @NotNull String msg) {
logger.log(level, msg, throwable);
}
}

View File

@@ -7,18 +7,18 @@ import org.jetbrains.annotations.NotNull;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class MinecraftVersion { public class MinecraftVersion {
@Getter @Getter
public static final int Version1; public static final int version1;
@Getter @Getter
public static final int Version2; public static final int version2;
@Getter @Getter
public static final int Version3; public static final int version3;
static { static {
String version = getMCVersion(); String version = getMCVersion();
String[] split = version.split("\\."); String[] split = version.split("\\.");
Version1 = Integer.parseInt(split[0]); version1 = Integer.parseInt(split[0]);
Version2 = Integer.parseInt(split[1]); version2 = Integer.parseInt(split[1]);
Version3 = split.length >= 3 ? Integer.parseInt(split[2]) : 0; version3 = split.length >= 3 ? Integer.parseInt(split[2]) : 0;
} }
/** /**
@@ -35,18 +35,18 @@ public class MinecraftVersion {
*/ */
public static int compareTo(@NotNull String version) { public static int compareTo(@NotNull String version) {
String[] split = version.split("\\."); String[] split = version.split("\\.");
int version1 = Integer.parseInt(split[0]); int compareVersion1 = Integer.parseInt(split[0]);
int version2 = Integer.parseInt(split[1]); int compareVersion2 = Integer.parseInt(split[1]);
int version3 = split.length >= 3 ? Integer.parseInt(split[2]) : 0; int compareVersion3 = split.length >= 3 ? Integer.parseInt(split[2]) : 0;
int compare = Integer.compare(Version1, version1); int compare = Integer.compare(compareVersion1, MinecraftVersion.version1);
if (compare != 0) { if (compare != 0) {
return compare; return compare;
} }
compare = Integer.compare(Version2, version2); compare = Integer.compare(compareVersion2, MinecraftVersion.version2);
if (compare != 0) { if (compare != 0) {
return compare; return compare;
} }
return Integer.compare(Version3, version3); return Integer.compare(compareVersion3, MinecraftVersion.version3);
} }
@NotNull @NotNull
@@ -61,8 +61,10 @@ public class MinecraftVersion {
@NotNull @NotNull
public static Class<?> getNMSClass(@NotNull String className) throws ClassNotFoundException { public static Class<?> getNMSClass(@NotNull String className) throws ClassNotFoundException {
if (Version2 >= 17) { if (version1 >= 1) {
return Class.forName("net.minecraft.server." + className); if (version2 >= 17) {
return Class.forName("net.minecraft.server." + className);
}
} }
String nmsVersion = getNMSVersion(); String nmsVersion = getNMSVersion();
return Class.forName("net.minecraft.server." + nmsVersion + "." + className); return Class.forName("net.minecraft.server." + nmsVersion + "." + className);
@@ -70,9 +72,8 @@ public class MinecraftVersion {
@NotNull @NotNull
public static Class<?> getNMSClassSilent(@NotNull String className) { public static Class<?> getNMSClassSilent(@NotNull String className) {
String nmsVersion = getNMSVersion();
try { try {
return Class.forName("net.minecraft.server." + nmsVersion + "." + className); return getNMSClass(className);
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@@ -80,15 +81,22 @@ public class MinecraftVersion {
@NotNull @NotNull
public static Class<?> getCraftBukkitClass(@NotNull String className) throws ClassNotFoundException { public static Class<?> getCraftBukkitClass(@NotNull String className) throws ClassNotFoundException {
if (version1 >= 1) {
if (version2 >= 21) {
return Class.forName("org.bukkit.craftbukkit." + className);
}
if (version2 == 20 && version3 >= 6) {
return Class.forName("org.bukkit.craftbukkit." + className);
}
}
String nmsVersion = getNMSVersion(); String nmsVersion = getNMSVersion();
return Class.forName("org.bukkit.craftbukkit." + nmsVersion + "." + className); return Class.forName("org.bukkit.craftbukkit." + nmsVersion + "." + className);
} }
@NotNull @NotNull
public static Class<?> getCraftBukkitClassSilent(@NotNull String className) { public static Class<?> getCraftBukkitClassSilent(@NotNull String className) {
String nmsVersion = getNMSVersion();
try { try {
return Class.forName("org.bukkit.craftbukkit." + nmsVersion + "." + className); return getCraftBukkitClass(className);
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }

View File

@@ -1,3 +1,6 @@
# 是否启用 redis 连接池功能
enable-redis: false
# redis 连接配置 # redis 连接配置
# 完整格式如下: # 完整格式如下:
# redis://用户名:密码@主机名:端口/数据库索引?参数名=参数值&参数名=参数值 # redis://用户名:密码@主机名:端口/数据库索引?参数名=参数值&参数名=参数值
@@ -8,18 +11,16 @@
# 若不设置数据库,则默认使用 0 # 若不设置数据库,则默认使用 0
redis-url: "redis://localhost:6379/0?clientName=HamsterCore&timeout=5s" redis-url: "redis://localhost:6379/0?clientName=HamsterCore&timeout=5s"
# 是否启用数据库连接池功能
enable-database: false
# 数据库连接池配置
datasource: datasource:
# 数据库链接驱动地址 # 数据库链接驱动地址旧版服务端低于1.13请使用com.mysql.jdbc.Driver
# 除非你知道自己在做什么,否则不建议更改该项
# 旧版服务端低于1.13请使用com.mysql.jdbc.Driver
driver: "com.mysql.cj.jdbc.Driver" driver: "com.mysql.cj.jdbc.Driver"
# 数据库链接填写格式: # MySQL数据库链接填写格式:
# jdbc:mysql://{数据库地址}:{数据库端口}/{使用的库名}?参数 # jdbc:mysql://{数据库地址}:{数据库端口}/{使用的库名}?参数
# 除非你知道自己在做什么,否则不建议随意更改参数
url: "jdbc:mysql://localhost:3306/Test?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true" url: "jdbc:mysql://localhost:3306/Test?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true"
# 如果你不需要做多端跨服,那么请使用 sqlite 作本地数据库
# driver: "org.sqlite.JDBC"
# url: "jdbc:sqlite:./plugins/HamsterCore/database.db"
# 用户名 # 用户名
username: "root" username: "root"
# 密码 # 密码

View File

@@ -4,8 +4,15 @@ version: ${version}
api-version: 1.13 api-version: 1.13
author: MiniDay author: MiniDay
description: ${description}
website: https://git.airgame.net/MiniDay/hamster-core website: https://git.airgame.net/MiniDay/hamster-core
description: 仓鼠核心:叁只仓鼠的 Minecraft 插件开发通用工具包
UPDATE_CHECKER:
VERSION: ${version}
CHECK_TYPE: GITEA_RELEASES
GIT_BASE_URL: https://git.airgame.net
GIT_REPO: MiniDay/hamster-core
DOWNLOAD_URL: https://jenkins.airgame.net/job/opensource/job/hamster-core/
load: STARTUP load: STARTUP

View File

@@ -1,6 +0,0 @@
version: ${version}
CHECK_TYPE: GITEA_RELEASES
GIT_BASE_URL: https://git.airgame.net
GIT_REPO: MiniDay/hamster-core
GIT_TOKEN: a44a69a4d1b8601bf6091403247759cd28764d5e
DOWNLOAD_URL: https://jenkins.airgame.net/job/opensource/job/hamster-core/

View File

@@ -1,12 +1,7 @@
@file:Suppress("VulnerableLibrariesLocal") @file:Suppress("VulnerableLibrariesLocal")
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
evaluationDependsOn(":core-common") evaluationDependsOn(":core-common")
val shade = configurations.create("shade")
val shadeJava8 = configurations.create("shadeJava8")
dependencies { dependencies {
api(project(":core-common")) { isTransitive = false } api(project(":core-common")) { isTransitive = false }
compileOnly("net.md-5:bungeecord-api:1.20-R0.1") compileOnly("net.md-5:bungeecord-api:1.20-R0.1")
@@ -26,13 +21,12 @@ dependencies {
} }
// https://mvnrepository.com/artifact/org.quartz-scheduler/quartz // https://mvnrepository.com/artifact/org.quartz-scheduler/quartz
api("org.quartz-scheduler:quartz:2.3.2") { isTransitive = false } api("org.quartz-scheduler:quartz:2.3.2") { isTransitive = false }
// https://mvnrepository.com/artifact/com.sun.mail/jakarta.mail
api("com.sun.mail:jakarta.mail:2.0.1")
implementation("com.zaxxer:HikariCP:4.0.3") { exclude(group = "org.slf4j") } implementation("com.zaxxer:HikariCP:4.0.3") { exclude(group = "org.slf4j") }
// https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-stdlib
shade("org.jetbrains.kotlin:kotlin-stdlib:1.9.23") { exclude(group = "org.jetbrains") }
// https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-stdlib-jdk8 // https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-stdlib-jdk8
shadeJava8("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.23") { exclude(group = "org.jetbrains") } implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.23") { exclude(group = "org.jetbrains") }
} }
tasks { tasks {
@@ -45,23 +39,6 @@ tasks {
archiveBaseName = "HamsterCore-BungeeCord" archiveBaseName = "HamsterCore-BungeeCord"
} }
shadowJar { shadowJar {
from(shade.map { if (it.isDirectory) it else zipTree(it) })
destinationDirectory = rootProject.layout.buildDirectory destinationDirectory = rootProject.layout.buildDirectory
} }
shadowJar {
from(shade.map { if (it.isDirectory) it else zipTree(it) })
destinationDirectory = rootProject.layout.buildDirectory
}
val shadowJava8 = register<ShadowJar>("shadowJava8") {
dependsOn(":core-common:build")
archiveClassifier = "Java8"
from(project.configurations.runtimeClasspath.get().map { if (it.isDirectory) it else zipTree(it) })
from(processResources.get().outputs)
from(jar.get().outputs)
from(shadeJava8.map { if (it.isDirectory) it else zipTree(it) })
destinationDirectory = rootProject.layout.buildDirectory
}
build {
dependsOn(shadowJava8)
}
} }

View File

@@ -1,13 +1,15 @@
package cn.hamster3.mc.plugin.core.bungee; package cn.hamster3.mc.plugin.core.bungee;
import cn.hamster3.mc.plugin.core.bungee.api.CoreBungeeAPI; import cn.hamster3.mc.plugin.core.bungee.api.CoreBungeeAPI;
import cn.hamster3.mc.plugin.core.bungee.util.BungeeSimpleLogger;
import cn.hamster3.mc.plugin.core.common.api.CoreAPI; import cn.hamster3.mc.plugin.core.common.api.CoreAPI;
import cn.hamster3.mc.plugin.core.common.config.ConfigSection;
import cn.hamster3.mc.plugin.core.common.config.YamlConfig; import cn.hamster3.mc.plugin.core.common.config.YamlConfig;
import cn.hamster3.mc.plugin.core.common.util.UpdateCheckUtils; import cn.hamster3.mc.plugin.core.common.util.UpdateCheckUtils;
import com.zaxxer.hikari.HikariDataSource;
import lombok.Getter; import lombok.Getter;
import net.kyori.adventure.platform.bungeecord.BungeeAudiences; import net.kyori.adventure.platform.bungeecord.BungeeAudiences;
import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.plugin.Plugin; import net.md_5.bungee.api.plugin.Plugin;
import java.io.File; import java.io.File;
@@ -17,25 +19,25 @@ import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.StandardCopyOption; import java.nio.file.StandardCopyOption;
import java.util.logging.Logger;
@SuppressWarnings("CallToPrintStackTrace")
public class HamsterCorePlugin extends Plugin { public class HamsterCorePlugin extends Plugin {
@Getter @Getter
private static HamsterCorePlugin instance; private static HamsterCorePlugin instance;
@Getter @Getter
private static BungeeSimpleLogger simpleLogger;
@Getter
private BungeeAudiences audienceProvider; private BungeeAudiences audienceProvider;
@Override @Override
public void onLoad() { public void onLoad() {
instance = this; instance = this;
simpleLogger = new BungeeSimpleLogger(this);
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
Logger logger = getLogger(); simpleLogger.info("仓鼠核心正在初始化");
logger.info("仓鼠核心正在初始化");
try { try {
File dataFolder = getDataFolder(); File dataFolder = getDataFolder();
if (dataFolder.mkdir()) { if (dataFolder.mkdir()) {
logger.info("已生成插件存档文件夹"); simpleLogger.info("已生成插件存档文件夹");
} }
File configFile = new File(dataFolder, "config.yml"); File configFile = new File(dataFolder, "config.yml");
if (!configFile.exists()) { if (!configFile.exists()) {
@@ -46,57 +48,60 @@ public class HamsterCorePlugin extends Plugin {
} }
} }
CoreBungeeAPI.init(configFile); CoreBungeeAPI.init(configFile);
logger.info("已初始化 CoreAPI"); simpleLogger.info("已初始化 CoreAPI");
} catch (Exception e) { } catch (Exception e) {
logger.warning("初始化 CoreAPI 出错"); simpleLogger.error(e, "初始化 CoreAPI 出错");
e.printStackTrace();
} }
long time = System.currentTimeMillis() - start; long time = System.currentTimeMillis() - start;
logger.info("仓鼠核心初始化完成,总计耗时 " + time + " ms"); simpleLogger.info("仓鼠核心初始化完成,总计耗时 " + time + " ms");
} }
@Override @Override
public void onEnable() { public void onEnable() {
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
Logger logger = getLogger(); simpleLogger.info("仓鼠核心正在启动");
logger.info("仓鼠核心正在启动");
audienceProvider = BungeeAudiences.create(this); audienceProvider = BungeeAudiences.create(this);
logger.info("已创建 AudienceProvider"); simpleLogger.info("已创建 AudienceProvider");
CoreAPI.getInstance().getExecutorService().submit(this::checkUpdate);
long time = System.currentTimeMillis() - start; long time = System.currentTimeMillis() - start;
logger.info("仓鼠核心启动完成,总计耗时 " + time + " ms"); simpleLogger.info("仓鼠核心启动完成,总计耗时 " + time + " ms");
CoreAPI.getInstance().getExecutorService().submit(() -> {
for (Plugin plugin : ProxyServer.getInstance().getPluginManager().getPlugins()) {
try (InputStream stream = plugin.getResourceAsStream("update.yml")) {
if (stream == null) {
continue;
}
try (InputStreamReader reader = new InputStreamReader(stream, StandardCharsets.UTF_8)) {
YamlConfig config = YamlConfig.load(reader);
UpdateCheckUtils.showUpdate(
plugin.getDescription().getName(), config,
(s) -> ProxyServer.getInstance().getConsole().sendMessage(TextComponent.fromLegacyText(s))
);
}
} catch (IOException ignored) {
}
}
});
} }
@Override @Override
public void onDisable() { public void onDisable() {
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
Logger logger = getLogger(); if (CoreAPI.getInstance().isEnableRedis()) {
CoreAPI.getInstance().getJedisPool().close(); CoreAPI.getInstance().getJedisPool().close();
logger.info("已关闭 Redis 连接池"); simpleLogger.info("已关闭 Redis 连接池");
CoreAPI.getInstance().getHikariDataSource().close(); }
logger.info("已关闭数据库连接池"); if (CoreAPI.getInstance().isEnableDatabase()) {
((HikariDataSource) CoreAPI.getInstance().getDataSource()).close();
simpleLogger.info("已关闭数据库连接池");
}
CoreAPI.getInstance().getExecutorService().shutdownNow(); CoreAPI.getInstance().getExecutorService().shutdownNow();
logger.info("已关闭 ExecutorService 线程池"); simpleLogger.info("已关闭 ExecutorService 线程池");
CoreAPI.getInstance().getScheduledService().shutdownNow(); CoreAPI.getInstance().getScheduledService().shutdownNow();
logger.info("已关闭 ScheduledExecutorService 线程池"); simpleLogger.info("已关闭 ScheduledExecutorService 线程池");
long time = System.currentTimeMillis() - start; long time = System.currentTimeMillis() - start;
logger.info("仓鼠核心已关闭,总计耗时 " + time + " ms"); simpleLogger.info("仓鼠核心已关闭,总计耗时 " + time + " ms");
}
private void checkUpdate() {
for (Plugin plugin : ProxyServer.getInstance().getPluginManager().getPlugins()) {
try (InputStream stream = plugin.getResourceAsStream("bungee.yml")) {
if (stream == null) {
continue;
}
try (InputStreamReader reader = new InputStreamReader(stream, StandardCharsets.UTF_8)) {
YamlConfig config = YamlConfig.load(reader);
ConfigSection section = config.getSection("UPDATE_CHECKER");
if (section == null) {
continue;
}
UpdateCheckUtils.checkUpdate(plugin.getDescription().getName(), section);
}
} catch (IOException ignored) {
}
}
} }
} }

View File

@@ -1,6 +1,7 @@
package cn.hamster3.mc.plugin.core.bungee.api; package cn.hamster3.mc.plugin.core.bungee.api;
import cn.hamster3.mc.plugin.core.bungee.HamsterCorePlugin; import cn.hamster3.mc.plugin.core.bungee.HamsterCorePlugin;
import cn.hamster3.mc.plugin.core.bungee.util.BungeeSimpleLogger;
import cn.hamster3.mc.plugin.core.common.api.CoreAPI; import cn.hamster3.mc.plugin.core.common.api.CoreAPI;
import cn.hamster3.mc.plugin.core.common.config.ConfigSection; import cn.hamster3.mc.plugin.core.common.config.ConfigSection;
import cn.hamster3.mc.plugin.core.common.config.YamlConfig; import cn.hamster3.mc.plugin.core.common.config.YamlConfig;
@@ -15,7 +16,6 @@ import org.jetbrains.annotations.NotNull;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.logging.Logger;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public final class CoreBungeeAPI extends CoreAPI { public final class CoreBungeeAPI extends CoreAPI {
@@ -51,8 +51,8 @@ public final class CoreBungeeAPI extends CoreAPI {
} }
@Override @Override
public @NotNull Logger getLogger() { public @NotNull BungeeSimpleLogger getLogger() {
return HamsterCorePlugin.getInstance().getLogger(); return HamsterCorePlugin.getSimpleLogger();
} }
@Override @Override

View File

@@ -0,0 +1,38 @@
package cn.hamster3.mc.plugin.core.bungee.util;
import cn.hamster3.mc.plugin.core.common.util.SimpleLogger;
import lombok.Getter;
import net.md_5.bungee.api.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import java.util.logging.Level;
import java.util.logging.Logger;
@Getter
@SuppressWarnings("unused")
public class BungeeSimpleLogger extends SimpleLogger {
@NotNull
private final Plugin plugin;
@NotNull
private final Logger logger;
public BungeeSimpleLogger(@NotNull Plugin plugin) {
this.plugin = plugin;
logger = plugin.getLogger();
}
@Override
public void log(@NotNull Level level, @NotNull String msg) {
logger.log(level, msg);
}
@Override
public void log(@NotNull Level level, @NotNull Throwable throwable) {
logger.log(level, "", throwable);
}
@Override
public void log(@NotNull Level level, @NotNull Throwable throwable, @NotNull String msg) {
logger.log(level, msg, throwable);
}
}

View File

@@ -3,4 +3,12 @@ main: cn.hamster3.mc.plugin.core.bungee.HamsterCorePlugin
version: ${version} version: ${version}
author: MiniDay author: MiniDay
description: 仓鼠核心:叁只仓鼠的 Minecraft 插件开发通用工具包 description: ${description}
website: https://git.airgame.net/MiniDay/hamster-core
UPDATE_CHECKER:
VERSION: ${version}
CHECK_TYPE: GITEA_RELEASES
GIT_BASE_URL: https://git.airgame.net
GIT_REPO: MiniDay/hamster-core
DOWNLOAD_URL: https://jenkins.airgame.net/job/opensource/job/hamster-core/

View File

@@ -1,3 +1,6 @@
# 是否启用 redis 连接池功能
enable-redis: false
# redis 连接配置 # redis 连接配置
# 完整格式如下: # 完整格式如下:
# redis://用户名:密码@主机名:端口/数据库索引?参数名=参数值&参数名=参数值 # redis://用户名:密码@主机名:端口/数据库索引?参数名=参数值&参数名=参数值
@@ -8,13 +11,15 @@
# 若不设置数据库,则默认使用 0 # 若不设置数据库,则默认使用 0
redis-url: "redis://localhost:6379/0?clientName=HamsterCore&timeout=5s" redis-url: "redis://localhost:6379/0?clientName=HamsterCore&timeout=5s"
# 是否启用数据库连接池功能
enable-database: false
# 数据库连接池配置
datasource: datasource:
# 数据库链接驱动地址 # 数据库链接驱动地址
# 除非你知道自己在做什么,否则不建议更改该项
driver: "com.mysql.cj.jdbc.Driver" driver: "com.mysql.cj.jdbc.Driver"
# 数据库链接填写格式: # MySQL数据库链接填写格式:
# jdbc:mysql://{数据库地址}:{数据库端口}/{使用的库名}?参数 # jdbc:mysql://{数据库地址}:{数据库端口}/{使用的库名}?参数
# 除非你知道自己在做什么,否则不建议随意更改参数
url: "jdbc:mysql://localhost:3306/Test?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true" url: "jdbc:mysql://localhost:3306/Test?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true"
# 用户名 # 用户名
username: "root" username: "root"
@@ -24,8 +29,8 @@ datasource:
# 推荐值1~3 # 推荐值1~3
minimum-idle: 0 minimum-idle: 0
# 最大链接数 # 最大链接数
# 推荐值:不低于5 # 推荐值:不低于3
maximum-pool-size: 5 maximum-pool-size: 3
# 保持连接池可用的间隔 # 保持连接池可用的间隔
# 除非你的服务器数据库连接经常断开,否则不建议启用该选项 # 除非你的服务器数据库连接经常断开,否则不建议启用该选项
# 单位:毫秒 # 单位:毫秒

View File

@@ -1,6 +0,0 @@
version: ${version}
CHECK_TYPE: GITEA_RELEASES
GIT_BASE_URL: https://git.airgame.net
GIT_REPO: MiniDay/hamster-core
GIT_TOKEN: a44a69a4d1b8601bf6091403247759cd28764d5e
DOWNLOAD_URL: https://jenkins.airgame.net/job/opensource/job/hamster-core/

View File

@@ -12,6 +12,10 @@ dependencies {
exclude(group = "org.jetbrains") exclude(group = "org.jetbrains")
exclude(group = "com.google.code.gson") exclude(group = "com.google.code.gson")
} }
compileOnlyApi("net.kyori:adventure-text-serializer-legacy:4.13.1") {
exclude(group = "org.jetbrains")
exclude(group = "com.google.code.gson")
}
// https://mvnrepository.com/artifact/redis.clients/jedis // https://mvnrepository.com/artifact/redis.clients/jedis
compileOnlyApi("redis.clients:jedis:5.1.2") { compileOnlyApi("redis.clients:jedis:5.1.2") {
@@ -19,6 +23,8 @@ dependencies {
exclude(group = "org.slf4j") exclude(group = "org.slf4j")
} }
compileOnlyApi("org.quartz-scheduler:quartz:2.3.2") { isTransitive = false } compileOnlyApi("org.quartz-scheduler:quartz:2.3.2") { isTransitive = false }
// https://mvnrepository.com/artifact/com.sun.mail/jakarta.mail
compileOnlyApi("com.sun.mail:jakarta.mail:2.0.1")
// https://mvnrepository.com/artifact/com.zaxxer/HikariCP // https://mvnrepository.com/artifact/com.zaxxer/HikariCP
compileOnly("com.zaxxer:HikariCP:4.0.3") { isTransitive = false } compileOnly("com.zaxxer:HikariCP:4.0.3") { isTransitive = false }

View File

@@ -3,11 +3,13 @@ package cn.hamster3.mc.plugin.core.common.api;
import cn.hamster3.mc.plugin.core.common.config.ConfigSection; import cn.hamster3.mc.plugin.core.common.config.ConfigSection;
import cn.hamster3.mc.plugin.core.common.thread.NamedThreadFactory; import cn.hamster3.mc.plugin.core.common.thread.NamedThreadFactory;
import cn.hamster3.mc.plugin.core.common.util.CoreUtils; import cn.hamster3.mc.plugin.core.common.util.CoreUtils;
import cn.hamster3.mc.plugin.core.common.util.SimpleLogger;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.HikariDataSource;
import lombok.Getter; import lombok.Getter;
import net.kyori.adventure.platform.AudienceProvider; import net.kyori.adventure.platform.AudienceProvider;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPool;
import javax.sql.DataSource; import javax.sql.DataSource;
@@ -16,64 +18,79 @@ import java.sql.SQLException;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.logging.Logger;
@Getter
@SuppressWarnings("unused") @SuppressWarnings("unused")
public abstract class CoreAPI { public abstract class CoreAPI {
@Getter @Getter
protected static CoreAPI instance; protected static CoreAPI instance;
/** private final boolean enableRedis;
* Redis 连接池 private final boolean enableDatabase;
*/
@Getter
@NotNull
private final JedisPool jedisPool;
/**
* HamsterCore 公用数据库连接池
*/
@Getter
@NotNull
private final HikariDataSource hikariDataSource;
/** /**
* 异步线程池 * 异步线程池
*/ */
@Getter @NotNull
private final ExecutorService executorService; private final ExecutorService executorService;
/** /**
* 调度器线程池 * 调度器线程池
*/ */
@Getter @NotNull
private final ScheduledExecutorService scheduledService; private final ScheduledExecutorService scheduledService;
/**
* 公用 Redis 连接池
*/
@Nullable
private JedisPool jedisPool;
/**
* 公用数据库连接池
*/
@Nullable
private HikariDataSource hikariDataSource;
public CoreAPI(@NotNull ConfigSection config) { public CoreAPI(@NotNull ConfigSection config) {
SimpleLogger logger = getLogger();
executorService = Executors.newCachedThreadPool(new NamedThreadFactory("HamsterCore - Executor")); executorService = Executors.newCachedThreadPool(new NamedThreadFactory("HamsterCore - Executor"));
scheduledService = Executors.newScheduledThreadPool(1, new NamedThreadFactory("HamsterCore - Scheduler")); scheduledService = Executors.newScheduledThreadPool(1, new NamedThreadFactory("HamsterCore - Scheduler"));
logger.info("已创建线程池");
getLogger().info("正在创建 Redis 连接池"); enableRedis = config.getBoolean("enable-redis", true);
jedisPool = new JedisPool(config.getString("redis-url")); if (enableRedis) {
getLogger().info("Redis 连接池创建完成"); logger.info("正在创建 Redis 连接池");
jedisPool = new JedisPool(config.getString("redis-url"));
ConfigSection datasourceConfig = config.getSection("datasource"); logger.info("Redis 连接池创建完成");
if (datasourceConfig == null) { } else {
throw new IllegalArgumentException("配置文件中未找到 datasource 节点"); logger.info("未启用 Redis 功能");
}
enableDatabase = config.getBoolean("enable-database", true);
if (enableDatabase) {
ConfigSection datasourceConfig = config.getSection("datasource");
if (datasourceConfig == null) {
throw new IllegalArgumentException("配置文件中未找到 datasource 节点");
}
logger.info("正在创建数据库连接池");
hikariDataSource = (HikariDataSource) CoreUtils.getDataSource(datasourceConfig);
logger.info("数据库连接池创建完成");
} else {
logger.info("未启用数据库功能");
} }
getLogger().info("正在创建数据库连接池");
hikariDataSource = (HikariDataSource) CoreUtils.getDataSource(datasourceConfig);
getLogger().info("数据库连接池创建完成");
} }
/** /**
* 获取 HamsterCore 公用数据库连接池 * 获取公用数据库连接池
* *
* @return 公用数据库连接池 * @return 公用数据库连接池
*/ */
@NotNull @NotNull
public DataSource getDataSource() { public DataSource getDataSource() {
return hikariDataSource; if (hikariDataSource != null) {
return hikariDataSource;
}
throw new IllegalStateException("仓鼠核心未启用数据库功能");
} }
/** /**
* 获取 HamsterCore 公用数据库连接 * 获取公用数据库连接
* *
* @return 公用数据库连接 * @return 公用数据库连接
* @throws SQLException - * @throws SQLException -
@@ -84,7 +101,15 @@ public abstract class CoreAPI {
} }
@NotNull @NotNull
public abstract Logger getLogger(); public JedisPool getJedisPool() {
if (jedisPool != null) {
return jedisPool;
}
throw new IllegalStateException("仓鼠核心未启用 Redis 功能");
}
@NotNull
public abstract SimpleLogger getLogger();
/** /**
* @return GSON 工具 * @return GSON 工具

View File

@@ -4,10 +4,7 @@ import lombok.Getter;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.ArrayList; import java.util.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@SuppressWarnings({"unused", "unchecked"}) @SuppressWarnings({"unused", "unchecked"})
@Getter @Getter
@@ -27,6 +24,15 @@ public class ConfigSection {
return map.containsKey(key); return map.containsKey(key);
} }
public Set<String> getKeys() {
return map.keySet();
}
@Nullable
public Object get(@NotNull String key) {
return map.get(key);
}
@Nullable @Nullable
public ConfigSection getSection(@NotNull String key) { public ConfigSection getSection(@NotNull String key) {
Object o = map.get(key); Object o = map.get(key);

View File

@@ -0,0 +1,98 @@
package cn.hamster3.mc.plugin.core.common.util;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
import java.util.Arrays;
import java.util.logging.Level;
@Getter
@SuppressWarnings("unused")
public abstract class SimpleLogger {
public abstract void log(@NotNull Level level, @NotNull String msg);
public abstract void log(@NotNull Level level, @NotNull Throwable throwable);
public abstract void log(@NotNull Level level, @NotNull Throwable throwable, @NotNull String msg);
public void log(@NotNull Level level, @NotNull String msg, @NotNull Object... args) {
try {
log(level, String.format(msg, args));
} catch (Exception e) {
log(Level.WARNING, "输出日志 %s 时遇到一个异常", msg);
log(Level.WARNING, "日志参数: %s", Arrays.toString(args));
log(Level.WARNING, e, "异常信息: ");
}
}
public void log(@NotNull Level level, @NotNull Throwable throwable, @NotNull String msg, @NotNull Object... args) {
try {
log(level, String.format(msg, args), throwable);
} catch (Exception e) {
log(Level.WARNING, "输出日志 " + msg + " 时遇到一个异常");
log(Level.WARNING, "日志参数: " + Arrays.toString(args));
log(Level.WARNING, "异常参数: ", throwable);
log(Level.WARNING, "异常信息: ", e);
}
}
public void info(@NotNull String msg) {
log(Level.INFO, msg);
}
public void info(@NotNull String msg, @NotNull Object... args) {
log(Level.INFO, msg, args);
}
public void info(@NotNull Throwable throwable) {
log(Level.INFO, throwable);
}
public void info(@NotNull Throwable throwable, @NotNull String msg) {
log(Level.INFO, throwable, msg);
}
public void info(@NotNull Throwable throwable, @NotNull String msg, @NotNull Object... args) {
log(Level.INFO, throwable, msg, args);
}
public void warn(@NotNull String msg) {
log(Level.WARNING, msg);
}
public void warn(@NotNull String msg, @NotNull Object... args) {
log(Level.WARNING, msg, args);
}
public void warn(@NotNull Throwable throwable) {
log(Level.WARNING, throwable);
}
public void warn(@NotNull Throwable throwable, @NotNull String msg) {
log(Level.WARNING, throwable, msg);
}
public void warn(@NotNull Throwable throwable, @NotNull String msg, @NotNull Object... args) {
log(Level.WARNING, throwable, msg, args);
}
public void error(@NotNull String msg) {
log(Level.SEVERE, msg);
}
public void error(@NotNull String msg, @NotNull Object... args) {
log(Level.SEVERE, msg, args);
}
public void error(@NotNull Throwable throwable) {
log(Level.SEVERE, throwable);
}
public void error(@NotNull Throwable throwable, @NotNull String msg) {
log(Level.SEVERE, throwable, msg);
}
public void error(@NotNull Throwable throwable, @NotNull String msg, @NotNull Object... args) {
log(Level.SEVERE, throwable, msg, args);
}
}

View File

@@ -1,10 +1,13 @@
package cn.hamster3.mc.plugin.core.common.util; package cn.hamster3.mc.plugin.core.common.util;
import cn.hamster3.mc.plugin.core.common.api.CoreAPI;
import cn.hamster3.mc.plugin.core.common.config.ConfigSection; import cn.hamster3.mc.plugin.core.common.config.ConfigSection;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonParser; import com.google.gson.JsonParser;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -14,6 +17,9 @@ import java.io.InputStreamReader;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URL; import java.net.URL;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public final class UpdateCheckUtils { public final class UpdateCheckUtils {
private static final JsonParser JSON_PARSER = new JsonParser(); private static final JsonParser JSON_PARSER = new JsonParser();
@@ -21,59 +27,66 @@ public final class UpdateCheckUtils {
private UpdateCheckUtils() { private UpdateCheckUtils() {
} }
public static void showUpdate(@NotNull String pluginName, @NotNull ConfigSection config, @NotNull UpdateReceiver sender) { public static void checkUpdate(@NotNull String pluginName, @NotNull ConfigSection updateConfig) throws IOException {
String version = config.getString("version"); checkUpdate(pluginName, updateConfig, CoreAPI.getInstance().getAudienceProvider().console());
if (version == null) { }
public static void checkUpdate(@NotNull String pluginName, @NotNull ConfigSection updateConfig, @NotNull Audience sender) throws IOException {
String version = updateConfig.getString("VERSION", "");
String checkType = updateConfig.getString("CHECK_TYPE", "");
String baseUrl = updateConfig.getString("GIT_BASE_URL");
String gitRepo = updateConfig.getString("GIT_REPO");
String downloadUrl = updateConfig.getString("DOWNLOAD_URL");
if (baseUrl == null || gitRepo == null) {
return; return;
} }
String checkType = config.getString("CHECK_TYPE", ""); String gitToken = updateConfig.getString("GIT_TOKEN");
try { String lastRelease = null;
switch (checkType) { switch (checkType) {
case "GITEA_RELEASES": { case "GITEA_RELEASES": {
String baseUrl = config.getString("GIT_BASE_URL"); lastRelease = getGiteaLastRelease(baseUrl, gitRepo, gitToken);
String gitRepo = config.getString("GIT_REPO"); break;
String downloadUrl = config.getString("DOWNLOAD_URL"); }
if (baseUrl == null || gitRepo == null || downloadUrl == null) { case "GITLAB_RELEASES": {
return; int projectID = getGitlabProjectID(baseUrl, gitRepo, gitToken);
} if (projectID < 0) {
String gitToken = config.getString("GIT_TOKEN"); break;
String lastRelease = getGiteaLastRelease(baseUrl, gitRepo, gitToken); }
if (lastRelease == null) { lastRelease = getGitlabLastRelease(baseUrl, projectID, gitToken);
break; break;
}
if (lastRelease.compareToIgnoreCase(version) <= 0) {
break;
}
sender.sendMessage(String.format("§a插件 §l%s§a 发布了新版本 %s", pluginName, lastRelease));
sender.sendMessage(String.format("§b下载链接: §n%s", downloadUrl));
break;
}
case "GITLAB_RELEASES": {
String baseUrl = config.getString("GIT_BASE_URL");
String gitRepo = config.getString("GIT_REPO");
String downloadUrl = config.getString("DOWNLOAD_URL");
if (baseUrl == null || gitRepo == null || downloadUrl == null) {
return;
}
String gitToken = config.getString("GIT_TOKEN");
int projectID = getGitlabProjectID(baseUrl, gitRepo, gitToken);
if (projectID < 0) {
break;
}
String lastRelease = getGitlabLastRelease(baseUrl, projectID, gitToken);
if (lastRelease == null) {
break;
}
if (lastRelease.compareToIgnoreCase(version) <= 0) {
break;
}
sender.sendMessage(String.format("§a插件 §l%s§a 发布了新版本 %s", pluginName, lastRelease));
sender.sendMessage(String.format("§b下载链接: §n%s", downloadUrl));
break;
}
} }
} catch (Exception ignored) {
} }
if (lastRelease == null) {
return;
}
if (compareVersion(lastRelease, version) <= 0) {
return;
}
sender.sendMessage(LegacyComponentSerializer.legacySection().deserialize(
String.format("§a插件 §l%s§a 发布了新版本 %s", pluginName, lastRelease)
));
if (downloadUrl != null) {
sender.sendMessage(LegacyComponentSerializer.legacySection().deserialize("§b下载链接: §n" + downloadUrl));
}
}
public static int compareVersion(@NotNull String version1, String version2) {
List<Integer> collect1 = Arrays.stream(version1.split("[+-]")[0].split("\\."))
.map(Integer::parseInt).collect(Collectors.toList());
List<Integer> collect2 = Arrays.stream(version2.split("[+-]")[0].split("\\."))
.map(Integer::parseInt).collect(Collectors.toList());
int max = Math.max(collect1.size(), collect2.size());
for (int i = 0; i < max; i++) {
int v1 = i < collect1.size() ? collect1.get(i) : 0;
int v2 = i < collect2.size() ? collect2.get(i) : 0;
if (v1 > v2) {
return 1;
}
if (v1 < v2) {
return -1;
}
}
return 0;
} }
@Nullable @Nullable
@@ -103,12 +116,12 @@ public final class UpdateCheckUtils {
} }
public static int getGitlabProjectID(@NotNull String baseUrl, @NotNull String repo, @Nullable String token) throws IOException { public static int getGitlabProjectID(@NotNull String baseUrl, @NotNull String repo, @Nullable String token) throws IOException {
URL url = new URL("https://" + baseUrl + "/api/v4/projects?search=" + repo); URL url = new URL(baseUrl + "/api/v4/projects?search=" + repo);
HttpURLConnection connection = (HttpURLConnection) url.openConnection(); HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true); connection.setDoInput(true);
connection.setRequestMethod("GET"); connection.setRequestMethod("GET");
if (token != null) { if (token != null) {
connection.setRequestProperty("PRIVATE-TOKEN", "token " + token); connection.setRequestProperty("PRIVATE-TOKEN", token);
} }
connection.connect(); connection.connect();
try (InputStream stream = connection.getInputStream()) { try (InputStream stream = connection.getInputStream()) {
@@ -130,7 +143,7 @@ public final class UpdateCheckUtils {
connection.setDoInput(true); connection.setDoInput(true);
connection.setRequestMethod("GET"); connection.setRequestMethod("GET");
if (token != null) { if (token != null) {
connection.setRequestProperty("Authorization", "token " + token); connection.setRequestProperty("PRIVATE-TOKEN", token);
} }
connection.connect(); connection.connect();
try (InputStream stream = connection.getInputStream()) { try (InputStream stream = connection.getInputStream()) {
@@ -140,8 +153,4 @@ public final class UpdateCheckUtils {
} }
} }
} }
public interface UpdateReceiver {
void sendMessage(@NotNull String message);
}
} }

View File

@@ -0,0 +1,146 @@
package cn.hamster3.mc.plugin.core.common.util.async;
import cn.hamster3.mc.plugin.core.common.api.CoreAPI;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
@SuppressWarnings({"unused", "CallToPrintStackTrace"})
public class CompletableTask<T> {
@NotNull
private final List<Consumer<T>> onSuccess;
@NotNull
private final List<Consumer<Throwable>> onFailed;
@NotNull
private State state;
private T value;
private Throwable throwable;
public CompletableTask() {
state = State.WAITING;
onSuccess = new ArrayList<>();
onFailed = new ArrayList<>();
}
@NotNull
public static CompletableTask<Void> runAsync(@NotNull RunTask runTask) {
CompletableTask<Void> task = new CompletableTask<>();
CoreAPI.getInstance().getExecutorService().submit(() -> {
try {
runTask.run();
task.success(null);
} catch (Exception e) {
task.failed(e);
}
});
return task;
}
@NotNull
public static <T> CompletableTask<T> supplyAsync(@NotNull SupplyTask<T> supplyTask) {
CompletableTask<T> task = new CompletableTask<>();
CoreAPI.getInstance().getExecutorService().submit(() -> {
try {
T call = supplyTask.call();
task.success(call);
} catch (Exception e) {
task.failed(e);
}
});
return task;
}
public void success(@Nullable T value) {
if (state != State.WAITING) {
throw new IllegalStateException();
}
this.value = value;
state = State.SUCCESS;
synchronized (this) {
notifyAll();
}
for (Consumer<T> success : onSuccess) {
try {
success.accept(value);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void failed(@Nullable Throwable throwable) {
if (state != State.WAITING) {
throw new IllegalStateException();
}
this.value = null;
state = State.FAILED;
this.throwable = throwable;
synchronized (this) {
notifyAll();
}
for (Consumer<Throwable> consumer : onFailed) {
try {
consumer.accept(throwable);
} catch (Exception e) {
e.printStackTrace();
}
}
}
@NotNull
public CompletableTask<T> onSuccess(@NotNull Consumer<T> consumer) {
if (state == State.SUCCESS) {
try {
consumer.accept(value);
} catch (Exception e) {
e.printStackTrace();
}
} else {
onSuccess.add(consumer);
}
return this;
}
@NotNull
public CompletableTask<T> onFailed(@NotNull Consumer<Throwable> consumer) {
if (state == State.FAILED) {
try {
consumer.accept(throwable);
} catch (Exception e) {
e.printStackTrace();
}
} else {
onFailed.add(consumer);
}
return this;
}
public T get() throws InterruptedException {
if (state == State.WAITING) {
synchronized (this) {
wait();
}
}
if (state == State.SUCCESS) {
return value;
}
return null;
}
public T join() {
try {
return get();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public enum State {
WAITING,
SUCCESS,
FAILED
}
}

View File

@@ -0,0 +1,5 @@
package cn.hamster3.mc.plugin.core.common.util.async;
public interface RunTask {
void run() throws Exception;
}

View File

@@ -0,0 +1,5 @@
package cn.hamster3.mc.plugin.core.common.util.async;
public interface SupplyTask<T> {
T call() throws Exception;
}

View File

@@ -15,6 +15,8 @@ dependencies {
} }
// https://mvnrepository.com/artifact/org.quartz-scheduler/quartz // https://mvnrepository.com/artifact/org.quartz-scheduler/quartz
api("org.quartz-scheduler:quartz:2.3.2") { isTransitive = false } api("org.quartz-scheduler:quartz:2.3.2") { isTransitive = false }
// https://mvnrepository.com/artifact/com.sun.mail/jakarta.mail
api("com.sun.mail:jakarta.mail:2.0.1")
// https://mvnrepository.com/artifact/com.zaxxer/HikariCP // https://mvnrepository.com/artifact/com.zaxxer/HikariCP
implementation("com.zaxxer:HikariCP:5.1.0") { isTransitive = false } implementation("com.zaxxer:HikariCP:5.1.0") { isTransitive = false }

View File

@@ -4,6 +4,7 @@ import cn.hamster3.mc.plugin.core.common.api.CoreAPI;
import cn.hamster3.mc.plugin.core.common.config.YamlConfig; import cn.hamster3.mc.plugin.core.common.config.YamlConfig;
import cn.hamster3.mc.plugin.core.common.util.UpdateCheckUtils; import cn.hamster3.mc.plugin.core.common.util.UpdateCheckUtils;
import cn.hamster3.mc.plugin.core.velocity.api.CoreVelocityAPI; import cn.hamster3.mc.plugin.core.velocity.api.CoreVelocityAPI;
import cn.hamster3.mc.plugin.core.velocity.util.VelocitySimpleLogger;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.velocitypowered.api.event.PostOrder; import com.velocitypowered.api.event.PostOrder;
import com.velocitypowered.api.event.Subscribe; import com.velocitypowered.api.event.Subscribe;
@@ -15,11 +16,9 @@ import com.velocitypowered.api.plugin.annotation.DataDirectory;
import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.ProxyServer;
import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.HikariDataSource;
import lombok.Getter; import lombok.Getter;
import net.kyori.adventure.text.Component;
import org.slf4j.Logger; import org.slf4j.Logger;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@@ -38,25 +37,22 @@ public class HamsterCorePlugin {
@Getter @Getter
private static HamsterCorePlugin instance; private static HamsterCorePlugin instance;
@Getter @Getter
private final java.util.logging.Logger logger; private final VelocitySimpleLogger simpleLogger;
@Getter
private final Logger slf4jLogger;
@Getter @Getter
private final ProxyServer proxyServer; private final ProxyServer proxyServer;
@Getter @Getter
private final File dataFolder; private final File dataFolder;
@Inject @Inject
public HamsterCorePlugin(Logger slf4jLogger, ProxyServer proxyServer, @DataDirectory Path dataPath) { public HamsterCorePlugin(Logger logger, ProxyServer proxyServer, @DataDirectory Path dataPath) {
logger = java.util.logging.Logger.getLogger("hamster-core"); this.simpleLogger = new VelocitySimpleLogger(logger);
this.slf4jLogger = slf4jLogger;
this.proxyServer = proxyServer; this.proxyServer = proxyServer;
dataFolder = dataPath.toFile(); dataFolder = dataPath.toFile();
instance = this; instance = this;
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
try { try {
if (dataFolder.mkdir()) { if (dataFolder.mkdir()) {
slf4jLogger.info("已生成插件存档文件夹"); simpleLogger.info("已生成插件存档文件夹");
} }
File configFile = new File(dataFolder, "config.yml"); File configFile = new File(dataFolder, "config.yml");
if (!configFile.exists()) { if (!configFile.exists()) {
@@ -67,60 +63,62 @@ public class HamsterCorePlugin {
} }
} }
CoreVelocityAPI.init(configFile); CoreVelocityAPI.init(configFile);
slf4jLogger.info("已初始化 CoreAPI"); simpleLogger.info("已初始化 CoreAPI");
} catch (Exception e) { } catch (Exception e) {
slf4jLogger.error("初始化 CoreAPI 出错", e); simpleLogger.error("初始化 CoreAPI 出错", e);
} }
long time = System.currentTimeMillis() - start; long time = System.currentTimeMillis() - start;
slf4jLogger.info("HamsterCore 初始化完成,总计耗时 " + time + " ms"); simpleLogger.info("仓鼠核心初始化完成,总计耗时 " + time + " ms");
} }
@Subscribe(order = PostOrder.FIRST) @Subscribe(order = PostOrder.FIRST)
public void onProxyInitialization(ProxyInitializeEvent event) { public void onProxyInitialization(ProxyInitializeEvent event) {
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
slf4jLogger.info("仓鼠核心正在启动"); simpleLogger.info("仓鼠核心正在启动");
CoreAPI.getInstance().getExecutorService().submit(this::checkUpdate);
long time = System.currentTimeMillis() - start; long time = System.currentTimeMillis() - start;
slf4jLogger.info("仓鼠核心启动完成,总计耗时 " + time + " ms"); simpleLogger.info("仓鼠核心启动完成,总计耗时 " + time + " ms");
CoreAPI.getInstance().getExecutorService().submit(() -> {
for (PluginContainer plugin : proxyServer.getPluginManager().getPlugins()) {
String pluginName = plugin.getDescription().getName().orElse(null);
if (pluginName == null) {
continue;
}
Object pluginObject = plugin.getInstance().orElse(null);
if (pluginObject == null) {
continue;
}
try (InputStream stream = pluginObject.getClass().getResourceAsStream("/update.yml")) {
if (stream == null) {
continue;
}
try (InputStreamReader reader = new InputStreamReader(stream, StandardCharsets.UTF_8)) {
YamlConfig config = YamlConfig.load(reader);
UpdateCheckUtils.showUpdate(pluginName, config, (s) -> proxyServer.sendMessage(Component.text(s)));
} catch (IOException ignored) {
}
} catch (IOException e) {
e.printStackTrace();
}
}
});
} }
@Subscribe(order = PostOrder.LAST) @Subscribe(order = PostOrder.LAST)
public void onProxyShutdown(ProxyShutdownEvent event) { public void onProxyShutdown(ProxyShutdownEvent event) {
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
CoreAPI.getInstance().getJedisPool().close(); if (CoreAPI.getInstance().isEnableRedis()) {
slf4jLogger.info("已关闭 Redis 连接池"); CoreAPI.getInstance().getJedisPool().close();
if (CoreAPI.getInstance().getDataSource() instanceof HikariDataSource dataSource) { simpleLogger.info("已关闭 Redis 连接池");
dataSource.close(); }
slf4jLogger.info("已关闭数据库连接池"); if (CoreAPI.getInstance().isEnableDatabase()) {
((HikariDataSource) CoreAPI.getInstance().getDataSource()).close();
simpleLogger.info("已关闭数据库连接池");
} }
CoreAPI.getInstance().getExecutorService().shutdownNow(); CoreAPI.getInstance().getExecutorService().shutdownNow();
slf4jLogger.info("已关闭 ExecutorService 线程池"); simpleLogger.info("已关闭 ExecutorService 线程池");
CoreAPI.getInstance().getScheduledService().shutdownNow(); CoreAPI.getInstance().getScheduledService().shutdownNow();
slf4jLogger.info("已关闭 ScheduledExecutorService 线程池"); simpleLogger.info("已关闭 ScheduledExecutorService 线程池");
long time = System.currentTimeMillis() - start; long time = System.currentTimeMillis() - start;
slf4jLogger.info("HamsterCore 关闭完成,总计耗时 " + time + " ms"); simpleLogger.info("仓鼠核心已关闭,总计耗时 " + time + " ms");
}
private void checkUpdate() {
for (PluginContainer plugin : proxyServer.getPluginManager().getPlugins()) {
String pluginName = plugin.getDescription().getName().orElse(null);
if (pluginName == null) {
continue;
}
Object pluginObject = plugin.getInstance().orElse(null);
if (pluginObject == null) {
continue;
}
try (InputStream stream = pluginObject.getClass().getResourceAsStream("/update.yml")) {
if (stream == null) {
continue;
}
try (InputStreamReader reader = new InputStreamReader(stream, StandardCharsets.UTF_8)) {
YamlConfig config = YamlConfig.load(reader);
UpdateCheckUtils.checkUpdate(pluginName, config);
}
} catch (Exception ignored) {
}
}
} }
} }

View File

@@ -7,16 +7,16 @@ import cn.hamster3.mc.plugin.core.common.data.DisplayMessage;
import cn.hamster3.mc.plugin.core.common.impl.ComponentTypeAdapter; import cn.hamster3.mc.plugin.core.common.impl.ComponentTypeAdapter;
import cn.hamster3.mc.plugin.core.common.impl.MessageTypeAdapter; import cn.hamster3.mc.plugin.core.common.impl.MessageTypeAdapter;
import cn.hamster3.mc.plugin.core.velocity.HamsterCorePlugin; import cn.hamster3.mc.plugin.core.velocity.HamsterCorePlugin;
import cn.hamster3.mc.plugin.core.velocity.impl.AudienceProviderImpl;
import cn.hamster3.mc.plugin.core.velocity.util.VelocitySimpleLogger;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import cn.hamster3.mc.plugin.core.velocity.impl.AudienceProviderImpl;
import net.kyori.adventure.platform.AudienceProvider; import net.kyori.adventure.platform.AudienceProvider;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.logging.Logger;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public final class CoreVelocityAPI extends CoreAPI { public final class CoreVelocityAPI extends CoreAPI {
@@ -57,8 +57,8 @@ public final class CoreVelocityAPI extends CoreAPI {
} }
@Override @Override
public @NotNull Logger getLogger() { public @NotNull VelocitySimpleLogger getLogger() {
return HamsterCorePlugin.getInstance().getLogger(); return HamsterCorePlugin.getInstance().getSimpleLogger();
} }
@Override @Override

View File

@@ -0,0 +1,57 @@
package cn.hamster3.mc.plugin.core.velocity.util;
import cn.hamster3.mc.plugin.core.common.util.SimpleLogger;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import java.util.logging.Level;
@Getter
public class VelocitySimpleLogger extends SimpleLogger {
@NotNull
private final Logger logger;
public VelocitySimpleLogger(@NotNull Logger logger) {
this.logger = logger;
}
@Override
public void log(@NotNull Level level, @NotNull String msg) {
if (level == Level.INFO) {
logger.info(msg);
} else if (level == Level.WARNING) {
logger.warn(msg);
} else if (level == Level.SEVERE) {
logger.error(msg);
} else {
logger.trace(msg);
}
}
@Override
public void log(@NotNull Level level, @NotNull Throwable throwable) {
if (level == Level.INFO) {
logger.info("", throwable);
} else if (level == Level.WARNING) {
logger.warn("", throwable);
} else if (level == Level.SEVERE) {
logger.error("", throwable);
} else {
logger.trace("", throwable);
}
}
@Override
public void log(@NotNull Level level, @NotNull Throwable throwable, @NotNull String msg) {
if (level == Level.INFO) {
logger.info(msg, throwable);
} else if (level == Level.WARNING) {
logger.warn(msg, throwable);
} else if (level == Level.SEVERE) {
logger.error(msg, throwable);
} else {
logger.trace(msg, throwable);
}
}
}

View File

@@ -1,3 +1,6 @@
# 是否启用 redis 连接池功能
enable-redis: false
# redis 连接配置 # redis 连接配置
# 完整格式如下: # 完整格式如下:
# redis://用户名:密码@主机名:端口/数据库索引?参数名=参数值&参数名=参数值 # redis://用户名:密码@主机名:端口/数据库索引?参数名=参数值&参数名=参数值
@@ -8,18 +11,16 @@
# 若不设置数据库,则默认使用 0 # 若不设置数据库,则默认使用 0
redis-url: "redis://localhost:6379/0?clientName=HamsterCore&timeout=5s" redis-url: "redis://localhost:6379/0?clientName=HamsterCore&timeout=5s"
# 是否启用数据库连接池功能
enable-database: false
# 数据库连接池配置
datasource: datasource:
# 数据库链接驱动地址 # 数据库链接驱动地址
# 除非你知道自己在做什么,否则不建议更改该项
# 旧版服务端低于1.13请使用com.mysql.jdbc.Driver
driver: "com.mysql.cj.jdbc.Driver" driver: "com.mysql.cj.jdbc.Driver"
# 数据库链接填写格式: # MySQL数据库链接填写格式:
# jdbc:mysql://{数据库地址}:{数据库端口}/{使用的库名}?参数 # jdbc:mysql://{数据库地址}:{数据库端口}/{使用的库名}?参数
# 除非你知道自己在做什么,否则不建议随意更改参数
url: "jdbc:mysql://localhost:3306/Test?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true" url: "jdbc:mysql://localhost:3306/Test?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true"
# 如果你不需要做多端跨服,那么请使用 sqlite 作本地数据库
# driver: "org.sqlite.JDBC"
# url: "jdbc:sqlite:./plugins/HamsterCore/database.db"
# 用户名 # 用户名
username: "root" username: "root"
# 密码 # 密码

View File

@@ -1,6 +1,5 @@
version: ${version} VERSION: ${version}
CHECK_TYPE: GITEA_RELEASES CHECK_TYPE: GITEA_RELEASES
GIT_BASE_URL: https://git.airgame.net GIT_BASE_URL: https://git.airgame.net
GIT_REPO: MiniDay/hamster-core GIT_REPO: MiniDay/hamster-core
GIT_TOKEN: a44a69a4d1b8601bf6091403247759cd28764d5e
DOWNLOAD_URL: https://jenkins.airgame.net/job/opensource/job/hamster-core/ DOWNLOAD_URL: https://jenkins.airgame.net/job/opensource/job/hamster-core/

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip distributionUrl=https://mirrors.cloud.tencent.com/gradle/gradle-8.5-bin.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists