Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8febf11d63 | |||
| 30f60c2849 | |||
| 5d0595e1c0 | |||
| 9b382320a5 |
@@ -4,8 +4,8 @@
|
||||
|
||||
## 仓库
|
||||
|
||||
- 远端:私有仓库 `dmz7p4xmqh-coder/CraftBank`(默认分支 `main`)。
|
||||
- `gh` 与 `git` 已安装并登录,可直接用于推送与发行。
|
||||
- 远端:私有 Gitea 仓库 `https://gitea.kernelneko.top/nachonekoawa/CraftBank`(默认分支 `main`)。
|
||||
- `git` 已安装;Gitea 推送与发行使用用户提供的访问令牌,运行时通过环境变量或临时请求头使用,严禁把 token 写入仓库、README 或 git remote。
|
||||
|
||||
## 每次改动后的固定流程
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
1. **构建验证**:运行 `./gradlew shadowJar`,确认编译通过并产出 `build/libs/CraftBank-<version>.jar`。
|
||||
2. **更新 README**:若改动影响功能、指令、权限、配置项或 PlaceholderAPI 变量,同步更新 `README.md`。
|
||||
3. **提交并推送**到 `main`。
|
||||
4. **发布 Release**:用 `gh release create` 创建新发行版,附上编译好的 jar。
|
||||
4. **发布 Release**:在 Gitea 创建新发行版,附上编译好的 jar。
|
||||
|
||||
## 版本号约定(语义化版本)
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
## 仓库
|
||||
|
||||
- 远端:私有仓库 `dmz7p4xmqh-coder/CraftBank`(默认分支 `main`)。
|
||||
- `gh` 与 `git` 已安装并登录,可直接用于推送与发行。
|
||||
- 远端:私有 Gitea 仓库 `https://gitea.kernelneko.top/nachonekoawa/CraftBank`(默认分支 `main`)。
|
||||
- `git` 已安装;Gitea 推送与发行使用用户提供的访问令牌,运行时通过环境变量或临时请求头使用,严禁把 token 写入仓库、README 或 git remote。
|
||||
|
||||
## 每次改动后的固定流程
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
1. **构建验证**:运行 `./gradlew shadowJar`,确认编译通过并产出 `build/libs/CraftBank-<version>.jar`。
|
||||
2. **更新 README**:若改动影响功能、指令、权限、配置项或 PlaceholderAPI 变量,同步更新 `README.md`。
|
||||
3. **提交并推送**到 `main`。
|
||||
4. **发布 Release**:用 `gh release create` 创建新发行版,附上编译好的 jar。
|
||||
4. **发布 Release**:在 Gitea 创建新发行版,附上编译好的 jar。
|
||||
|
||||
## 版本号约定(语义化版本)
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
# 🏦 CraftBank(工艺银行)
|
||||
|
||||
> 适用于 **Folia 1.21.8** 的全能经济与银行核心插件 —— 旨在完全替代 XConomy 等传统经济插件。
|
||||
> 适用于 **Folia / Purpur 1.21.8** 的全能经济与银行核心插件 —— 旨在完全替代 XConomy 等传统经济插件。
|
||||
|
||||
[](https://papermc.io/software/folia)
|
||||
[](https://purpurmc.org/)
|
||||
[](https://adoptium.net/)
|
||||
[](https://www.spigotmc.org/resources/vault.34315/)
|
||||
|
||||
CraftBank 内置完善的「个人钱包 / 现金」系统,并引入深度模拟的银行系统:实体银行卡、可交易支票、活期储蓄与严格的定期存款(带利息)。全程遵循 **Folia 多核服务端**的线程模型,使用 `AsyncScheduler` / `GlobalRegionScheduler` / `RegionScheduler` 调度,绝不使用已弃用的 `Bukkit.getScheduler()`。
|
||||
CraftBank 内置完善的「个人钱包 / 现金」系统,并引入深度模拟的银行系统:实体银行卡、可交易支票、活期储蓄与严格的定期存款(带利息)。在 Folia 上遵循多核服务端线程模型,使用 `AsyncScheduler` / `GlobalRegionScheduler` / `RegionScheduler` 调度;在 Purpur / Paper 上自动回退到 Bukkit 同步 / 异步任务调度。
|
||||
|
||||
---
|
||||
|
||||
@@ -15,6 +16,7 @@ CraftBank 内置完善的「个人钱包 / 现金」系统,并引入深度模
|
||||
| 模块 | 说明 |
|
||||
|------|------|
|
||||
| 💵 **核心经济** | 每位玩家独立现金账户;实现 Vault `Economy` 接口并以 `Highest` 优先级注册,接管全服扣款 |
|
||||
| 🧩 **平台兼容** | 同一个 jar 支持 Folia 与 Purpur/Paper;运行时自动选择正确调度器 |
|
||||
| 💳 **银行卡** | 实体物品,PDC 绑定 UUID + 序列号;右键指定方块开柜;丢失可挂失补办(旧卡作废)|
|
||||
| 📜 **支票** | `/cheque <金额>` 具现化为可流通实体物品;右键兑现入钱包;PDC 防伪 + 防刷 |
|
||||
| 💰 **活期储蓄** | 存取自由,按真实时间戳逐日复利结算(含离线补偿,带封顶)|
|
||||
@@ -65,6 +67,8 @@ CraftBank 内置完善的「个人钱包 / 现金」系统,并引入深度模
|
||||
|
||||
## 🔌 PlaceholderAPI 变量
|
||||
|
||||
### CraftBank 原生变量
|
||||
|
||||
| 变量 | 含义 |
|
||||
|------|------|
|
||||
| `%craftbank_cash%` | 现金(纯数字)|
|
||||
@@ -76,6 +80,28 @@ CraftBank 内置完善的「个人钱包 / 现金」系统,并引入深度模
|
||||
| `%craftbank_has_card%` | 是否持有有效银行卡 |
|
||||
| `%craftbank_savings_rate%` | 当前活期日利率百分比 |
|
||||
|
||||
### XConomy 兼容变量
|
||||
|
||||
CraftBank 会额外注册 `xconomy` PlaceholderAPI 扩展,原先使用 XConomy 的变量无需替换即可继续解析。
|
||||
|
||||
| 变量 | 含义 |
|
||||
|------|------|
|
||||
| `%xconomy_balance%` | 现金余额,按 XConomy 默认显示格式输出 |
|
||||
| `%xconomy_balance_value%` | 现金余额原始数字 |
|
||||
| `%xconomy_balance_formatted%` | 现金余额缩写格式(k / m)|
|
||||
| `%xconomy_top_player_<名次>%` | 指定名次的玩家名,例如 `%xconomy_top_player_1%` |
|
||||
| `%xconomy_top_balance_<名次>%` | 指定名次的余额 |
|
||||
| `%xconomy_top_balance_value_<名次>%` | 指定名次的余额原始数字 |
|
||||
| `%xconomy_top_balance_formatted_<名次>%` | 指定名次的余额缩写格式 |
|
||||
| `%xconomy_top_rank%` | 当前玩家现金排行榜名次 |
|
||||
| `%xconomy_top_rank_<玩家名>%` | 指定玩家现金排行榜名次 |
|
||||
| `%xconomy_top_hidden%` | 兼容返回 `0`(CraftBank 不隐藏排行榜)|
|
||||
| `%xconomy_paypermission%` | 兼容返回 `DEFAULT` |
|
||||
| `%xconomy_paypermission_global%` | 兼容返回 `1` |
|
||||
| `%xconomy_paytoggle%` | 兼容返回 `1` |
|
||||
| `%xconomy_sum_balance%` | 全服现金总额 |
|
||||
| `%xconomy_sum_balance_value%` | 全服现金总额原始数字 |
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ 配置文件
|
||||
@@ -97,8 +123,8 @@ CraftBank 内置完善的「个人钱包 / 现金」系统,并引入深度模
|
||||
|
||||
## 🤖 Folia 线程安全设计
|
||||
|
||||
- **后台任务**(数据库、`/baltop` 排序、利息扫描)走 `AsyncScheduler`。
|
||||
- **玩家相关操作**(扣物品、开 GUI、发消息)从异步切回时调度到 `player.getScheduler()`(实体区域线程)。
|
||||
- **Folia 后台任务**(数据库、`/baltop` 排序、利息扫描)走 `AsyncScheduler`;Purpur/Paper 走 Bukkit 异步任务。
|
||||
- **Folia 玩家相关操作**(扣物品、开 GUI、发消息)从异步切回时调度到 `player.getScheduler()`(实体区域线程);Purpur/Paper 走 Bukkit 主线程任务。
|
||||
- 经济数据缓存使用 `ConcurrentHashMap`,单账户余额变更用 `synchronized` 保证原子,抵御来自其它插件异步线程(经 Vault)的并发刷钱。
|
||||
- 支票兑现在玩家区域线程独占操作物品栏;定期领取用数据库 `UPDATE ... WHERE claimed = 0` 原子置位防重复发放。
|
||||
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ plugins {
|
||||
}
|
||||
|
||||
group = "com.craftbank"
|
||||
version = "1.1.0"
|
||||
version = "1.2.0"
|
||||
|
||||
// 用当前 JDK(25)编译,通过 --release 21 产出兼容 Java 21 的字节码,
|
||||
// 避免 toolchain 触发额外的 JDK 下载。Folia 要求运行时为 Java 21+。
|
||||
|
||||
@@ -15,6 +15,7 @@ import com.craftbank.economy.VaultEconomyProvider;
|
||||
import com.craftbank.gui.BankGUI;
|
||||
import com.craftbank.gui.PendingInput;
|
||||
import com.craftbank.hooks.CraftBankExpansion;
|
||||
import com.craftbank.hooks.XConomyExpansion;
|
||||
import com.craftbank.items.BankCardManager;
|
||||
import com.craftbank.items.ChequeManager;
|
||||
import com.craftbank.items.Keys;
|
||||
@@ -22,9 +23,9 @@ import com.craftbank.listeners.InteractListener;
|
||||
import com.craftbank.listeners.InventoryListener;
|
||||
import com.craftbank.listeners.PlayerConnectionListener;
|
||||
import com.craftbank.model.PlayerAccount;
|
||||
import com.craftbank.platform.PlatformScheduler;
|
||||
import com.craftbank.util.Msg;
|
||||
import net.milkbowl.vault.economy.Economy;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
import org.bukkit.command.TabExecutor;
|
||||
import org.bukkit.plugin.ServicePriority;
|
||||
@@ -50,6 +51,8 @@ public final class CraftBank extends JavaPlugin {
|
||||
private ChequeManager chequeManager;
|
||||
private BankGUI bankGUI;
|
||||
private VaultEconomyProvider vaultProvider;
|
||||
private PlatformScheduler scheduler;
|
||||
private XConomyExpansion xConomyExpansion;
|
||||
private Msg msg;
|
||||
|
||||
@Override
|
||||
@@ -82,6 +85,7 @@ public final class CraftBank extends JavaPlugin {
|
||||
this.bankCardManager = new BankCardManager(this);
|
||||
this.chequeManager = new ChequeManager(this);
|
||||
this.bankGUI = new BankGUI(this);
|
||||
this.scheduler = new PlatformScheduler(this);
|
||||
|
||||
registerVault();
|
||||
registerCommands();
|
||||
@@ -95,7 +99,7 @@ public final class CraftBank extends JavaPlugin {
|
||||
runAsync(() -> economyManager.loadBlocking(p.getUniqueId(), p.getName(), true));
|
||||
}
|
||||
|
||||
getLogger().info("CraftBank 已启用 (Folia 模式)。");
|
||||
getLogger().info("CraftBank 已启用 (" + (scheduler.isFolia() ? "Folia" : "Purpur/Paper") + " 模式)。");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -154,7 +158,10 @@ public final class CraftBank extends JavaPlugin {
|
||||
if (getServer().getPluginManager().getPlugin("PlaceholderAPI") != null) {
|
||||
try {
|
||||
new CraftBankExpansion(this).register();
|
||||
getLogger().info("已挂接 PlaceholderAPI。");
|
||||
this.xConomyExpansion = new XConomyExpansion(this);
|
||||
this.xConomyExpansion.register();
|
||||
runAsync(this.xConomyExpansion::refreshCache);
|
||||
getLogger().info("已挂接 PlaceholderAPI (craftbank + xconomy 兼容变量)。");
|
||||
} catch (Throwable t) {
|
||||
getLogger().warning("PlaceholderAPI 挂接失败: " + t.getMessage());
|
||||
}
|
||||
@@ -164,7 +171,7 @@ public final class CraftBank extends JavaPlugin {
|
||||
private void startBackgroundTasks() {
|
||||
long periodMinutes = 60L;
|
||||
// 后台周期任务: 结算在线玩家活期利息 + 自动结算到期定期存款。
|
||||
getServer().getAsyncScheduler().runAtFixedRate(this, task -> {
|
||||
scheduler.runAsyncRepeating(() -> {
|
||||
for (PlayerAccount account : economyManager.getAllCached()) {
|
||||
bankManager.settleSavingsInterest(account);
|
||||
}
|
||||
@@ -172,13 +179,19 @@ public final class CraftBank extends JavaPlugin {
|
||||
}, periodMinutes, periodMinutes, TimeUnit.MINUTES);
|
||||
|
||||
// 定期落库脏账户, 降低崩溃丢数据风险。
|
||||
getServer().getAsyncScheduler().runAtFixedRate(this, task -> {
|
||||
scheduler.runAsyncRepeating(() -> {
|
||||
for (PlayerAccount account : economyManager.getAllCached()) {
|
||||
if (account.isDirty()) {
|
||||
databaseManager.saveAccount(account);
|
||||
}
|
||||
}
|
||||
}, 5L, 5L, TimeUnit.MINUTES);
|
||||
|
||||
scheduler.runAsyncRepeating(() -> {
|
||||
if (xConomyExpansion != null) {
|
||||
xConomyExpansion.refreshCache();
|
||||
}
|
||||
}, 5L, 5L, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
@@ -187,17 +200,17 @@ public final class CraftBank extends JavaPlugin {
|
||||
|
||||
/** 在异步线程立即执行 (用于数据库读写等纯后台任务)。 */
|
||||
public void runAsync(Runnable runnable) {
|
||||
getServer().getAsyncScheduler().runNow(this, task -> runnable.run());
|
||||
scheduler.runAsync(runnable);
|
||||
}
|
||||
|
||||
/** 在指定玩家所在的实体区域线程执行 (用于操作物品、打开 GUI、发消息)。 */
|
||||
public void runForPlayer(org.bukkit.entity.Player player, Runnable runnable) {
|
||||
player.getScheduler().run(this, task -> runnable.run(), null);
|
||||
scheduler.runForPlayer(player, runnable);
|
||||
}
|
||||
|
||||
/** 在全局区域线程执行。 */
|
||||
public void runGlobal(Runnable runnable) {
|
||||
getServer().getGlobalRegionScheduler().run(this, task -> runnable.run());
|
||||
scheduler.runGlobal(runnable);
|
||||
}
|
||||
|
||||
/** 异步持久化某账户 (脏标记已由数据变更方法设置)。 */
|
||||
|
||||
@@ -187,6 +187,21 @@ public class DatabaseManager {
|
||||
return result;
|
||||
}
|
||||
|
||||
/** 全服现金总额。 */
|
||||
public double getTotalCash() {
|
||||
String sql = "SELECT SUM(cash) FROM craftbank_players";
|
||||
try (Connection conn = dataSource.getConnection();
|
||||
PreparedStatement ps = conn.prepareStatement(sql);
|
||||
ResultSet rs = ps.executeQuery()) {
|
||||
if (rs.next()) {
|
||||
return rs.getDouble(1);
|
||||
}
|
||||
} catch (SQLException ex) {
|
||||
plugin.getLogger().log(Level.SEVERE, "读取全服现金总额失败", ex);
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// 定期存款
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
package com.craftbank.hooks;
|
||||
|
||||
import com.craftbank.CraftBank;
|
||||
import com.craftbank.model.PlayerAccount;
|
||||
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* XConomy PlaceholderAPI 兼容层, 让原有 %xconomy_*% 变量无需替换即可使用。
|
||||
*/
|
||||
public final class XConomyExpansion extends PlaceholderExpansion {
|
||||
|
||||
private static final int RANKING_SIZE = 100;
|
||||
|
||||
private final CraftBank plugin;
|
||||
private final DecimalFormat numberFormat = new DecimalFormat("#,##0.00");
|
||||
private final List<Map.Entry<String, Double>> top = new CopyOnWriteArrayList<>();
|
||||
private volatile double sumBalance;
|
||||
|
||||
public XConomyExpansion(CraftBank plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getIdentifier() {
|
||||
return "xconomy";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getAuthor() {
|
||||
return "CraftBank";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getVersion() {
|
||||
return plugin.getDescription().getVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean persist() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onRequest(OfflinePlayer player, @NotNull String identifier) {
|
||||
String id = identifier.toLowerCase();
|
||||
|
||||
if (id.equals("balance") || id.equals("balance_value") || id.equals("balance_formatted")) {
|
||||
double cash = getCash(player);
|
||||
return switch (id) {
|
||||
case "balance_value" -> decimal(cash);
|
||||
case "balance_formatted" -> formatCompact(cash);
|
||||
default -> formatDisplay(cash);
|
||||
};
|
||||
}
|
||||
|
||||
if (id.startsWith("top_player_")) {
|
||||
Integer index = parseIndex(id, "top_player_");
|
||||
if (index == null) {
|
||||
return "[XConomy]Invalid index";
|
||||
}
|
||||
return topName(index);
|
||||
}
|
||||
|
||||
if (id.equals("top_rank") || id.startsWith("top_rank_")) {
|
||||
String name = id.equals("top_rank") ? (player == null ? null : player.getName())
|
||||
: identifier.substring("top_rank_".length());
|
||||
return rankOf(name);
|
||||
}
|
||||
|
||||
if (id.startsWith("top_balance_formatted_")) {
|
||||
Integer index = parseIndex(id, "top_balance_formatted_");
|
||||
return index == null ? "[XConomy]Invalid index" : topBalance(index, true, false);
|
||||
}
|
||||
|
||||
if (id.startsWith("top_balance_value_")) {
|
||||
Integer index = parseIndex(id, "top_balance_value_");
|
||||
return index == null ? "[XConomy]Invalid index" : topBalance(index, false, true);
|
||||
}
|
||||
|
||||
if (id.startsWith("top_balance_")) {
|
||||
Integer index = parseIndex(id, "top_balance_");
|
||||
return index == null ? "[XConomy]Invalid index" : topBalance(index, false, false);
|
||||
}
|
||||
|
||||
if (id.contains("top_hidden")) {
|
||||
return "0";
|
||||
}
|
||||
|
||||
if (id.contains("paypermission")) {
|
||||
if (id.contains("_global")) {
|
||||
return "1";
|
||||
}
|
||||
return player == null ? "Error" : "DEFAULT";
|
||||
}
|
||||
|
||||
if (id.equals("paytoggle")) {
|
||||
return player == null ? "Error" : "1";
|
||||
}
|
||||
|
||||
if (id.contains("sum_balance")) {
|
||||
return id.contains("sum_balance_value") ? decimal(sumBalance) : formatDisplay(sumBalance);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void refreshCache() {
|
||||
top.clear();
|
||||
top.addAll(plugin.getDatabaseManager().getTopCash(RANKING_SIZE));
|
||||
sumBalance = plugin.getDatabaseManager().getTotalCash();
|
||||
}
|
||||
|
||||
private double getCash(OfflinePlayer player) {
|
||||
if (player == null) {
|
||||
return 0.0;
|
||||
}
|
||||
PlayerAccount account = plugin.getEconomyManager().getCached(player.getUniqueId());
|
||||
return account == null ? 0.0 : account.getCash();
|
||||
}
|
||||
|
||||
private String topName(int oneBasedIndex) {
|
||||
if (oneBasedIndex > top.size()) {
|
||||
return "No data";
|
||||
}
|
||||
return top.get(oneBasedIndex - 1).getKey();
|
||||
}
|
||||
|
||||
private String topBalance(int oneBasedIndex, boolean compact, boolean raw) {
|
||||
if (oneBasedIndex > top.size()) {
|
||||
return "No data";
|
||||
}
|
||||
double balance = top.get(oneBasedIndex - 1).getValue();
|
||||
if (raw) {
|
||||
return decimal(balance);
|
||||
}
|
||||
return compact ? formatCompact(balance) : formatDisplay(balance);
|
||||
}
|
||||
|
||||
private String rankOf(String name) {
|
||||
if (name == null) {
|
||||
return "No data";
|
||||
}
|
||||
for (int i = 0; i < top.size(); i++) {
|
||||
if (name.equalsIgnoreCase(top.get(i).getKey())) {
|
||||
return Integer.toString(i + 1);
|
||||
}
|
||||
}
|
||||
return "No data";
|
||||
}
|
||||
|
||||
private Integer parseIndex(String id, String prefix) {
|
||||
String value = id.substring(prefix.length());
|
||||
if (!value.matches("\\d+") || value.matches("0+")) {
|
||||
return null;
|
||||
}
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
|
||||
private String formatDisplay(double amount) {
|
||||
return ChatColor.translateAlternateColorCodes('&', numberFormat.format(amount) + " "
|
||||
+ (round(amount).compareTo(BigDecimal.ONE) == 0 ? "dollar" : "dollars"));
|
||||
}
|
||||
|
||||
private String formatCompact(double amount) {
|
||||
double abs = Math.abs(amount);
|
||||
if (abs >= 1_000_000.0) {
|
||||
return numberFormat.format(amount / 1_000_000.0) + "m dollars";
|
||||
}
|
||||
if (abs >= 1_000.0) {
|
||||
return numberFormat.format(amount / 1_000.0) + "k dollars";
|
||||
}
|
||||
return formatDisplay(amount);
|
||||
}
|
||||
|
||||
private String decimal(double amount) {
|
||||
return round(amount).toString();
|
||||
}
|
||||
|
||||
private BigDecimal round(double amount) {
|
||||
return BigDecimal.valueOf(amount).setScale(2, RoundingMode.DOWN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package com.craftbank.platform;
|
||||
|
||||
import com.craftbank.CraftBank;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 在 Folia 上使用区域调度器, 在 Purpur/Paper 上回退到 Bukkit 调度器。
|
||||
*/
|
||||
public final class PlatformScheduler {
|
||||
|
||||
private final CraftBank plugin;
|
||||
private final boolean folia;
|
||||
|
||||
public PlatformScheduler(CraftBank plugin) {
|
||||
this.plugin = plugin;
|
||||
this.folia = hasMethod(plugin.getServer().getClass(), "getGlobalRegionScheduler")
|
||||
&& hasMethod(plugin.getServer().getClass(), "getAsyncScheduler");
|
||||
}
|
||||
|
||||
public boolean isFolia() {
|
||||
return folia;
|
||||
}
|
||||
|
||||
public void runAsync(Runnable runnable) {
|
||||
if (!folia) {
|
||||
plugin.getServer().getScheduler().runTaskAsynchronously(plugin, runnable);
|
||||
return;
|
||||
}
|
||||
invokeAsync("runNow", new Class<?>[] {org.bukkit.plugin.Plugin.class, java.util.function.Consumer.class},
|
||||
plugin, (java.util.function.Consumer<Object>) task -> runnable.run());
|
||||
}
|
||||
|
||||
public void runForPlayer(Player player, Runnable runnable) {
|
||||
if (!folia) {
|
||||
plugin.getServer().getScheduler().runTask(plugin, runnable);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Object scheduler = player.getClass().getMethod("getScheduler").invoke(player);
|
||||
Method run = scheduler.getClass().getMethod("run",
|
||||
org.bukkit.plugin.Plugin.class, java.util.function.Consumer.class, Runnable.class);
|
||||
run.invoke(scheduler, plugin, (java.util.function.Consumer<Object>) task -> runnable.run(), null);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new IllegalStateException("Folia player scheduler invocation failed", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void runGlobal(Runnable runnable) {
|
||||
if (!folia) {
|
||||
plugin.getServer().getScheduler().runTask(plugin, runnable);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Object scheduler = plugin.getServer().getClass().getMethod("getGlobalRegionScheduler").invoke(plugin.getServer());
|
||||
Method run = scheduler.getClass().getMethod("run",
|
||||
org.bukkit.plugin.Plugin.class, java.util.function.Consumer.class);
|
||||
run.invoke(scheduler, plugin, (java.util.function.Consumer<Object>) task -> runnable.run());
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new IllegalStateException("Folia global scheduler invocation failed", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void runAsyncRepeating(Runnable runnable, long initialDelay, long period, TimeUnit unit) {
|
||||
if (!folia) {
|
||||
long initialTicks = Math.max(1L, unit.toSeconds(initialDelay) * 20L);
|
||||
long periodTicks = Math.max(1L, unit.toSeconds(period) * 20L);
|
||||
plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, runnable, initialTicks, periodTicks);
|
||||
return;
|
||||
}
|
||||
invokeAsync("runAtFixedRate",
|
||||
new Class<?>[] {org.bukkit.plugin.Plugin.class, java.util.function.Consumer.class, long.class, long.class, TimeUnit.class},
|
||||
plugin, (java.util.function.Consumer<Object>) task -> runnable.run(), initialDelay, period, unit);
|
||||
}
|
||||
|
||||
private void invokeAsync(String name, Class<?>[] parameterTypes, Object... args) {
|
||||
try {
|
||||
Object scheduler = plugin.getServer().getClass().getMethod("getAsyncScheduler").invoke(plugin.getServer());
|
||||
Method method = scheduler.getClass().getMethod(name, parameterTypes);
|
||||
method.invoke(scheduler, args);
|
||||
} catch (ReflectiveOperationException ex) {
|
||||
throw new IllegalStateException("Folia async scheduler invocation failed", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasMethod(Class<?> type, String name) {
|
||||
for (Method method : type.getMethods()) {
|
||||
if (method.getName().equals(name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ main: com.craftbank.CraftBank
|
||||
api-version: '1.21'
|
||||
folia-supported: true
|
||||
authors: [CraftBank]
|
||||
description: All-in-one economy and bank core plugin for Folia 1.21.8.
|
||||
description: All-in-one economy and bank core plugin for Folia/Purpur 1.21.8.
|
||||
softdepend: [Vault, PlaceholderAPI]
|
||||
loadbefore: [Vault]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user