重构配置文件保存机制:原子写入+保留全量旧配置
This commit is contained in:
@@ -1,457 +1,498 @@
|
||||
package Sdf1_login;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
public class ConfigManager {
|
||||
|
||||
private final File dataFolder;
|
||||
private final Map<String, String> messages
|
||||
= new LinkedHashMap<>();
|
||||
private final Map<String, String> smtpSettings
|
||||
= new LinkedHashMap<>();
|
||||
|
||||
// ===== 公共字段 =====
|
||||
public String adminTag = "admin";
|
||||
public String adminPassword = "qweasd";
|
||||
public int maxAccountsPerIP = 3;
|
||||
public String approvalMode = "manual";
|
||||
public int autoApproveDelayMinutes = 30;
|
||||
public int loginTimeout = 60;
|
||||
public boolean afkEnabled = false;
|
||||
public int afkTimeout = 300;
|
||||
public boolean smtpSsl = true;
|
||||
public String smtpVerifyCode = "";
|
||||
public long smtpVerifyExpire = 0;
|
||||
public long smtpLastSendTime = 0;
|
||||
public String smtpConfiguringPlayer = "";
|
||||
public String broadcastServerUrl = "";
|
||||
public int httpPort = 8080;
|
||||
public String garbageTag = "admin";
|
||||
|
||||
public double checkinRewardFixed = 0;
|
||||
public double checkinRewardMin = 0;
|
||||
public double checkinRewardMax = 0;
|
||||
public boolean checkinGivePoints = true;
|
||||
public int checkinPoints = 10;
|
||||
public String checkinRewardType = "none";
|
||||
public int backCheckPointMultiplier = 5;
|
||||
public int checkinBondMin = 1;
|
||||
public int checkinBondMax = 3;
|
||||
|
||||
public double providerPointsPerScore = 2.0;
|
||||
public int baseEconomyReward = 100;
|
||||
public double providerEconomyPerScore = 50.0;
|
||||
public int requesterPointsReward = 10;
|
||||
|
||||
public boolean garbageEnabled = true;
|
||||
public int garbageInterval = 300;
|
||||
public int garbageMaxRounds = 1;
|
||||
|
||||
// ===== 区域防护配置 =====
|
||||
public String areaProtectAdminMode = "tag"; // "tag" 或 "op",不共存
|
||||
public String areaProtectAdminTag = "admin"; // 管理员Tag名
|
||||
public double areaProtectPricePerSqM = 10.0; // 每平米价格(债券)
|
||||
public int areaProtectMaxLands = 3; // 每个玩家最大领地数
|
||||
|
||||
// ===== 传送配置 =====
|
||||
public int tpRequestValidSeconds = 90; // 请求有效时间(秒)
|
||||
public int tpSendIntervalSeconds = 10; // 发送间隔(秒)
|
||||
|
||||
// ★ 构造函数接收 File,和 Main.java 调用一致 ★
|
||||
public ConfigManager(File dataFolder) {
|
||||
this.dataFolder = dataFolder;
|
||||
}
|
||||
public String rewardChannel = "债券"; // bonds = 债券,economy = Vault经济
|
||||
|
||||
|
||||
// ==================== 消息 ====================
|
||||
|
||||
public void loadMessages() {
|
||||
messages.clear();
|
||||
File file = new File(dataFolder, "消息.txt");
|
||||
if (!file.exists()) createDefaultMessages(file);
|
||||
loadMap(file, messages);
|
||||
}
|
||||
|
||||
public String msg(String key) {
|
||||
String val = messages.getOrDefault(key, key);
|
||||
val = val.replace("<b>", "&l");
|
||||
val = val.replace("</b>", "&r");
|
||||
val = val.replace("<br>", "\n");
|
||||
val = val.replace("\\n", "\n");
|
||||
val = val.replaceAll(
|
||||
"&([0-9a-fk-orA-FK-OR])", "§$1");
|
||||
return val;
|
||||
}
|
||||
|
||||
private void createDefaultMessages(File f) {
|
||||
List<String> L = new ArrayList<>();
|
||||
L.add("# ===== Sdf1_login 消息配置 =====");
|
||||
L.add("login_timeout=§c登录超时");
|
||||
L.add("not_registered=§e您尚未注册");
|
||||
L.add("not_logged_in=§e您尚未登录");
|
||||
L.add("already_registered=§c您已注册过");
|
||||
L.add("already_logged_in=§c您已登录");
|
||||
L.add("reg_success=§a注册成功!");
|
||||
L.add("login_success=§a登录成功!");
|
||||
L.add("login_failed=§c密码错误");
|
||||
L.add("password_wrong=§c密码错误");
|
||||
L.add("password_changed=§a密码修改成功!");
|
||||
L.add("password_format_error=§c密码需6位以上含大小写和数字");
|
||||
L.add("password_same=§c新密码不能与旧密码相同");
|
||||
L.add("checkin_already=§c您今日已签到");
|
||||
L.add("checkin_success=§a签到成功!获得 {points} 积分");
|
||||
L.add("chat_muted=§c你已被禁言");
|
||||
L.add("未知参数=§c未知参数或权限不足");
|
||||
writeLines(f, L);
|
||||
}
|
||||
|
||||
// ==================== SMTP ====================
|
||||
|
||||
public void loadSmtp() {
|
||||
smtpSettings.clear();
|
||||
File file = new File(dataFolder, "SMTP设置.txt");
|
||||
if (!file.exists()) createDefaultSmtp(file);
|
||||
loadMap(file, smtpSettings);
|
||||
}
|
||||
|
||||
public String getSmtp(String key) {
|
||||
return smtpSettings.getOrDefault(key, "");
|
||||
}
|
||||
|
||||
public void setSmtp(String key, String value) {
|
||||
smtpSettings.put(key, value);
|
||||
}
|
||||
|
||||
public void saveSmtp() {
|
||||
File file = new File(dataFolder, "SMTP设置.txt");
|
||||
List<String> L = new ArrayList<>();
|
||||
L.add("# ===== SMTP 配置 =====");
|
||||
for (Map.Entry<String, String> e
|
||||
: smtpSettings.entrySet()) {
|
||||
L.add(e.getKey() + "=" + e.getValue());
|
||||
}
|
||||
writeLines(file, L);
|
||||
}
|
||||
|
||||
private void createDefaultSmtp(File f) {
|
||||
List<String> L = new ArrayList<>();
|
||||
L.add("# ===== SMTP 配置 =====");
|
||||
L.add("smtp地址=smtp.example.com");
|
||||
L.add("smtp端口=465");
|
||||
L.add("smtp账号=");
|
||||
L.add("smtp密码=");
|
||||
L.add("发件人名称=Sdf1_login");
|
||||
L.add("smtp加密=true");
|
||||
writeLines(f, L);
|
||||
}
|
||||
|
||||
// ==================== 插件设置 ====================
|
||||
|
||||
public void loadSettings() {
|
||||
File file = new File(dataFolder, "插件设置.txt");
|
||||
if (!file.exists()) createDefaultSettings(file);
|
||||
Map<String, String> m = new LinkedHashMap<>();
|
||||
loadMap(file, m);
|
||||
|
||||
adminTag = m.getOrDefault("管理标签", adminTag);
|
||||
adminPassword = m.getOrDefault("管理密码", adminPassword);
|
||||
maxAccountsPerIP = parseInt(m.getOrDefault("每IP最大账号数", "3"), 3);
|
||||
approvalMode = m.getOrDefault("审批模式", approvalMode);
|
||||
autoApproveDelayMinutes = parseInt(m.getOrDefault("自动审批延迟分钟", "30"), 30);
|
||||
loginTimeout = parseInt(m.getOrDefault("登录超时秒数", "60"), 60);
|
||||
afkEnabled = Boolean.parseBoolean(m.getOrDefault("挂机踢出", "false"));
|
||||
afkTimeout = parseInt(m.getOrDefault("挂机超时秒数", "300"), 300);
|
||||
smtpSsl = Boolean.parseBoolean(m.getOrDefault("smtp加密", "true"));
|
||||
httpPort = parseInt(m.getOrDefault("http-port", "8080"), 8080);
|
||||
broadcastServerUrl = m.getOrDefault("resource-pack-url", "");
|
||||
|
||||
providerPointsPerScore = parseDouble(m.getOrDefault("服务商积分倍率", "2.0"), 2.0);
|
||||
baseEconomyReward = parseInt(m.getOrDefault("基础经济奖励", "100"), 100);
|
||||
providerEconomyPerScore = parseDouble(m.getOrDefault("经济奖励每评分", "50.0"), 50.0);
|
||||
requesterPointsReward = parseInt(m.getOrDefault("报单人积分奖励", "10"), 10);
|
||||
|
||||
garbageEnabled = Boolean.parseBoolean(m.getOrDefault("垃圾站_启用", "true"));
|
||||
garbageInterval = parseInt(m.getOrDefault("垃圾站_清理间隔秒", "300"), 300);
|
||||
garbageMaxRounds = parseInt(m.getOrDefault("垃圾站_保留轮数", "1"), 1);
|
||||
|
||||
checkinRewardFixed = parseDouble(m.getOrDefault("签到固定金额", "0"), 0);
|
||||
checkinRewardMin = parseDouble(m.getOrDefault("签到最小金额", "0"), 0);
|
||||
checkinRewardMax = parseDouble(m.getOrDefault("签到最大金额", "0"), 0);
|
||||
checkinGivePoints = Boolean.parseBoolean(m.getOrDefault("签到给积分", "true"));
|
||||
checkinPoints = parseInt(m.getOrDefault("签到积分数量", "10"), 10);
|
||||
backCheckPointMultiplier = parseInt(m.getOrDefault("补签积分倍率", "5"), 5);
|
||||
checkinBondMin = parseInt(m.getOrDefault("签到债券最小", "1"), 1);
|
||||
checkinBondMax = parseInt(m.getOrDefault("签到债券最大", "3"), 3);
|
||||
if (checkinBondMax < checkinBondMin) checkinBondMax = checkinBondMin;
|
||||
|
||||
if (checkinRewardFixed > 0) checkinRewardType = "fixed";
|
||||
else if (checkinRewardMin > 0 && checkinRewardMax > checkinRewardMin) checkinRewardType = "range";
|
||||
else checkinRewardType = "none";
|
||||
rewardChannel = m.getOrDefault("奖励发放方式", "债券");
|
||||
rewardChannel = normalizeRewardChannel(rewardChannel);
|
||||
|
||||
// ===== 区域防护配置 =====
|
||||
areaProtectAdminMode = m.getOrDefault("区域防护_管理员模式", "tag").toLowerCase();
|
||||
areaProtectAdminTag = m.getOrDefault("区域防护_管理员Tag", adminTag);
|
||||
areaProtectPricePerSqM = parseDouble(m.getOrDefault("区域防护_每平米价格", "10"), 10.0);
|
||||
areaProtectMaxLands = parseInt(m.getOrDefault("区域防护_最大领地数", "3"), 3);
|
||||
|
||||
tpRequestValidSeconds = parseIntFromString(m.getOrDefault("传送_请求有效秒", "90"));
|
||||
tpSendIntervalSeconds = parseIntFromString(m.getOrDefault("传送_发送间隔秒", "10"));
|
||||
|
||||
// 验证合法值
|
||||
if (!"economy".equalsIgnoreCase(rewardChannel)
|
||||
&& !"bonds".equalsIgnoreCase(rewardChannel)) {
|
||||
rewardChannel = "bonds";
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ★ 自动补充新配置项 ★
|
||||
boolean needAppend = false;
|
||||
List<String> newKeys = new ArrayList<>();
|
||||
if (!m.containsKey("签到债券最小")) { needAppend = true; newKeys.add("签到债券最小=" + checkinBondMin); }
|
||||
if (!m.containsKey("签到债券最大")) { needAppend = true; newKeys.add("签到债券最大=" + checkinBondMax); }
|
||||
if (!m.containsKey("奖励发放方式")) {
|
||||
needAppend = true;
|
||||
newKeys.add("奖励发放方式=债券");
|
||||
}
|
||||
if (needAppend) {
|
||||
try {
|
||||
FileWriter fw = new FileWriter(file, true);
|
||||
BufferedWriter bw = new BufferedWriter(fw);
|
||||
bw.newLine();
|
||||
for (String key : newKeys) { bw.write(key); bw.newLine(); }
|
||||
bw.flush(); bw.close();
|
||||
System.out.println("[Config] 已补充签到债券配置");
|
||||
|
||||
} catch (IOException ignored) {}
|
||||
if (!m.containsKey("奖励发放方式")) {
|
||||
try {
|
||||
FileWriter fw = new FileWriter(file, true);
|
||||
BufferedWriter bw = new BufferedWriter(fw);
|
||||
bw.newLine();
|
||||
bw.write("奖励发放方式=bonds");
|
||||
bw.newLine();
|
||||
bw.flush(); bw.close();
|
||||
} catch (IOException ignored) {}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 奖励方式标准化:中英文全兼容
|
||||
* "债券" "bonds" "Bond" "BONDS" → "bonds"
|
||||
* "经济" "economy" "Economy" "ECONOMY" → "economy"
|
||||
* 其他任何值 → "bonds"(默认债券)
|
||||
*/
|
||||
private String normalizeRewardChannel(String raw) {
|
||||
if (raw == null) return "bonds";
|
||||
String s = raw.trim().toLowerCase();
|
||||
// 中文
|
||||
if (s.contains("债券")) return "bonds";
|
||||
if (s.contains("经济")) return "economy";
|
||||
// 英文
|
||||
if (s.contains("bond")) return "bonds";
|
||||
if (s.contains("econ")) return "economy";
|
||||
if (s.contains("Vault")) return "economy";
|
||||
// 兜底
|
||||
return "bonds";
|
||||
}
|
||||
|
||||
public void saveSettings() {
|
||||
File file = new File(dataFolder, "插件设置.txt");
|
||||
|
||||
// ★ 读取已有配置,保留Web通信等其他模块管理的配置行
|
||||
java.util.LinkedHashMap<String, String> existingPairs = new java.util.LinkedHashMap<>();
|
||||
java.util.List<String> existingLines = new ArrayList<>();
|
||||
if (file.exists()) {
|
||||
try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8))) {
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
existingLines.add(line);
|
||||
String trimmed = line.trim();
|
||||
if (!trimmed.isEmpty() && !trimmed.startsWith("#") && trimmed.contains("=")) {
|
||||
int eq = trimmed.indexOf('=');
|
||||
String k = trimmed.substring(0, eq).trim();
|
||||
existingPairs.put(k, trimmed.substring(eq + 1).trim());
|
||||
}
|
||||
}
|
||||
} catch (IOException ignored) {}
|
||||
}
|
||||
|
||||
// ★ ConfigManager管理的配置项
|
||||
java.util.LinkedHashMap<String, String> managed = new java.util.LinkedHashMap<>();
|
||||
managed.put("管理标签", adminTag);
|
||||
managed.put("管理密码", adminPassword);
|
||||
managed.put("每IP最大账号数", String.valueOf(maxAccountsPerIP));
|
||||
managed.put("审批模式", approvalMode);
|
||||
managed.put("自动审批延迟分钟", String.valueOf(autoApproveDelayMinutes));
|
||||
managed.put("登录超时秒数", String.valueOf(loginTimeout));
|
||||
managed.put("挂机踢出", String.valueOf(afkEnabled));
|
||||
managed.put("挂机超时秒数", String.valueOf(afkTimeout));
|
||||
managed.put("resource-pack-url", broadcastServerUrl);
|
||||
managed.put("http-port", String.valueOf(httpPort));
|
||||
managed.put("垃圾站_启用", String.valueOf(garbageEnabled));
|
||||
managed.put("垃圾站_清理间隔秒", String.valueOf(garbageInterval));
|
||||
managed.put("垃圾站_保留轮数", String.valueOf(garbageMaxRounds));
|
||||
managed.put("签到固定金额", String.valueOf(checkinRewardFixed));
|
||||
managed.put("签到最小金额", String.valueOf(checkinRewardMin));
|
||||
managed.put("签到最大金额", String.valueOf(checkinRewardMax));
|
||||
managed.put("签到给积分", String.valueOf(checkinGivePoints));
|
||||
managed.put("签到积分数量", String.valueOf(checkinPoints));
|
||||
managed.put("签到债券最小", String.valueOf(checkinBondMin));
|
||||
managed.put("签到债券最大", String.valueOf(checkinBondMax));
|
||||
managed.put("补签积分倍率", String.valueOf(backCheckPointMultiplier));
|
||||
managed.put("奖励发放方式", "债券");
|
||||
managed.put("传送_请求有效秒", String.valueOf(tpRequestValidSeconds));
|
||||
managed.put("传送_发送间隔秒", String.valueOf(tpSendIntervalSeconds));
|
||||
|
||||
// ★ 保留非ConfigManager管理的配置行(如web通信-*等)
|
||||
java.util.List<String> preservedLines = new ArrayList<>();
|
||||
for (String line : existingLines) {
|
||||
String trimmed = line.trim();
|
||||
if (trimmed.isEmpty() || trimmed.startsWith("#")) continue;
|
||||
if (!trimmed.contains("=")) continue;
|
||||
int eq = trimmed.indexOf('=');
|
||||
String key = trimmed.substring(0, eq).trim();
|
||||
if (!managed.containsKey(key)) {
|
||||
preservedLines.add(key + "=" + trimmed.substring(eq + 1).trim());
|
||||
}
|
||||
}
|
||||
|
||||
// ★ 写入完整配置
|
||||
List<String> L = new ArrayList<>();
|
||||
L.add("# ===== Sdf1_login 插件设置 =====");
|
||||
for (java.util.Map.Entry<String, String> e : managed.entrySet()) {
|
||||
L.add(e.getKey() + "=" + e.getValue());
|
||||
}
|
||||
// 追加保留的其他模块配置
|
||||
if (!preservedLines.isEmpty()) {
|
||||
L.add("");
|
||||
L.add("# ===== 其他模块配置 =====");
|
||||
L.addAll(preservedLines);
|
||||
}
|
||||
writeLines(file, L);
|
||||
}
|
||||
|
||||
private void createDefaultSettings(File f) {
|
||||
List<String> L = new ArrayList<>();
|
||||
L.add("# ===== Sdf1_login 插件设置 =====");
|
||||
L.add("管理标签=admin");
|
||||
L.add("管理密码=qweasd");
|
||||
L.add("每IP最大账号数=3");
|
||||
L.add("审批模式=manual");
|
||||
L.add("自动审批延迟分钟=30");
|
||||
L.add("登录超时秒数=60");
|
||||
L.add("挂机踢出=false");
|
||||
L.add("挂机超时秒数=300");
|
||||
L.add("resource-pack-url=");
|
||||
L.add("http-port=8080");
|
||||
L.add("垃圾站_启用=true");
|
||||
L.add("垃圾站_清理间隔秒=300");
|
||||
L.add("垃圾站_保留轮数=1");
|
||||
L.add("签到固定金额=0");
|
||||
L.add("签到最小金额=50");
|
||||
L.add("签到最大金额=500");
|
||||
L.add("签到给积分=true");
|
||||
L.add("签到积分数量=10");
|
||||
L.add("签到债券最小=1");
|
||||
L.add("签到债券最大=3");
|
||||
L.add("补签积分倍率=5");
|
||||
L.add("奖励发放方式=债券");
|
||||
// 区域防护配置
|
||||
L.add("区域防护_管理员模式=tag");
|
||||
L.add("区域防护_管理员Tag=admin");
|
||||
L.add("区域防护_每平米价格=10");
|
||||
L.add("区域防护_最大领地数=3");
|
||||
L.add("传送_请求有效秒=90");
|
||||
L.add("传送_发送间隔秒=10");
|
||||
// Web通信配置(默认禁用,需手动开启)
|
||||
L.add("");
|
||||
L.add("# ===== Web通信配置 =====");
|
||||
L.add("web通信-启用=false");
|
||||
L.add("web通信-地址=https://caoyuan.ypshidifu.cn/plugin");
|
||||
L.add("web通信-Token有效期秒=600");
|
||||
L.add("web通信-同步间隔分钟=5");
|
||||
L.add("web通信-回调端口=9090");
|
||||
L.add("web通信-密钥=sdf1_web_comm_2026_ypshidifu");
|
||||
writeLines(f, L);
|
||||
}
|
||||
public List<String> getAfkWhitelist() {
|
||||
List<String> list = new ArrayList<>();
|
||||
File file = new File(dataFolder, "挂机白名单.txt");
|
||||
if (!file.exists()) {
|
||||
try {
|
||||
file.createNewFile();
|
||||
FileWriter fw = new FileWriter(file);
|
||||
fw.write("# 每行一个玩家名\n");
|
||||
fw.flush();
|
||||
fw.close();
|
||||
} catch (IOException ignored) {}
|
||||
return list;
|
||||
}
|
||||
try (BufferedReader r = new BufferedReader(
|
||||
new InputStreamReader(
|
||||
new FileInputStream(file),
|
||||
StandardCharsets.UTF_8))) {
|
||||
String line;
|
||||
while ((line = r.readLine()) != null) {
|
||||
line = line.trim();
|
||||
if (!line.isEmpty() && !line.startsWith("#"))
|
||||
list.add(line);
|
||||
}
|
||||
} catch (IOException ignored) {}
|
||||
return list;
|
||||
}
|
||||
|
||||
// ==================== 其他 ====================
|
||||
|
||||
public String generateSmtpVerifyCode() {
|
||||
String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
StringBuilder sb = new StringBuilder();
|
||||
java.util.Random r = new java.util.Random();
|
||||
for (int i = 0; i < 6; i++) sb.append(chars.charAt(r.nextInt(chars.length())));
|
||||
smtpVerifyCode = sb.toString();
|
||||
smtpVerifyExpire = System.currentTimeMillis() + 300000L;
|
||||
return smtpVerifyCode;
|
||||
}
|
||||
|
||||
public boolean checkSmtpVerifyCode(String code) {
|
||||
if (System.currentTimeMillis() > smtpVerifyExpire) { smtpVerifyCode = ""; return false; }
|
||||
return smtpVerifyCode.equalsIgnoreCase(code);
|
||||
}
|
||||
|
||||
public void clearSmtpVerifyCode() { smtpVerifyCode = ""; smtpVerifyExpire = 0; }
|
||||
public boolean canSendSmtpVerify() { return System.currentTimeMillis() - smtpLastSendTime >= 60000L; }
|
||||
public void recordSmtpSendTime() { smtpLastSendTime = System.currentTimeMillis(); }
|
||||
|
||||
// ==================== 文件工具 ====================
|
||||
|
||||
private void loadMap(File file, Map<String, String> map) {
|
||||
try (BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8))) {
|
||||
String line;
|
||||
while ((line = r.readLine()) != null) {
|
||||
line = line.trim();
|
||||
if (line.isEmpty() || line.startsWith("#")) continue;
|
||||
int eq = line.indexOf('=');
|
||||
if (eq > 0) map.put(line.substring(0, eq).trim(), line.substring(eq + 1).trim());
|
||||
}
|
||||
} catch (IOException ignored) {}
|
||||
}
|
||||
|
||||
private void writeLines(File file, List<String> lines) {
|
||||
try (BufferedWriter w = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))) {
|
||||
for (String l : lines) { w.write(l); w.newLine(); }
|
||||
} catch (IOException ignored) {}
|
||||
}
|
||||
|
||||
private double parseDouble(String s, double def) { try { return Double.parseDouble(s.trim()); } catch (Exception e) { return def; } }
|
||||
private int parseInt(String s, int def) { try { return Integer.parseInt(s.trim()); } catch (Exception e) { return def; } }
|
||||
|
||||
package Sdf1_login;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
public class ConfigManager {
|
||||
|
||||
private final File dataFolder;
|
||||
private final Map<String, String> messages
|
||||
= new LinkedHashMap<>();
|
||||
private final Map<String, String> smtpSettings
|
||||
= new LinkedHashMap<>();
|
||||
|
||||
// ===== 公共字段 =====
|
||||
public String adminTag = "admin";
|
||||
public String adminPassword = "qweasd";
|
||||
public int maxAccountsPerIP = 3;
|
||||
public String approvalMode = "manual";
|
||||
public int autoApproveDelayMinutes = 30;
|
||||
public int loginTimeout = 60;
|
||||
public boolean afkEnabled = false;
|
||||
public int afkTimeout = 300;
|
||||
public boolean smtpSsl = true;
|
||||
public String smtpVerifyCode = "";
|
||||
public long smtpVerifyExpire = 0;
|
||||
public long smtpLastSendTime = 0;
|
||||
public String smtpConfiguringPlayer = "";
|
||||
public String broadcastServerUrl = "";
|
||||
public int httpPort = 8080;
|
||||
public String garbageTag = "admin";
|
||||
|
||||
public double checkinRewardFixed = 0;
|
||||
public double checkinRewardMin = 0;
|
||||
public double checkinRewardMax = 0;
|
||||
public boolean checkinGivePoints = true;
|
||||
public int checkinPoints = 10;
|
||||
public String checkinRewardType = "none";
|
||||
public int backCheckPointMultiplier = 5;
|
||||
public int checkinBondMin = 1;
|
||||
public int checkinBondMax = 3;
|
||||
|
||||
public double providerPointsPerScore = 2.0;
|
||||
public int baseEconomyReward = 100;
|
||||
public double providerEconomyPerScore = 50.0;
|
||||
public int requesterPointsReward = 10;
|
||||
|
||||
public boolean garbageEnabled = true;
|
||||
public int garbageInterval = 300;
|
||||
public int garbageMaxRounds = 1;
|
||||
|
||||
// ===== 区域防护配置 =====
|
||||
public String areaProtectAdminMode = "tag"; // "tag" 或 "op",不共存
|
||||
public String areaProtectAdminTag = "admin"; // 管理员Tag名
|
||||
public double areaProtectPricePerSqM = 10.0; // 每平米价格(债券)
|
||||
public int areaProtectMaxLands = 3; // 每个玩家最大领地数
|
||||
|
||||
// ===== 传送配置 =====
|
||||
public int tpRequestValidSeconds = 90; // 请求有效时间(秒)
|
||||
public int tpSendIntervalSeconds = 10; // 发送间隔(秒)
|
||||
|
||||
// ★ 构造函数接收 File,和 Main.java 调用一致 ★
|
||||
public ConfigManager(File dataFolder) {
|
||||
this.dataFolder = dataFolder;
|
||||
}
|
||||
public String rewardChannel = "债券"; // bonds = 债券,economy = Vault经济
|
||||
|
||||
|
||||
// ==================== 消息 ====================
|
||||
|
||||
public void loadMessages() {
|
||||
messages.clear();
|
||||
File file = new File(dataFolder, "消息.txt");
|
||||
if (!file.exists()) createDefaultMessages(file);
|
||||
loadMap(file, messages);
|
||||
}
|
||||
|
||||
public String msg(String key) {
|
||||
String val = messages.getOrDefault(key, key);
|
||||
val = val.replace("<b>", "&l");
|
||||
val = val.replace("</b>", "&r");
|
||||
val = val.replace("<br>", "\n");
|
||||
val = val.replace("\\n", "\n");
|
||||
val = val.replaceAll(
|
||||
"&([0-9a-fk-orA-FK-OR])", "§$1");
|
||||
return val;
|
||||
}
|
||||
|
||||
private void createDefaultMessages(File f) {
|
||||
List<String> L = new ArrayList<>();
|
||||
L.add("# ===== Sdf1_login 消息配置 =====");
|
||||
L.add("login_timeout=§c登录超时");
|
||||
L.add("not_registered=§e您尚未注册");
|
||||
L.add("not_logged_in=§e您尚未登录");
|
||||
L.add("already_registered=§c您已注册过");
|
||||
L.add("already_logged_in=§c您已登录");
|
||||
L.add("reg_success=§a注册成功!");
|
||||
L.add("login_success=§a登录成功!");
|
||||
L.add("login_failed=§c密码错误");
|
||||
L.add("password_wrong=§c密码错误");
|
||||
L.add("password_changed=§a密码修改成功!");
|
||||
L.add("password_format_error=§c密码需6位以上含大小写和数字");
|
||||
L.add("password_same=§c新密码不能与旧密码相同");
|
||||
L.add("checkin_already=§c您今日已签到");
|
||||
L.add("checkin_success=§a签到成功!获得 {points} 积分");
|
||||
L.add("chat_muted=§c你已被禁言");
|
||||
L.add("未知参数=§c未知参数或权限不足");
|
||||
writeLines(f, L);
|
||||
}
|
||||
|
||||
// ==================== SMTP ====================
|
||||
|
||||
public void loadSmtp() {
|
||||
smtpSettings.clear();
|
||||
File file = new File(dataFolder, "SMTP设置.txt");
|
||||
if (!file.exists()) createDefaultSmtp(file);
|
||||
loadMap(file, smtpSettings);
|
||||
}
|
||||
|
||||
public String getSmtp(String key) {
|
||||
return smtpSettings.getOrDefault(key, "");
|
||||
}
|
||||
|
||||
public void setSmtp(String key, String value) {
|
||||
smtpSettings.put(key, value);
|
||||
}
|
||||
|
||||
public void saveSmtp() {
|
||||
File file = new File(dataFolder, "SMTP设置.txt");
|
||||
List<String> L = new ArrayList<>();
|
||||
L.add("# ===== SMTP 配置 =====");
|
||||
for (Map.Entry<String, String> e
|
||||
: smtpSettings.entrySet()) {
|
||||
L.add(e.getKey() + "=" + e.getValue());
|
||||
}
|
||||
writeLines(file, L);
|
||||
}
|
||||
|
||||
private void createDefaultSmtp(File f) {
|
||||
List<String> L = new ArrayList<>();
|
||||
L.add("# ===== SMTP 配置 =====");
|
||||
L.add("smtp地址=smtp.example.com");
|
||||
L.add("smtp端口=465");
|
||||
L.add("smtp账号=");
|
||||
L.add("smtp密码=");
|
||||
L.add("发件人名称=Sdf1_login");
|
||||
L.add("smtp加密=true");
|
||||
writeLines(f, L);
|
||||
}
|
||||
|
||||
// ==================== 插件设置 ====================
|
||||
|
||||
public void loadSettings() {
|
||||
File file = new File(dataFolder, "插件设置.txt");
|
||||
if (!file.exists()) createDefaultSettings(file);
|
||||
Map<String, String> m = new LinkedHashMap<>();
|
||||
loadMap(file, m);
|
||||
|
||||
adminTag = m.getOrDefault("管理标签", adminTag);
|
||||
adminPassword = m.getOrDefault("管理密码", adminPassword);
|
||||
maxAccountsPerIP = parseInt(m.getOrDefault("每IP最大账号数", "3"), 3);
|
||||
approvalMode = m.getOrDefault("审批模式", approvalMode);
|
||||
autoApproveDelayMinutes = parseInt(m.getOrDefault("自动审批延迟分钟", "30"), 30);
|
||||
loginTimeout = parseInt(m.getOrDefault("登录超时秒数", "60"), 60);
|
||||
afkEnabled = Boolean.parseBoolean(m.getOrDefault("挂机踢出", "false"));
|
||||
afkTimeout = parseInt(m.getOrDefault("挂机超时秒数", "300"), 300);
|
||||
smtpSsl = Boolean.parseBoolean(m.getOrDefault("smtp加密", "true"));
|
||||
httpPort = parseInt(m.getOrDefault("http-port", "8080"), 8080);
|
||||
broadcastServerUrl = m.getOrDefault("resource-pack-url", "");
|
||||
|
||||
providerPointsPerScore = parseDouble(m.getOrDefault("服务商积分倍率", "2.0"), 2.0);
|
||||
baseEconomyReward = parseInt(m.getOrDefault("基础经济奖励", "100"), 100);
|
||||
providerEconomyPerScore = parseDouble(m.getOrDefault("经济奖励每评分", "50.0"), 50.0);
|
||||
requesterPointsReward = parseInt(m.getOrDefault("报单人积分奖励", "10"), 10);
|
||||
|
||||
garbageEnabled = Boolean.parseBoolean(m.getOrDefault("垃圾站_启用", "true"));
|
||||
garbageInterval = parseInt(m.getOrDefault("垃圾站_清理间隔秒", "300"), 300);
|
||||
garbageMaxRounds = parseInt(m.getOrDefault("垃圾站_保留轮数", "1"), 1);
|
||||
|
||||
checkinRewardFixed = parseDouble(m.getOrDefault("签到固定金额", "0"), 0);
|
||||
checkinRewardMin = parseDouble(m.getOrDefault("签到最小金额", "0"), 0);
|
||||
checkinRewardMax = parseDouble(m.getOrDefault("签到最大金额", "0"), 0);
|
||||
checkinGivePoints = Boolean.parseBoolean(m.getOrDefault("签到给积分", "true"));
|
||||
checkinPoints = parseInt(m.getOrDefault("签到积分数量", "10"), 10);
|
||||
backCheckPointMultiplier = parseInt(m.getOrDefault("补签积分倍率", "5"), 5);
|
||||
checkinBondMin = parseInt(m.getOrDefault("签到债券最小", "1"), 1);
|
||||
checkinBondMax = parseInt(m.getOrDefault("签到债券最大", "3"), 3);
|
||||
if (checkinBondMax < checkinBondMin) checkinBondMax = checkinBondMin;
|
||||
|
||||
if (checkinRewardFixed > 0) checkinRewardType = "fixed";
|
||||
else if (checkinRewardMin > 0 && checkinRewardMax > checkinRewardMin) checkinRewardType = "range";
|
||||
else checkinRewardType = "none";
|
||||
rewardChannel = m.getOrDefault("奖励发放方式", "债券");
|
||||
rewardChannel = normalizeRewardChannel(rewardChannel);
|
||||
|
||||
// ===== 区域防护配置 =====
|
||||
areaProtectAdminMode = m.getOrDefault("区域防护_管理员模式", "tag").toLowerCase();
|
||||
areaProtectAdminTag = m.getOrDefault("区域防护_管理员Tag", adminTag);
|
||||
areaProtectPricePerSqM = parseDouble(m.getOrDefault("区域防护_每平米价格", "10"), 10.0);
|
||||
areaProtectMaxLands = parseInt(m.getOrDefault("区域防护_最大领地数", "3"), 3);
|
||||
|
||||
tpRequestValidSeconds = parseIntFromString(m.getOrDefault("传送_请求有效秒", "90"));
|
||||
tpSendIntervalSeconds = parseIntFromString(m.getOrDefault("传送_发送间隔秒", "10"));
|
||||
|
||||
// 验证合法值
|
||||
if (!"economy".equalsIgnoreCase(rewardChannel)
|
||||
&& !"bonds".equalsIgnoreCase(rewardChannel)) {
|
||||
rewardChannel = "bonds";
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ★ 自动补充新配置项 ★
|
||||
boolean needAppend = false;
|
||||
List<String> newKeys = new ArrayList<>();
|
||||
if (!m.containsKey("签到债券最小")) { needAppend = true; newKeys.add("签到债券最小=" + checkinBondMin); }
|
||||
if (!m.containsKey("签到债券最大")) { needAppend = true; newKeys.add("签到债券最大=" + checkinBondMax); }
|
||||
if (!m.containsKey("奖励发放方式")) {
|
||||
needAppend = true;
|
||||
newKeys.add("奖励发放方式=债券");
|
||||
}
|
||||
if (needAppend) {
|
||||
try {
|
||||
FileWriter fw = new FileWriter(file, true);
|
||||
BufferedWriter bw = new BufferedWriter(fw);
|
||||
bw.newLine();
|
||||
for (String key : newKeys) { bw.write(key); bw.newLine(); }
|
||||
bw.flush(); bw.close();
|
||||
System.out.println("[Config] 已补充签到债券配置");
|
||||
|
||||
} catch (IOException ignored) {}
|
||||
if (!m.containsKey("奖励发放方式")) {
|
||||
try {
|
||||
FileWriter fw = new FileWriter(file, true);
|
||||
BufferedWriter bw = new BufferedWriter(fw);
|
||||
bw.newLine();
|
||||
bw.write("奖励发放方式=bonds");
|
||||
bw.newLine();
|
||||
bw.flush(); bw.close();
|
||||
} catch (IOException ignored) {}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 奖励方式标准化:中英文全兼容
|
||||
* "债券" "bonds" "Bond" "BONDS" → "bonds"
|
||||
* "经济" "economy" "Economy" "ECONOMY" → "economy"
|
||||
* 其他任何值 → "bonds"(默认债券)
|
||||
*/
|
||||
private String normalizeRewardChannel(String raw) {
|
||||
if (raw == null) return "bonds";
|
||||
String s = raw.trim().toLowerCase();
|
||||
// 中文
|
||||
if (s.contains("债券")) return "bonds";
|
||||
if (s.contains("经济")) return "economy";
|
||||
// 英文
|
||||
if (s.contains("bond")) return "bonds";
|
||||
if (s.contains("econ")) return "economy";
|
||||
if (s.contains("Vault")) return "economy";
|
||||
// 兜底
|
||||
return "bonds";
|
||||
}
|
||||
|
||||
public void saveSettings() {
|
||||
File file = new File(dataFolder, "插件设置.txt");
|
||||
|
||||
// ★ 读取已有配置,保留Web通信等其他模块管理的配置行
|
||||
java.util.LinkedHashMap<String, String> existingPairs = new java.util.LinkedHashMap<>();
|
||||
java.util.List<String> existingLines = new ArrayList<>();
|
||||
if (file.exists()) {
|
||||
try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8))) {
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
existingLines.add(line);
|
||||
String trimmed = line.trim();
|
||||
if (!trimmed.isEmpty() && !trimmed.startsWith("#") && trimmed.contains("=")) {
|
||||
int eq = trimmed.indexOf('=');
|
||||
String k = trimmed.substring(0, eq).trim();
|
||||
existingPairs.put(k, trimmed.substring(eq + 1).trim());
|
||||
}
|
||||
}
|
||||
} catch (IOException ignored) {}
|
||||
}
|
||||
|
||||
// ★ ConfigManager管理的配置项
|
||||
java.util.LinkedHashMap<String, String> managed = new java.util.LinkedHashMap<>();
|
||||
managed.put("管理标签", adminTag);
|
||||
managed.put("管理密码", adminPassword);
|
||||
managed.put("每IP最大账号数", String.valueOf(maxAccountsPerIP));
|
||||
managed.put("审批模式", approvalMode);
|
||||
managed.put("自动审批延迟分钟", String.valueOf(autoApproveDelayMinutes));
|
||||
managed.put("登录超时秒数", String.valueOf(loginTimeout));
|
||||
managed.put("挂机踢出", String.valueOf(afkEnabled));
|
||||
managed.put("挂机超时秒数", String.valueOf(afkTimeout));
|
||||
managed.put("resource-pack-url", broadcastServerUrl);
|
||||
managed.put("http-port", String.valueOf(httpPort));
|
||||
managed.put("垃圾站_启用", String.valueOf(garbageEnabled));
|
||||
managed.put("垃圾站_清理间隔秒", String.valueOf(garbageInterval));
|
||||
managed.put("垃圾站_保留轮数", String.valueOf(garbageMaxRounds));
|
||||
managed.put("签到固定金额", String.valueOf(checkinRewardFixed));
|
||||
managed.put("签到最小金额", String.valueOf(checkinRewardMin));
|
||||
managed.put("签到最大金额", String.valueOf(checkinRewardMax));
|
||||
managed.put("签到给积分", String.valueOf(checkinGivePoints));
|
||||
managed.put("签到积分数量", String.valueOf(checkinPoints));
|
||||
managed.put("签到债券最小", String.valueOf(checkinBondMin));
|
||||
managed.put("签到债券最大", String.valueOf(checkinBondMax));
|
||||
managed.put("补签积分倍率", String.valueOf(backCheckPointMultiplier));
|
||||
managed.put("奖励发放方式", "债券");
|
||||
managed.put("传送_请求有效秒", String.valueOf(tpRequestValidSeconds));
|
||||
managed.put("传送_发送间隔秒", String.valueOf(tpSendIntervalSeconds));
|
||||
|
||||
// ★ 保留非ConfigManager管理的配置行(如web通信-*等)
|
||||
// ★ 同时保留连续的注释块(如 "# ===== Web通信配置 =====")
|
||||
java.util.List<String> preservedLines = new ArrayList<>();
|
||||
for (String line : existingLines) {
|
||||
String trimmed = line.trim();
|
||||
if (trimmed.isEmpty()) continue;
|
||||
// 如果是注释行,检查后面是否有非管理配置,保留这种注释块
|
||||
if (trimmed.startsWith("#")) {
|
||||
// 检查后续是否有非管理配置
|
||||
int idx = existingLines.indexOf(line);
|
||||
boolean hasNonManaged = false;
|
||||
for (int j = idx + 1; j < existingLines.size(); j++) {
|
||||
String next = existingLines.get(j).trim();
|
||||
if (next.isEmpty() || next.startsWith("#")) continue;
|
||||
if (next.contains("=")) {
|
||||
int eq = next.indexOf('=');
|
||||
String k = next.substring(0, eq).trim();
|
||||
if (!managed.containsKey(k)) { hasNonManaged = true; break; }
|
||||
}
|
||||
}
|
||||
if (hasNonManaged) preservedLines.add(line);
|
||||
continue;
|
||||
}
|
||||
if (!trimmed.contains("=")) continue;
|
||||
int eq = trimmed.indexOf('=');
|
||||
String key = trimmed.substring(0, eq).trim();
|
||||
if (!managed.containsKey(key)) {
|
||||
preservedLines.add(key + "=" + trimmed.substring(eq + 1).trim());
|
||||
}
|
||||
}
|
||||
|
||||
// ★ 写入完整配置:管理的配置在前,其他模块配置在后(带注释分隔)
|
||||
List<String> L = new ArrayList<>();
|
||||
L.add("# ===== Sdf1_login 插件设置 =====");
|
||||
for (java.util.Map.Entry<String, String> e : managed.entrySet()) {
|
||||
L.add(e.getKey() + "=" + e.getValue());
|
||||
}
|
||||
// 追加保留的其他模块配置
|
||||
if (!preservedLines.isEmpty()) {
|
||||
L.add("");
|
||||
// 从 preservedLines 中找出开头的注释块并添加
|
||||
int firstDataIdx = 0;
|
||||
for (int i = 0; i < preservedLines.size(); i++) {
|
||||
if (preservedLines.get(i).startsWith("#")) {
|
||||
L.add(preservedLines.get(i));
|
||||
firstDataIdx = i + 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int i = firstDataIdx; i < preservedLines.size(); i++) {
|
||||
L.add(preservedLines.get(i));
|
||||
}
|
||||
}
|
||||
writeLines(file, L);
|
||||
}
|
||||
|
||||
private void createDefaultSettings(File f) {
|
||||
List<String> L = new ArrayList<>();
|
||||
L.add("# ===== Sdf1_login 插件设置 =====");
|
||||
L.add("管理标签=admin");
|
||||
L.add("管理密码=qweasd");
|
||||
L.add("每IP最大账号数=3");
|
||||
L.add("审批模式=manual");
|
||||
L.add("自动审批延迟分钟=30");
|
||||
L.add("登录超时秒数=60");
|
||||
L.add("挂机踢出=false");
|
||||
L.add("挂机超时秒数=300");
|
||||
L.add("resource-pack-url=");
|
||||
L.add("http-port=8080");
|
||||
L.add("垃圾站_启用=true");
|
||||
L.add("垃圾站_清理间隔秒=300");
|
||||
L.add("垃圾站_保留轮数=1");
|
||||
L.add("签到固定金额=0");
|
||||
L.add("签到最小金额=50");
|
||||
L.add("签到最大金额=500");
|
||||
L.add("签到给积分=true");
|
||||
L.add("签到积分数量=10");
|
||||
L.add("签到债券最小=1");
|
||||
L.add("签到债券最大=3");
|
||||
L.add("补签积分倍率=5");
|
||||
L.add("奖励发放方式=债券");
|
||||
// 区域防护配置
|
||||
L.add("区域防护_管理员模式=tag");
|
||||
L.add("区域防护_管理员Tag=admin");
|
||||
L.add("区域防护_每平米价格=10");
|
||||
L.add("区域防护_最大领地数=3");
|
||||
L.add("传送_请求有效秒=90");
|
||||
L.add("传送_发送间隔秒=10");
|
||||
// Web通信配置(默认禁用,需手动开启)
|
||||
L.add("");
|
||||
L.add("# ===== Web通信配置 =====");
|
||||
L.add("web通信-启用=false");
|
||||
L.add("web通信-地址=https://caoyuan.ypshidifu.cn/plugin");
|
||||
L.add("web通信-Token有效期秒=600");
|
||||
L.add("web通信-同步间隔分钟=5");
|
||||
L.add("web通信-回调端口=9090");
|
||||
L.add("web通信-密钥=sdf1_web_comm_2026_ypshidifu");
|
||||
writeLines(f, L);
|
||||
}
|
||||
public List<String> getAfkWhitelist() {
|
||||
List<String> list = new ArrayList<>();
|
||||
File file = new File(dataFolder, "挂机白名单.txt");
|
||||
if (!file.exists()) {
|
||||
try {
|
||||
file.createNewFile();
|
||||
FileWriter fw = new FileWriter(file);
|
||||
fw.write("# 每行一个玩家名\n");
|
||||
fw.flush();
|
||||
fw.close();
|
||||
} catch (IOException ignored) {}
|
||||
return list;
|
||||
}
|
||||
try (BufferedReader r = new BufferedReader(
|
||||
new InputStreamReader(
|
||||
new FileInputStream(file),
|
||||
StandardCharsets.UTF_8))) {
|
||||
String line;
|
||||
while ((line = r.readLine()) != null) {
|
||||
line = line.trim();
|
||||
if (!line.isEmpty() && !line.startsWith("#"))
|
||||
list.add(line);
|
||||
}
|
||||
} catch (IOException ignored) {}
|
||||
return list;
|
||||
}
|
||||
|
||||
// ==================== 其他 ====================
|
||||
|
||||
public String generateSmtpVerifyCode() {
|
||||
String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
StringBuilder sb = new StringBuilder();
|
||||
java.util.Random r = new java.util.Random();
|
||||
for (int i = 0; i < 6; i++) sb.append(chars.charAt(r.nextInt(chars.length())));
|
||||
smtpVerifyCode = sb.toString();
|
||||
smtpVerifyExpire = System.currentTimeMillis() + 300000L;
|
||||
return smtpVerifyCode;
|
||||
}
|
||||
|
||||
public boolean checkSmtpVerifyCode(String code) {
|
||||
if (System.currentTimeMillis() > smtpVerifyExpire) { smtpVerifyCode = ""; return false; }
|
||||
return smtpVerifyCode.equalsIgnoreCase(code);
|
||||
}
|
||||
|
||||
public void clearSmtpVerifyCode() { smtpVerifyCode = ""; smtpVerifyExpire = 0; }
|
||||
public boolean canSendSmtpVerify() { return System.currentTimeMillis() - smtpLastSendTime >= 60000L; }
|
||||
public void recordSmtpSendTime() { smtpLastSendTime = System.currentTimeMillis(); }
|
||||
|
||||
// ==================== 文件工具 ====================
|
||||
|
||||
private void loadMap(File file, Map<String, String> map) {
|
||||
try (BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8))) {
|
||||
String line;
|
||||
while ((line = r.readLine()) != null) {
|
||||
line = line.trim();
|
||||
if (line.isEmpty() || line.startsWith("#")) continue;
|
||||
int eq = line.indexOf('=');
|
||||
if (eq > 0) map.put(line.substring(0, eq).trim(), line.substring(eq + 1).trim());
|
||||
}
|
||||
} catch (IOException ignored) {}
|
||||
}
|
||||
|
||||
private void writeLines(File file, List<String> lines) {
|
||||
// ★ 原子写入:先写临时文件再rename,防止写入中断导致文件损坏
|
||||
File tmp = new File(file.getParentFile(), file.getName() + ".tmp");
|
||||
try (BufferedWriter w = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(tmp), StandardCharsets.UTF_8))) {
|
||||
for (String l : lines) { w.write(l); w.newLine(); }
|
||||
w.flush(); // 强制刷盘
|
||||
w.close(); // 确保关闭
|
||||
// renameTo 是原子操作,成功则替换失败则保留原文件
|
||||
if (!file.renameTo(tmp) && !tmp.renameTo(file)) {
|
||||
// 如果rename失败,删除临时文件
|
||||
tmp.delete();
|
||||
}
|
||||
} catch (IOException ignored) {
|
||||
// 写入失败时删除临时文件,保留原文件不变
|
||||
tmp.delete();
|
||||
}
|
||||
}
|
||||
|
||||
private double parseDouble(String s, double def) { try { return Double.parseDouble(s.trim()); } catch (Exception e) { return def; } }
|
||||
private int parseInt(String s, int def) { try { return Integer.parseInt(s.trim()); } catch (Exception e) { return def; } }
|
||||
|
||||
|
||||
/**
|
||||
* 将自然语言时间字符串解析为秒数
|
||||
|
||||
Reference in New Issue
Block a user