feat: 初版完成

初版完成
This commit is contained in:
2022-10-23 05:41:01 +08:00
parent b0e4920e63
commit bcd0410fe1
37 changed files with 1453 additions and 174 deletions

View File

@@ -3,7 +3,7 @@ plugins {
} }
group 'cn.hamster3.mc.plugin' group 'cn.hamster3.mc.plugin'
version '0.0.1-SNAPSHOT' version '1.0.0'
subprojects { subprojects {
apply plugin: 'java-library' apply plugin: 'java-library'
@@ -19,12 +19,14 @@ subprojects {
} }
configurations { configurations {
shade
api.extendsFrom apiShade api.extendsFrom apiShade
implementation.extendsFrom implementationShade implementation.extendsFrom implementationShade
} }
dependencies { dependencies {
compileOnly group: 'org.jetbrains', name: 'annotations', version: '21.0.1' // https://mvnrepository.com/artifact/org.jetbrains/annotations
compileOnly 'org.jetbrains:annotations:23.0.0'
} }
tasks.withType(JavaCompile) { tasks.withType(JavaCompile) {

View File

@@ -8,20 +8,18 @@ dependencies {
} }
compileOnly 'org.spigotmc:spigot-api:1.12.2-R0.1-SNAPSHOT' compileOnly 'org.spigotmc:spigot-api:1.12.2-R0.1-SNAPSHOT'
compileOnly 'net.milkbowl.vault:VaultAPI:1.7'
compileOnly 'org.black_ixx:playerpoints:2.1.3'
// https://mvnrepository.com/artifact/com.zaxxer/HikariCP // https://mvnrepository.com/artifact/com.zaxxer/HikariCP
//noinspection GradlePackageUpdate //noinspection GradlePackageUpdate
apiShade 'com.zaxxer:HikariCP:4.0.3' apiShade 'com.zaxxer:HikariCP:4.0.3'
// https://mvnrepository.com/artifact/mysql/mysql-connector-java
shade 'mysql:mysql-connector-java:8.0.31'
// https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp // https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp
apiShade 'com.squareup.okhttp3:okhttp:4.10.0' apiShade 'com.squareup.okhttp3:okhttp:4.10.0'
// https://mvnrepository.com/artifact/net.kyori/adventure-platform-bukkit // https://mvnrepository.com/artifact/net.kyori/adventure-platform-bukkit
apiShade 'net.kyori:adventure-platform-bukkit:4.1.2' apiShade 'net.kyori:adventure-platform-bukkit:4.1.2'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.0'
}
test {
useJUnitPlatform()
} }
processResources { processResources {
@@ -38,11 +36,14 @@ tasks.create("shadowJar", Jar) {
tasks.jar.outputs.files.collect { tasks.jar.outputs.files.collect {
it.isDirectory() ? it : zipTree(it) it.isDirectory() ? it : zipTree(it)
}, },
configurations.implementationShade.collect { configurations.shade.collect {
it.isDirectory() ? it : zipTree(it) it.isDirectory() ? it : zipTree(it)
}, },
configurations.apiShade.collect { configurations.apiShade.collect {
it.isDirectory() ? it : zipTree(it) it.isDirectory() ? it : zipTree(it)
},
configurations.implementationShade.collect {
it.isDirectory() ? it : zipTree(it)
} }
]) ])
destinationDir(getRootProject().buildDir) destinationDir(getRootProject().buildDir)

View File

@@ -1,11 +1,25 @@
package cn.hamster3.mc.plugin.core.bukkit; package cn.hamster3.mc.plugin.core.bukkit;
import cn.hamster3.mc.plugin.core.bukkit.api.CoreBukkitAPI;
import cn.hamster3.mc.plugin.core.bukkit.command.ParentCommand;
import cn.hamster3.mc.plugin.core.bukkit.command.debug.BlockInfoCommand;
import cn.hamster3.mc.plugin.core.bukkit.command.debug.YamlCommand;
import cn.hamster3.mc.plugin.core.bukkit.hook.PointAPI;
import cn.hamster3.mc.plugin.core.bukkit.hook.VaultAPI;
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.listener.PageListener; import cn.hamster3.mc.plugin.core.bukkit.page.listener.PageListener;
import cn.hamster3.mc.plugin.core.common.constant.CoreConstantObjects; import cn.hamster3.mc.plugin.core.common.constant.CoreConstantObjects;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.plugin.java.JavaPlugin;
import java.util.List;
import java.util.logging.Logger;
public class HamsterCorePlugin extends JavaPlugin { public class HamsterCorePlugin extends JavaPlugin {
private static final ParentCommand COMMAND_EXECUTOR = new ParentCommand("core");
private static HamsterCorePlugin instance; private static HamsterCorePlugin instance;
public static HamsterCorePlugin getInstance() { public static HamsterCorePlugin getInstance() {
@@ -16,10 +30,6 @@ public class HamsterCorePlugin extends JavaPlugin {
Bukkit.getScheduler().runTask(instance, runnable); Bukkit.getScheduler().runTask(instance, runnable);
} }
public static void async(Runnable runnable) {
Bukkit.getScheduler().runTaskAsynchronously(instance, runnable);
}
@Override @Override
public void onLoad() { public void onLoad() {
instance = this; instance = this;
@@ -27,23 +37,49 @@ public class HamsterCorePlugin extends JavaPlugin {
@Override @Override
public void onEnable() { public void onEnable() {
Logger logger = getLogger();
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
getLogger().info("仓鼠核心正在启动..."); logger.info("仓鼠核心正在启动...");
CoreBukkitAPI.init();
logger.info("CoreAPI 已初始化.");
VaultAPI.reloadVaultHook();
logger.info("完成 VaultAPI 挂载.");
PointAPI.reloadPlayerPointAPIHook();
logger.info("完成 PlayerPoints 挂载.");
Bukkit.getPluginManager().registerEvents(PageListener.INSTANCE, this); Bukkit.getPluginManager().registerEvents(PageListener.INSTANCE, this);
getLogger().info("已注册 PageListener."); logger.info("已注册 PageListener.");
Bukkit.getPluginManager().registerEvents(CallbackListener.INSTANCE, this);
logger.info("已注册 CallbackListener.");
Bukkit.getPluginManager().registerEvents(DebugListener.INSTANCE, this);
logger.info("已注册 DebugListener.");
COMMAND_EXECUTOR.addChildCommand(BlockInfoCommand.INSTANCE);
logger.info("已添加指令: " + BlockInfoCommand.INSTANCE.getName() + " .");
COMMAND_EXECUTOR.addChildCommand(YamlCommand.INSTANCE);
logger.info("已添加指令: " + YamlCommand.INSTANCE.getName() + " .");
long time = System.currentTimeMillis() - start; long time = System.currentTimeMillis() - start;
getLogger().info("仓鼠核心已启动,总计耗时 " + time + " ms."); logger.info("仓鼠核心已启动,总计耗时 " + time + " ms.");
} }
@Override @Override
public void onDisable() { public void onDisable() {
Logger logger = getLogger();
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
getLogger().info("仓鼠核心正在关闭..."); logger.info("仓鼠核心正在关闭...");
CoreConstantObjects.WORKER_EXECUTOR.shutdownNow(); CoreConstantObjects.WORKER_EXECUTOR.shutdownNow();
getLogger().info("已暂停 WORKER_EXECUTOR."); logger.info("已暂停 WORKER_EXECUTOR.");
CoreConstantObjects.SCHEDULED_EXECUTOR.shutdownNow(); CoreConstantObjects.SCHEDULED_EXECUTOR.shutdownNow();
getLogger().info("已暂停 SCHEDULED_EXECUTOR."); logger.info("已暂停 SCHEDULED_EXECUTOR.");
long time = System.currentTimeMillis() - start; long time = System.currentTimeMillis() - start;
getLogger().info("仓鼠核心已关闭,总计耗时 " + time + " ms."); logger.info("仓鼠核心已关闭,总计耗时 " + time + " ms.");
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
return COMMAND_EXECUTOR.onCommand(sender, command, label, args);
}
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
return COMMAND_EXECUTOR.onTabComplete(sender, command, alias, args);
} }
} }

View File

@@ -1,31 +1,46 @@
package cn.hamster3.mc.plugin.core.bukkit.api; 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.common.api.CoreCommonAPI; import cn.hamster3.mc.plugin.core.common.api.CoreAPI;
import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.HikariDataSource;
import net.kyori.adventure.platform.AudienceProvider; import net.kyori.adventure.platform.AudienceProvider;
import net.kyori.adventure.platform.bukkit.BukkitAudiences; import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.sql.Connection;
import java.sql.SQLException;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class CoreBukkitAPI extends CoreCommonAPI { public class CoreBukkitAPI extends CoreAPI {
private BukkitAudiences audienceProvider; private final BukkitAudiences audienceProvider;
private HikariDataSource datasource; private final HikariDataSource datasource;
public CoreBukkitAPI() {
HamsterCorePlugin plugin = HamsterCorePlugin.getInstance();
audienceProvider = BukkitAudiences.create(plugin);
plugin.saveDefaultConfig();
FileConfiguration config = plugin.getConfig();
ConfigurationSection datasourceConfig = config.getConfigurationSection("datasource");
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));
hikariConfig.setIdleTimeout(datasourceConfig.getLong("idle-timeout", 5 * 60 * 1000));
hikariConfig.setMaxLifetime(datasourceConfig.getLong("max-lifetime", 0));
datasource = new HikariDataSource(hikariConfig);
}
public static CoreBukkitAPI getInstance() { public static CoreBukkitAPI getInstance() {
return (CoreBukkitAPI) instance; return (CoreBukkitAPI) instance;
} }
public static void init() { public static void init() {
CoreBukkitAPI api = new CoreBukkitAPI(); instance = new CoreBukkitAPI();
instance = api;
api.audienceProvider = BukkitAudiences.create(HamsterCorePlugin.getInstance());
HikariConfig hikariConfig = new HikariConfig();
} }
@Override @Override
@@ -38,17 +53,9 @@ public class CoreBukkitAPI extends CoreCommonAPI {
return datasource; return datasource;
} }
@Override public void reportError(@NotNull String projectID, @NotNull Throwable exception) {
public @NotNull Connection getConnection() throws SQLException {
return datasource.getConnection();
} }
@Override public void reportFile(@NotNull String projectID, @NotNull String filename, byte @NotNull [] bytes) {
public void reportError(@NotNull String apiKey, @NotNull String projectID, @NotNull Throwable exception) {
}
@Override
public void reportFile(@NotNull String apiKey, @NotNull String projectID, @NotNull String filename, byte @NotNull [] bytes) {
} }
} }

View File

@@ -0,0 +1,20 @@
package cn.hamster3.mc.plugin.core.bukkit.command;
import org.bukkit.command.TabExecutor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public abstract class ChildCommand implements TabExecutor {
@NotNull
public abstract String getName();
@NotNull
public abstract String getUsage();
@Nullable
public abstract String getPermission();
@NotNull
public abstract String getDescription();
}

View File

@@ -0,0 +1,93 @@
package cn.hamster3.mc.plugin.core.bukkit.command;
import cn.hamster3.mc.plugin.core.bukkit.constant.CoreMessage;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@SuppressWarnings("unused")
public class ParentCommand implements TabExecutor {
@NotNull
private final String name;
@Nullable
private final ParentCommand parent;
@NotNull
private final List<ChildCommand> childCommands;
public ParentCommand(@NotNull String name) {
this.name = name;
parent = null;
childCommands = new ArrayList<>();
}
public ParentCommand(@NotNull String name, @Nullable ParentCommand parent) {
this.name = name;
this.parent = parent;
childCommands = new ArrayList<>();
}
@NotNull
public String getUsage() {
if (parent == null) {
return "/" + name;
}
return parent.getUsage() + " " + name;
}
@NotNull
public List<ChildCommand> getChildCommands() {
return childCommands;
}
public void addChildCommand(@NotNull ChildCommand command) {
childCommands.add(command);
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (args.length == 0) {
sender.sendMessage("§e==================== [" + name + "使用帮助] ====================");
int maxLength = childCommands.stream()
.filter(o -> o.getPermission() == null || sender.hasPermission(o.getPermission()))
.map(o -> o.getUsage().length())
.max(Integer::compareTo)
.orElse(-1);
for (ChildCommand child : childCommands) {
String permission = child.getPermission();
if (permission != null && !sender.hasPermission(permission)) {
continue;
}
sender.sendMessage(String.format("§a%s %-" + maxLength + "s - %s", getUsage(), child.getUsage(), child.getDescription()));
}
return true;
}
for (ChildCommand childCommand : childCommands) {
if (childCommand.getName().equalsIgnoreCase(args[0])) {
return childCommand.onCommand(sender, command, label, Arrays.copyOfRange(args, 1, args.length));
}
}
CoreMessage.COMMAND_NOT_FOUND.show(sender);
return true;
}
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
for (ChildCommand child : childCommands) {
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()
.map(ChildCommand::getName)
.filter(o -> o.toLowerCase().startsWith(args[0]))
.collect(Collectors.toList());
}
}

View File

@@ -0,0 +1,81 @@
package cn.hamster3.mc.plugin.core.bukkit.command.debug;
import cn.hamster3.mc.plugin.core.bukkit.command.ChildCommand;
import cn.hamster3.mc.plugin.core.bukkit.constant.CoreMessage;
import cn.hamster3.mc.plugin.core.bukkit.listener.DebugListener;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.UUID;
@SuppressWarnings("SpellCheckingInspection")
public class BlockInfoCommand extends ChildCommand {
public static final BlockInfoCommand INSTANCE = new BlockInfoCommand();
private BlockInfoCommand() {
}
@Override
public @NotNull String getName() {
return "blockinfo";
}
@Override
public @NotNull String getUsage() {
return "blockinfo [on/off]";
}
@Override
public @Nullable String getPermission() {
return null;
}
@Override
public @NotNull String getDescription() {
return "开启方块信息查询模式";
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (!(sender instanceof Player)) {
CoreMessage.COMMAND_MUST_USED_BY_PLAYER.show(sender);
return true;
}
Player player = (Player) sender;
UUID uuid = player.getUniqueId();
if (args.length >= 1) {
switch (args[0]) {
case "1":
case "on": {
DebugListener.BLOCK_INFO.add(uuid);
CoreMessage.COMMAND_DEBUG_BLOCK_INFO_ON.show(player);
return true;
}
case "0":
case "off": {
DebugListener.BLOCK_INFO.remove(uuid);
CoreMessage.COMMAND_DEBUG_BLOCK_INFO_OFF.show(player);
return true;
}
}
}
if (DebugListener.BLOCK_INFO.contains(uuid)) {
DebugListener.BLOCK_INFO.remove(uuid);
CoreMessage.COMMAND_DEBUG_BLOCK_INFO_OFF.show(player);
} else {
DebugListener.BLOCK_INFO.add(uuid);
CoreMessage.COMMAND_DEBUG_BLOCK_INFO_ON.show(player);
}
return true;
}
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
return null;
}
}

View File

@@ -0,0 +1,98 @@
package cn.hamster3.mc.plugin.core.bukkit.command.debug;
import cn.hamster3.mc.plugin.core.bukkit.HamsterCorePlugin;
import cn.hamster3.mc.plugin.core.bukkit.command.ChildCommand;
import cn.hamster3.mc.plugin.core.bukkit.util.BukkitUtils;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class YamlCommand extends ChildCommand {
public static final YamlCommand INSTANCE = new YamlCommand();
private YamlCommand() {
}
@Override
public @NotNull String getName() {
return "yaml";
}
@Override
public @NotNull String getUsage() {
return "yaml";
}
@Override
public @Nullable String getPermission() {
return null;
}
@Override
public @NotNull String getDescription() {
return "将当前信息保存至 yaml 中";
}
@SuppressWarnings("deprecation")
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
YamlConfiguration config = new YamlConfiguration();
config.set("mc-version", BukkitUtils.getMCVersion());
config.set("nms-version", BukkitUtils.getNMSVersion());
config.set("server-version", Bukkit.getBukkitVersion());
config.set("bukkit-version", Bukkit.getVersion());
if (sender instanceof Player) {
Player player = (Player) sender;
config.set("player-name", player.getName());
config.set("player-uuid", player.getUniqueId().toString());
config.set("location", player.getLocation());
config.set("hand-item", player.getInventory().getItemInHand());
}
ItemStack stack = new ItemStack(Material.DIAMOND_SWORD);
ItemMeta meta = stack.getItemMeta();
List<String> lore = new ArrayList<>();
lore.add("§e测试 lore 1");
lore.add("§e测试 lore 2");
lore.add("§e测试 lore 3");
meta.addEnchant(Enchantment.DAMAGE_ALL, 1, true);
meta.setDisplayName("§a测试物品");
meta.setLore(lore);
meta.addItemFlags(ItemFlag.values());
stack.setItemMeta(meta);
config.set("test-item", stack);
File dataFolder = new File(HamsterCorePlugin.getInstance().getDataFolder(), "yaml");
if (dataFolder.mkdirs()) {
HamsterCorePlugin.getInstance().getLogger().info("创建 yaml 存档文件夹...");
}
File saveFile = new File(dataFolder, sender.getName() + "_" + System.currentTimeMillis() + ".yml");
try {
config.save(saveFile);
} catch (IOException e) {
e.printStackTrace();
}
sender.sendMessage("§a信息已保存至文件 " + saveFile.getAbsolutePath());
return true;
}
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
return Collections.emptyList();
}
}

View File

@@ -0,0 +1,20 @@
package cn.hamster3.mc.plugin.core.bukkit.constant;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
public enum CoreMessage {
COMMAND_NOT_FOUND,
COMMAND_MUST_USED_BY_PLAYER,
COMMAND_DEBUG_BLOCK_INFO_ON,
COMMAND_DEBUG_BLOCK_INFO_OFF;
public void show(CommandSender sender) {
sender.sendMessage(name());
}
public void show(Player player) {
player.sendMessage(name());
}
}

View File

@@ -0,0 +1,158 @@
package cn.hamster3.mc.plugin.core.bukkit.hook;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
/**
* Vault Economy API
*/
@SuppressWarnings("unused")
public class EconomyAPI {
private EconomyAPI() {
}
/**
* 返回服务器是否安装了经济插件
*
* @return true代表安装了false代表未安装
*/
public static boolean isSetupEconomy() {
return VaultAPI.isSetupVault() && VaultAPI.getEconomy() != null;
}
/**
* 给玩家钱
*
* @param player 玩家
* @param money 钱的数量
* @return 成功则返回 true
*/
public static boolean giveMoney(@NotNull final OfflinePlayer player, final double money) {
if (isSetupEconomy()) {
return VaultAPI.getEconomy().depositPlayer(player, money).transactionSuccess();
}
return false;
}
/**
* 给玩家钱
*
* @param uuid 玩家的uuid
* @param money 钱的数量
* @return 成功则返回 true
*/
public static boolean giveMoney(@NotNull final UUID uuid, final double money) {
return giveMoney(Bukkit.getOfflinePlayer(uuid), money);
}
/**
* 从玩家账户上扣钱
*
* @param player 玩家
* @param money 钱的数量
* @return 成功则返回 true
*/
public static boolean takeMoney(@NotNull final OfflinePlayer player, final double money) {
if (isSetupEconomy()) {
return VaultAPI.getEconomy().withdrawPlayer(player, money).transactionSuccess();
}
return false;
}
/**
* 从玩家账户上扣钱
*
* @param uuid 玩家的uuid
* @param money 钱的数量
* @return 成功则返回 true
*/
public static boolean takeMoney(@NotNull final UUID uuid, final double money) {
return takeMoney(Bukkit.getOfflinePlayer(uuid), money);
}
/**
* 设置玩家的余额
*
* @param player 玩家
* @param money 钱的数量
* @return 成功则返回 true
*/
public static boolean setMoney(@NotNull OfflinePlayer player, final double money) {
if (!isSetupEconomy()) {
return false;
}
double v = seeMoney(player);
if (v > money) {
return takeMoney(player, v - money);
} else if (v < money) {
return giveMoney(player, money - v);
}
return true;
}
/**
* 设置玩家的余额
*
* @param uuid 玩家的uuid
* @param money 钱的数量
* @return 成功则返回 true
*/
public static boolean setMoney(@NotNull final UUID uuid, final double money) {
return setMoney(Bukkit.getOfflinePlayer(uuid), money);
}
/**
* 检查玩家有多少钱
* <p>
* 若没有安装经济插件则返回NaN
*
* @param player 玩家
* @return 玩家的余额
*/
public static double seeMoney(@NotNull final OfflinePlayer player) {
if (!isSetupEconomy()) {
return Double.NaN;
}
return VaultAPI.getEconomy().getBalance(player);
}
/**
* 检查玩家有多少钱
* <p>
* 若没有安装经济插件则返回NaN
*
* @param uuid 玩家的uuid
* @return 玩家的余额
*/
public static double seeMoney(@NotNull final UUID uuid) {
return seeMoney(Bukkit.getOfflinePlayer(uuid));
}
/**
* 检测玩家是否有足够的钱
*
* @param player 玩家
* @param money 金钱的数量
* @return 是否有足够的钱(若没有安装经济插件则直接返回 false
*/
public static boolean hasMoney(@NotNull final OfflinePlayer player, final double money) {
if (!isSetupEconomy()) {
return false;
}
return VaultAPI.getEconomy().has(player, money);
}
/**
* 检测玩家是否有足够的钱
*
* @param uuid 玩家的uuid
* @param money 金钱的数量
* @return 是否有足够的钱(若没有安装经济插件则直接返回 false
*/
public static boolean hasMoney(@NotNull final UUID uuid, final double money) {
return hasMoney(Bukkit.getOfflinePlayer(uuid), money);
}
}

View File

@@ -0,0 +1,178 @@
package cn.hamster3.mc.plugin.core.bukkit.hook;
import cn.hamster3.mc.plugin.core.bukkit.HamsterCorePlugin;
import org.black_ixx.playerpoints.PlayerPoints;
import org.black_ixx.playerpoints.PlayerPointsAPI;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.plugin.Plugin;
import java.util.UUID;
/**
* PlayerPointsAPI
*/
@SuppressWarnings("unused")
public class PointAPI {
private static PlayerPointsAPI playerPointsAPI;
private PointAPI() {
}
/**
* 重载 PlayerPointAPI 点券系统挂接
*/
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;
}
/**
* 返回服务器是否安装了 PlayerPointAPI 插件
*
* @return true代表安装了false代表未安装
*/
public static boolean isSetupPlayerPointAPI() {
return playerPointsAPI != null;
}
/**
* 给予玩家点券
*
* @param player 玩家
* @param point 点券数量
*/
public static void givePoint(final OfflinePlayer player, final int point) {
if (playerPointsAPI != null) {
playerPointsAPI.give(player.getUniqueId(), point);
}
}
/**
* 给予玩家点券
*
* @param uuid 玩家的uuid
* @param point 点券数量
*/
public static void givePoint(final UUID uuid, final int point) {
if (playerPointsAPI != null) {
playerPointsAPI.give(uuid, point);
}
}
/**
* 扣除玩家点券
*
* @param player 玩家
* @param point 点券数量
*/
public static void takePoint(final OfflinePlayer player, final int point) {
if (playerPointsAPI != null) {
playerPointsAPI.take(player.getUniqueId(), point);
}
}
/**
* 扣除玩家点券
*
* @param uuid 玩家的uuid
* @param point 点券数量
*/
public static void takePoint(final UUID uuid, final int point) {
if (playerPointsAPI != null) {
playerPointsAPI.take(uuid, point);
}
}
/**
* 设置玩家的点券
*
* @param player 玩家
* @param point 点券数量
*/
public static void setPoint(final OfflinePlayer player, final int point) {
if (playerPointsAPI != null) {
playerPointsAPI.set(player.getUniqueId(), point);
}
}
/**
* 设置玩家的点券
*
* @param uuid 玩家的uuid
* @param point 点券数量
*/
public static void setPoint(final UUID uuid, final int point) {
if (playerPointsAPI != null) {
playerPointsAPI.set(uuid, point);
}
}
/**
* 查看玩家的点券数量
*
* @param player 玩家
* @return 玩家的点券数量
*/
public static int seePoint(final OfflinePlayer player) {
if (playerPointsAPI != null) {
return playerPointsAPI.look(player.getUniqueId());
}
return 0;
}
/**
* 查看玩家的点券数量
*
* @param uuid 玩家的uuid
* @return 玩家的点券数量
*/
public static int seePoint(final UUID uuid) {
if (playerPointsAPI != null) {
return playerPointsAPI.look(uuid);
}
return 0;
}
/**
* 检查玩家是否有足够的点券
*
* @param player 玩家
* @param point 点券数量
* @return 若未安装 PlayerPointAPI 则直接返回 false
*/
public static boolean hasPoint(final OfflinePlayer player, final int point) {
if (playerPointsAPI != null) {
return playerPointsAPI.look(player.getUniqueId()) >= point;
}
return false;
}
/**
* 检查玩家是否有足够的点券
*
* @param uuid 玩家的uuid
* @param point 点券数量
* @return 若未安装 PlayerPointAPI 则直接返回 false
*/
public static boolean hasPoint(final UUID uuid, final int point) {
if (playerPointsAPI != null) {
return playerPointsAPI.look(uuid) >= point;
}
return false;
}
}

View File

@@ -0,0 +1,96 @@
package cn.hamster3.mc.plugin.core.bukkit.hook;
import cn.hamster3.mc.plugin.core.bukkit.HamsterCorePlugin;
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;
/**
* Vault API
*/
@SuppressWarnings("unused")
public class VaultAPI {
private static boolean vaultEnabled;
private static Chat chat;
private static Economy economy;
private static Permission permission;
private VaultAPI() {
}
public static void reloadVaultHook() {
chat = null;
economy = null;
permission = null;
vaultEnabled = Bukkit.getPluginManager().isPluginEnabled("Vault");
if (!vaultEnabled) {
HamsterCorePlugin.getInstance().getLogger().warning("未检测到 Vault 插件!");
return;
}
HamsterCorePlugin.getInstance().getLogger().info("已连接 Vault!");
RegisteredServiceProvider<Chat> chatProvider = Bukkit.getServer().getServicesManager().getRegistration(Chat.class);
if (chatProvider != null) {
chat = chatProvider.getProvider();
HamsterCorePlugin.getInstance().getLogger().info("聊天系统挂接成功.");
} else {
HamsterCorePlugin.getInstance().getLogger().warning("未检测到聊天系统!");
}
RegisteredServiceProvider<Economy> economyProvider = Bukkit.getServer().getServicesManager().getRegistration(Economy.class);
if (economyProvider != null) {
economy = economyProvider.getProvider();
HamsterCorePlugin.getInstance().getLogger().info("经济系统挂接成功.");
} else {
HamsterCorePlugin.getInstance().getLogger().warning("未检测到经济系统!");
}
RegisteredServiceProvider<Permission> permissionProvider = Bukkit.getServer().getServicesManager().getRegistration(Permission.class);
if (permissionProvider != null) {
permission = permissionProvider.getProvider();
HamsterCorePlugin.getInstance().getLogger().info("权限系统挂接成功.");
} else {
HamsterCorePlugin.getInstance().getLogger().warning("未检测到权限插件!");
}
}
/**
* 返回服务器是否安装了 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

@@ -0,0 +1,78 @@
package cn.hamster3.mc.plugin.core.bukkit.listener;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import java.util.HashMap;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
public class CallbackListener implements Listener {
public static final CallbackListener INSTANCE = new CallbackListener();
public static final HashMap<UUID, CompletableFuture<String>> CHATS = new HashMap<>();
public static final HashMap<UUID, CompletableFuture<Block>> BLOCKS = new HashMap<>();
public static final HashMap<UUID, CompletableFuture<Entity>> ENTITIES = new HashMap<>();
private CallbackListener() {
}
@EventHandler(ignoreCancelled = true)
public void onAsyncPlayerChat(AsyncPlayerChatEvent event) {
Player player = event.getPlayer();
CompletableFuture<String> future = CHATS.remove(player.getUniqueId());
if (future == null) {
return;
}
future.complete(event.getMessage());
}
@EventHandler(ignoreCancelled = true)
public void onPlayerInteract(PlayerInteractEvent event) {
if (event.getAction() != Action.RIGHT_CLICK_BLOCK) {
return;
}
Player player = event.getPlayer();
CompletableFuture<Block> future = BLOCKS.remove(player.getUniqueId());
if (future == null) {
return;
}
future.complete(event.getClickedBlock());
}
@EventHandler(ignoreCancelled = true)
public void onPlayerInteractEntity(PlayerInteractEntityEvent event) {
Player player = event.getPlayer();
CompletableFuture<Entity> future = ENTITIES.remove(player.getUniqueId());
if (future == null) {
return;
}
future.complete(event.getRightClicked());
}
@EventHandler(ignoreCancelled = true)
public void onPlayerQuit(PlayerQuitEvent event) {
Player player = event.getPlayer();
UUID uuid = player.getUniqueId();
CompletableFuture<String> chat = CHATS.remove(uuid);
if (chat != null) {
chat.cancel(true);
}
CompletableFuture<Block> block = BLOCKS.remove(uuid);
if (block != null) {
block.cancel(true);
}
CompletableFuture<Entity> entity = ENTITIES.remove(uuid);
if (entity != null) {
entity.cancel(true);
}
}
}

View File

@@ -0,0 +1,57 @@
package cn.hamster3.mc.plugin.core.bukkit.listener;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import java.util.HashSet;
import java.util.UUID;
public class DebugListener implements Listener {
public static final DebugListener INSTANCE = new DebugListener();
/**
* 要查看方块信息的玩家
*/
public static final HashSet<UUID> BLOCK_INFO = new HashSet<>();
private DebugListener() {
}
@EventHandler(ignoreCancelled = true)
public void onPlayerQuit(PlayerQuitEvent event) {
Player player = event.getPlayer();
UUID uuid = player.getUniqueId();
BLOCK_INFO.remove(uuid);
}
@EventHandler(ignoreCancelled = true)
public void onPlayerInteract(PlayerInteractEvent event) {
Player player = event.getPlayer();
UUID uuid = player.getUniqueId();
if (!BLOCK_INFO.contains(uuid)) {
return;
}
switch (event.getAction()) {
case LEFT_CLICK_BLOCK:
case LEFT_CLICK_AIR: {
break;
}
default: {
return;
}
}
Block block = event.getClickedBlock();
player.sendMessage("§e==============================");
player.sendMessage(String.format("§a方块位置: %s %d %d %d", block.getWorld().getName(), block.getX(), block.getY(), block.getZ()));
player.sendMessage("§a方块材质: " + block.getType().name());
player.sendMessage("§a方块能量: " + block.getBlockPower());
player.sendMessage("§a方块湿度: " + block.getHumidity());
player.sendMessage("§a自发光亮度: " + block.getLightLevel());
player.sendMessage("§a获取天空亮度: " + block.getLightFromSky());
player.sendMessage("§a方块吸收亮度: " + block.getLightFromBlocks());
event.setCancelled(true);
}
}

View File

@@ -1,9 +1,9 @@
package cn.hamster3.mc.plugin.core.bukkit.page.handler; package cn.hamster3.mc.plugin.core.bukkit.page.handler;
import cn.hamster3.mc.plugin.core.bukkit.page.ButtonGroup;
import cn.hamster3.mc.plugin.core.bukkit.page.PageManager;
import cn.hamster3.mc.plugin.core.bukkit.HamsterCorePlugin; import cn.hamster3.mc.plugin.core.bukkit.HamsterCorePlugin;
import cn.hamster3.mc.plugin.core.bukkit.page.ButtonGroup;
import cn.hamster3.mc.plugin.core.bukkit.page.PageConfig; import cn.hamster3.mc.plugin.core.bukkit.page.PageConfig;
import cn.hamster3.mc.plugin.core.bukkit.page.PageManager;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Sound; import org.bukkit.Sound;
import org.bukkit.entity.HumanEntity; import org.bukkit.entity.HumanEntity;

View File

@@ -1,8 +1,8 @@
package cn.hamster3.mc.plugin.core.bukkit.page.handler; package cn.hamster3.mc.plugin.core.bukkit.page.handler;
import cn.hamster3.mc.plugin.core.bukkit.page.ButtonGroup; import cn.hamster3.mc.plugin.core.bukkit.page.ButtonGroup;
import cn.hamster3.mc.plugin.core.bukkit.page.PageElement;
import cn.hamster3.mc.plugin.core.bukkit.page.PageConfig; import cn.hamster3.mc.plugin.core.bukkit.page.PageConfig;
import cn.hamster3.mc.plugin.core.bukkit.page.PageElement;
import org.bukkit.entity.HumanEntity; import org.bukkit.entity.HumanEntity;
import org.bukkit.event.inventory.ClickType; import org.bukkit.event.inventory.ClickType;
import org.bukkit.event.inventory.InventoryAction; import org.bukkit.event.inventory.InventoryAction;

View File

@@ -1,12 +1,9 @@
package cn.hamster3.mc.plugin.core.bukkit.util; package cn.hamster3.mc.plugin.core.bukkit.util;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.OfflinePlayer; import org.bukkit.OfflinePlayer;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.entity.HumanEntity; import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
@@ -17,7 +14,10 @@ import org.jetbrains.annotations.NotNull;
import java.util.UUID; import java.util.UUID;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class BukkitUtils { public final class BukkitUtils {
private BukkitUtils() {
}
@NotNull @NotNull
public static String getMCVersion() { public static String getMCVersion() {
return Bukkit.getBukkitVersion().split("-")[0]; return Bukkit.getBukkitVersion().split("-")[0];
@@ -68,8 +68,10 @@ public class BukkitUtils {
/** /**
* 获取玩家的头颅 * 获取玩家的头颅
* 在1.11以上的服务端中获取头颅材质是在服务器上运行的 * <p>
* 因此建议使用异步线程调用该方法 * 在 1.11 以上的服务端建议使用异步线程调用该方法
* <p>
* 因为这些服务端中通过网络获取头颅材质是在服务器上运行的
* *
* @param offlinePlayer 要获取的玩家 * @param offlinePlayer 要获取的玩家
* @return 玩家的头颅物品 * @return 玩家的头颅物品
@@ -165,30 +167,4 @@ public class BukkitUtils {
} }
return stack.getType().name(); return stack.getType().name();
} }
/**
* 创建 SQL 连接池
*
* @param config SQL 配置
* @return SQL 连接池
*/
@NotNull
public static HikariDataSource getHikariDataSource(@NotNull ConfigurationSection config) {
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setDriverClassName(config.getString("driver"));
hikariConfig.setJdbcUrl(config.getString("url"));
hikariConfig.setUsername(config.getString("user"));
hikariConfig.setPassword(config.getString("password"));
hikariConfig.setMaximumPoolSize(config.getInt("maximumPoolSize", 3));
hikariConfig.setMinimumIdle(config.getInt("minimumIdle", 1));
hikariConfig.setIdleTimeout(config.getLong("idleTimeout", 5 * 60 * 1000));
hikariConfig.setMaxLifetime(config.getLong("maxLifetime", 0));
return new HikariDataSource(hikariConfig);
}
// todo public static TextComponent getItemDisplayInfo(@NotNull ItemStack stack)
} }

View File

@@ -0,0 +1,26 @@
package cn.hamster3.mc.plugin.core.bukkit.util;
import cn.hamster3.mc.plugin.core.bukkit.listener.CallbackListener;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.entity.HumanEntity;
import java.util.concurrent.CompletableFuture;
@SuppressWarnings("unused")
public final class CallbackUtils {
private CallbackUtils() {
}
public static CompletableFuture<String> getPlayerChat(HumanEntity player) {
return CallbackListener.CHATS.computeIfAbsent(player.getUniqueId(), o -> new CompletableFuture<>());
}
public static CompletableFuture<Block> getPlayerClickedBlock(HumanEntity player) {
return CallbackListener.BLOCKS.computeIfAbsent(player.getUniqueId(), o -> new CompletableFuture<>());
}
public static CompletableFuture<Entity> getPlayerClickedEntity(HumanEntity player) {
return CallbackListener.ENTITIES.computeIfAbsent(player.getUniqueId(), o -> new CompletableFuture<>());
}
}

View File

@@ -0,0 +1,23 @@
datasource:
# 数据库链接驱动地址
# 除非你知道自己在做什么,否则不建议更改该项
driver: "com.mysql.cj.jdbc.Driver"
# 数据库链接填写格式:
# jdbc:mysql://{数据库地址}:{数据库端口}/{使用的库名}?参数
# 除非你知道自己在做什么,否则不建议随意更改参数
url: "jdbc:mysql://sql.hamster3.cn:3306/Test1?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false"
# 用户名
username: "Test"
# 密码
password: "Test123.."
# 最大链接数
maximum-pool-size: 10
# 最小链接数
minimum-idle: 1
# 超时回收时间
# 单位:毫秒
idle-timeout: 300000
# 链接最长存活时间
# 单位:毫秒
# 建议设置为比数据库上的 wait_timeout 参数少 30 秒
max-lifetime: 1800000

View File

@@ -1,3 +1,22 @@
name: HamsterCore name: HamsterCore
main: cn.hamster3.mc.plugin.core.bukkit.HamsterCorePlugin main: cn.hamster3.mc.plugin.core.bukkit.HamsterCorePlugin
version: ${version} version: ${version}
author: MiniDay
website: https://github.com/MiniDay/hamster-core
description: 仓鼠核心Minecraft 插件开发通用工具包
softdepend:
- Vault
- PlayerPoints
commands:
hamstercore:
aliases: [ hcore, core ]
description: 仓鼠核心调试指令
permission: hamster.core.admin
permission-message: §c你没有这个权限
permissions:
hamster.core.admin:
default: op

View File

@@ -1,4 +1,4 @@
setArchivesBaseName("HamsterCore-Proxy") setArchivesBaseName("HamsterCore-BungeeCord")
evaluationDependsOn(':hamster-core-common') evaluationDependsOn(':hamster-core-common')
@@ -8,21 +8,16 @@ dependencies {
} }
//noinspection GradlePackageUpdate //noinspection GradlePackageUpdate
compileOnly('net.md-5:bungeecord-api:1.17-R0.1-SNAPSHOT') compileOnly('net.md-5:bungeecord-api:1.17-R0.1-SNAPSHOT')
// https://mvnrepository.com/artifact/net.kyori/adventure-platform-bungeecord
apiShade 'net.kyori:adventure-platform-bungeecord:4.1.2'
// https://mvnrepository.com/artifact/com.zaxxer/HikariCP // https://mvnrepository.com/artifact/com.zaxxer/HikariCP
//noinspection GradlePackageUpdate //noinspection GradlePackageUpdate
apiShade 'com.zaxxer:HikariCP:4.0.3' apiShade 'com.zaxxer:HikariCP:4.0.3'
// https://mvnrepository.com/artifact/mysql/mysql-connector-java
shade 'mysql:mysql-connector-java:8.0.31'
// https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp // https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp
apiShade 'com.squareup.okhttp3:okhttp:4.10.0' apiShade 'com.squareup.okhttp3:okhttp:4.10.0'
// https://mvnrepository.com/artifact/net.kyori/adventure-platform-bungeecord
apiShade 'net.kyori:adventure-platform-bungeecord:4.1.2'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.9.0'
}
test {
useJUnitPlatform()
} }
processResources { processResources {
@@ -39,11 +34,14 @@ tasks.create("shadowJar", Jar) {
tasks.jar.outputs.files.collect { tasks.jar.outputs.files.collect {
it.isDirectory() ? it : zipTree(it) it.isDirectory() ? it : zipTree(it)
}, },
configurations.implementationShade.collect { configurations.shade.collect {
it.isDirectory() ? it : zipTree(it) it.isDirectory() ? it : zipTree(it)
}, },
configurations.apiShade.collect { configurations.apiShade.collect {
it.isDirectory() ? it : zipTree(it) it.isDirectory() ? it : zipTree(it)
},
configurations.implementationShade.collect {
it.isDirectory() ? it : zipTree(it)
} }
]) ])
destinationDir(getRootProject().buildDir) destinationDir(getRootProject().buildDir)

View File

@@ -1,8 +1,11 @@
package cn.hamster3.mc.plugin.core.proxy; package cn.hamster3.mc.plugin.core.bungee;
import cn.hamster3.mc.plugin.core.bungee.api.CoreBungeeAPI;
import cn.hamster3.mc.plugin.core.common.constant.CoreConstantObjects; import cn.hamster3.mc.plugin.core.common.constant.CoreConstantObjects;
import net.md_5.bungee.api.plugin.Plugin; import net.md_5.bungee.api.plugin.Plugin;
import java.util.logging.Logger;
public class HamsterCorePlugin extends Plugin { public class HamsterCorePlugin extends Plugin {
private static HamsterCorePlugin instance; private static HamsterCorePlugin instance;
@@ -17,21 +20,25 @@ public class HamsterCorePlugin extends Plugin {
@Override @Override
public void onEnable() { public void onEnable() {
Logger logger = getLogger();
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
getLogger().info("仓鼠核心正在启动..."); logger.info("仓鼠核心正在启动...");
CoreBungeeAPI.init();
logger.info("CoreAPI 已初始化.");
long time = System.currentTimeMillis() - start; long time = System.currentTimeMillis() - start;
getLogger().info("仓鼠核心已启动,总计耗时 " + time + " ms."); logger.info("仓鼠核心已启动,总计耗时 " + time + " ms.");
} }
@Override @Override
public void onDisable() { public void onDisable() {
Logger logger = getLogger();
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
getLogger().info("仓鼠核心正在关闭..."); logger.info("仓鼠核心正在关闭...");
CoreConstantObjects.WORKER_EXECUTOR.shutdownNow(); CoreConstantObjects.WORKER_EXECUTOR.shutdownNow();
getLogger().info("已暂停 WORKER_EXECUTOR."); logger.info("已暂停 WORKER_EXECUTOR.");
CoreConstantObjects.SCHEDULED_EXECUTOR.shutdownNow(); CoreConstantObjects.SCHEDULED_EXECUTOR.shutdownNow();
getLogger().info("已暂停 SCHEDULED_EXECUTOR."); logger.info("已暂停 SCHEDULED_EXECUTOR.");
long time = System.currentTimeMillis() - start; long time = System.currentTimeMillis() - start;
getLogger().info("仓鼠核心已关闭,总计耗时 " + time + " ms."); logger.info("仓鼠核心已关闭,总计耗时 " + time + " ms.");
} }
} }

View File

@@ -0,0 +1,54 @@
package cn.hamster3.mc.plugin.core.bungee.api;
import cn.hamster3.mc.plugin.core.bungee.HamsterCorePlugin;
import cn.hamster3.mc.plugin.core.bungee.util.BungeeCordUtils;
import cn.hamster3.mc.plugin.core.common.api.CoreAPI;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import net.kyori.adventure.platform.AudienceProvider;
import net.kyori.adventure.platform.bungeecord.BungeeAudiences;
import net.md_5.bungee.config.Configuration;
import org.jetbrains.annotations.NotNull;
@SuppressWarnings("unused")
public class CoreBungeeAPI extends CoreAPI {
private final BungeeAudiences audienceProvider;
private final HikariDataSource datasource;
public CoreBungeeAPI() {
HamsterCorePlugin plugin = HamsterCorePlugin.getInstance();
audienceProvider = BungeeAudiences.create(plugin);
Configuration config = BungeeCordUtils.getPluginConfig(plugin);
Configuration datasourceConfig = config.getSection("datasource");
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));
hikariConfig.setIdleTimeout(datasourceConfig.getLong("idle-timeout", 5 * 60 * 1000));
hikariConfig.setMaxLifetime(datasourceConfig.getLong("max-lifetime", 0));
datasource = new HikariDataSource(hikariConfig);
}
public static CoreBungeeAPI getInstance() {
return (CoreBungeeAPI) instance;
}
public static void init() {
instance = new CoreBungeeAPI();
}
@Override
public @NotNull AudienceProvider getAudienceProvider() {
return audienceProvider;
}
@Override
public @NotNull HikariDataSource getDataSource() {
return datasource;
}
}

View File

@@ -0,0 +1,43 @@
package cn.hamster3.mc.plugin.core.bungee.util;
import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.config.Configuration;
import net.md_5.bungee.config.ConfigurationProvider;
import net.md_5.bungee.config.YamlConfiguration;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
public final class BungeeCordUtils {
private BungeeCordUtils() {
}
public static Configuration getPluginConfig(Plugin plugin) {
File configFile = new File(plugin.getDataFolder(), "config.yml");
if (!configFile.exists()) {
return saveDefaultConfig(plugin);
}
try {
return ConfigurationProvider.getProvider(YamlConfiguration.class).load(configFile);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static Configuration saveDefaultConfig(Plugin plugin) {
try {
if (plugin.getDataFolder().mkdir()) {
plugin.getLogger().info("创建插件文件夹...");
}
File configFile = new File(plugin.getDataFolder(), "config.yml");
InputStream in = plugin.getResourceAsStream("config.yml");
Files.copy(in, configFile.toPath());
return ConfigurationProvider.getProvider(YamlConfiguration.class).load(configFile);
} catch (Exception ignored) {
}
return null;
}
}

View File

@@ -0,0 +1,3 @@
name: HamsterCore-BungeeCord
main: cn.hamster3.mc.plugin.core.bungee.HamsterCorePlugin
version: ${version}

View File

@@ -0,0 +1,23 @@
datasource:
# 数据库链接驱动地址
# 除非你知道自己在做什么,否则不建议更改该项
driver: "com.mysql.cj.jdbc.Driver"
# 数据库链接填写格式:
# jdbc:mysql://{数据库地址}:{数据库端口}/{使用的库名}?参数
# 除非你知道自己在做什么,否则不建议随意更改参数
url: "jdbc:mysql://sql.hamster3.cn:3306/Test1?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false"
# 用户名
username: "Test"
# 密码
password: "Test123.."
# 最大链接数
maximum-pool-size: 10
# 最小链接数
minimum-idle: 1
# 超时回收时间
# 单位:毫秒
idle-timeout: 300000
# 链接最长存活时间
# 单位:毫秒
# 建议设置为比数据库上的 wait_timeout 参数少 30 秒
max-lifetime: 1800000

View File

@@ -13,7 +13,7 @@ dependencies {
// https://mvnrepository.com/artifact/net.kyori/adventure-text-minimessage // https://mvnrepository.com/artifact/net.kyori/adventure-text-minimessage
api 'net.kyori:adventure-text-minimessage:4.11.0' api 'net.kyori:adventure-text-minimessage:4.11.0'
// https://mvnrepository.com/artifact/net.kyori/adventure-platform-api // https://mvnrepository.com/artifact/net.kyori/adventure-platform-api
implementation 'net.kyori:adventure-platform-api:4.1.2' api 'net.kyori:adventure-platform-api:4.1.2'
// https://mvnrepository.com/artifact/net.kyori/adventure-text-serializer-gson // https://mvnrepository.com/artifact/net.kyori/adventure-text-serializer-gson
api 'net.kyori:adventure-text-serializer-gson:4.11.0' api 'net.kyori:adventure-text-serializer-gson:4.11.0'

View File

@@ -0,0 +1,36 @@
package cn.hamster3.mc.plugin.core.common.api;
import net.kyori.adventure.platform.AudienceProvider;
import org.jetbrains.annotations.NotNull;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
@SuppressWarnings("unused")
public abstract class CoreAPI {
protected static CoreAPI instance;
public static CoreAPI getInstance() {
return instance;
}
@NotNull
public abstract AudienceProvider getAudienceProvider();
@NotNull
public abstract DataSource getDataSource();
@NotNull
public Connection getConnection() throws SQLException {
return getDataSource().getConnection();
}
public void reportError(@NotNull String apiKey, @NotNull String projectID, @NotNull Throwable exception) {
}
public void reportFile(@NotNull String apiKey, @NotNull String projectID, @NotNull String filename, byte @NotNull [] bytes) {
}
}

View File

@@ -1,30 +0,0 @@
package cn.hamster3.mc.plugin.core.common.api;
import net.kyori.adventure.platform.AudienceProvider;
import org.jetbrains.annotations.NotNull;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
@SuppressWarnings("unused")
public abstract class CoreCommonAPI {
protected static CoreCommonAPI instance;
public static CoreCommonAPI getInstance() {
return instance;
}
@NotNull
public abstract AudienceProvider getAudienceProvider();
@NotNull
public abstract DataSource getDataSource();
@NotNull
public abstract Connection getConnection() throws SQLException;
public abstract void reportError(@NotNull String apiKey, @NotNull String projectID, @NotNull Throwable exception);
public abstract void reportFile(@NotNull String apiKey, @NotNull String projectID, @NotNull String filename, byte @NotNull [] bytes);
}

View File

@@ -5,9 +5,16 @@ import com.google.gson.*;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.UUID;
import java.util.concurrent.*; import java.util.concurrent.*;
@SuppressWarnings("unused")
public interface CoreConstantObjects { public interface CoreConstantObjects {
/**
* Minecraft 默认指定的空 UUID
*/
UUID NIL_UUID = new UUID(0L, 0L);
/** /**
* GSON 工具 * GSON 工具
*/ */
@@ -28,14 +35,14 @@ public interface CoreConstantObjects {
*/ */
JsonParser JSON_PARSER = new JsonParser(); JsonParser JSON_PARSER = new JsonParser();
/**
* 调度器线程
*/
ScheduledExecutorService SCHEDULED_EXECUTOR = Executors.newScheduledThreadPool(1, new APIThreadFactory("HamsterCore - Scheduler"));
/** /**
* 异步线程 * 异步线程
*/ */
ExecutorService WORKER_EXECUTOR = new ThreadPoolExecutor(1, Integer.MAX_VALUE, 60, TimeUnit.MINUTES, new SynchronousQueue<>(), new APIThreadFactory("HamsterCore - Executor")); ExecutorService WORKER_EXECUTOR = new ThreadPoolExecutor(1, Integer.MAX_VALUE, 60, TimeUnit.MINUTES, new SynchronousQueue<>(), new APIThreadFactory("HamsterCore - Executor"));
/**
* 调度器线程
*/
ScheduledExecutorService SCHEDULED_EXECUTOR = Executors.newScheduledThreadPool(1, new APIThreadFactory("HamsterCore - Scheduler"));
class APIThreadFactory implements ThreadFactory { class APIThreadFactory implements ThreadFactory {
private final String name; private final String name;

View File

@@ -6,15 +6,19 @@ import java.util.Collections;
import java.util.Stack; import java.util.Stack;
/** /**
* 算数表达式求值 * 算数表达式求值工具
* <p>
* 传入算数表达式,将返回一个浮点值结果 * 传入算数表达式,将返回一个浮点值结果
* <p>
* 如果计算过程错误将返回一个NaN * 如果计算过程错误将返回一个NaN
* <p> * <p>
* 我也忘了这个类是哪里抄来的 * 我也忘了这个类是哪里抄来的
* 反正它运行起来比直接用JavaScript引擎计算要快很多 * <p>
* 反正它运行起来比直接用 JavaScript 引擎计算要快很多
*/ */
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class Calculator { public final class Calculator {
public static final Calculator INSTANCE = new Calculator();
// 默认除法运算精度 // 默认除法运算精度
private static final int DEF_DIV_SCALE = 16; private static final int DEF_DIV_SCALE = 16;

View File

@@ -2,6 +2,9 @@ package cn.hamster3.mc.plugin.core.common.util;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public final class CaseUtils { public final class CaseUtils {
private CaseUtils() {
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <T> T caseObject(Object o) { public static <T> T caseObject(Object o) {
return (T) o; return (T) o;

View File

@@ -0,0 +1,182 @@
package cn.hamster3.mc.plugin.core.common.util;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@SuppressWarnings("unused")
public final class CommonUtils {
private CommonUtils() {
}
public static void zipCompressionFolder(@NotNull File folder, @NotNull File zipFile) throws IOException {
ZipOutputStream stream = new ZipOutputStream(Files.newOutputStream(zipFile.toPath()));
putFileToZipStream(stream, "", folder);
stream.close();
}
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();
}
/**
* 替换颜色代码
*
* @param string 要替换的字符串
* @return 替换后的字符串
*/
@Nullable
public static String replaceColorCode(@Nullable String string) {
if (string == null) return null;
return string.replace("&", "§");
}
/**
* 替换颜色代码
* <p>
* 添加这个方法是因为 ConfigurationSection 中的 getString 方法有 @Nullable 注解
* <p>
* 导致 idea 会弹出某些警告,让人非常不爽
*
* @param string 要替换的字符串
* @param defaultValue 若 string 为空则使用该字符串
* @return 替换后的字符串
*/
@NotNull
public static String replaceColorCode(@Nullable String string, @NotNull String defaultValue) {
if (string == null) {
return replaceColorCode(defaultValue);
}
return replaceColorCode(string);
}
/**
* 替换颜色代码
*
* @param strings 要替换的字符串
* @return 替换后的字符串
*/
@NotNull
public static ArrayList<String> replaceColorCode(@Nullable Iterable<String> strings) {
ArrayList<String> list = new ArrayList<>();
if (strings == null) return list;
for (String s : strings) {
list.add(replaceColorCode(s));
}
return list;
}
/**
* 替换颜色代码
*
* @param strings 要替换的字符串
* @return 替换后的字符串
*/
@NotNull
public static ArrayList<String> replaceColorCode(@Nullable String[] strings) {
ArrayList<String> list = new ArrayList<>();
if (strings == null) return list;
for (String s : strings) {
list.add(replaceColorCode(s));
}
return list;
}
@NotNull
public static String[] replaceStringList(@NotNull String[] strings, @NotNull String key, @NotNull String value) {
for (int i = 0; i < strings.length; i++) {
strings[i] = strings[i].replace(key, value);
}
return strings;
}
@NotNull
public static List<String> replaceStringList(@NotNull Iterable<String> strings, @NotNull String key, @NotNull String value) {
ArrayList<String> list = new ArrayList<>();
for (String s : strings) {
list.add(s.replace(key, value));
}
return list;
}
@NotNull
public static List<String> replaceStringList(@NotNull List<String> strings, @NotNull String key, @NotNull String value) {
strings.replaceAll(s -> s.replace(key, value));
return strings;
}
public static boolean startsWithIgnoreCase(@NotNull String string, @NotNull String prefix) {
return string.regionMatches(true, 0, prefix, 0, prefix.length());
}
public static boolean endsWithIgnoreCase(@NotNull String string, @NotNull String suffix) {
int strOffset = string.length() - suffix.length();
return string.regionMatches(true, strOffset, suffix, 0, suffix.length());
}
@NotNull
public static ArrayList<String> startsWith(@NotNull Iterable<String> strings, @NotNull String with) {
ArrayList<String> list = new ArrayList<>();
for (String string : strings) {
if (string.startsWith(with)) {
list.add(string);
}
}
return list;
}
@NotNull
public static ArrayList<String> endsWith(@NotNull Iterable<String> strings, @NotNull String with) {
ArrayList<String> list = new ArrayList<>();
for (String string : strings) {
if (string.endsWith(with)) {
list.add(string);
}
}
return list;
}
@NotNull
public static ArrayList<String> startsWithIgnoreCase(@NotNull Iterable<String> strings, @NotNull String start) {
ArrayList<String> list = new ArrayList<>();
for (String string : strings) {
if (startsWithIgnoreCase(string, start)) {
list.add(string);
}
}
return list;
}
@NotNull
public static ArrayList<String> endsWithIgnoreCase(@NotNull Iterable<String> strings, @NotNull String end) {
ArrayList<String> list = new ArrayList<>();
for (String string : strings) {
if (endsWithIgnoreCase(string, end)) {
list.add(string);
}
}
return list;
}
}

View File

@@ -1,35 +0,0 @@
package cn.hamster3.mc.plugin.core.common.util;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@SuppressWarnings("unused")
public class FileUtils {
@SuppressWarnings("IOStreamConstructor")
public static void zipCompressionFolder(File folder, File zipFile) throws IOException {
ZipOutputStream stream = new ZipOutputStream(new FileOutputStream(zipFile));
putFileToZipStream(stream, "", folder);
stream.close();
}
public static void putFileToZipStream(ZipOutputStream stream, String path, 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();
}
}

View File

@@ -11,7 +11,19 @@ import org.jetbrains.annotations.NotNull;
import java.time.Duration; import java.time.Duration;
public class SerializeUtils { /**
* 序列化相关工具
*/
public final class SerializeUtils {
private SerializeUtils() {
}
/**
* 将 adventure 中的 title 对象序列化为 json
*
* @param title -
* @return -
*/
@NotNull @NotNull
public static JsonObject serializeTitle(@NotNull Title title) { public static JsonObject serializeTitle(@NotNull Title title) {
JsonObject object = new JsonObject(); JsonObject object = new JsonObject();
@@ -24,6 +36,12 @@ public class SerializeUtils {
return object; return object;
} }
/**
* 将 json 反序列化为 adventure 中的 title
*
* @param object -
* @return -
*/
@NotNull @NotNull
public static Title deserializeTitle(@NotNull JsonObject object) { public static Title deserializeTitle(@NotNull JsonObject object) {
if (object.has("times")) { if (object.has("times")) {

View File

@@ -1,3 +0,0 @@
name: HamsterCore-Proxy
main: cn.hamster3.mc.plugin.core.proxy.HamsterCorePlugin
version: ${version}

View File

@@ -1,5 +1,5 @@
rootProject.name = 'hamster-core' rootProject.name = 'hamster-core'
include 'hamster-core-common' include 'hamster-core-common'
include 'hamster-core-bukkit' include 'hamster-core-bukkit'
include 'hamster-core-proxy' include 'hamster-core-bungeecord'