feat: 领取记录清理、BaoTa第三路备选更新检查
- TreasureManager: forceCleanClaims()完整清理claimedSet+claimDb;reload()调用forceCleanClaims() - UpdateChecker: 添加BaoTa HTML第三路备选通道,GitHub/Gitee限流后自动降级
This commit is contained in:
Generated
+1
-1
@@ -8,7 +8,7 @@
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="21" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_25" default="true" project-jdk-name="25" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -11,7 +11,7 @@
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<java.version>21</java.version>
|
||||
<java.version>25</java.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
@@ -37,10 +37,10 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.11.0</version>
|
||||
<version>3.14.0</version>
|
||||
<configuration>
|
||||
<source>21</source>
|
||||
<target>21</target>
|
||||
<source>25</source>
|
||||
<target>25</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
@@ -897,8 +897,6 @@ public class Main extends JavaPlugin
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
@@ -588,11 +588,15 @@ public class TreasureInventory {
|
||||
player.getInventory()
|
||||
.addItem(give);
|
||||
|
||||
// 限时物品记录领取限制
|
||||
if (r.getDurationSec() > 0) {
|
||||
// ★ 限时/限次物品记录领取限制(有时限 或 有攻击次数 都要记录)
|
||||
if (r.getDurationSec() > 0
|
||||
|| r.getAttackUsesLimit() > 0) {
|
||||
tm.claim(player.getName(),
|
||||
r.getDisplayName(),
|
||||
config.name);
|
||||
tm.addClaim(player.getName(),
|
||||
r.getDisplayName(),
|
||||
config.name);
|
||||
}
|
||||
|
||||
msg.append("§a✓ ")
|
||||
|
||||
@@ -476,9 +476,10 @@ public class TreasureListener implements Listener {
|
||||
// ========== 4. 确认是容器 ==========
|
||||
InventoryType topType = e.getView()
|
||||
.getTopInventory().getType();
|
||||
// 只放行玩家背包和创造模式背包,其他所有容器都拦截
|
||||
// 只放行玩家背包、创造模式背包和玩家合成背包,其他所有容器都拦截
|
||||
if (topType == InventoryType.PLAYER
|
||||
|| topType == InventoryType.CREATIVE) {
|
||||
|| topType == InventoryType.CREATIVE
|
||||
|| topType == InventoryType.CRAFTING) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -210,6 +210,7 @@ public class TreasureManager {
|
||||
/** 加载领取记录 */
|
||||
private void loadClaims() {
|
||||
claimedSet.clear();
|
||||
claimDb.clear();
|
||||
if (!claimFile.exists()) return;
|
||||
try {
|
||||
BufferedReader br = new BufferedReader(
|
||||
@@ -219,9 +220,14 @@ public class TreasureManager {
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
line = line.trim();
|
||||
if (!line.isEmpty()
|
||||
&& !line.startsWith("#")) {
|
||||
if (line.isEmpty() || line.startsWith("#"))
|
||||
continue;
|
||||
if (line.contains("|")) {
|
||||
// claimedSet 格式: 玩家|物品|区域
|
||||
claimedSet.add(line);
|
||||
} else if (line.contains(".")) {
|
||||
// claimDb 格式: 玩家.物品.区域
|
||||
claimDb.put(line, true);
|
||||
}
|
||||
}
|
||||
br.close();
|
||||
@@ -231,9 +237,8 @@ public class TreasureManager {
|
||||
}
|
||||
}
|
||||
public void reload() {
|
||||
// ★ 先清内存
|
||||
claimDb.clear();
|
||||
saveClaims(); // 写空文件
|
||||
// ★ 先销毁领取记录文件并清空所有内存
|
||||
forceCleanClaims();
|
||||
|
||||
// 清除玩家背包里的自定义物品
|
||||
for (org.bukkit.entity.Player p :
|
||||
@@ -546,12 +551,13 @@ public class TreasureManager {
|
||||
}
|
||||
|
||||
// ★ 检查2: 无领取记录→视为过期
|
||||
if (!shouldRemove
|
||||
&& !isClaimed(playerName,
|
||||
rName, region)) {
|
||||
// 宝箱物品(区域名含"宝箱")不需要领取限制
|
||||
if (!shouldRemove && !isTreasureItem(playerName, region)) {
|
||||
if (!isClaimed(playerName, rName, region)) {
|
||||
shouldRemove = true;
|
||||
reason = "无领取记录(过期/重载)";
|
||||
}
|
||||
}
|
||||
|
||||
// 检查3: 已过期
|
||||
if (!shouldRemove
|
||||
@@ -620,9 +626,21 @@ public class TreasureManager {
|
||||
public boolean isClaimed(String player,
|
||||
String itemName,
|
||||
String regionName) {
|
||||
String key = player + "."
|
||||
+ itemName + "." + regionName;
|
||||
return claimDb.containsKey(key);
|
||||
// ★ 检查claimDb(限时物品)
|
||||
String key = player + "." + itemName + "." + regionName;
|
||||
if (claimDb.containsKey(key)) return true;
|
||||
|
||||
// ★ 检查claimedSet(寻宝物品/宝箱物品)
|
||||
String key2 = player + "|" + itemName + "|" + regionName;
|
||||
return claimedSet.contains(key2);
|
||||
}
|
||||
|
||||
/** 检查是否是宝箱物品(不检查领取限制) */
|
||||
public boolean isTreasureItem(String playerName,
|
||||
String regionName) {
|
||||
// 宝箱物品区域名包含"宝箱"或" Treasure"关键词
|
||||
return regionName != null &&
|
||||
(regionName.contains("宝箱") || regionName.contains("Treasure"));
|
||||
}
|
||||
|
||||
|
||||
@@ -900,7 +918,7 @@ public class TreasureManager {
|
||||
}
|
||||
|
||||
public void forceCleanClaims() {
|
||||
// ★ 暴力方案:直接删文件再重建
|
||||
// ★ 暴力方案:直接删文件再重建,清空所有内存
|
||||
if (claimFile.exists()) {
|
||||
boolean deleted = claimFile.delete();
|
||||
plugin.getLogger().info(
|
||||
@@ -918,6 +936,7 @@ public class TreasureManager {
|
||||
+ e.getMessage());
|
||||
}
|
||||
claimedSet.clear();
|
||||
claimDb.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1571,9 +1590,14 @@ public class TreasureManager {
|
||||
try {
|
||||
FileWriter fw = new FileWriter(
|
||||
claimFile);
|
||||
// 保存 claimDb(限时物品,用 . 分隔)
|
||||
for (String key : claimDb.keySet()) {
|
||||
fw.write(key + "\n");
|
||||
}
|
||||
// 保存 claimedSet(寻宝物品,用 | 分隔)
|
||||
for (String key : claimedSet) {
|
||||
fw.write(key + "\n");
|
||||
}
|
||||
fw.close();
|
||||
} catch (IOException e) {
|
||||
plugin.getLogger().warning(
|
||||
|
||||
@@ -55,6 +55,10 @@ public class UpdateChecker {
|
||||
private static final String GITEE_API =
|
||||
"https://gitee.com/api/v5/repos/" + GITEE_OWNER + "/" + GITEE_REPO + "/releases/latest";
|
||||
|
||||
// 第三路备选:宝塔静态HTML
|
||||
private static final String FALLBACK_HTML =
|
||||
"https://caoyuan.ypshidifu.cn/update_check.html";
|
||||
|
||||
// 仓库页面链接
|
||||
private static final String GITHUB_LINK =
|
||||
"https://github.com/" + GITHUB_OWNER + "/" + GITHUB_REPO + "/releases";
|
||||
@@ -202,7 +206,13 @@ public class UpdateChecker {
|
||||
} else if (ge != null && ge.isSuccess()) {
|
||||
chosen = ge;
|
||||
} else {
|
||||
// 双通道均失败
|
||||
// ★ 双通道均失败,尝试第三路备选(宝塔静态HTML)
|
||||
plugin.getLogger().info("[Sdf1_game] GitHub/Gitee 均失败,尝试第三路备选 BaoTa...");
|
||||
CheckResult fbResult = tryFallbackChannel();
|
||||
if (fbResult != null && fbResult.isSuccess()) {
|
||||
chosen = fbResult;
|
||||
plugin.getLogger().info("[Sdf1_game] 第三路备选 BaoTa 检查成功: " + fbResult.version);
|
||||
} else {
|
||||
failCount++;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (gh != null && gh.error != null) {
|
||||
@@ -212,11 +222,14 @@ public class UpdateChecker {
|
||||
if (sb.length() > 0) sb.append("\n");
|
||||
sb.append("§7Gitee: §c").append(ge.error);
|
||||
}
|
||||
if (fbResult != null && fbResult.error != null) {
|
||||
if (sb.length() > 0) sb.append("\n");
|
||||
sb.append("§7BaoTa: §c").append(fbResult.error);
|
||||
}
|
||||
if (failCount >= TRIP_THRESHOLD) {
|
||||
circuitTripped = true;
|
||||
plugin.getLogger().warning(
|
||||
"[Sdf1_game] 更新检测已熔断,连续失败" + failCount + "次");
|
||||
// 熔断后安排2分钟后重试
|
||||
scheduleRetry();
|
||||
}
|
||||
if (sender != null) {
|
||||
@@ -234,6 +247,7 @@ public class UpdateChecker {
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 至少一个通道成功
|
||||
failCount = 0;
|
||||
@@ -314,6 +328,83 @@ public class UpdateChecker {
|
||||
|
||||
// ========== HTTP 请求 ==========
|
||||
|
||||
/**
|
||||
* 第三路备选:从宝塔静态HTML解析版本信息
|
||||
* HTML中用 <!--UPDATE_DATA 包裹结构化数据:
|
||||
* pluginName|version|downloadLink
|
||||
*/
|
||||
private CheckResult tryFallbackChannel() {
|
||||
HttpURLConnection conn = null;
|
||||
try {
|
||||
URL url = new URL(FALLBACK_HTML);
|
||||
conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setRequestMethod("GET");
|
||||
conn.setRequestProperty("User-Agent", "Sdf1-Plugin-UpdateChecker/1.0");
|
||||
conn.setConnectTimeout(TIMEOUT_MS);
|
||||
conn.setReadTimeout(TIMEOUT_MS);
|
||||
conn.setInstanceFollowRedirects(true);
|
||||
|
||||
int code = conn.getResponseCode();
|
||||
if (code != 200) {
|
||||
return new CheckResult(null, "BaoTa", FALLBACK_HTML,
|
||||
"BaoTa: HTTP " + code);
|
||||
}
|
||||
|
||||
BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(conn.getInputStream(), "UTF-8"));
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
sb.append(line).append("\n");
|
||||
}
|
||||
reader.close();
|
||||
|
||||
// 解析 <!--UPDATE_DATA ... UPDATE_DATA--> 块
|
||||
String html = sb.toString();
|
||||
|
||||
// ★ 去除 Cloudflare 注入的 CDN-CGI 链接(<a href=...>...</a>)
|
||||
html = html.replaceAll("<a\\s[^>]*>[^<]*</a>", "");
|
||||
|
||||
// 解析 <!--UPDATE_DATA ... UPDATE_DATA--> 块
|
||||
int start = html.indexOf("<!--UPDATE_DATA");
|
||||
int end = html.indexOf("UPDATE_DATA-->");
|
||||
if (start < 0 || end < 0 || end <= start) {
|
||||
plugin.getLogger().warning("[Sdf1_game] BaoTa: 无法解析更新数据块");
|
||||
return new CheckResult(null, "BaoTa", FALLBACK_HTML,
|
||||
"BaoTa: 无法解析更新数据块");
|
||||
}
|
||||
String block = html.substring(start, end);
|
||||
|
||||
// 逐行查找匹配当前插件名的记录
|
||||
String[] lines = block.split("\n");
|
||||
for (String l : lines) {
|
||||
l = l.trim();
|
||||
// 跳过空行、注释标记行、HTML注入标签
|
||||
if (l.isEmpty() || l.startsWith("<!--") || l.startsWith("<")) continue;
|
||||
if (l.startsWith("Sdf1_game|")) {
|
||||
// 格式: Sdf1_game|1.0.0|https://...
|
||||
String[] parts = l.split("\\|");
|
||||
if (parts.length >= 2) {
|
||||
String ver = parts[1].trim();
|
||||
String link = parts.length >= 3 ? parts[2].trim() : GITHUB_LINK;
|
||||
return new CheckResult(ver, "BaoTa", link, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new CheckResult(null, "BaoTa", FALLBACK_HTML,
|
||||
"BaoTa: 未找到 " + GITHUB_REPO + " 的版本信息");
|
||||
|
||||
} catch (java.net.SocketTimeoutException e) {
|
||||
return new CheckResult(null, "BaoTa", FALLBACK_HTML, "BaoTa: 连接超时");
|
||||
} catch (Exception e) {
|
||||
return new CheckResult(null, "BaoTa", FALLBACK_HTML,
|
||||
"BaoTa: " + e.getMessage());
|
||||
} finally {
|
||||
if (conn != null) conn.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
private static class CheckResult {
|
||||
final String version;
|
||||
final String source;
|
||||
@@ -340,9 +431,10 @@ public class UpdateChecker {
|
||||
conn.setRequestMethod("GET");
|
||||
conn.setRequestProperty("User-Agent",
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
|
||||
+ "(KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36");
|
||||
+ "(KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36");
|
||||
conn.setRequestProperty("Accept", "*/*");
|
||||
conn.setRequestProperty("Accept-Language", "zh-CN,zh;q=0.9");
|
||||
conn.setRequestProperty("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8");
|
||||
conn.setRequestProperty("Connection", "keep-alive");
|
||||
conn.setConnectTimeout(TIMEOUT_MS);
|
||||
conn.setReadTimeout(TIMEOUT_MS);
|
||||
conn.setInstanceFollowRedirects(true);
|
||||
@@ -353,7 +445,10 @@ public class UpdateChecker {
|
||||
return new CheckResult(null, sourceName, link, sourceName + ": 未找到 (HTTP 404)");
|
||||
}
|
||||
if (code == 403) {
|
||||
return new CheckResult(null, sourceName, link, sourceName + ": 被限流 (HTTP 403)");
|
||||
String errBody = "";
|
||||
try { BufferedReader er = new BufferedReader(new InputStreamReader(conn.getErrorStream())); StringBuilder eb = new StringBuilder(); String el; while ((el = er.readLine()) != null) eb.append(el); er.close(); errBody = eb.toString(); } catch (Exception ignored) {}
|
||||
String detail = errBody.isEmpty() ? "" : " | " + errBody.substring(0, Math.min(errBody.length(), 200));
|
||||
return new CheckResult(null, sourceName, link, sourceName + ": 被限流 (HTTP 403)" + detail);
|
||||
}
|
||||
if (code != 200) {
|
||||
return new CheckResult(null, sourceName, link, sourceName + ": HTTP " + code);
|
||||
|
||||
Reference in New Issue
Block a user