Update: 最新功能更新 - Main/RadioManager/GUIManager/LoginManager等模块优化

This commit is contained in:
ypsdf1
2026-05-15 20:50:12 +08:00
parent 8b73031ef0
commit 302328bc8e
15 changed files with 3852 additions and 4270 deletions
+7 -1
View File
@@ -36,4 +36,10 @@ build/
.vscode/ .vscode/
### Mac OS ### ### Mac OS ###
.DS_Store .DS_Store
# 开发辅助文件(本地保留,不推送到仓库)
txt/
*.xlsx
*.patch
新建*.txt
-1
View File
@@ -62,5 +62,4 @@
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
</project> </project>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+9 -7
View File
@@ -25,7 +25,10 @@ public class ConfigManager {
public long smtpVerifyExpire = 0; public long smtpVerifyExpire = 0;
public long smtpLastSendTime = 0; public long smtpLastSendTime = 0;
public String smtpConfiguringPlayer = ""; public String smtpConfiguringPlayer = "";
public String updatePlatform = "gh"; public String broadcastServerUrl = "";
public int httpPort = 8080;
public String garbageTag= "admin";
public ConfigManager(File dataFolder) { public ConfigManager(File dataFolder) {
@@ -321,11 +324,8 @@ public class ConfigManager {
garbageMaxRounds = parseInt( garbageMaxRounds = parseInt(
m.getOrDefault("垃圾站_保留轮数", m.getOrDefault("垃圾站_保留轮数",
"1"), 1); "1"), 1);
// loadSettings() 末尾添加:
} }
public void saveSettings() { public void saveSettings() {
File file = new File(dataFolder, "插件设置.txt"); File file = new File(dataFolder, "插件设置.txt");
List<String> L = new ArrayList<>(); List<String> L = new ArrayList<>();
@@ -344,8 +344,7 @@ public class ConfigManager {
L.add("垃圾站_清理间隔秒=" + garbageInterval); L.add("垃圾站_清理间隔秒=" + garbageInterval);
L.add("垃圾站_保留轮数=" + garbageMaxRounds); L.add("垃圾站_保留轮数=" + garbageMaxRounds);
writeLines(file, L);
writeLines(file, L);
} }
public double providerPointsPerScore = 2.0; public double providerPointsPerScore = 2.0;
public int baseEconomyReward = 100; public int baseEconomyReward = 100;
@@ -375,6 +374,9 @@ public class ConfigManager {
L.add("垃圾站_启用=true"); L.add("垃圾站_启用=true");
L.add("垃圾站_清理间隔秒=300"); L.add("垃圾站_清理间隔秒=300");
L.add("垃圾站_保留轮数=1"); L.add("垃圾站_保留轮数=1");
L.add("# ---- 资源包(HTTP) ----");
L.add("resource-pack-url=");
L.add("http-port=8080");
writeLines(f, L); writeLines(f, L);
+3 -14
View File
@@ -213,18 +213,6 @@ public class DatabaseManager {
+ ")"); + ")");
safeAdd(st, "menu_snowball", safeAdd(st, "menu_snowball",
"INTEGER DEFAULT 1"); "INTEGER DEFAULT 1");
// ===== 独立任务进度表 =====
st.executeUpdate(
"CREATE TABLE IF NOT EXISTS "
+ "quest_progress ("
+ "player_name TEXT NOT NULL, "
+ "condition_key TEXT NOT NULL, "
+ "progress_value INTEGER DEFAULT 0, "
+ "completed INTEGER DEFAULT 0, "
+ "PRIMARY KEY (player_name, "
+ "condition_key)"
+ ")");
st.close(); st.close();
logger.info("[Sdf1_login] 数据库初始化完成"); logger.info("[Sdf1_login] 数据库初始化完成");
} catch (Exception e) { } catch (Exception e) {
@@ -873,8 +861,9 @@ public class DatabaseManager {
return list; return list;
} }
/*
获取指定ID的备份数据 /**
* 获取指定ID的备份数据
*/ */
public String[] getInventoryBackupById( public String[] getInventoryBackupById(
int backupId) { int backupId) {
+122 -291
View File
@@ -5,6 +5,7 @@ import org.bukkit.Material;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.*; import org.bukkit.inventory.*;
import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.ItemMeta;
import java.io.File;
import java.util.*; import java.util.*;
@@ -41,7 +42,7 @@ public class GUIManager {
g.setItem(14, mkItem(Material.EMERALD, g.setItem(14, mkItem(Material.EMERALD,
"§d§l积分商城")); "§d§l积分商城"));
g.setItem(16, mkItem(Material.CHEST, g.setItem(16, mkItem(Material.CHEST,
"§e§l任务中心")); "§e§l新人礼包"));
g.setItem(18, mkItem(Material.BOOK, g.setItem(18, mkItem(Material.BOOK,
"§6工单系统", "§6工单系统",
"§7提交bug、求助、举报")); "§7提交bug、求助、举报"));
@@ -105,6 +106,32 @@ public class GUIManager {
p.openInventory(g); p.openInventory(g);
} }
// 在 openGiftStages 方法之前添加:
/**
* 获取任务列表(带真实完成状态)
* 使用 QuestTracker 解析 .md 文件
*/
public List<Map<String, Object>> getQuestList(
String category) {
QuestTracker qt = plugin.getQuestTracker();
if (qt == null) return new ArrayList<>();
List<QuestTracker.QuestFile> quests =
qt.getQuests(category);
List<Map<String, Object>> list =
new ArrayList<>();
for (QuestTracker.QuestFile qf : quests) {
Map<String, Object> map =
new LinkedHashMap<>();
map.put("name", qf.displayName);
map.put("conditions", qf.conditions);
map.put("rewards", qf.rewards);
map.put("questFile", qf);
list.add(map);
}
return list;
}
/** /**
* 打开账号删除选择面板 * 打开账号删除选择面板
* 展示该玩家IP下所有账号,供选择删除 * 展示该玩家IP下所有账号,供选择删除
@@ -370,7 +397,6 @@ public class GUIManager {
"§7双击管理")); "§7双击管理"));
slot++; slot++;
} }
g.setItem(53, mkItem(Material.ARROW, "§7返回")); g.setItem(53, mkItem(Material.ARROW, "§7返回"));
p.openInventory(g); p.openInventory(g);
} }
@@ -379,279 +405,154 @@ public class GUIManager {
Inventory g = Bukkit.createInventory( Inventory g = Bukkit.createInventory(
null, 27, T_TASK_CENTER); null, 27, T_TASK_CENTER);
fillBg(g); fillBg(g);
QuestTracker qt =
plugin.getQuestTracker();
String player = p.getName();
// 新人任务进度
List<QuestTracker.QuestFile> newQuests =
qt.getQuests("新人任务");
int newTotal = newQuests.size();
int newDone =
qt.getCompletedStageCount(
player, newQuests);
QuestTracker.QuestFile newCurrent =
qt.getCurrentStage(
player, newQuests);
String newInfo = newDone + "/" + newTotal;
List<String> newLore = new ArrayList<>();
newLore.add("§7进度: §e" + newInfo);
if (newCurrent != null) {
newLore.add("§7当前: §f"
+ newCurrent.displayName);
if (newCurrent.hasConditions()) {
List<String> unmet =
qt.getUnmetConditions(
player,
newCurrent.conditions);
if (!unmet.isEmpty()) {
for (String c : unmet) {
newLore.add("§7 "
+ qt.getProgress(
player, c));
}
} else {
newLore.add("§a✓ 条件已满足");
}
}
} else {
newLore.add("§a§l全部完成!");
}
g.setItem(10, mkItem(Material.BOOK, g.setItem(10, mkItem(Material.BOOK,
"§e§l新人任务 §7[" "§e§l新人任务",
+ newInfo + "]", "§7完成新手阶段获取奖励"));
newLore.toArray(new String[0])));
// 主线任务
List<QuestTracker.QuestFile> mainQuests =
qt.getQuests("主线任务");
g.setItem(12, mkItem(Material.COMPASS, g.setItem(12, mkItem(Material.COMPASS,
"§b§l主线任务 §7[" "§b§l主线任务",
+ qt.getCompletedStageCount(
player, mainQuests)
+ "/" + mainQuests.size()
+ "]",
"§7查看主线任务列表")); "§7查看主线任务列表"));
// 支线任务
List<QuestTracker.QuestFile> sideQuests =
qt.getQuests("支线任务");
g.setItem(14, mkItem(Material.PAPER, g.setItem(14, mkItem(Material.PAPER,
"§a§l支线任务 §7[" "§a§l支线任务",
+ qt.getCompletedStageCount(
player, sideQuests)
+ "/" + sideQuests.size()
+ "]",
"§7查看支线任务列表")); "§7查看支线任务列表"));
g.setItem(16, mkItem(Material.EMERALD, g.setItem(16, mkItem(Material.EMERALD,
"§d§l每日签到", "§d§l每日签到",
"§7签到获取积分")); "§7签到获取积分"));
g.setItem(22, mkItem(Material.ARROW, "§7返回"));
// 打卡上报按钮(slot 21)
g.setItem(21, mkItem(
Material.EMERALD,
"§a§l打卡上报",
"§7验证当前阶段任务完成情况",
"§7满足条件将自动标记完成"));
// 返回按钮(slot 26)
g.setItem(26, mkItem(
Material.ARROW, "§7返回"));
p.openInventory(g); p.openInventory(g);
} }
public void openGiftStages(Player p) { public void openGiftStages(Player p) {
Inventory g = Bukkit.createInventory( Inventory g = Bukkit.createInventory(
null, 54, T_GIFT_STAGES); null, 27, T_GIFT_STAGES);
fillBg(g); fillBg(g);
QuestTracker qt = QuestTracker qt = plugin.getQuestTracker();
plugin.getQuestTracker(); if (qt == null) {
g.setItem(22, mkItem(Material.BARRIER,
"§7任务系统未加载"));
p.openInventory(g);
return;
}
List<QuestTracker.QuestFile> quests = List<QuestTracker.QuestFile> quests =
qt.getQuests("新人任务"); qt.getQuests("新人任务");
int total = quests.size(); String playerName = p.getName();
int completed =
qt.getCompletedStageCount(
p.getName(), quests);
QuestTracker.QuestFile current =
qt.getCurrentStage(
p.getName(), quests);
g.setItem(4, mkItem(Material.BOOK, int max = quests.size();
"§e§l新人任务进度", for (int i = 0; i < max && i < 9; i++) {
"§7已完成: §e" + completed QuestTracker.QuestFile qf = quests.get(i);
+ "/" + total, boolean completed =
current != null qt.isStageCompleted(playerName, qf);
? "§7当前: §f" boolean claimed =
+ current.displayName qt.hasClaimed(playerName, qf);
: "§a§l全部完成!"));
for (int i = 0;
i < quests.size() && i < 44; i++) {
QuestTracker.QuestFile qf =
quests.get(i);
boolean done =
qt.isStageCompleted(
p.getName(), qf);
boolean isCurrent =
(current != null
&& current == qf);
Material mat; Material mat;
List<String> lore = new ArrayList<>(); String status;
if (completed && claimed) {
if (done) {
mat = Material.LIME_WOOL; mat = Material.LIME_WOOL;
} else if (isCurrent) { status = "§a已领取";
} else if (completed) {
mat = Material.YELLOW_WOOL; mat = Material.YELLOW_WOOL;
status = "§e可领取";
} else { } else {
mat = Material.RED_WOOL; mat = Material.RED_WOOL;
status = "§c未达标";
} }
if (!qf.details.isEmpty()) { // 显示进度
lore.add("§7---- 任务详情 ----"); List<String> lore = new ArrayList<>();
for (String d : qf.details) { lore.add("§7" + status);
lore.add("§f" + d); for (String cond : qf.conditions) {
} String progress =
} qt.getProgress(playerName, cond);
lore.add("§7" + progress);
if (qf.hasConditions()
&& (done || isCurrent)) {
lore.add("");
for (String c : qf.conditions) {
if (qt.isOptional(c))
continue;
if (qt.isConditionMet(
p.getName(), c)) {
lore.add("§7 §a✓ "
+ qt.getProgress(
p.getName(), c));
} else {
lore.add("§7 §c✗ "
+ qt.getProgress(
p.getName(), c));
}
}
}
if (!qf.rewards.isEmpty()) {
lore.add("");
lore.add("§7---- 奖励 ----");
for (String r : qf.rewards) {
lore.add("§a" + r);
}
}
if (done
&& !qf.rewards.isEmpty()
&& !qt.hasClaimed(
p.getName(), qf)) {
lore.add("");
lore.add("§e§l点击领取奖励");
}
if (done && qt.hasClaimed(
p.getName(), qf)) {
lore.add("");
lore.add("§7已领取奖励");
} }
g.setItem(10 + i, mkItem(mat, g.setItem(10 + i, mkItem(mat,
"§e" + qf.displayName, "§e" + qf.displayName,
lore.toArray(new String[0]))); lore.toArray(new String[0])));
} }
// 返回按钮 g.setItem(26, mkItem(Material.ARROW, "§7返回"));
g.setItem(53, mkItem(
Material.ARROW, "§7返回"));
p.openInventory(g); p.openInventory(g);
} }
public void openTaskList(Player p, String folderName) {
Inventory g = Bukkit.createInventory(null, 54,
public void openTaskList(Player p,
String folderName) {
QuestTracker qt =
plugin.getQuestTracker();
List<QuestTracker.QuestFile> tasks =
qt.getQuests(folderName);
int size = Math.min(54,
Math.max(54,
((tasks.size() / 9) + 1) * 9));
Inventory g = Bukkit.createInventory(
null, size,
"§d§l" + folderName); "§d§l" + folderName);
fillBg(g); fillBg(g);
for (int i = 0; // [FIX] 使用 QuestTracker 获取真实任务数据
i < tasks.size() QuestTracker qt = plugin.getQuestTracker();
&& i < 45; i++) { if (qt == null) {
QuestTracker.QuestFile qf = g.setItem(22, mkItem(Material.BARRIER,
tasks.get(i); "§7任务系统未加载"));
boolean done = p.openInventory(g);
qt.isStageCompleted( return;
p.getName(), qf); }
Material mat = done
? Material.LIME_WOOL List<QuestTracker.QuestFile> quests =
: Material.BOOK; qt.getQuests(folderName);
String playerName = p.getName();
if (quests.isEmpty()) {
g.setItem(22, mkItem(Material.BARRIER,
"§7暂无任务"));
p.openInventory(g);
return;
}
int slot = 0;
for (QuestTracker.QuestFile qf : quests) {
if (slot >= 45) break;
boolean completed =
qt.isStageCompleted(playerName, qf);
boolean claimed =
qt.hasClaimed(playerName, qf);
Material mat;
String status;
if (completed && claimed) {
mat = Material.LIME_WOOL;
status = "§a✓ 已完成并领取";
} else if (completed) {
mat = Material.YELLOW_WOOL;
status = "§e✓ 已完成 - 可领取奖励";
} else {
mat = Material.RED_WOOL;
status = "§c未完成";
}
List<String> lore = new ArrayList<>(); List<String> lore = new ArrayList<>();
lore.add(status);
lore.add("§7───── 条件 ─────");
// 解锁条件 // 显示每个条件及完成状态
if (qf.hasConditions()) { for (String cond : qf.conditions) {
lore.add("§7──── 解锁条件 ────"); String progress =
for (String c : qf.conditions) { qt.getProgress(playerName, cond);
if (qt.isOptional(c)) if (qt.isOptional(cond)) {
continue; lore.add("§7[可选] " + progress);
if (qt.isConditionMet( } else {
p.getName(), c)) { lore.add("§e" + progress);
lore.add("§a ✓ "
+ qt.getProgress(
p.getName(), c));
} else {
lore.add("§c ✗ "
+ qt.getProgress(
p.getName(), c));
}
} }
} }
// 任务详情
if (!qf.details.isEmpty()) {
lore.add("");
lore.add("§7──── 任务详情 ────");
for (String d : qf.details) {
lore.add("§f" + d);
}
}
// 任务奖励
if (!qf.rewards.isEmpty()) { if (!qf.rewards.isEmpty()) {
lore.add(""); lore.add("");
lore.add("§7──── 任务奖励 ────"); lore.add("§7───── 奖励 ─────");
for (String r : qf.rewards) { for (String r : qf.rewards) {
lore.add("§a" + r); lore.add("§a" + r);
} }
} }
g.setItem(i, mkItem(mat, g.setItem(slot, mkItem(mat,
(done ? "§a✓ " : "§e") "§e" + qf.displayName,
+ qf.displayName,
lore.toArray(new String[0]))); lore.toArray(new String[0])));
slot++;
} }
if (tasks.isEmpty()) { g.setItem(53, mkItem(Material.ARROW, "§7返回"));
g.setItem(22, mkItem(
Material.BARRIER,
"§7暂无任务"));
}
g.setItem(size - 1, mkItem(
Material.ARROW, "§7返回"));
p.openInventory(g); p.openInventory(g);
} }
@@ -683,81 +584,11 @@ public class GUIManager {
Material.SNOWBALL, Material.SNOWBALL,
"§e§l雪球菜单", "§e§l雪球菜单",
"§7点击为该玩家发放菜单雪球")); "§7点击为该玩家发放菜单雪球"));
g.setItem(16, mkItem(
Material.ENDER_EYE,
"§b§l打卡记录",
"§7查看该玩家的群系打卡记录"));
g.setItem(22, mkItem(Material.ARROW, "§7返回")); g.setItem(22, mkItem(Material.ARROW, "§7返回"));
p.openInventory(g); p.openInventory(g);
} }
public void openCheckInRecords(
Player p, String target) {
Inventory g = Bukkit.createInventory(
null, 54,
"§b§l打卡记录: " + target);
fillBg(g);
QuestTracker qt =
plugin.getQuestTracker();
List<String> records =
qt.getPlayerRecords(target);
java.text.SimpleDateFormat sdf =
new java.text.SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss");
int slot = 0;
for (int i = records.size() - 1;
i >= 0 && slot < 45; i--) {
String r = records.get(i);
String[] parts = r.split("\\|");
if (parts.length < 7) continue;
String world = parts[1];
String biome = parts[2];
String category = parts[3];
String x = parts[4];
String y = parts[5];
String z = parts[6];
String timeStr = "";
if (parts.length >= 8) {
try {
long ts = Long.parseLong(
parts[7]);
timeStr = sdf.format(
new java.util.Date(
ts));
} catch (Exception e) {
timeStr = parts[7];
}
}
g.setItem(slot, mkItem(
Material.PAPER,
"§e" + category + " - "
+ biome,
"§7时间: " + timeStr,
"§7世界: " + world,
"§7坐标: " + x
+ ", " + y
+ ", " + z));
slot++;
}
if (slot == 0) {
g.setItem(22, mkItem(
Material.BARRIER,
"§7暂无打卡记录"));
}
g.setItem(53, mkItem(
Material.ARROW, "§7返回"));
p.openInventory(g);
}
/** /**
* 打开账号请求面板 * 打开账号请求面板
* 玩家通过 /oa 命令触发 * 玩家通过 /oa 命令触发
+96 -89
View File
@@ -1,89 +1,96 @@
package Sdf1_login; package Sdf1_login;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.*; import java.util.*;
public class InviteManager { public class InviteManager {
private final Main plugin; private final Main plugin;
public InviteManager(Main plugin) { public InviteManager(Main plugin) {
this.plugin = plugin; this.plugin = plugin;
} }
public String generateCode(Player p) { public String generateCode(Player p) {
String code = PasswordUtils.generateInviteCode(); String code = PasswordUtils.generateInviteCode();
plugin.getDb().setField( plugin.getDb().setField(
p.getName(), "invite_code", code); p.getName(), "invite_code", code);
return code; return code;
} }
public void onInviteeCheckIn(String inviteeName) { public void onInviteeCheckIn(String inviteeName) {
String invitedBy = (String) plugin.getDb() String invitedBy = (String) plugin.getDb()
.getField(inviteeName, "invited_by"); .getField(inviteeName, "invited_by");
if (invitedBy == null || invitedBy.isEmpty()) if (invitedBy == null || invitedBy.isEmpty())
return; return;
String month = new SimpleDateFormat("yyyy-MM") String month = new SimpleDateFormat("yyyy-MM")
.format(new Date()); .format(new Date());
String lastMonth = (String) plugin.getDb() String lastMonth = (String) plugin.getDb()
.getField(invitedBy, "invite_month"); .getField(invitedBy, "invite_month");
if (!month.equals(lastMonth)) { if (!month.equals(lastMonth)) {
plugin.getDb().setField( plugin.getDb().setField(
invitedBy, "invite_month", month); invitedBy, "invite_month", month);
plugin.getDb().setField( plugin.getDb().setField(
invitedBy, "monthly_invite_count", 0); invitedBy, "monthly_invite_count", 0);
} }
int count = ((Number) plugin.getDb() int count = ((Number) plugin.getDb()
.getField(invitedBy, .getField(invitedBy,
"monthly_invite_count")) "monthly_invite_count"))
.intValue(); .intValue();
plugin.getDb().setField( plugin.getDb().setField(
invitedBy, "monthly_invite_count", invitedBy, "monthly_invite_count",
count + 1); count + 1);
double rate = plugin.getCommission() double rate = 0.10;
.getCommissionRate(invitedBy); if (count + 1 > 15) {
int bonus = (int) (1 * rate); rate = 0.60;
if (bonus > 0) { } else if (count + 1 > 10) {
plugin.getDb().addPoints(invitedBy, bonus); rate = 0.35;
Player inviterPlayer = Bukkit } else if (count + 1 > 5) {
.getPlayerExact(invitedBy); rate = 0.15;
if (inviterPlayer != null }
&& inviterPlayer.isOnline()) {
inviterPlayer.sendMessage( int bonus = (int) (1 * rate);
plugin.getConfig2() if (bonus > 0) {
.msg("invite_referral_bonus") plugin.getDb().addPoints(invitedBy, bonus);
.replace("{points}", Player inviterPlayer = Bukkit
String.valueOf(bonus))); .getPlayerExact(invitedBy);
} if (inviterPlayer != null
} && inviterPlayer.isOnline()) {
} inviterPlayer.sendMessage(
plugin.getConfig2()
public List<Map<String, Object>> getAllInviteData() { .msg("invite_referral_bonus")
List<Map<String, Object>> result = .replace("{points}",
new ArrayList<>(); String.valueOf(bonus)));
for (Map<String, Object> user }
: plugin.getDb().getAllUsers()) { }
String code = (String) user.getOrDefault( }
"invite_code", "");
if (!code.isEmpty()) { public List<Map<String, Object>> getAllInviteData() {
Map<String, Object> row = List<Map<String, Object>> result =
new LinkedHashMap<>(); new ArrayList<>();
row.put("player", for (Map<String, Object> user
user.get("player_name")); : plugin.getDb().getAllUsers()) {
row.put("code", code); String code = (String) user.getOrDefault(
row.put("monthly", "invite_code", "");
user.getOrDefault( if (!code.isEmpty()) {
"monthly_invite_count", 0)); Map<String, Object> row =
result.add(row); new LinkedHashMap<>();
} row.put("player",
} user.get("player_name"));
return result; row.put("code", code);
} row.put("monthly",
} user.getOrDefault(
"monthly_invite_count", 0));
result.add(row);
}
}
return result;
}
}
+308 -302
View File
@@ -1,302 +1,308 @@
package Sdf1_login; package Sdf1_login;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.Sound; import org.bukkit.Sound;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
public class LoginManager { public class LoginManager {
private final Main plugin; private final Main plugin;
public LoginManager(Main plugin) { public LoginManager(Main plugin) {
this.plugin = plugin; this.plugin = plugin;
} }
public boolean handleRegister(Player p, public boolean handleRegister(Player p,
String[] args) { String[] args) {
String name = p.getName(); String name = p.getName();
// 已注册玩家不能再注册 // 已注册玩家不能再注册
if (plugin.getDb().userExists(name)) { if (plugin.getDb().userExists(name)) {
p.sendMessage(plugin.getConfig2() p.sendMessage(plugin.getConfig2()
.msg("already_registered")); .msg("already_registered"));
return true; return true;
} }
// IP注册限制检查 // IP注册限制检查
String ip = plugin.getPlayerIP(p); String ip = plugin.getPlayerIP(p);
if (ip != null && plugin.getConfig2() if (ip != null && plugin.getConfig2()
.maxAccountsPerIP > 0) { .maxAccountsPerIP > 0) {
if (!plugin.getIPGroup() if (!plugin.getIPGroup()
.canRegister(ip)) { .canRegister(ip)) {
int count = plugin.getIPGroup() int count = plugin.getIPGroup()
.getAccountCount(ip); .getAccountCount(ip);
int max = plugin.getConfig2() int max = plugin.getConfig2()
.maxAccountsPerIP; .maxAccountsPerIP;
p.sendMessage("§c该IP注册已达上限(" p.sendMessage("§c该IP注册已达上限("
+ count + "/" + max + ")"); + count + "/" + max + ")");
return true; return true;
} }
} }
if (args.length < 1) { if (args.length < 1) {
p.sendMessage("§c用法: /reg <密码>"); p.sendMessage("§c用法: /reg <密码>");
p.sendMessage("§7密码要求: 6位以上," p.sendMessage("§7密码要求: 6位以上,"
+ "包含大小写字母和数字"); + "包含大小写字母和数字");
return true; return true;
} }
String pwd = args[0]; String pwd = args[0];
if (pwd.length() < 6) { if (pwd.length() < 6) {
p.sendMessage("§c密码长度不足6位"); p.sendMessage("§c密码长度不足6位");
return true; return true;
} }
if (!pwd.matches(".*[a-z].*") if (!pwd.matches(".*[a-z].*")
|| !pwd.matches(".*[A-Z].*") || !pwd.matches(".*[A-Z].*")
|| !pwd.matches(".*[0-9].*")) { || !pwd.matches(".*[0-9].*")) {
p.sendMessage("§c密码必须包含大写字母、" p.sendMessage("§c密码必须包含大写字母、"
+ "小写字母和数字"); + "小写字母和数字");
return true; return true;
} }
String salt = String salt =
PasswordUtils.generateSalt(); PasswordUtils.generateSalt();
String hash = String hash =
PasswordUtils.hash(pwd, salt); PasswordUtils.hash(pwd, salt);
plugin.getDb().createUser( plugin.getDb().createUser(
name, hash, salt); name, hash, salt);
plugin.getLoggedIn().add(name); plugin.getLoggedIn().add(name);
plugin.getDb().setLoggedIn(name, true); p.setAllowFlight(false);
plugin.getDb().setField(name, p.setFlying(false);
"last_login_time", plugin.getDb().setLoggedIn(name, true);
System.currentTimeMillis()); plugin.getDb().setField(name,
plugin.getDb().setField(name, "last_login_time",
"last_online_check", System.currentTimeMillis());
System.currentTimeMillis()); plugin.getDb().setField(name,
plugin.getDb().recordIP(name, ip); "last_online_check",
plugin.getDb().setField(name, System.currentTimeMillis());
"register_time", plugin.getDb().recordIP(name, ip);
System.currentTimeMillis()); plugin.getDb().setField(name,
plugin.restoreInventory(p); "register_time",
plugin.recordIPLogin(p); System.currentTimeMillis());
plugin.giveMenuSnowball(p); plugin.restoreInventory(p);
plugin.recordIPLogin(p);
p.sendMessage(plugin.getConfig2() plugin.giveMenuSnowball(p);
.msg("reg_success"));
playRegisterSound(p); p.sendMessage(plugin.getConfig2()
.msg("reg_success"));
try { playRegisterSound(p);
org.bukkit.plugin.Plugin cy =
Bukkit.getPluginManager() try {
.getPlugin("CY_beibao"); org.bukkit.plugin.Plugin cy =
if (cy != null && cy.isEnabled()) { Bukkit.getPluginManager()
cy.getClass().getMethod( .getPlugin("CY_beibao");
"onSdf1Activation", if (cy != null && cy.isEnabled()) {
String.class, cy.getClass().getMethod(
int.class, int.class) "onSdf1Activation",
.invoke(cy, name, 9, 0); String.class,
} int.class, int.class)
} catch (Exception ignored) { .invoke(cy, name, 9, 0);
} }
return true; } catch (Exception ignored) {
} }
return true;
private void playRegisterSound(Player p) { }
p.playSound(p.getLocation(),
Sound.BLOCK_NOTE_BLOCK_CHIME, private void playRegisterSound(Player p) {
1.0f, 0.79f); p.playSound(p.getLocation(),
p.playSound(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_CHIME,
Sound.BLOCK_NOTE_BLOCK_BASS, 1.0f, 0.79f);
1.0f, 0.79f); p.playSound(p.getLocation(),
p.playSound(p.getLocation(), Sound.BLOCK_NOTE_BLOCK_BASS,
Sound.BLOCK_NOTE_BLOCK_PLING, 1.0f, 0.79f);
1.0f, 0.79f); p.playSound(p.getLocation(),
} Sound.BLOCK_NOTE_BLOCK_PLING,
1.0f, 0.79f);
}
public boolean handleLogin(Player p,
String[] args) {
String name = p.getName(); public boolean handleLogin(Player p,
try { String[] args) {
if (plugin.getLoggedIn().contains(name)) { String name = p.getName();
p.sendMessage(plugin.getConfig2() try {
.msg("already_logged_in")); if (plugin.getLoggedIn().contains(name)) {
return true; p.sendMessage(plugin.getConfig2()
} .msg("already_logged_in"));
if (!plugin.getDb().userExists(name)) { return true;
p.sendMessage(plugin.getConfig2() }
.msg("not_registered")); if (!plugin.getDb().userExists(name)) {
return true; p.sendMessage(plugin.getConfig2()
} .msg("not_registered"));
if (args.length < 1) { return true;
p.sendMessage("§c用法: /login <密码>"); }
return true; if (args.length < 1) {
} p.sendMessage("§c用法: /login <密码>");
String pwd = args[0]; return true;
String salt = (String) plugin.getDb() }
.getField(name, "password_salt"); String pwd = args[0];
if (salt == null) { String salt = (String) plugin.getDb()
p.sendMessage( .getField(name, "password_salt");
"§c数据异常,请联系管理员"); if (salt == null) {
plugin.getLogger().severe( p.sendMessage(
"[Sdf1_login] 玩家 " + name "§c数据异常,请联系管理员");
+ " 盐值为null"); plugin.getLogger().severe(
return true; "[Sdf1_login] 玩家 " + name
} + " 盐值为null");
String hash = return true;
PasswordUtils.hash(pwd, salt); }
String hash =
boolean matchMain = PasswordUtils.hash(pwd, salt);
plugin.getDb().checkPassword(
name, hash); boolean matchMain =
if (matchMain) { plugin.getDb().checkPassword(
plugin.getLoggedIn().add(name); name, hash);
plugin.getDb().setLoggedIn( if (matchMain) {
name, true); plugin.getLoggedIn().add(name);
plugin.getDb().setField(name, plugin.getDb().setLoggedIn(
"last_login_time", name, true);
System.currentTimeMillis()); plugin.getDb().setLoggedIn(
plugin.getDb().setField(name, name, true);
"last_online_check", plugin.getDb().setField(name,
System.currentTimeMillis()); "last_login_time",
// 只在有有效备份时恢复 System.currentTimeMillis());
plugin.restoreInventory(p); plugin.getDb().setField(name,
plugin.recordIPLogin(p); "last_online_check",
plugin.giveMenuSnowball(p); System.currentTimeMillis());
p.sendMessage(plugin.getConfig2() // 只在有有效备份时恢复
.msg("login_success")); plugin.restoreInventory(p);
activateCY(p, name); plugin.recordIPLogin(p);
return true; plugin.giveMenuSnowball(p);
} p.sendMessage(plugin.getConfig2()
.msg("login_success"));
boolean matchTemp = activateCY(p, name);
plugin.getDb() return true;
.checkPasswordOrTemp( }
name, hash);
if (matchTemp) { boolean matchTemp =
plugin.getLoggedIn().add(name); plugin.getDb()
plugin.getDb().setLoggedIn( .checkPasswordOrTemp(
name, true); name, hash);
plugin.getDb().setField(name, if (matchTemp) {
"last_login_time", plugin.getLoggedIn().add(name);
System.currentTimeMillis()); plugin.getDb().setLoggedIn(
plugin.getDb().setField(name, name, true);
"last_online_check", p.setAllowFlight(false);
System.currentTimeMillis()); p.setFlying(false);
plugin.restoreInventory(p); plugin.getDb().setField(name,
plugin.recordIPLogin(p); "last_login_time",
plugin.giveMenuSnowball(p); System.currentTimeMillis());
plugin.getNeedsPasswordChange() plugin.getDb().setField(name,
.add(name); "last_online_check",
p.sendMessage(plugin.getConfig2() System.currentTimeMillis());
.msg("login_success")); plugin.restoreInventory(p);
p.sendMessage( plugin.recordIPLogin(p);
"§c§l[警告] §f您使用的是临时密码,请尽快修改密码!"); plugin.giveMenuSnowball(p);
p.sendMessage( plugin.getNeedsPasswordChange()
"§7用法: /sdf1_login pw"); .add(name);
activateCY(p, name); p.sendMessage(plugin.getConfig2()
return true; .msg("login_success"));
} p.sendMessage(
"§c§l[警告] §f您使用的是临时密码,请尽快修改密码!");
p.sendMessage(
p.sendMessage(plugin.getConfig2() "§7用法: /sdf1_login pw");
.msg("login_failed")); activateCY(p, name);
} catch (Exception e) { return true;
plugin.getLogger().severe( }
"[Sdf1_login] handleLogin异常: "
+ e.getMessage());
e.printStackTrace(); p.sendMessage(plugin.getConfig2()
p.sendMessage( .msg("login_failed"));
"§c登录过程出错,请联系管理员"); } catch (Exception e) {
} plugin.getLogger().severe(
return true; "[Sdf1_login] handleLogin异常: "
} + e.getMessage());
e.printStackTrace();
p.sendMessage(
public void handleReset(Player p) { "§c登录过程出错,请联系管理员");
String name = p.getName(); }
String emailAddr = (String) return true;
plugin.getDb().getField( }
name, "email");
if (emailAddr == null
|| emailAddr.isEmpty()) { public void handleReset(Player p) {
p.sendMessage( String name = p.getName();
"§c未设置邮箱,无法重置密码"); String emailAddr = (String)
return; plugin.getDb().getField(
} name, "email");
String tempPwd = PasswordUtils if (emailAddr == null
.generateTempPassword(); || emailAddr.isEmpty()) {
String salt = (String) p.sendMessage(
plugin.getDb().getField( "§c未设置邮箱,无法重置密码");
name, "password_salt"); return;
String hash = PasswordUtils.hash( }
tempPwd, salt); String tempPwd = PasswordUtils
plugin.getDb().setField(name, .generateTempPassword();
"temp_password", hash); String salt = (String)
plugin.getDb().setField(name, plugin.getDb().getField(
"temp_pw_expire", name, "password_salt");
System.currentTimeMillis() String hash = PasswordUtils.hash(
+ 300000L); tempPwd, salt);
plugin.getDb().setField(name,
plugin.getDb().setField(name, "temp_password", hash);
"temp_pw_used", 0); plugin.getDb().setField(name,
p.sendMessage("§e正在发送临时密码..."); "temp_pw_expire",
final String to = emailAddr; System.currentTimeMillis()
final String fName = name; + 300000L);
final String pwd = tempPwd;
final Main self = plugin; plugin.getDb().setField(name,
Bukkit.getScheduler() "temp_pw_used", 0);
.runTaskAsynchronously(self, p.sendMessage("§e正在发送临时密码...");
() -> { final String to = emailAddr;
final boolean[] sent = {false}; final String fName = name;
Thread t = new Thread(() -> { final String pwd = tempPwd;
sent[0] = self.getEmail() final Main self = plugin;
.sendTempPassword( Bukkit.getScheduler()
to, fName, pwd); .runTaskAsynchronously(self,
}, "Sdf1_login-reset"); () -> {
t.setDaemon(true); final boolean[] sent = {false};
t.start(); Thread t = new Thread(() -> {
try { sent[0] = self.getEmail()
t.join(15000); .sendTempPassword(
} catch ( to, fName, pwd);
InterruptedException ignored) { }, "Sdf1_login-reset");
} t.setDaemon(true);
if (t.isAlive()) { t.start();
t.interrupt(); try {
Bukkit.getScheduler().runTask( t.join(15000);
self, () -> } catch (
p.sendMessage( InterruptedException ignored) {
"§c邮件发送超时")); }
return; if (t.isAlive()) {
} t.interrupt();
Bukkit.getScheduler().runTask( Bukkit.getScheduler().runTask(
self, () -> { self, () ->
if (sent[0]) { p.sendMessage(
p.sendMessage( "§c邮件发送超时"));
"§a临时密码已发送到 " return;
+ to); }
} else { Bukkit.getScheduler().runTask(
p.sendMessage( self, () -> {
"§c邮件发送失败"); if (sent[0]) {
} p.sendMessage(
}); "§a临时密码已发送到 "
}); + to);
} } else {
p.sendMessage(
private void activateCY(Player p, "§c邮件发送失败");
String name) { }
try { });
org.bukkit.plugin.Plugin cy = });
Bukkit.getPluginManager() }
.getPlugin("CY_beibao");
if (cy != null && cy.isEnabled()) { private void activateCY(Player p,
cy.getClass().getMethod( String name) {
"onSdf1Activation", try {
String.class, org.bukkit.plugin.Plugin cy =
int.class, int.class) Bukkit.getPluginManager()
.invoke(cy, name, 0, 0); .getPlugin("CY_beibao");
} if (cy != null && cy.isEnabled()) {
} catch (Exception ignored) { cy.getClass().getMethod(
} "onSdf1Activation",
} String.class,
} int.class, int.class)
.invoke(cy, name, 0, 0);
}
} catch (Exception ignored) {
}
}
}
File diff suppressed because it is too large Load Diff
+78 -173
View File
@@ -23,7 +23,7 @@ import java.util.regex.Pattern;
public class QuestTracker { public class QuestTracker {
private final Plugin plugin; private final Plugin plugin;
private final PlayerStorage storage; private final QuestStorage storage;
private final Map<String, String> private final Map<String, String>
playerBiome = new HashMap<>(); playerBiome = new HashMap<>();
private final Map<String, List<QuestFile>> private final Map<String, List<QuestFile>>
@@ -76,9 +76,9 @@ public class QuestTracker {
public QuestTracker(Plugin plugin) { public QuestTracker(Plugin plugin) {
this.plugin = plugin; this.plugin = plugin;
this.storage = new PlayerStorage( this.storage = new QuestStorage(
plugin.getDataFolder(), plugin.getDataFolder());
plugin.getLogger());
loadAllQuests(); loadAllQuests();
} }
@@ -1490,30 +1490,22 @@ public class QuestTracker {
} }
return unmet; return unmet;
} }
public String getProgress( public String getProgress(
String player, String player, String rawCond) {
String rawCond) { if (player == null || rawCond == null)
if (player == null
|| rawCond == null)
return "§7未知"; return "§7未知";
String key = stripOptional(rawCond); String key = stripOptional(rawCond);
// 打卡群系进度
if (key.contains("打卡")
&& (key.contains("群系")
|| key.contains("风景点"))) {
if (key.contains("打卡")
&& key.contains("群系")) {
String biome = String biome =
extractBiome(key); extractBiome(key);
if ("ALL".equals(biome)) { if ("ALL".equals(biome)) {
int total = getCounter( int total = getCounter(
player, "biome_ALL"); player, "biome_ALL");
return "§7群系打卡: §e" return "§7群系打卡: §e"
+ total + "/1"; + total + "/1";
} }
if (!biome.isEmpty()) { if (!biome.isEmpty()) {
int catCount = getCounter( int catCount = getCounter(
player, player,
@@ -1522,16 +1514,12 @@ public class QuestTracker {
+ "群系: §e" + "群系: §e"
+ catCount + "/1"; + catCount + "/1";
} }
int total = getCounter( int total = getCounter(
player, "biome_ALL"); player, "biome_ALL");
return "§7群系打卡: §e" return "§7群系打卡: §e"
+ total + "/1"; + total + "/1";
} }
// 阶段依赖
if (key.contains("完成") if (key.contains("完成")
&& key.contains("阶段")) { && key.contains("阶段")) {
boolean done = checkStageDependency( boolean done = checkStageDependency(
@@ -1541,9 +1529,6 @@ public class QuestTracker {
: "§c✗ " + key; : "§c✗ " + key;
} }
// 注册登录(检查主数据库是否已注册)
// 注册登录(检查主数据库是否已注册)
if ("完成注册登录".equals(key)) { if ("完成注册登录".equals(key)) {
boolean done = getDb() != null boolean done = getDb() != null
&& getDb().userExists( && getDb().userExists(
@@ -1553,7 +1538,6 @@ public class QuestTracker {
: "§c✗ 完成注册登录"; : "§c✗ 完成注册登录";
} }
// 绑定邮箱
if (key.contains("绑定Email") if (key.contains("绑定Email")
|| key.contains("绑定邮箱")) { || key.contains("绑定邮箱")) {
Object val = Object val =
@@ -1566,7 +1550,6 @@ public class QuestTracker {
: "§c✗ 绑定Email"; : "§c✗ 绑定Email";
} }
// 在线时间
if (key.contains("在线") if (key.contains("在线")
&& key.contains("小时")) { && key.contains("小时")) {
int needed = parseNumberFromText(key); int needed = parseNumberFromText(key);
@@ -1578,17 +1561,16 @@ public class QuestTracker {
+ needed + "小时"; + needed + "小时";
} }
// 打招呼/发消息/聊天
if (key.contains("打招呼") if (key.contains("打招呼")
|| key.contains("打个招呼") || key.contains("打个招呼")
|| key.contains("发消息") || key.contains("发消息")
|| key.contains("聊天")) { || key.contains("聊天")) {
int cur = getCounter( int cur = getCounter(
player, "CHAT"); player, "CHAT");
return "§7聊天: §e" + cur + "/1"; return "§7聊天: §e"
+ cur + "/1";
} }
// 破坏/放置方块
if ((key.contains("破坏") if ((key.contains("破坏")
|| key.contains("放置")) || key.contains("放置"))
&& key.contains("方块")) { && key.contains("方块")) {
@@ -1606,20 +1588,17 @@ public class QuestTracker {
&& key.contains("登录")) { && key.contains("登录")) {
int needed = parseNumberFromText(key); int needed = parseNumberFromText(key);
if (needed <= 0) needed = 1; if (needed <= 0) needed = 1;
int totalDays = getIntField( Object val =
player, getDb().getField(
"total_checkin_days"); player,
if (totalDays == 0) { "checkin_streak");
totalDays = getIntField( int streak = val instanceof Number
player, ? ((Number) val).intValue()
"checkin_streak"); : 0;
}
return "§7连续登录: §e" return "§7连续登录: §e"
+ totalDays + "/" + needed + "天"; + streak + "/" + needed + "天";
} }
// 挖矿
if (key.startsWith("MINE:")) { if (key.startsWith("MINE:")) {
String[] parts = key.split(":"); String[] parts = key.split(":");
if (parts.length >= 3) { if (parts.length >= 3) {
@@ -1634,7 +1613,6 @@ public class QuestTracker {
} }
} }
// 击杀生物
if (key.startsWith("KILL:")) { if (key.startsWith("KILL:")) {
String[] parts = key.split(":"); String[] parts = key.split(":");
if (parts.length >= 3) { if (parts.length >= 3) {
@@ -1649,7 +1627,6 @@ public class QuestTracker {
} }
} }
// 击杀玩家
if (key.startsWith("KILL_PLAYER")) { if (key.startsWith("KILL_PLAYER")) {
String[] parts = key.split(":"); String[] parts = key.split(":");
int needed = parts.length >= 2 int needed = parts.length >= 2
@@ -1660,7 +1637,6 @@ public class QuestTracker {
+ cur + "/" + needed; + cur + "/" + needed;
} }
// 合成
if (key.startsWith("CRAFT")) { if (key.startsWith("CRAFT")) {
String[] parts = key.split(":"); String[] parts = key.split(":");
int needed = parts.length >= 2 int needed = parts.length >= 2
@@ -1671,7 +1647,6 @@ public class QuestTracker {
+ cur + "/" + needed; + cur + "/" + needed;
} }
// 钓鱼
if (key.startsWith("FISH")) { if (key.startsWith("FISH")) {
String[] parts = key.split(":"); String[] parts = key.split(":");
int needed = parts.length >= 2 int needed = parts.length >= 2
@@ -1682,7 +1657,6 @@ public class QuestTracker {
+ cur + "/" + needed; + cur + "/" + needed;
} }
// 在线时长(毫秒)
if (key.startsWith("PLAYTIME")) { if (key.startsWith("PLAYTIME")) {
String[] parts = key.split(":"); String[] parts = key.split(":");
long needed = parts.length >= 2 long needed = parts.length >= 2
@@ -1694,7 +1668,7 @@ public class QuestTracker {
+ formatMs(cur) + "/" + formatMs(cur) + "/"
+ formatMs(needed); + formatMs(needed);
} }
// 上报村庄
if (key.contains("上报") if (key.contains("上报")
&& key.contains("村庄")) { && key.contains("村庄")) {
int needed = parseNumberFromText(key); int needed = parseNumberFromText(key);
@@ -1705,7 +1679,6 @@ public class QuestTracker {
+ cur + "/" + needed; + cur + "/" + needed;
} }
// 探索村庄
if (key.contains("探索") if (key.contains("探索")
&& key.contains("村庄")) { && key.contains("村庄")) {
int cur = getCounter( int cur = getCounter(
@@ -1713,40 +1686,28 @@ public class QuestTracker {
return "§7探索村庄: §e" return "§7探索村庄: §e"
+ cur + "/1"; + cur + "/1";
} }
// ===== 修改后 =====
if (key.contains("7天") if (key.contains("7天")
|| key.contains("七天")) { || key.contains("七天")) {
int needed = parseNumberFromText(key); int needed = parseNumberFromText(key);
if (needed <= 0) needed = 1; if (needed <= 0) needed = 1;
int streak = getIntField(
// 优先使用 total_checkin_days(累计天数)
int totalDays = getIntField(
player, player,
"total_checkin_days"); "checkin_streak");
if (totalDays == 0) { if (streak == 0) {
totalDays = getIntField( streak = getIntField(
player,
"checkin_streak");
}
if (totalDays == 0) {
totalDays = getIntField(
player, "streak"); player, "streak");
} }
if (streak == 0) {
plugin.getLogger().info( streak = getIntField(
"[QuestTracker] 登录检测: " player,
+ player "total_checkin_days");
+ " 需要=" + needed }
+ " 实际(累计天数)=" + totalDays);
return "§7近7天登录: §e" return "§7近7天登录: §e"
+ totalDays + "/" + streak + "/"
+ needed + "天"; + needed + "天";
} }
// 完成新手任务全部
if (key.contains("新手任务") if (key.contains("新手任务")
&& key.contains("全部")) { && key.contains("全部")) {
List<QuestFile> quests = List<QuestFile> quests =
@@ -1763,7 +1724,6 @@ public class QuestTracker {
+ quests.size(); + quests.size();
} }
// 完成主线任务全部
if (key.contains("主线任务") if (key.contains("主线任务")
&& key.contains("全部")) { && key.contains("全部")) {
List<QuestFile> quests = List<QuestFile> quests =
@@ -1782,23 +1742,7 @@ public class QuestTracker {
return "§7" + key; return "§7" + key;
} }
private int getIntField(
String player, String field) {
try {
Object val = getDb().getField(
player, field);
if (val instanceof Number) {
return ((Number) val).intValue();
}
if (val != null) {
return Integer.parseInt(
val.toString().trim());
}
} catch (Exception e) {
// 忽略
}
return 0;
}
public List<String> checkInBiome( public List<String> checkInBiome(
String player, String player,
@@ -2051,32 +1995,42 @@ public class QuestTracker {
return false; return false;
String key = stripOptional(rawCond); String key = stripOptional(rawCond);
// 打卡群系
// 无条件类文本直接通过
if (key.equals("无前级条件")
|| key.equals("无条件")
|| key.equals("无")
|| key.equals("none")
|| key.equals("无需条件")
|| key.equals("无解锁条件")) {
return true;
}
if (key.contains("打卡") if (key.contains("打卡")
&& (key.contains("群系") && (key.contains("群系")
|| key.contains("风景点"))) { || key.contains("风景点"))) {
String biome = String biome =
extractBiome(key); extractBiome(key);
if ("ALL".equals(biome)) { if ("ALL".equals(biome)) {
int total = getCounter( int total = getCounter(
player, "biome_ALL"); player, "biome_ALL");
return total >= 1; return total >= 1;
} }
if (!biome.isEmpty()) { if (!biome.isEmpty()) {
int catCount = getCounter( int catCount = getCounter(
player, player,
"biomecat_" + biome); "biomecat_" + biome);
return catCount >= 1; // ← 加这一行 return catCount >= 1;
} }
return getCounter( return getCounter(
player, "biome_ALL") >= 1; player, "biome_ALL") >= 1;
} }
// 注册登录 if (key.contains("完成")
&& key.contains("阶段")) {
return checkStageDependency(
player, key);
}
if ("完成注册登录".equals(key)) { if ("完成注册登录".equals(key)) {
return getDb() != null return getDb() != null
@@ -2084,8 +2038,6 @@ public class QuestTracker {
player); player);
} }
// 绑定邮箱
if (key.contains("绑定Email") if (key.contains("绑定Email")
|| key.contains("绑定邮箱")) { || key.contains("绑定邮箱")) {
Object val = Object val =
@@ -2095,7 +2047,6 @@ public class QuestTracker {
&& !val.toString().isEmpty(); && !val.toString().isEmpty();
} }
// 在线时间
if (key.contains("在线") if (key.contains("在线")
&& key.contains("小时")) { && key.contains("小时")) {
int needed = parseNumberFromText(key); int needed = parseNumberFromText(key);
@@ -2105,7 +2056,6 @@ public class QuestTracker {
return cur >= needed * 3600000L; return cur >= needed * 3600000L;
} }
// 打招呼/发消息/聊天
if (key.contains("打招呼") if (key.contains("打招呼")
|| key.contains("打个招呼") || key.contains("打个招呼")
|| key.contains("发消息") || key.contains("发消息")
@@ -2114,7 +2064,6 @@ public class QuestTracker {
player, "CHAT") >= 1; player, "CHAT") >= 1;
} }
// 破坏/放置方块
if ((key.contains("破坏") if ((key.contains("破坏")
|| key.contains("放置")) || key.contains("放置"))
&& key.contains("方块")) { && key.contains("方块")) {
@@ -2127,25 +2076,20 @@ public class QuestTracker {
return total >= needed; return total >= needed;
} }
// 连续登录
if (key.contains("连续") if (key.contains("连续")
&& key.contains("登录")) { && key.contains("登录")) {
int needed = parseNumberFromText(key); int needed = parseNumberFromText(key);
if (needed <= 0) needed = 1; if (needed <= 0) needed = 1;
// 优先用累计天数,因为 Object val =
// streak 可能大于 total(数据不一致时) getDb().getField(
int totalDays = getIntField( player,
player, "checkin_streak");
"total_checkin_days"); int streak = val instanceof Number
if (totalDays == 0) { ? ((Number) val).intValue()
totalDays = getIntField( : 0;
player, return streak >= needed;
"checkin_streak");
}
return totalDays >= needed;
} }
// 挖矿
if (key.startsWith("MINE:")) { if (key.startsWith("MINE:")) {
String[] p = key.split(":"); String[] p = key.split(":");
if (p.length >= 3) { if (p.length >= 3) {
@@ -2155,7 +2099,6 @@ public class QuestTracker {
} }
} }
// 击杀生物
if (key.startsWith("KILL:")) { if (key.startsWith("KILL:")) {
String[] p = key.split(":"); String[] p = key.split(":");
if (p.length >= 3) { if (p.length >= 3) {
@@ -2165,7 +2108,6 @@ public class QuestTracker {
} }
} }
// 击杀玩家
if (key.startsWith("KILL_PLAYER")) { if (key.startsWith("KILL_PLAYER")) {
String[] p = key.split(":"); String[] p = key.split(":");
int n = p.length >= 2 int n = p.length >= 2
@@ -2174,7 +2116,6 @@ public class QuestTracker {
"KILL_PLAYER") >= n; "KILL_PLAYER") >= n;
} }
// 合成
if (key.startsWith("CRAFT")) { if (key.startsWith("CRAFT")) {
String[] p = key.split(":"); String[] p = key.split(":");
int n = p.length >= 2 int n = p.length >= 2
@@ -2183,7 +2124,6 @@ public class QuestTracker {
player, "CRAFT") >= n; player, "CRAFT") >= n;
} }
// 钓鱼
if (key.startsWith("FISH")) { if (key.startsWith("FISH")) {
String[] p = key.split(":"); String[] p = key.split(":");
int n = p.length >= 2 int n = p.length >= 2
@@ -2192,7 +2132,6 @@ public class QuestTracker {
player, "FISH") >= n; player, "FISH") >= n;
} }
// 在线时长
if (key.startsWith("PLAYTIME")) { if (key.startsWith("PLAYTIME")) {
String[] p = key.split(":"); String[] p = key.split(":");
long n = p.length >= 2 long n = p.length >= 2
@@ -2200,69 +2139,27 @@ public class QuestTracker {
return getCounterLong(player, return getCounterLong(player,
"PLAYTIME") >= n; "PLAYTIME") >= n;
} }
// 上报村庄
if (key.contains("上报") if (key.contains("上报")
&& key.contains("村庄")) { && key.contains("村庄")) {
int needed = parseNumberFromText(key); int needed = parseNumberFromText(key);
if (needed <= 0) needed = 1; if (needed <= 0) needed = 1;
int cur = getCounter( return getCounter(
player, "REPORT_VILLAGE"); player, "REPORT_VILLAGE")
return cur >= needed; >= needed;
} }
// 探索村庄
if (key.contains("探索") if (key.contains("探索")
&& key.contains("村庄")) { && key.contains("村庄")) {
return getCounter( return getCounter(
player, "REPORT_VILLAGE") player, "REPORT_VILLAGE")
>= 1; >= 1;
} }
// 近7天登录记录
if (key.contains("7天") if (key.contains("7天")
|| key.contains("七天")) { || key.contains("七天")) {
int needed = parseNumberFromText(key); int needed = parseNumberFromText(key);
if (needed <= 0) needed = 1; if (needed <= 0) needed = 1;
// 优先使用 total_checkin_days
int totalDays = getIntField(
player,
"total_checkin_days");
if (totalDays == 0) {
totalDays = getIntField(
player,
"checkin_streak");
}
if (totalDays == 0) {
totalDays = getIntField(
player, "streak");
return totalDays >= needed;
}
// 诊断:读取数据库所有字段
try {
var user = getDb().getUser(
player);
if (user != null) {
plugin.getLogger().info(
"[QuestTracker] "
+ "登录诊断 "
+ player + ": "
+ user.toString());
} else {
plugin.getLogger().info(
"[QuestTracker] "
+ "登录诊断: "
+ player
+ " getUser返回null");
}
} catch (Exception e) {
plugin.getLogger().info(
"[QuestTracker] "
+ "登录诊断异常: "
+ e.getMessage());
}
int streak = getIntField( int streak = getIntField(
player, player,
"checkin_streak"); "checkin_streak");
@@ -2275,17 +2172,9 @@ public class QuestTracker {
player, player,
"total_checkin_days"); "total_checkin_days");
} }
plugin.getLogger().info(
"[QuestTracker] 登录检测: "
+ player
+ " 需要=" + needed
+ " 实际=" + streak);
return streak >= needed; return streak >= needed;
} }
// 完成新手任务全部内容
if (key.contains("新手任务") if (key.contains("新手任务")
&& key.contains("全部")) { && key.contains("全部")) {
List<QuestFile> quests = List<QuestFile> quests =
@@ -2299,7 +2188,6 @@ public class QuestTracker {
return true; return true;
} }
// 完成主线任务全部内容
if (key.contains("主线任务") if (key.contains("主线任务")
&& key.contains("全部")) { && key.contains("全部")) {
List<QuestFile> quests = List<QuestFile> quests =
@@ -2365,6 +2253,23 @@ public class QuestTracker {
} }
return 0; return 0;
} }
private int getIntField(
String player, String field) {
try {
Object val = getDb().getField(
player, field);
if (val instanceof Number) {
return ((Number) val).intValue();
}
if (val != null) {
return Integer.parseInt(
val.toString().trim());
}
} catch (Exception e) {
// 忽略
}
return 0;
}
private long getCounterLong( private long getCounterLong(
String player, String key) { String player, String key) {
@@ -14,15 +14,20 @@ import java.util.logging.Logger;
public class RadioDownloadListener public class RadioDownloadListener
implements Listener { implements Listener {
private final Logger log;
private final Main plugin; private final Main plugin;
private final Logger log;
private final Map<UUID, Integer> retryCount = private final Map<UUID, Integer> retryCount =
new ConcurrentHashMap<>(); new ConcurrentHashMap<>();
private static final int MAX_RETRY = 2; private final Map<UUID, Integer> totalAttempts =
new ConcurrentHashMap<>();
private static final int MAX_RETRY = 3;
// 每位玩家最多总共尝试5次,防死循环
private static final int MAX_TOTAL = 5;
public RadioDownloadListener(Main plugin) { public RadioDownloadListener(Main plugin) {
this.plugin = plugin; this.plugin = plugin;
this.log = plugin.getLogger(); this.log = plugin.getLogger();
} }
@EventHandler @EventHandler
@@ -33,86 +38,81 @@ public class RadioDownloadListener
String status = e.getStatus().name(); String status = e.getStatus().name();
log.info("[Radio] " + p.getName() log.info("[Radio] " + p.getName()
+ " status: " + status); + " 资源包: " + status);
switch (status) { switch (status) {
case "ACCEPTED":
break;
case "DOWNLOADED":
break;
case "SUCCESS": case "SUCCESS":
log.info("[Radio] " + p.getName() log.info("[Radio] " + p.getName()
+ " 资源包加载成功"); + " 资源包加载成功");
retryCount.remove(uuid); retryCount.remove(uuid);
totalAttempts.remove(uuid);
break; break;
case "FAILED_DOWNLOAD": case "FAILED_DOWNLOAD":
handleRetry(p, uuid); int retry = retryCount
.getOrDefault(uuid, 0) + 1;
int total = totalAttempts
.getOrDefault(uuid, 0) + 1;
retryCount.put(uuid, retry);
totalAttempts.put(uuid, total);
log.warning("[Radio] " + p.getName()
+ " 下载失败 (重试"
+ retry + "/" + MAX_RETRY
+ ", 总计" + total
+ "/" + MAX_TOTAL + ")");
if (total >= MAX_TOTAL) {
p.sendMessage(
"§7[Radio] §f资源包加载失败,跳过");
retryCount.remove(uuid);
totalAttempts.remove(uuid);
break;
}
if (retry > MAX_RETRY) {
retryCount.put(uuid, 0);
Bukkit.getScheduler()
.runTaskLater(plugin,
() -> {
if (p.isOnline()
&& plugin
.radio
!= null) {
plugin.radio
.sendResourcePack(
p);
}
}, 100L);
break;
}
p.sendMessage(
"§e[Radio] §f下载失败,重试中...");
Bukkit.getScheduler()
.runTaskLater(plugin,
() -> {
if (p.isOnline()
&& plugin.radio
!= null) {
plugin.radio
.sendResourcePack(
p);
}
}, 60L);
break; break;
case "DECLINED": case "DECLINED":
log.info("[Radio] " + p.getName() log.info("[Radio] " + p.getName()
+ " 拒绝下载"); + " 拒绝下载");
p.sendMessage(
"§e[Radio] §f你拒绝了资源包");
retryCount.remove(uuid);
break;
case "DISCARDED":
log.warning("[Radio] " + p.getName()
+ " DISCARDED");
p.sendMessage(
"§c[Radio] §f资源包下载失败"
+ ",可能原因:");
p.sendMessage(
"§71. CDN/防火墙拦截");
p.sendMessage(
"§72. SSL证书问题");
retryCount.remove(uuid);
break;
case "INVALID_URL":
log.warning("[Radio] " + p.getName()
+ " URL无效");
retryCount.remove(uuid); retryCount.remove(uuid);
totalAttempts.remove(uuid);
break; break;
default: default:
log.info("[Radio] " + p.getName() retryCount.remove(uuid);
+ " 未知: " + status); totalAttempts.remove(uuid);
break; break;
} }
} }
}
private void handleRetry(Player p, UUID uuid) {
int count = retryCount
.getOrDefault(uuid, 0) + 1;
retryCount.put(uuid, count);
log.warning("[Radio] " + p.getName()
+ " 下载失败 (" + count
+ "/" + MAX_RETRY + ")");
if (count <= MAX_RETRY) {
p.sendMessage(
"§e[Radio] §f下载失败,重试中... ("
+ count + "/" + MAX_RETRY + ")");
Bukkit.getScheduler().runTaskLater(
plugin,
() -> {
if (p.isOnline()
&& plugin.radio != null) {
plugin.radio
.sendResourcePack(p);
}
}, 60L);
} else {
p.sendMessage(
"§c[Radio] §f资源包加载失败,"
+ "部分音乐无法播放");
retryCount.remove(uuid);
}
}
}
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1641,4 +1641,4 @@ public class TicketManager {
} }
return it; return it;
} }
} }
+1 -2
View File
@@ -86,5 +86,4 @@ commands:
usage: /垃圾清理 usage: /垃圾清理
menu: menu:
description: 雪球菜单开关控制 description: 雪球菜单开关控制
usage: /menu <开/关> usage: /menu <开/关>
commands: