Merge branch 'refs/heads/dev'

This commit is contained in:
2024-10-28 00:29:24 +08:00
40 changed files with 886 additions and 379 deletions

View File

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

View File

@@ -5,7 +5,7 @@ plugins {
}
group = "cn.hamster3.mc.plugin"
version = "1.3.3"
version = "1.3.4-SNAPSHOT"
description = "叁只仓鼠的 Minecraft 插件开发通用工具包"
subprojects {
@@ -38,6 +38,11 @@ subprojects {
}
tasks {
processResources {
filesMatching("update.yml") {
expand(rootProject.properties)
}
}
withType<JavaCompile> {
options.encoding = "UTF-8"
}
@@ -45,11 +50,6 @@ subprojects {
from(rootProject.file("LICENSE"))
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
processResources {
filesMatching("update.yml") {
expand(rootProject.properties)
}
}
build {
dependsOn(shadowJar)
}

View File

@@ -19,21 +19,15 @@ dependencies {
exclude(group = "org.jetbrains")
}
// https://mvnrepository.com/artifact/redis.clients/jedis
api("redis.clients:jedis:5.1.2") {
api("redis.clients:jedis:5.1.4") {
exclude(group = "com.google.code.gson")
exclude(group = "org.slf4j")
}
// https://mvnrepository.com/artifact/org.quartz-scheduler/quartz
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/
implementation("de.tr7zw:item-nbt-api:2.12.2")
implementation("de.tr7zw:item-nbt-api:2.13.2")
// https://mvnrepository.com/artifact/com.zaxxer/HikariCP
implementation("com.zaxxer:HikariCP:4.0.3") { exclude(group = "org.slf4j") }
// https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-stdlib-jdk8
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.23") { exclude(group = "org.jetbrains") }
}
tasks {

View File

@@ -11,11 +11,14 @@ 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.page.handler.PageHandler;
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.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.util.UpdateCheckUtils;
import com.zaxxer.hikari.HikariDataSource;
import de.tr7zw.changeme.nbtapi.NBT;
import lombok.Getter;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import org.bukkit.Bukkit;
@@ -33,12 +36,13 @@ import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.logging.Logger;
public class HamsterCorePlugin extends JavaPlugin {
@Getter
private static HamsterCorePlugin instance;
@Getter
private static BukkitSimpleLogger simpleLogger;
@Getter
private BukkitAudiences audienceProvider;
/**
@@ -66,15 +70,15 @@ public class HamsterCorePlugin extends JavaPlugin {
@Override
public void onLoad() {
instance = this;
simpleLogger = new BukkitSimpleLogger(getInstance());
long start = System.currentTimeMillis();
Logger logger = getLogger();
logger.info("仓鼠核心正在初始化");
logger.info("Minecraft 版本: " + MinecraftVersion.getMCVersion());
logger.info("NMS 版本: " + MinecraftVersion.getNMSVersion());
simpleLogger.info("仓鼠核心正在初始化");
simpleLogger.info("Minecraft 版本: " + MinecraftVersion.getMCVersion());
simpleLogger.info("NMS 版本: " + MinecraftVersion.getNMSVersion());
try {
File dataFolder = getDataFolder();
if (dataFolder.mkdir()) {
logger.info("已生成插件存档文件夹");
simpleLogger.info("已生成插件存档文件夹");
}
File configFile = new File(dataFolder, "config.yml");
if (!configFile.exists()) {
@@ -85,54 +89,59 @@ public class HamsterCorePlugin extends JavaPlugin {
}
}
CoreBukkitAPI.init(configFile);
logger.info("已初始化 CoreAPI");
simpleLogger.info("已初始化 CoreAPI");
} catch (Exception e) {
logger.warning("初始化 CoreAPI 出错");
e.printStackTrace();
simpleLogger.error(e, "初始化 CoreAPI 出错");
}
CoreMessage.init(this);
logger.info("已初始化语言文本");
simpleLogger.info("已初始化语言文本");
long time = System.currentTimeMillis() - start;
logger.info("仓鼠核心初始化完成,总计耗时 " + time + " ms");
simpleLogger.info("仓鼠核心初始化完成,总计耗时 " + time + " ms");
}
@Override
public void onEnable() {
long start = System.currentTimeMillis();
Logger logger = getLogger();
logger.info("仓鼠核心正在启动");
simpleLogger.info("仓鼠核心正在启动");
NBT.preloadApi();
audienceProvider = BukkitAudiences.create(this);
logger.info("已创建 AudienceProvider");
simpleLogger.info("已创建 AudienceProvider");
CoreCommand.INSTANCE.register();
LoreCommand.INSTANCE.register();
NBTCommand.INSTANCE.register();
Bukkit.getPluginManager().registerEvents(PageListener.INSTANCE, this);
logger.info("已注册 PageListener");
simpleLogger.info("已注册 PageListener");
Bukkit.getPluginManager().registerEvents(CallbackListener.INSTANCE, this);
logger.info("已注册 CallbackListener");
simpleLogger.info("已注册 CallbackListener");
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;
sync(() -> {
PointAPI.reloadPlayerPointAPIHook();
VaultAPI.reloadVaultHook();
async(this::checkUpdate);
});
logger.info("仓鼠核心启动完成,总计耗时 " + time + " ms");
simpleLogger.info("仓鼠核心启动完成,总计耗时 " + time + " ms");
}
@Override
public void onDisable() {
long start = System.currentTimeMillis();
Logger logger = getLogger();
CoreAPI.getInstance().getJedisPool().close();
logger.info("已关闭 Redis 连接池");
CoreAPI.getInstance().getHikariDataSource().close();
logger.info("已关闭数据库连接池");
if (CoreAPI.getInstance().isEnableRedis()) {
CoreAPI.getInstance().getJedisPool().close();
simpleLogger.info("已关闭 Redis 连接池");
}
if (CoreAPI.getInstance().isEnableDatabase()) {
((HikariDataSource) CoreAPI.getInstance().getDataSource()).close();
simpleLogger.info("已关闭数据库连接池");
}
CoreAPI.getInstance().getExecutorService().shutdownNow();
logger.info("已关闭 ExecutorService 线程池");
simpleLogger.info("已关闭 ExecutorService 线程池");
CoreAPI.getInstance().getScheduledService().shutdownNow();
logger.info("已关闭 ScheduledExecutorService 线程池");
simpleLogger.info("已关闭 ScheduledExecutorService 线程池");
for (Player player : Bukkit.getOnlinePlayers()) {
InventoryView view = player.getOpenInventory();
Inventory inventory = view.getTopInventory();
@@ -141,9 +150,9 @@ public class HamsterCorePlugin extends JavaPlugin {
}
player.closeInventory();
}
logger.info("已关闭所有玩家的界面");
simpleLogger.info("已关闭所有玩家的界面");
long time = System.currentTimeMillis() - start;
logger.info("仓鼠核心已关闭,总计耗时 " + time + " ms");
simpleLogger.info("仓鼠核心已关闭,总计耗时 " + time + " ms");
}
private void checkUpdate() {

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.impl.ItemStackAdapter;
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.common.api.CoreAPI;
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.IOException;
import java.util.logging.Logger;
@SuppressWarnings("unused")
public final class CoreBukkitAPI extends CoreAPI {
@@ -62,8 +62,8 @@ public final class CoreBukkitAPI extends CoreAPI {
}
@Override
public @NotNull Logger getLogger() {
return HamsterCorePlugin.getInstance().getLogger();
public @NotNull BukkitSimpleLogger getLogger() {
return HamsterCorePlugin.getSimpleLogger();
}
@Override

View File

@@ -11,6 +11,10 @@ public abstract class ChildCommand implements TabExecutor {
@NotNull
public abstract String getUsage();
public int getArgumentCount() {
return 0;
}
@NotNull
public abstract String getDescription();

View File

@@ -14,10 +14,10 @@ import java.util.stream.Collectors;
@SuppressWarnings("unused")
public abstract class ParentCommand extends ChildCommand {
@NotNull
private final List<ChildCommand> childCommands;
private final Map<String, ChildCommand> childCommands;
public ParentCommand() {
childCommands = new ArrayList<>();
childCommands = new LinkedHashMap<>();
}
@NotNull
@@ -33,8 +33,8 @@ public abstract class ParentCommand extends ChildCommand {
}
@NotNull
public List<ChildCommand> getChildCommands() {
return childCommands;
public Collection<ChildCommand> getChildCommands() {
return childCommands.values();
}
@NotNull
@@ -78,7 +78,7 @@ public abstract class ParentCommand extends ChildCommand {
@NotNull
public List<ChildCommand> getEndChildCommands() {
ArrayList<ChildCommand> list = new ArrayList<>();
for (ChildCommand command : childCommands) {
for (ChildCommand command : getChildCommands()) {
if (command instanceof ParentCommand) {
list.addAll(((ParentCommand) command).getEndChildCommands());
} else {
@@ -89,13 +89,16 @@ public abstract class ParentCommand extends ChildCommand {
}
public void addChildCommand(@NotNull ChildCommand command) {
childCommands.add(command);
if (childCommands.containsKey(command.getName())) {
throw new IllegalArgumentException("command " + command.getName() + " already exists!");
}
childCommands.put(command.getName(), command);
}
@NotNull
public Map<String, String> getCommandHelp(CommandSender sender) {
HashMap<String, String> map = new HashMap<>();
for (ChildCommand child : childCommands) {
Map<String, String> map = new LinkedHashMap<>();
for (ChildCommand child : getChildCommands()) {
if (!child.hasPermission(sender)) {
continue;
}
@@ -111,14 +114,12 @@ public abstract class ParentCommand extends ChildCommand {
public void sendHelp(@NotNull CommandSender sender) {
sender.sendMessage("§e==================== [ " + getName() + " 使用帮助] ====================");
Map<String, String> helpMap = getCommandHelp(sender);
int maxLength = helpMap.keySet().stream()
Map<String, String> map = getCommandHelp(sender);
int maxLength = map.keySet().stream()
.map(String::length)
.max(Integer::compareTo)
.orElse(-1);
ArrayList<Map.Entry<String, String>> list = new ArrayList<>(helpMap.entrySet());
list.sort(Map.Entry.comparingByKey());
for (Map.Entry<String, String> entry : list) {
for (Map.Entry<String, String> entry : map.entrySet()) {
sender.sendMessage(String.format("§a%-" + maxLength + "s - %s", entry.getKey(), entry.getValue()));
}
}
@@ -133,7 +134,7 @@ public abstract class ParentCommand extends ChildCommand {
sendHelp(sender);
return true;
}
for (ChildCommand childCommand : childCommands) {
for (ChildCommand childCommand : getChildCommands()) {
if (!childCommand.getName().equalsIgnoreCase(args[0])) {
continue;
}
@@ -141,6 +142,10 @@ public abstract class ParentCommand extends ChildCommand {
CoreMessage.COMMAND_NOT_HAS_PERMISSION.show(sender);
return true;
}
if (args.length - 1 < getArgumentCount()) {
sender.sendMessage(getUsage() + " " + childCommand.getUsage());
return true;
}
return childCommand.onCommand(sender, command, label, Arrays.copyOfRange(args, 1, args.length));
}
CoreMessage.COMMAND_NOT_FOUND.show(sender);
@@ -150,18 +155,18 @@ public abstract class ParentCommand extends ChildCommand {
@Override
public List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, String[] args) {
if (args.length == 0) {
return childCommands.stream()
return getChildCommands().stream()
.filter(o -> o.hasPermission(sender))
.map(ChildCommand::getName)
.collect(Collectors.toList());
}
for (ChildCommand child : childCommands) {
for (ChildCommand child : getChildCommands()) {
if (args[0].equalsIgnoreCase(child.getName())) {
return child.onTabComplete(sender, command, alias, Arrays.copyOfRange(args, 1, args.length));
}
}
args[0] = args[0].toLowerCase();
return childCommands.stream()
return getChildCommands().stream()
.filter(o -> o.hasPermission(sender))
.map(ChildCommand::getName)
.filter(o -> o.toLowerCase().startsWith(args[0].toLowerCase()))

View File

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

View File

@@ -5,6 +5,9 @@ import cn.hamster3.mc.plugin.core.bukkit.command.ParentCommand;
import cn.hamster3.mc.plugin.core.bukkit.constant.CoreMessage;
import cn.hamster3.mc.plugin.core.bukkit.util.CoreBukkitUtils;
import de.tr7zw.changeme.nbtapi.*;
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT;
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBTCompoundList;
import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBTList;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
@@ -62,13 +65,12 @@ public class NBTCommand extends ParentCommand {
sender.sendMessage("§c你的手持物品为空");
return true;
}
NBTItem nbtItem = new NBTItem(stack);
sendNBTCompound(nbtItem, sender, 0);
ReadWriteNBT readWriteNBT = NBT.itemStackToNBT(stack);
sendNBTCompound(readWriteNBT, sender, 0);
return true;
}
@SuppressWarnings("ForLoopReplaceableByForEach")
private void sendNBTCompound(NBTCompound compound, CommandSender sender, int level) {
private void sendNBTCompound(ReadWriteNBT compound, CommandSender sender, int level) {
StringBuilder prefixBuilder = new StringBuilder();
for (int i = 0; i < level; i++) {
prefixBuilder.append(" ");
@@ -78,31 +80,31 @@ public class NBTCommand extends ParentCommand {
NBTType type = compound.getType(key);
switch (type) {
case NBTTagByte: {
sender.sendMessage(String.format("%s- %s:%d §e§l[%s]", space, key, compound.getByte(key), NBT_TYPE_NAME.get(type)));
sender.sendMessage(String.format("%s- %s=%d §e§l[%s]", space, key, compound.getByte(key), NBT_TYPE_NAME.get(type)));
break;
}
case NBTTagShort: {
sender.sendMessage(String.format("%s- %s:%d §e§l[%s]", space, key, compound.getShort(key), NBT_TYPE_NAME.get(type)));
sender.sendMessage(String.format("%s- %s=%d §e§l[%s]", space, key, compound.getShort(key), NBT_TYPE_NAME.get(type)));
break;
}
case NBTTagInt: {
sender.sendMessage(String.format("%s- %s:%d §e§l[%s]", space, key, compound.getInteger(key), NBT_TYPE_NAME.get(type)));
sender.sendMessage(String.format("%s- %s=%d §e§l[%s]", space, key, compound.getInteger(key), NBT_TYPE_NAME.get(type)));
break;
}
case NBTTagLong: {
sender.sendMessage(String.format("%s- %s:%d §e§l[%s]", space, key, compound.getLong(key), NBT_TYPE_NAME.get(type)));
sender.sendMessage(String.format("%s- %s=%d §e§l[%s]", space, key, compound.getLong(key), NBT_TYPE_NAME.get(type)));
break;
}
case NBTTagFloat: {
sender.sendMessage(String.format("%s- %s:%f §e§l[%s]", space, key, compound.getFloat(key), NBT_TYPE_NAME.get(type)));
sender.sendMessage(String.format("%s- %s=%f §e§l[%s]", space, key, compound.getFloat(key), NBT_TYPE_NAME.get(type)));
break;
}
case NBTTagDouble: {
sender.sendMessage(String.format("%s- %s:%f §e§l[%s]", space, key, compound.getDouble(key), NBT_TYPE_NAME.get(type)));
sender.sendMessage(String.format("%s- %s=%f §e§l[%s]", space, key, compound.getDouble(key), NBT_TYPE_NAME.get(type)));
break;
}
case NBTTagString: {
sender.sendMessage(String.format("%s- %s:%s §e§l[%s]", space, key, compound.getString(key), NBT_TYPE_NAME.get(type)));
sender.sendMessage(String.format("%s- %s=%s §e§l[%s]", space, key, compound.getString(key), NBT_TYPE_NAME.get(type)));
break;
}
case NBTTagCompound: {
@@ -112,20 +114,20 @@ public class NBTCommand extends ParentCommand {
}
case NBTTagByteArray: {
byte[] array = compound.getByteArray(key);
sender.sendMessage(String.format("%s- %s:%s §e§l[%s]", space, key, Arrays.toString(array), NBT_TYPE_NAME.get(type)));
sender.sendMessage(String.format("%s- %s=%s §e§l[%s]", space, key, Arrays.toString(array), NBT_TYPE_NAME.get(type)));
break;
}
case NBTTagIntArray: {
int[] array = compound.getIntArray(key);
sender.sendMessage(String.format("%s- %s:%s §e§l[%s]", space, key, Arrays.toString(array), NBT_TYPE_NAME.get(type)));
sender.sendMessage(String.format("%s- %s=%s §e§l[%s]", space, key, Arrays.toString(array), NBT_TYPE_NAME.get(type)));
if (array != null && array.length == 4) {
sender.sendMessage(String.format("%s- %s:%s §e§l[uuid]", space, key, compound.getUUID(key)));
sender.sendMessage(String.format("%s- %s=%s §e§l[uuid]", space, key, compound.getUUID(key)));
}
break;
}
case NBTTagLongArray: {
long[] array = compound.getLongArray(key);
sender.sendMessage(String.format("%s- %s:%s §e§l[%s]", space, key, Arrays.toString(array), NBT_TYPE_NAME.get(type)));
sender.sendMessage(String.format("%s- %s=%s §e§l[%s]", space, key, Arrays.toString(array), NBT_TYPE_NAME.get(type)));
break;
}
case NBTTagList: {
@@ -137,7 +139,7 @@ public class NBTCommand extends ParentCommand {
String listSpace = space + " ";
switch (listType) {
case NBTTagString: {
NBTList<String> list = compound.getStringList(key);
ReadWriteNBTList<String> list = compound.getStringList(key);
for (String string : list) {
sender.sendMessage(String.format("%s- %s", listSpace, string));
}
@@ -145,7 +147,7 @@ public class NBTCommand extends ParentCommand {
}
case NBTTagIntArray: {
boolean all4Length = true;
NBTList<int[]> list = compound.getIntArrayList(key);
ReadWriteNBTList<int[]> list = compound.getIntArrayList(key);
for (int[] intArray : list) {
if (intArray.length != 4) {
all4Length = false;
@@ -153,7 +155,7 @@ public class NBTCommand extends ParentCommand {
}
}
if (all4Length) {
NBTList<UUID> uuidList = compound.getUUIDList(key);
ReadWriteNBTList<UUID> uuidList = compound.getUUIDList(key);
for (int i = 0; i < list.size(); i++) {
int[] intArray = list.get(i);
UUID uuid = uuidList.get(i);
@@ -169,37 +171,37 @@ public class NBTCommand extends ParentCommand {
break;
}
case NBTTagInt: {
NBTList<Integer> list = compound.getIntegerList(key);
ReadWriteNBTList<Integer> list = compound.getIntegerList(key);
for (Integer value : list) {
sender.sendMessage(String.format("%s- %d", listSpace, value));
}
break;
}
case NBTTagLong: {
NBTList<Long> list = compound.getLongList(key);
ReadWriteNBTList<Long> list = compound.getLongList(key);
for (Long value : list) {
sender.sendMessage(String.format("%s- %d", listSpace, value));
}
break;
}
case NBTTagFloat: {
NBTList<Float> list = compound.getFloatList(key);
ReadWriteNBTList<Float> list = compound.getFloatList(key);
for (Float value : list) {
sender.sendMessage(String.format("%s- %f", listSpace, value));
}
break;
}
case NBTTagDouble: {
NBTList<Double> list = compound.getDoubleList(key);
ReadWriteNBTList<Double> list = compound.getDoubleList(key);
for (Double value : list) {
sender.sendMessage(String.format("%s- %f", listSpace, value));
}
break;
}
case NBTTagCompound: {
NBTCompoundList compoundList = compound.getCompoundList(key);
ReadWriteNBTCompoundList compoundList = compound.getCompoundList(key);
for (int i = 0; i < compoundList.size(); i++) {
NBTListCompound listCompound = compoundList.get(i);
ReadWriteNBT listCompound = compoundList.get(i);
sendNBTCompound(listCompound, sender, level + 1);
}
}

View File

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

View File

@@ -20,7 +20,7 @@ public class EconomyAPI {
* @return true代表安装了false代表未安装
*/
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;
import cn.hamster3.mc.plugin.core.bukkit.HamsterCorePlugin;
import lombok.Getter;
import org.black_ixx.playerpoints.PlayerPoints;
import org.black_ixx.playerpoints.PlayerPointsAPI;
import org.bukkit.Bukkit;
@@ -14,6 +15,7 @@ import java.util.UUID;
*/
@SuppressWarnings("unused")
public class PointAPI {
@Getter
private static PlayerPointsAPI playerPointsAPI;
private PointAPI() {
@@ -25,20 +27,10 @@ public class PointAPI {
public static void reloadPlayerPointAPIHook() {
Plugin plugin = Bukkit.getPluginManager().getPlugin("PlayerPoints");
if (plugin == null) {
HamsterCorePlugin.getInstance().getLogger().warning("未检测到 PlayerPointAPI 插件");
return;
}
playerPointsAPI = ((PlayerPoints) plugin).getAPI();
HamsterCorePlugin.getInstance().getLogger().info("PlayerPointAPI 挂接成功");
}
/**
* 获取 PlayerPointsAPI 实例
*
* @return PlayerPointsAPI 实例
*/
public static PlayerPointsAPI getPlayerPointsAPI() {
return playerPointsAPI;
HamsterCorePlugin.getSimpleLogger().info("已挂接 PlayerPointAPI");
}
/**

View File

@@ -1,22 +1,27 @@
package cn.hamster3.mc.plugin.core.bukkit.hook;
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.economy.Economy;
import net.milkbowl.vault.permission.Permission;
import org.bukkit.Bukkit;
import org.bukkit.plugin.RegisteredServiceProvider;
import java.util.logging.Logger;
/**
* Vault API
*/
@Getter
@SuppressWarnings("unused")
public class VaultAPI {
@Getter
private static boolean vaultEnabled;
@Getter
private static Chat chat;
@Getter
private static Economy economy;
@Getter
private static Permission permission;
private VaultAPI() {
@@ -27,74 +32,27 @@ public class VaultAPI {
economy = null;
permission = null;
vaultEnabled = Bukkit.getPluginManager().isPluginEnabled("Vault");
Logger logger = HamsterCorePlugin.getInstance().getLogger();
BukkitSimpleLogger logger = HamsterCorePlugin.getSimpleLogger();
if (!vaultEnabled) {
logger.warning("未检测到 Vault 插件");
return;
}
logger.info("已连接 Vault");
logger.info("检测到服务器已安装 Vault 插件");
RegisteredServiceProvider<Chat> chatProvider = Bukkit.getServer().getServicesManager().getRegistration(Chat.class);
if (chatProvider != null) {
chat = chatProvider.getProvider();
logger.info("聊天系统挂接成功");
} else {
logger.warning("未检测到聊天系统");
}
RegisteredServiceProvider<Economy> economyProvider = Bukkit.getServer().getServicesManager().getRegistration(Economy.class);
if (economyProvider != null) {
economy = economyProvider.getProvider();
logger.info("经济系统挂接成功");
} else {
logger.warning("未检测到经济系统");
}
RegisteredServiceProvider<Permission> permissionProvider = Bukkit.getServer().getServicesManager().getRegistration(Permission.class);
if (permissionProvider != null) {
permission = permissionProvider.getProvider();
logger.info("权限系统挂接成功");
} else {
logger.warning("未检测到权限插件");
}
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
public String toString() {
return "ButtonGroup{" +
"name='" + name + '\'' +
", buttonNameMap=" + buttonNameMap +
'}';
"name='" + name + '\'' +
", buttonNameMap=" + buttonNameMap +
'}';
}
}

View File

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

View File

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

@@ -3,10 +3,10 @@ package cn.hamster3.mc.plugin.core.bukkit.util;
import cn.hamster3.mc.plugin.core.bukkit.listener.CallbackListener;
import cn.hamster3.mc.plugin.core.common.data.DisplayMessage;
import com.google.gson.JsonObject;
import de.tr7zw.changeme.nbtapi.NBTContainer;
import de.tr7zw.changeme.nbtapi.NBTItem;
import de.tr7zw.changeme.nbtapi.NBT;
import me.clip.placeholderapi.PlaceholderAPI;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import net.kyori.adventure.title.Title;
import org.bukkit.Bukkit;
@@ -241,23 +241,45 @@ public final class CoreBukkitUtils {
@NotNull
public static DisplayMessage loadDisplayMessage(@NotNull ConfigurationSection config) {
DisplayMessage displayMessage = new DisplayMessage();
String message = config.getString("message");
if (message != null) {
displayMessage.setMessage(LegacyComponentSerializer.legacySection().deserialize(message));
String miniMessage = config.getString("mini-message");
if (miniMessage != null) {
displayMessage.setMessage(MiniMessage.miniMessage().deserialize(miniMessage));
} else {
String message = config.getString("message");
if (message != null) {
displayMessage.setMessage(LegacyComponentSerializer.legacySection().deserialize(message));
}
}
String actionbar = config.getString("actionbar");
if (actionbar != null) {
displayMessage.setActionbar(LegacyComponentSerializer.legacySection().deserialize(actionbar));
String miniActionbar = config.getString("mini-actionbar");
if (miniActionbar != null) {
displayMessage.setActionbar(MiniMessage.miniMessage().deserialize(miniActionbar));
} else {
String actionbar = config.getString("actionbar");
if (actionbar != null) {
displayMessage.setActionbar(LegacyComponentSerializer.legacySection().deserialize(actionbar));
}
}
String title = config.getString("title");
String subtitle = config.getString("subtitle");
if (title != null || subtitle != null) {
String miniTitle = config.getString("mini-title");
String miniSubtitle = config.getString("mini-subtitle");
if (miniTitle != null || miniSubtitle != null) {
displayMessage.setTitle(
title, subtitle,
MiniMessage.miniMessage().deserialize(miniTitle == null ? "" : miniTitle),
MiniMessage.miniMessage().deserialize(miniSubtitle == null ? "" : miniSubtitle),
config.getInt("fade-in", 10),
config.getInt("stay", 70),
config.getInt("fade-out", 20)
);
} else {
String title = config.getString("title");
String subtitle = config.getString("subtitle");
if (title != null || subtitle != null) {
displayMessage.setTitle(
title, subtitle,
config.getInt("fade-in", 10),
config.getInt("stay", 70),
config.getInt("fade-out", 20)
);
}
}
String sound = config.getString("sound");
if (sound != null) {
@@ -330,7 +352,7 @@ public final class CoreBukkitUtils {
*/
@NotNull
public static String serializeItemStack(@NotNull ItemStack stack) {
return NBTItem.convertItemtoNBT(stack).toString();
return NBT.itemStackToNBT(stack).toString();
}
/**
@@ -341,7 +363,7 @@ public final class CoreBukkitUtils {
*/
@Nullable
public static ItemStack deserializeItemStack(@NotNull String string) {
return NBTItem.convertNBTtoItem(new NBTContainer(string));
return NBT.itemStackFromNBT(NBT.parseNBT(string));
}
/**

View File

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

View File

@@ -1,3 +1,6 @@
# 是否启用 redis 连接池功能
enable-redis: false
# redis 连接配置
# 完整格式如下:
# redis://用户名:密码@主机名:端口/数据库索引?参数名=参数值&参数名=参数值
@@ -8,15 +11,16 @@
# 若不设置数据库,则默认使用 0
redis-url: "redis://localhost:6379/0?clientName=HamsterCore&timeout=5s"
# 是否启用数据库连接池功能
enable-database: false
# 数据库连接池配置
datasource:
# 数据库链接驱动地址旧版服务端低于1.13请使用com.mysql.jdbc.Driver
driver: "com.mysql.cj.jdbc.Driver"
# MySQL数据库链接填写格式:
# jdbc:mysql://{数据库地址}:{数据库端口}/{使用的库名}?参数
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"
# 密码

View File

@@ -15,18 +15,12 @@ dependencies {
exclude(group = "org.jetbrains")
}
// https://mvnrepository.com/artifact/redis.clients/jedis
api("redis.clients:jedis:5.1.2") {
api("redis.clients:jedis:5.1.4") {
exclude(group = "com.google.code.gson")
exclude(group = "org.slf4j")
}
// https://mvnrepository.com/artifact/org.quartz-scheduler/quartz
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") }
// https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-stdlib-jdk8
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.23") { exclude(group = "org.jetbrains") }
}
tasks {

View File

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

View File

@@ -1,6 +1,7 @@
package cn.hamster3.mc.plugin.core.bungee.api;
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.config.ConfigSection;
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.IOException;
import java.util.logging.Logger;
@SuppressWarnings("unused")
public final class CoreBungeeAPI extends CoreAPI {
@@ -51,8 +51,8 @@ public final class CoreBungeeAPI extends CoreAPI {
}
@Override
public @NotNull Logger getLogger() {
return HamsterCorePlugin.getInstance().getLogger();
public @NotNull BungeeSimpleLogger getLogger() {
return HamsterCorePlugin.getSimpleLogger();
}
@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

@@ -1,3 +1,6 @@
# 是否启用 redis 连接池功能
enable-redis: false
# redis 连接配置
# 完整格式如下:
# redis://用户名:密码@主机名:端口/数据库索引?参数名=参数值&参数名=参数值
@@ -8,6 +11,10 @@
# 若不设置数据库,则默认使用 0
redis-url: "redis://localhost:6379/0?clientName=HamsterCore&timeout=5s"
# 是否启用数据库连接池功能
enable-database: false
# 数据库连接池配置
datasource:
# 数据库链接驱动地址
driver: "com.mysql.cj.jdbc.Driver"
@@ -22,8 +29,8 @@ datasource:
# 推荐值1~3
minimum-idle: 0
# 最大链接数
# 推荐值:不低于5
maximum-pool-size: 5
# 推荐值:不低于3
maximum-pool-size: 3
# 保持连接池可用的间隔
# 除非你的服务器数据库连接经常断开,否则不建议启用该选项
# 单位:毫秒

View File

@@ -18,13 +18,10 @@ dependencies {
}
// https://mvnrepository.com/artifact/redis.clients/jedis
compileOnlyApi("redis.clients:jedis:5.1.2") {
compileOnlyApi("redis.clients:jedis:5.1.4") {
exclude(group = "com.google.code.gson")
exclude(group = "org.slf4j")
}
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
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.thread.NamedThreadFactory;
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.zaxxer.hikari.HikariDataSource;
import lombok.Getter;
import net.kyori.adventure.platform.AudienceProvider;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import redis.clients.jedis.JedisPool;
import javax.sql.DataSource;
@@ -16,50 +18,62 @@ import java.sql.SQLException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.logging.Logger;
@Getter
@SuppressWarnings("unused")
public abstract class CoreAPI {
@Getter
protected static CoreAPI instance;
/**
* Redis 连接池
*/
@Getter
@NotNull
private final JedisPool jedisPool;
/**
* 公用数据库连接池
*/
@Getter
@NotNull
private final HikariDataSource hikariDataSource;
private final boolean enableRedis;
private final boolean enableDatabase;
/**
* 异步线程池
*/
@Getter
@NotNull
private final ExecutorService executorService;
/**
* 调度器线程池
*/
@Getter
@NotNull
private final ScheduledExecutorService scheduledService;
/**
* 公用 Redis 连接池
*/
@Nullable
private JedisPool jedisPool;
/**
* 公用数据库连接池
*/
@Nullable
private HikariDataSource hikariDataSource;
public CoreAPI(@NotNull ConfigSection config) {
SimpleLogger logger = getLogger();
executorService = Executors.newCachedThreadPool(new NamedThreadFactory("HamsterCore - Executor"));
scheduledService = Executors.newScheduledThreadPool(1, new NamedThreadFactory("HamsterCore - Scheduler"));
logger.info("已创建线程池");
getLogger().info("正在创建 Redis 连接池");
jedisPool = new JedisPool(config.getString("redis-url"));
getLogger().info("Redis 连接池创建完成");
ConfigSection datasourceConfig = config.getSection("datasource");
if (datasourceConfig == null) {
throw new IllegalArgumentException("配置文件中未找到 datasource 节点");
enableRedis = config.getBoolean("enable-redis", true);
if (enableRedis) {
logger.info("正在创建 Redis 连接池");
jedisPool = new JedisPool(config.getString("redis-url"));
logger.info("Redis 连接池创建完成");
} else {
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("数据库连接池创建完成");
}
/**
@@ -69,7 +83,10 @@ public abstract class CoreAPI {
*/
@NotNull
public DataSource getDataSource() {
return hikariDataSource;
if (hikariDataSource != null) {
return hikariDataSource;
}
throw new IllegalStateException("仓鼠核心未启用数据库功能");
}
/**
@@ -84,7 +101,15 @@ public abstract class CoreAPI {
}
@NotNull
public abstract Logger getLogger();
public JedisPool getJedisPool() {
if (jedisPool != null) {
return jedisPool;
}
throw new IllegalStateException("仓鼠核心未启用 Redis 功能");
}
@NotNull
public abstract SimpleLogger getLogger();
/**
* @return GSON 工具

View File

@@ -4,10 +4,9 @@ import lombok.Getter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;
@SuppressWarnings({"unused", "unchecked"})
@Getter
@@ -27,6 +26,15 @@ public class ConfigSection {
return map.containsKey(key);
}
public Set<String> getKeys() {
return map.keySet();
}
@Nullable
public Object get(@NotNull String key) {
return map.get(key);
}
@Nullable
public ConfigSection getSection(@NotNull String key) {
Object o = map.get(key);
@@ -65,16 +73,6 @@ public class ConfigSection {
return o.toString();
}
public List<String> getStringList(@NotNull String key) {
Object o = map.get(key);
if (o instanceof List) {
return (List<String>) o;
}
ArrayList<String> list = new ArrayList<>();
map.put(key, list);
return list;
}
public boolean getBoolean(@NotNull String key) {
String string = getString(key);
if (string == null) {
@@ -155,6 +153,45 @@ public class ConfigSection {
return Long.parseLong(string);
}
public <T> List<T> getList(@NotNull String key, Function<Object, T> function) {
ArrayList<T> list = new ArrayList<>();
Object object = map.get(key);
if (object instanceof List) {
List<?> mapList = (List<?>) object;
for (Object o : mapList) {
T result = function.apply(o);
list.add(result);
}
}
return list;
}
public List<String> getStringList(@NotNull String key) {
return getList(key, Object::toString);
}
public List<Integer> getIntegerList(@NotNull String key) {
return getList(key, o -> Integer.parseInt(o.toString()));
}
public <T> List<T> toList(BiFunction<ConfigSection, String, T> function) {
ArrayList<T> list = new ArrayList<>();
for (String key : getKeys()) {
T result = function.apply(this, key);
list.add(result);
}
return list;
}
public <T> Map<String, T> toMap(BiFunction<ConfigSection, String, T> function) {
HashMap<String, T> map = new HashMap<>();
for (String key : getKeys()) {
T result = function.apply(this, key);
map.put(key, result);
}
return map;
}
public void set(@NotNull String key, @Nullable Object value) {
if (value == null) {
map.remove(key);

View File

@@ -5,9 +5,12 @@ import org.yaml.snakeyaml.Yaml;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.function.Consumer;
@SuppressWarnings({"unused", "VulnerableCodeUsages"})
@SuppressWarnings("unused")
public class YamlConfig extends ConfigSection {
public static final Yaml YAML_LOADER = new Yaml();
@@ -19,11 +22,56 @@ public class YamlConfig extends ConfigSection {
super(map);
}
public static List<YamlConfig> loadFolder(@NotNull File file) throws IOException {
if (file.isDirectory()) {
ArrayList<YamlConfig> list = new ArrayList<>();
for (File subFile : Objects.requireNonNull(file.listFiles())) {
list.addAll(loadFolder(subFile));
}
return list;
}
if (file.isFile() && file.getName().endsWith(".yml")) {
return Collections.singletonList(load(file));
}
return Collections.emptyList();
}
public static List<YamlConfig> loadFolder(@NotNull File file, @NotNull Consumer<Exception> exceptionHandler) {
if (file.isDirectory()) {
ArrayList<YamlConfig> list = new ArrayList<>();
for (File subFile : Objects.requireNonNull(file.listFiles())) {
list.addAll(loadFolder(subFile, exceptionHandler));
}
return list;
}
if (file.isFile() && file.getName().endsWith(".yml")) {
try {
YamlConfig load = load(file);
return Collections.singletonList(load);
} catch (IOException e) {
exceptionHandler.accept(e);
}
}
return Collections.emptyList();
}
public static YamlConfig load(@NotNull Path path) {
try {
return load(Files.newInputStream(path));
} catch (IOException e) {
return new YamlConfig();
}
}
public static YamlConfig load(@NotNull File file) throws IOException {
try (FileInputStream stream = new FileInputStream(file)) {
try (InputStreamReader reader = new InputStreamReader(stream, StandardCharsets.UTF_8)) {
return load(reader);
}
return load(stream);
}
}
public static YamlConfig load(@NotNull InputStream stream) throws IOException {
try (InputStreamReader reader = new InputStreamReader(stream, StandardCharsets.UTF_8)) {
return load(reader);
}
}
@@ -32,11 +80,25 @@ public class YamlConfig extends ConfigSection {
return new YamlConfig(load);
}
public void save(@NotNull File file) throws IOException {
try (FileOutputStream stream = new FileOutputStream(file)) {
try (OutputStreamWriter writer = new OutputStreamWriter(stream, StandardCharsets.UTF_8)) {
YAML_LOADER.dump(map, writer);
}
public void save(@NotNull Path path) throws IOException {
try (OutputStream stream = Files.newOutputStream(path)) {
save(stream);
}
}
public void save(@NotNull File file) throws IOException {
try (FileOutputStream stream = new FileOutputStream(file)) {
save(stream);
}
}
public void save(@NotNull OutputStream stream) throws IOException {
try (OutputStreamWriter writer = new OutputStreamWriter(stream, StandardCharsets.UTF_8)) {
save(writer);
}
}
public void save(@NotNull Writer writer) {
YAML_LOADER.dump(map, writer);
}
}

View File

@@ -1,6 +1,7 @@
package cn.hamster3.mc.plugin.core.common.util;
import cn.hamster3.mc.plugin.core.common.config.ConfigSection;
import cn.hamster3.mc.plugin.core.common.config.YamlConfig;
import com.google.gson.JsonObject;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
@@ -8,42 +9,42 @@ import net.kyori.adventure.key.Key;
import net.kyori.adventure.sound.Sound;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.kyori.adventure.title.Title;
import net.kyori.adventure.translation.TranslationRegistry;
import org.jetbrains.annotations.NotNull;
import javax.sql.DataSource;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.text.MessageFormat;
import java.time.Duration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
@SuppressWarnings("unused")
public final class CoreUtils {
private CoreUtils() {
}
public static void zipCompressionFolder(@NotNull File folder, @NotNull File zipFile) throws IOException {
try (ZipOutputStream stream = new ZipOutputStream(Files.newOutputStream(zipFile.toPath()))) {
putFileToZipStream(stream, "", folder);
@NotNull
public static DataSource getDataSource(@NotNull ConfigSection datasourceConfig) {
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setDriverClassName(datasourceConfig.getString("driver"));
hikariConfig.setJdbcUrl(datasourceConfig.getString("url"));
hikariConfig.setUsername(datasourceConfig.getString("username"));
hikariConfig.setPassword(datasourceConfig.getString("password"));
hikariConfig.setMaximumPoolSize(datasourceConfig.getInt("maximum-pool-size", 3));
hikariConfig.setMinimumIdle(datasourceConfig.getInt("minimum-idle", 1));
long keepAliveTime = datasourceConfig.getLong("keep-alive-time", 0);
if (keepAliveTime > 5000) {
hikariConfig.setKeepaliveTime(keepAliveTime);
}
}
public static void putFileToZipStream(@NotNull ZipOutputStream stream, @NotNull String path, @NotNull File file) throws IOException {
if (file.isDirectory()) {
File[] files = file.listFiles();
if (files == null) {
throw new IOException();
}
for (File subFile : files) {
putFileToZipStream(stream, path + file.getName() + "/", subFile);
}
return;
}
ZipEntry entry = new ZipEntry(path + file.getName());
stream.putNextEntry(entry);
stream.write(Files.readAllBytes(file.toPath()));
stream.closeEntry();
hikariConfig.setIdleTimeout(datasourceConfig.getLong("idle-timeout", 10 * 60 * 1000));
hikariConfig.setMaxLifetime(datasourceConfig.getLong("max-lifetime", 30 * 60 * 1000));
hikariConfig.setValidationTimeout(datasourceConfig.getLong("validation-timeout", 5000));
hikariConfig.setPoolName(datasourceConfig.getString("name", "HamsterCore-Pool"));
return new HikariDataSource(hikariConfig);
}
/**
@@ -125,24 +126,30 @@ public final class CoreUtils {
);
}
@NotNull
public static DataSource getDataSource(@NotNull ConfigSection datasourceConfig) {
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setDriverClassName(datasourceConfig.getString("driver"));
hikariConfig.setJdbcUrl(datasourceConfig.getString("url"));
hikariConfig.setUsername(datasourceConfig.getString("username"));
hikariConfig.setPassword(datasourceConfig.getString("password"));
hikariConfig.setMaximumPoolSize(datasourceConfig.getInt("maximum-pool-size", 3));
hikariConfig.setMinimumIdle(datasourceConfig.getInt("minimum-idle", 1));
long keepAliveTime = datasourceConfig.getLong("keep-alive-time", 0);
if (keepAliveTime > 5000) {
hikariConfig.setKeepaliveTime(keepAliveTime);
public static void loadTranslation(@NotNull TranslationRegistry registry, @NotNull File folder) throws IOException {
for (File subFile : Objects.requireNonNull(folder.listFiles())) {
String filename = subFile.getName().split("\\.")[0];
String[] args = filename.split("_");
Locale locale = new Locale.Builder().setLanguage(args[0]).setRegion(args[1]).build();
YamlConfig config = YamlConfig.load(subFile);
Map<String, MessageFormat> map = loadTranslation(locale, config, "");
registry.registerAll(locale, map);
}
hikariConfig.setIdleTimeout(datasourceConfig.getLong("idle-timeout", 10 * 60 * 1000));
hikariConfig.setMaxLifetime(datasourceConfig.getLong("max-lifetime", 30 * 60 * 1000));
hikariConfig.setValidationTimeout(datasourceConfig.getLong("validation-timeout", 5000));
hikariConfig.setPoolName(datasourceConfig.getString("name", "HamsterCore-Pool"));
return new HikariDataSource(hikariConfig);
}
public static Map<String, MessageFormat> loadTranslation(@NotNull Locale locale, @NotNull ConfigSection config, @NotNull String prefix) {
HashMap<String, MessageFormat> result = new HashMap<>();
for (Map.Entry<String, Object> entry : config.getMap().entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();
if (value instanceof String) {
String string = (String) value;
result.put(prefix + key, new MessageFormat(string, locale));
} else if (value instanceof Map) {
loadTranslation(locale, config.getSectionOrCreate(key), "$prefix$key.");
}
}
return result;
}
/**

View File

@@ -0,0 +1,94 @@
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, "输出日志 " + msg + Arrays.toString(args) + " 时遇到一个异常", e);
}
}
public void log(@NotNull Level level, @NotNull Throwable throwable, @NotNull String msg, @NotNull Object... args) {
try {
log(level, throwable, String.format(msg, args));
} catch (Exception e) {
log(Level.WARNING, "输出日志 " + msg + Arrays.toString(args) + " 时遇到一个异常", e);
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

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

@@ -8,23 +8,18 @@ dependencies {
annotationProcessor("com.velocitypowered:velocity-api:3.3.0-SNAPSHOT")
compileOnlyApi("net.kyori:adventure-platform-api:4.3.2") { isTransitive = false }
// https://mvnrepository.com/artifact/redis.clients/jedis
api("redis.clients:jedis:5.1.2") {
api("redis.clients:jedis:5.1.4") {
exclude(group = "com.google.code.gson")
exclude(group = "org.slf4j")
}
// https://mvnrepository.com/artifact/org.quartz-scheduler/quartz
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
implementation("com.zaxxer:HikariCP:5.1.0") { isTransitive = false }
// https://mvnrepository.com/artifact/com.mysql/mysql-connector-j
runtimeOnly("com.mysql:mysql-connector-j:8.3.0")
// https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-stdlib
runtimeOnly("org.jetbrains.kotlin:kotlin-stdlib:1.9.23") { exclude(group = "org.jetbrains") }
}
sourceSets.create("templates") {

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.util.UpdateCheckUtils;
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.velocitypowered.api.event.PostOrder;
import com.velocitypowered.api.event.Subscribe;
@@ -36,25 +37,22 @@ public class HamsterCorePlugin {
@Getter
private static HamsterCorePlugin instance;
@Getter
private final java.util.logging.Logger logger;
@Getter
private final Logger slf4jLogger;
private final VelocitySimpleLogger simpleLogger;
@Getter
private final ProxyServer proxyServer;
@Getter
private final File dataFolder;
@Inject
public HamsterCorePlugin(Logger slf4jLogger, ProxyServer proxyServer, @DataDirectory Path dataPath) {
logger = java.util.logging.Logger.getLogger("hamster-core");
this.slf4jLogger = slf4jLogger;
public HamsterCorePlugin(Logger logger, ProxyServer proxyServer, @DataDirectory Path dataPath) {
this.simpleLogger = new VelocitySimpleLogger(logger);
this.proxyServer = proxyServer;
dataFolder = dataPath.toFile();
instance = this;
long start = System.currentTimeMillis();
try {
if (dataFolder.mkdir()) {
slf4jLogger.info("已生成插件存档文件夹");
simpleLogger.info("已生成插件存档文件夹");
}
File configFile = new File(dataFolder, "config.yml");
if (!configFile.exists()) {
@@ -65,38 +63,40 @@ public class HamsterCorePlugin {
}
}
CoreVelocityAPI.init(configFile);
slf4jLogger.info("已初始化 CoreAPI");
simpleLogger.info("已初始化 CoreAPI");
} catch (Exception e) {
slf4jLogger.error("初始化 CoreAPI 出错", e);
simpleLogger.error(e, "初始化 CoreAPI 出错");
}
long time = System.currentTimeMillis() - start;
slf4jLogger.info("仓鼠核心初始化完成,总计耗时 " + time + " ms");
simpleLogger.info("仓鼠核心初始化完成,总计耗时 " + time + " ms");
}
@Subscribe(order = PostOrder.FIRST)
public void onProxyInitialization(ProxyInitializeEvent event) {
long start = System.currentTimeMillis();
slf4jLogger.info("仓鼠核心正在启动");
simpleLogger.info("仓鼠核心正在启动");
CoreAPI.getInstance().getExecutorService().submit(this::checkUpdate);
long time = System.currentTimeMillis() - start;
slf4jLogger.info("仓鼠核心启动完成,总计耗时 " + time + " ms");
simpleLogger.info("仓鼠核心启动完成,总计耗时 " + time + " ms");
}
@Subscribe(order = PostOrder.LAST)
public void onProxyShutdown(ProxyShutdownEvent event) {
long start = System.currentTimeMillis();
CoreAPI.getInstance().getJedisPool().close();
slf4jLogger.info("已关闭 Redis 连接池");
if (CoreAPI.getInstance().getDataSource() instanceof HikariDataSource dataSource) {
dataSource.close();
slf4jLogger.info("已关闭数据库连接池");
if (CoreAPI.getInstance().isEnableRedis()) {
CoreAPI.getInstance().getJedisPool().close();
simpleLogger.info("已关闭 Redis 连接池");
}
if (CoreAPI.getInstance().isEnableDatabase()) {
((HikariDataSource) CoreAPI.getInstance().getDataSource()).close();
simpleLogger.info("已关闭数据库连接池");
}
CoreAPI.getInstance().getExecutorService().shutdownNow();
slf4jLogger.info("已关闭 ExecutorService 线程池");
simpleLogger.info("已关闭 ExecutorService 线程池");
CoreAPI.getInstance().getScheduledService().shutdownNow();
slf4jLogger.info("已关闭 ScheduledExecutorService 线程池");
simpleLogger.info("已关闭 ScheduledExecutorService 线程池");
long time = System.currentTimeMillis() - start;
slf4jLogger.info("仓鼠核心关闭完成,总计耗时 " + time + " ms");
simpleLogger.info("仓鼠核心关闭,总计耗时 " + time + " ms");
}
private void checkUpdate() {

View File

@@ -8,6 +8,7 @@ import cn.hamster3.mc.plugin.core.common.impl.ComponentTypeAdapter;
import cn.hamster3.mc.plugin.core.common.impl.MessageTypeAdapter;
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.GsonBuilder;
import net.kyori.adventure.platform.AudienceProvider;
@@ -16,7 +17,6 @@ import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.IOException;
import java.util.logging.Logger;
@SuppressWarnings("unused")
public final class CoreVelocityAPI extends CoreAPI {
@@ -57,8 +57,8 @@ public final class CoreVelocityAPI extends CoreAPI {
}
@Override
public @NotNull Logger getLogger() {
return HamsterCorePlugin.getInstance().getLogger();
public @NotNull VelocitySimpleLogger getLogger() {
return HamsterCorePlugin.getInstance().getSimpleLogger();
}
@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://用户名:密码@主机名:端口/数据库索引?参数名=参数值&参数名=参数值
@@ -8,6 +11,10 @@
# 若不设置数据库,则默认使用 0
redis-url: "redis://localhost:6379/0?clientName=HamsterCore&timeout=5s"
# 是否启用数据库连接池功能
enable-database: false
# 数据库连接池配置
datasource:
# 数据库链接驱动地址
driver: "com.mysql.cj.jdbc.Driver"
@@ -22,8 +29,8 @@ datasource:
# 推荐值1~3
minimum-idle: 0
# 最大链接数
# 推荐值:不低于5
maximum-pool-size: 5
# 推荐值:不低于3
maximum-pool-size: 3
# 保持连接池可用的间隔
# 除非你的服务器数据库连接经常断开,否则不建议启用该选项
# 单位:毫秒

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
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
zipStorePath=wrapper/dists