初始化Sdf1_login项目
This commit is contained in:
+39
@@ -0,0 +1,39 @@
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
.kotlin
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea/modules.xml
|
||||
.idea/jarRepositories.xml
|
||||
.idea/compiler.xml
|
||||
.idea/libraries/
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### Eclipse ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
||||
Generated
+10
@@ -0,0 +1,10 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
||||
# 已忽略包含查询文件的默认文件夹
|
||||
/queries/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
Generated
+7
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding">
|
||||
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
+14
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="MavenProjectsManager">
|
||||
<option name="originalFiles">
|
||||
<list>
|
||||
<option value="$PROJECT_DIR$/pom.xml" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="21" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
|
||||
http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>sdf1</groupId>
|
||||
<artifactId>Sdf1_login</artifactId>
|
||||
<version>1.0</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<java.version>21</java.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>papermc</id>
|
||||
<url>https://repo.papermc.io/repository/maven-public/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>jitpack.io</id>
|
||||
<url>https://jitpack.io</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.papermc.paper</groupId>
|
||||
<artifactId>paper-api</artifactId>
|
||||
<version>1.21.4-R0.1-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.MilkBowl</groupId>
|
||||
<artifactId>VaultAPI</artifactId>
|
||||
<version>1.7.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xerial</groupId>
|
||||
<artifactId>sqlite-jdbc</artifactId>
|
||||
<version>3.46.0.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>Sdf1_login</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.11.0</version>
|
||||
<configuration>
|
||||
<source>21</source>
|
||||
<target>21</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -0,0 +1,186 @@
|
||||
package Sdf1_login;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class AFKManager {
|
||||
|
||||
private final Map<UUID, Long> lastAction =
|
||||
new HashMap<>();
|
||||
private final Map<UUID, Integer> lastStage =
|
||||
new HashMap<>();
|
||||
private final ConfigManager config;
|
||||
private final JavaPlugin plugin;
|
||||
|
||||
public AFKManager(ConfigManager config,
|
||||
JavaPlugin plugin) {
|
||||
this.config = config;
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public void startCheck() {
|
||||
Bukkit.getScheduler().runTaskTimer(plugin,
|
||||
() -> {
|
||||
if (!config.afkEnabled) return;
|
||||
long now = System.currentTimeMillis();
|
||||
for (Player p :
|
||||
Bukkit.getOnlinePlayers()) {
|
||||
if (!isLoggedIn(p.getName()))
|
||||
continue;
|
||||
if (config.afkWhitelist
|
||||
.contains(p.getName()))
|
||||
continue;
|
||||
UUID uuid = p.getUniqueId();
|
||||
Long last = lastAction
|
||||
.getOrDefault(uuid, now);
|
||||
long idle = (now - last) / 1000;
|
||||
int timeout = config.afkTimeout;
|
||||
long remain = timeout - idle;
|
||||
|
||||
if (remain <= 0) {
|
||||
p.kickPlayer(config.msg(
|
||||
"afk_kicked"));
|
||||
lastAction.remove(uuid);
|
||||
lastStage.remove(uuid);
|
||||
continue;
|
||||
}
|
||||
|
||||
int stage =
|
||||
lastStage.getOrDefault(uuid, 0);
|
||||
|
||||
if (remain <= 10) {
|
||||
// 第三阶段:最后10秒
|
||||
if (stage < 3) {
|
||||
lastStage.put(uuid, 3);
|
||||
}
|
||||
// 聊天刷屏
|
||||
p.sendMessage(config.msg(
|
||||
"afk_warning",
|
||||
"duration", fmtSec(idle),
|
||||
"remain",
|
||||
fmtSec(remain)));
|
||||
// 红色大标题循环
|
||||
p.sendTitle(
|
||||
"§c§l⚠ 即将踢出 ⚠",
|
||||
"§c" + fmtSec(remain)
|
||||
+ "后被踢出",
|
||||
5, 25, 5);
|
||||
// 音效循环
|
||||
p.playSound(p.getLocation(),
|
||||
Sound.BLOCK_NOTE_BLOCK_PLING,
|
||||
1.0f, 0.79f);
|
||||
p.playSound(p.getLocation(),
|
||||
Sound.BLOCK_NOTE_BLOCK_BASS,
|
||||
1.0f, 0.79f);
|
||||
|
||||
} else if (remain <= 60) {
|
||||
// 第二阶段:剩余1分钟
|
||||
if (stage < 2) {
|
||||
lastStage.put(uuid, 2);
|
||||
// 仅一次聊天提醒
|
||||
p.sendMessage(config.msg(
|
||||
"afk_warning",
|
||||
"duration", fmtSec(idle),
|
||||
"remain",
|
||||
fmtSec(remain)));
|
||||
}
|
||||
// 持续大标题循环
|
||||
p.sendTitle(
|
||||
"§e§l⚠ 挂机警告 ⚠",
|
||||
"§f" + fmtSec(remain)
|
||||
+ "后被踢出",
|
||||
10, 40, 10);
|
||||
// 音效循环
|
||||
p.playSound(p.getLocation(),
|
||||
Sound.BLOCK_NOTE_BLOCK_PLING,
|
||||
1.0f, 0.79f);
|
||||
p.playSound(p.getLocation(),
|
||||
Sound.BLOCK_NOTE_BLOCK_BASS,
|
||||
1.0f, 0.79f);
|
||||
|
||||
} else if (remain <= 180) {
|
||||
// 第一阶段:剩余3分钟
|
||||
if (stage < 1) {
|
||||
lastStage.put(uuid, 1);
|
||||
// 一次聊天提醒
|
||||
p.sendMessage(config.msg(
|
||||
"afk_warning",
|
||||
"duration", fmtSec(idle),
|
||||
"remain",
|
||||
fmtSec(remain)));
|
||||
// 一次大标题
|
||||
p.sendTitle(
|
||||
"§e§l⚠ 挂机警告 ⚠",
|
||||
"§f" + fmtSec(remain)
|
||||
+ "后被踢出",
|
||||
10, 60, 20);
|
||||
// 3次音效
|
||||
p.playSound(p.getLocation(),
|
||||
Sound.BLOCK_NOTE_BLOCK_PLING,
|
||||
1.0f, 0.79f);
|
||||
p.playSound(p.getLocation(),
|
||||
Sound.BLOCK_NOTE_BLOCK_BASS,
|
||||
1.0f, 0.79f);
|
||||
Bukkit.getScheduler()
|
||||
.runTaskLater(plugin,
|
||||
() -> {
|
||||
if (p.isOnline()) {
|
||||
p.playSound(
|
||||
p.getLocation(),
|
||||
Sound.BLOCK_NOTE_BLOCK_PLING,
|
||||
1.0f, 0.79f);
|
||||
p.playSound(
|
||||
p.getLocation(),
|
||||
Sound.BLOCK_NOTE_BLOCK_BASS,
|
||||
1.0f, 0.79f);
|
||||
}
|
||||
}, 10L);
|
||||
Bukkit.getScheduler()
|
||||
.runTaskLater(plugin,
|
||||
() -> {
|
||||
if (p.isOnline()) {
|
||||
p.playSound(
|
||||
p.getLocation(),
|
||||
Sound.BLOCK_NOTE_BLOCK_PLING,
|
||||
1.0f, 0.79f);
|
||||
p.playSound(
|
||||
p.getLocation(),
|
||||
Sound.BLOCK_NOTE_BLOCK_BASS,
|
||||
1.0f, 0.79f);
|
||||
}
|
||||
}, 20L);
|
||||
}
|
||||
} else {
|
||||
lastStage.remove(uuid);
|
||||
}
|
||||
}
|
||||
}, 20L, 20L);
|
||||
}
|
||||
|
||||
public void recordAction(UUID uuid) {
|
||||
lastAction.put(uuid,
|
||||
System.currentTimeMillis());
|
||||
lastStage.remove(uuid);
|
||||
}
|
||||
|
||||
public void remove(UUID uuid) {
|
||||
lastAction.remove(uuid);
|
||||
lastStage.remove(uuid);
|
||||
}
|
||||
|
||||
private boolean isLoggedIn(String name) {
|
||||
return ((Main) plugin)
|
||||
.getLoggedIn().contains(name);
|
||||
}
|
||||
|
||||
private String fmtSec(long sec) {
|
||||
if (sec < 60) return sec + "秒";
|
||||
long m = sec / 60;
|
||||
long s = sec % 60;
|
||||
return m + "分" + (s > 0 ? s + "秒" : "");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,583 @@
|
||||
package Sdf1_login;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class ChatInputManager {
|
||||
|
||||
public enum InputType {
|
||||
NONE,
|
||||
CHANGE_PWD_STEP1,
|
||||
CHANGE_PWD_STEP2,
|
||||
CHANGE_PWD_STEP3,
|
||||
SET_EMAIL,
|
||||
EMAIL_VERIFY,
|
||||
SMTP_STEP1,
|
||||
SMTP_STEP2,
|
||||
SMTP_STEP3,
|
||||
SMTP_STEP4,
|
||||
SMTP_STEP5,
|
||||
SMTP_VERIFY,
|
||||
ADMIN_AUTH,
|
||||
ADMIN_SET_AFK_TIME,
|
||||
ADMIN_SET_PWD,
|
||||
ADMIN_SET_POINTS,
|
||||
ADMIN_DELETE_CONFIRM,
|
||||
INVITE_INPUT_CODE,
|
||||
BACK_CHECKIN
|
||||
}
|
||||
|
||||
public static class InputState {
|
||||
public InputType type = InputType.NONE;
|
||||
public String tmpStr = "";
|
||||
public int tmpInt = 0;
|
||||
public String targetPlayer = "";
|
||||
public Map<String, String> tmpMap =
|
||||
new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
private final Map<UUID, InputState> states =
|
||||
new ConcurrentHashMap<>();
|
||||
private final Map<String, String> verifyCodes =
|
||||
new ConcurrentHashMap<>();
|
||||
private final Map<String, Long> verifyExpiry =
|
||||
new ConcurrentHashMap<>();
|
||||
|
||||
public InputState getState(Player p) {
|
||||
return states.computeIfAbsent(
|
||||
p.getUniqueId(), k -> new InputState());
|
||||
}
|
||||
|
||||
public void clear(Player p) {
|
||||
InputState s = states.get(p.getUniqueId());
|
||||
if (s != null) {
|
||||
s.type = InputType.NONE;
|
||||
s.tmpMap.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void setVerifyCode(Player p, String code,
|
||||
String purpose) {
|
||||
String key = p.getUniqueId() + ":" + purpose;
|
||||
verifyCodes.put(key, code);
|
||||
verifyExpiry.put(key,
|
||||
System.currentTimeMillis() + 300000L);
|
||||
}
|
||||
|
||||
public boolean checkVerifyCode(Player p, String code,
|
||||
String purpose) {
|
||||
String key = p.getUniqueId() + ":" + purpose;
|
||||
String stored = verifyCodes.get(key);
|
||||
Long expiry = verifyExpiry.get(key);
|
||||
if (stored == null || expiry == null)
|
||||
return false;
|
||||
if (System.currentTimeMillis() > expiry) {
|
||||
verifyCodes.remove(key);
|
||||
verifyExpiry.remove(key);
|
||||
return false;
|
||||
}
|
||||
if (stored.equals(code)) {
|
||||
verifyCodes.remove(key);
|
||||
verifyExpiry.remove(key);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean handleInput(Player p, String msg,
|
||||
Main main) {
|
||||
InputState s = getState(p);
|
||||
if (s.type == InputType.NONE) return false;
|
||||
|
||||
if (msg.equalsIgnoreCase("00")) {
|
||||
clear(p);
|
||||
p.sendMessage(
|
||||
"§e[Sdf1_login] §f操作已取消");
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (s.type) {
|
||||
|
||||
case CHANGE_PWD_STEP1: {
|
||||
String salt = (String) main.getDb()
|
||||
.getField(p.getName(),
|
||||
"password_salt");
|
||||
if (salt == null) {
|
||||
clear(p);
|
||||
return true;
|
||||
}
|
||||
String hash =
|
||||
PasswordUtils.hash(msg, salt);
|
||||
if (main.getDb().checkPassword(
|
||||
p.getName(), hash)) {
|
||||
s.type =
|
||||
InputType.CHANGE_PWD_STEP2;
|
||||
p.sendMessage(
|
||||
"§e[Sdf1_login] §f请输入新密码(6-20位,含大写+小写+数字)");
|
||||
} else {
|
||||
p.sendMessage(
|
||||
"§c[Sdf1_login] §f旧密码错误!");
|
||||
clear(p);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case CHANGE_PWD_STEP2: {
|
||||
if (!PasswordUtils.validate(msg)) {
|
||||
p.sendMessage(main.getConfig2()
|
||||
.msg("password_invalid"));
|
||||
return true;
|
||||
}
|
||||
s.tmpStr = msg;
|
||||
s.type =
|
||||
InputType.CHANGE_PWD_STEP3;
|
||||
p.sendMessage(
|
||||
"§e[Sdf1_login] §f请再次输入新密码确认");
|
||||
return true;
|
||||
}
|
||||
|
||||
case CHANGE_PWD_STEP3: {
|
||||
if (!msg.equals(s.tmpStr)) {
|
||||
p.sendMessage(
|
||||
"§c[Sdf1_login] §f两次密码不一致");
|
||||
s.type =
|
||||
InputType.CHANGE_PWD_STEP2;
|
||||
return true;
|
||||
}
|
||||
String salt =
|
||||
PasswordUtils.generateSalt();
|
||||
String hash =
|
||||
PasswordUtils.hash(msg, salt);
|
||||
main.getDb().setField(p.getName(),
|
||||
"password_hash", hash);
|
||||
main.getDb().setField(p.getName(),
|
||||
"password_salt", salt);
|
||||
main.getDb().setField(p.getName(),
|
||||
"temp_password", "");
|
||||
main.getDb().setField(p.getName(),
|
||||
"temp_pw_used", 0);
|
||||
main.getNeedsPasswordChange()
|
||||
.remove(p.getName());
|
||||
p.sendMessage(
|
||||
"§a[Sdf1_login] §f密码修改成功!");
|
||||
clear(p);
|
||||
return true;
|
||||
}
|
||||
|
||||
case SET_EMAIL: {
|
||||
if (!msg.contains("@")
|
||||
|| !msg.contains(".")) {
|
||||
p.sendMessage(main.getConfig2()
|
||||
.msg("email_invalid"));
|
||||
return true;
|
||||
}
|
||||
String code =
|
||||
PasswordUtils
|
||||
.generateVerifyCode();
|
||||
setVerifyCode(p, code, "email");
|
||||
main.getEmail().sendVerifyCode(
|
||||
msg, p.getName(), code);
|
||||
s.tmpStr = msg;
|
||||
s.type =
|
||||
InputType.EMAIL_VERIFY;
|
||||
p.sendMessage(main.getConfig2()
|
||||
.msg("email_verify_sent",
|
||||
"email", msg));
|
||||
return true;
|
||||
}
|
||||
|
||||
case EMAIL_VERIFY: {
|
||||
if (checkVerifyCode(p, msg, "email")) {
|
||||
main.getDb().setField(
|
||||
p.getName(),
|
||||
"email", s.tmpStr);
|
||||
p.sendMessage(main.getConfig2()
|
||||
.msg(
|
||||
"email_verify_success"));
|
||||
clear(p);
|
||||
} else {
|
||||
p.sendMessage(main.getConfig2()
|
||||
.msg(
|
||||
"email_verify_failed"));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case SMTP_STEP1: {
|
||||
s.tmpMap.put("smtp地址", msg);
|
||||
s.type = InputType.SMTP_STEP2;
|
||||
p.sendMessage(
|
||||
"§e[Sdf1_login] §f当前端口: "
|
||||
+ main.getConfig2()
|
||||
.getSmtp("smtp端口"));
|
||||
p.sendMessage(
|
||||
"§e[Sdf1_login] §f输入新端口(输入0跳过):");
|
||||
return true;
|
||||
}
|
||||
|
||||
case SMTP_STEP2: {
|
||||
if (!msg.equals("0"))
|
||||
s.tmpMap.put("smtp端口", msg);
|
||||
s.type = InputType.SMTP_STEP3;
|
||||
p.sendMessage(
|
||||
"§e[Sdf1_login] §f输入发件邮箱(输入0跳过):");
|
||||
return true;
|
||||
}
|
||||
|
||||
case SMTP_STEP3: {
|
||||
if (!msg.equals("0"))
|
||||
s.tmpMap.put("发件邮箱", msg);
|
||||
s.type = InputType.SMTP_STEP4;
|
||||
p.sendMessage(
|
||||
"§e[Sdf1_login] §f输入授权码(输入0跳过):");
|
||||
return true;
|
||||
}
|
||||
|
||||
case SMTP_STEP4: {
|
||||
if (!msg.equals("0"))
|
||||
s.tmpMap.put("授权码", msg);
|
||||
s.type = InputType.SMTP_STEP5;
|
||||
p.sendMessage(
|
||||
"§e[Sdf1_login] §f输入测试接收邮箱(输入0跳过):");
|
||||
return true;
|
||||
}
|
||||
|
||||
case SMTP_STEP5: {
|
||||
if (!msg.equals("0"))
|
||||
s.tmpMap.put("测试邮箱", msg);
|
||||
String testEmail =
|
||||
s.tmpMap.getOrDefault(
|
||||
"测试邮箱", "");
|
||||
String senderEmail =
|
||||
s.tmpMap.getOrDefault(
|
||||
"发件邮箱", "");
|
||||
String recipient =
|
||||
!testEmail.isEmpty()
|
||||
? testEmail : senderEmail;
|
||||
if (recipient == null
|
||||
|| recipient.isEmpty()) {
|
||||
p.sendMessage(
|
||||
"§c[Sdf1_login] §f请至少填写发件邮箱");
|
||||
clear(p);
|
||||
return true;
|
||||
}
|
||||
String code =
|
||||
PasswordUtils
|
||||
.generateVerifyCode();
|
||||
setVerifyCode(p, code, "smtp");
|
||||
main.getEmail().sendVerifyCode(
|
||||
recipient,
|
||||
p.getName(), code);
|
||||
s.type =
|
||||
InputType.SMTP_VERIFY;
|
||||
p.sendMessage(main.getConfig2()
|
||||
.msg("smtp_verify_sent",
|
||||
"email", recipient));
|
||||
return true;
|
||||
}
|
||||
|
||||
case SMTP_VERIFY: {
|
||||
if (checkVerifyCode(p, msg, "smtp")) {
|
||||
String addr =
|
||||
s.tmpMap.getOrDefault(
|
||||
"smtp地址",
|
||||
main.getConfig2()
|
||||
.getSmtp("smtp地址"));
|
||||
String port =
|
||||
s.tmpMap.getOrDefault(
|
||||
"smtp端口",
|
||||
main.getConfig2()
|
||||
.getSmtp("smtp端口"));
|
||||
String sender =
|
||||
s.tmpMap.getOrDefault(
|
||||
"发件邮箱",
|
||||
main.getConfig2()
|
||||
.getSmtp("发件邮箱"));
|
||||
String auth =
|
||||
s.tmpMap.getOrDefault(
|
||||
"授权码",
|
||||
main.getConfig2()
|
||||
.getSmtp("授权码"));
|
||||
String test =
|
||||
s.tmpMap.getOrDefault(
|
||||
"测试邮箱",
|
||||
main.getConfig2()
|
||||
.getSmtp("测试邮箱"));
|
||||
main.getConfig2().setSmtp(
|
||||
"smtp地址", addr);
|
||||
main.getConfig2().setSmtp(
|
||||
"smtp端口", port);
|
||||
main.getConfig2().setSmtp(
|
||||
"发件邮箱", sender);
|
||||
main.getConfig2().setSmtp(
|
||||
"授权码", auth);
|
||||
main.getConfig2().setSmtp(
|
||||
"测试邮箱", test);
|
||||
p.sendMessage(main.getConfig2()
|
||||
.msg(
|
||||
"smtp_verify_success"));
|
||||
clear(p);
|
||||
} else {
|
||||
p.sendMessage(main.getConfig2()
|
||||
.msg(
|
||||
"smtp_verify_failed"));
|
||||
clear(p);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case ADMIN_AUTH: {
|
||||
String pass =
|
||||
main.getConfig2().adminPassword;
|
||||
if (msg.equals(pass)) {
|
||||
if ("qweasd".equals(pass)) {
|
||||
p.sendMessage(
|
||||
"§c§l[警告] §f当前使用默认管理密码,请尽快修改!");
|
||||
}
|
||||
Bukkit.getScheduler()
|
||||
.runTask(main, () -> {
|
||||
main.getGui()
|
||||
.openAdmin(p);
|
||||
});
|
||||
} else {
|
||||
p.sendMessage(
|
||||
"§c[Sdf1_login] §f管理密码错误!");
|
||||
}
|
||||
clear(p);
|
||||
return true;
|
||||
}
|
||||
|
||||
case ADMIN_SET_AFK_TIME: {
|
||||
int seconds =
|
||||
parseAfkTime(msg);
|
||||
if (seconds > 0) {
|
||||
main.getConfig2()
|
||||
.afkTimeout = seconds;
|
||||
int min = seconds / 60;
|
||||
p.sendMessage(main.getConfig2()
|
||||
.msg("afk_set_time",
|
||||
"duration",
|
||||
min + "分钟"));
|
||||
} else {
|
||||
p.sendMessage(
|
||||
"§c[Sdf1_login] §f时间格式无效");
|
||||
}
|
||||
clear(p);
|
||||
return true;
|
||||
}
|
||||
|
||||
case ADMIN_SET_PWD: {
|
||||
if (msg.equals("0")) {
|
||||
p.sendMessage(
|
||||
"§e[Sdf1_login] §f操作已取消");
|
||||
clear(p);
|
||||
return true;
|
||||
}
|
||||
String target = s.targetPlayer;
|
||||
String salt =
|
||||
PasswordUtils.generateSalt();
|
||||
String hash =
|
||||
PasswordUtils.hash(msg, salt);
|
||||
main.getDb().setField(target,
|
||||
"password_hash", hash);
|
||||
main.getDb().setField(target,
|
||||
"password_salt", salt);
|
||||
p.sendMessage(main.getConfig2()
|
||||
.msg("admin_set_password",
|
||||
"user", target));
|
||||
clear(p);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
case ADMIN_SET_POINTS: {
|
||||
try {
|
||||
int pts =
|
||||
Integer.parseInt(msg);
|
||||
main.getDb().setField(
|
||||
s.targetPlayer,
|
||||
"points", pts);
|
||||
p.sendMessage(main.getConfig2()
|
||||
.msg("admin_points_set",
|
||||
"user",
|
||||
s.targetPlayer,
|
||||
"points",
|
||||
String.valueOf(pts)));
|
||||
} catch (NumberFormatException e) {
|
||||
p.sendMessage(
|
||||
"§c[Sdf1_login] §f请输入数字");
|
||||
}
|
||||
clear(p);
|
||||
return true;
|
||||
}
|
||||
|
||||
case ADMIN_DELETE_CONFIRM: {
|
||||
String[] parts =
|
||||
msg.split("\\s+", 2);
|
||||
if (parts.length < 2) {
|
||||
p.sendMessage(main.getConfig2()
|
||||
.msg("admin_delete_confirm"));
|
||||
p.sendMessage(
|
||||
"§c格式: <玩家名> <你的密码>");
|
||||
return true;
|
||||
}
|
||||
String targetName = parts[0];
|
||||
String pwd = parts[1];
|
||||
String salt = (String) main.getDb()
|
||||
.getField(p.getName(),
|
||||
"password_salt");
|
||||
if (salt == null) {
|
||||
clear(p);
|
||||
return true;
|
||||
}
|
||||
String hash =
|
||||
PasswordUtils.hash(pwd, salt);
|
||||
if (main.getDb().checkPassword(
|
||||
p.getName(), hash)
|
||||
&& targetName
|
||||
.equalsIgnoreCase(
|
||||
s.targetPlayer)) {
|
||||
main.getDb()
|
||||
.deleteUser(targetName);
|
||||
p.sendMessage(main.getConfig2()
|
||||
.msg("admin_delete_success",
|
||||
"user",
|
||||
targetName));
|
||||
} else {
|
||||
p.sendMessage(main.getConfig2()
|
||||
.msg(
|
||||
"admin_delete_failed"));
|
||||
}
|
||||
clear(p);
|
||||
return true;
|
||||
}
|
||||
|
||||
case BACK_CHECKIN: {
|
||||
String result =
|
||||
main.getCheckIn()
|
||||
.backCheckIn(p, msg);
|
||||
p.sendMessage(result);
|
||||
clear(p);
|
||||
return true;
|
||||
}
|
||||
|
||||
case INVITE_INPUT_CODE: {
|
||||
String code =
|
||||
msg.trim().toUpperCase();
|
||||
String inviter =
|
||||
findInvokerByCode(main, code);
|
||||
if (inviter != null
|
||||
&& !inviter.equals(
|
||||
p.getName())) {
|
||||
main.getDb().setField(
|
||||
p.getName(),
|
||||
"invited_by", inviter);
|
||||
// 更新邀请人数据
|
||||
String month =
|
||||
new SimpleDateFormat("yyyy-MM")
|
||||
.format(new Date());
|
||||
String lastMonth =
|
||||
(String) main.getDb()
|
||||
.getField(inviter,
|
||||
"invite_month");
|
||||
if (!month.equals(lastMonth)) {
|
||||
main.getDb().setField(
|
||||
inviter,
|
||||
"invite_month", month);
|
||||
main.getDb().setField(
|
||||
inviter,
|
||||
"monthly_invite_count",
|
||||
0);
|
||||
}
|
||||
int cnt = ((Number) main.getDb()
|
||||
.getField(inviter,
|
||||
"monthly_invite_count"))
|
||||
.intValue();
|
||||
main.getDb().setField(
|
||||
inviter,
|
||||
"monthly_invite_count",
|
||||
cnt + 1);
|
||||
// 通知邀请人
|
||||
Player inviterPlayer =
|
||||
Bukkit.getPlayerExact(
|
||||
inviter);
|
||||
if (inviterPlayer != null
|
||||
&& inviterPlayer
|
||||
.isOnline()) {
|
||||
inviterPlayer.sendMessage(
|
||||
main.getConfig2().msg(
|
||||
"invite_referral_bonus",
|
||||
"points",
|
||||
String.valueOf(
|
||||
cnt + 1)));
|
||||
}
|
||||
p.sendMessage(main.getConfig2()
|
||||
.msg("invite_bound",
|
||||
"user", inviter));
|
||||
} else {
|
||||
p.sendMessage(
|
||||
"§c[Sdf1_login] §f邀请码无效");
|
||||
}
|
||||
clear(p);
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private int parseAfkTime(String input) {
|
||||
String s = input.toLowerCase().trim();
|
||||
if (s.contains("小时") || s.contains("时")) {
|
||||
String num =
|
||||
s.replaceAll("[^0-9]", "");
|
||||
if (!num.isEmpty())
|
||||
return Integer.parseInt(num) * 3600;
|
||||
}
|
||||
if (s.contains("分钟") || s.contains("分")) {
|
||||
String num =
|
||||
s.replaceAll("[^0-9]", "");
|
||||
if (!num.isEmpty())
|
||||
return Integer.parseInt(num) * 60;
|
||||
}
|
||||
if (s.endsWith("h")) {
|
||||
String num =
|
||||
s.replaceAll("[^0-9]", "");
|
||||
if (!num.isEmpty())
|
||||
return Integer.parseInt(num) * 3600;
|
||||
}
|
||||
if (s.endsWith("m")) {
|
||||
String num =
|
||||
s.replaceAll("[^0-9]", "");
|
||||
if (!num.isEmpty())
|
||||
return Integer.parseInt(num) * 60;
|
||||
}
|
||||
String num =
|
||||
s.replaceAll("[^0-9]", "");
|
||||
if (!num.isEmpty())
|
||||
return Integer.parseInt(num) * 60;
|
||||
return -1;
|
||||
}
|
||||
|
||||
private String findInvokerByCode(Main main,
|
||||
String code) {
|
||||
for (Map<String, Object> user
|
||||
: main.getDb().getAllUsers()) {
|
||||
String ic = (String) user.getOrDefault(
|
||||
"invite_code", "");
|
||||
if (code.equals(ic)) {
|
||||
return (String) user
|
||||
.get("player_name");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package Sdf1_login;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
public class CheckInManager {
|
||||
|
||||
private final Main plugin;
|
||||
|
||||
public CheckInManager(Main plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public boolean isCheckedInToday(String name) {
|
||||
String last = (String) plugin.getDb()
|
||||
.getField(name, "last_checkin_date");
|
||||
String today = new SimpleDateFormat("yyyy-MM-dd")
|
||||
.format(new Date());
|
||||
return today.equals(last);
|
||||
}
|
||||
|
||||
public String checkIn(Player p) {
|
||||
String name = p.getName();
|
||||
if (isCheckedInToday(name)) {
|
||||
return plugin.getConfig2().msg("checkin_already");
|
||||
}
|
||||
|
||||
int streak = ((Number) plugin.getDb()
|
||||
.getField(name, "checkin_streak")).intValue();
|
||||
String lastDate = (String) plugin.getDb()
|
||||
.getField(name, "last_checkin_date");
|
||||
String today = new SimpleDateFormat("yyyy-MM-dd")
|
||||
.format(new Date());
|
||||
|
||||
// 检查是否连续
|
||||
if (lastDate != null && !lastDate.isEmpty()) {
|
||||
try {
|
||||
java.util.Date lastD = new SimpleDateFormat(
|
||||
"yyyy-MM-dd").parse(lastDate);
|
||||
java.util.Date todayD = new SimpleDateFormat(
|
||||
"yyyy-MM-dd").parse(today);
|
||||
long diff = todayD.getTime() - lastD.getTime();
|
||||
if (diff > 86400000L * 2) {
|
||||
streak = 0; // 断签
|
||||
} else if (diff > 86400000L) {
|
||||
streak++; // 连续
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
|
||||
if (streak == 0) streak = 1;
|
||||
else streak++;
|
||||
|
||||
double multi = 1.0;
|
||||
if (streak >= 21) multi = 1.5;
|
||||
else if (streak >= 14) multi = 1.3;
|
||||
else if (streak >= 7) multi = 1.1;
|
||||
|
||||
int points = (int) (1 * multi);
|
||||
plugin.getDb().addPoints(name, points);
|
||||
plugin.getDb().setField(
|
||||
name, "checkin_streak", streak);
|
||||
plugin.getDb().setField(
|
||||
name, "last_checkin_date", today);
|
||||
int total = ((Number) plugin.getDb()
|
||||
.getField(name, "total_checkin_days"))
|
||||
.intValue();
|
||||
plugin.getDb().setField(
|
||||
name, "total_checkin_days", total + 1);
|
||||
|
||||
return plugin.getConfig2().msg("checkin_success",
|
||||
"points", String.valueOf(points),
|
||||
"streak", String.valueOf(streak),
|
||||
"multi", String.valueOf(multi));
|
||||
}
|
||||
|
||||
public String backCheckIn(Player p, String dateStr) {
|
||||
String name = p.getName();
|
||||
if (!plugin.getDb().deductPoints(name, 10)) {
|
||||
return plugin.getConfig2().msg("backcheckin_no_point");
|
||||
}
|
||||
try {
|
||||
java.util.Date target = new SimpleDateFormat(
|
||||
"yyyy-MM-dd").parse(dateStr);
|
||||
java.util.Date now = new SimpleDateFormat(
|
||||
"yyyy-MM-dd").parse(
|
||||
new SimpleDateFormat("yyyy-MM-dd")
|
||||
.format(new Date()));
|
||||
long diff = now.getTime() - target.getTime();
|
||||
if (diff < 0 || diff > 3 * 86400000L) {
|
||||
plugin.getDb().addPoints(name, 10); // 退还
|
||||
return plugin.getConfig2().msg(
|
||||
"backcheckin_expired");
|
||||
}
|
||||
plugin.getDb().addPoints(name, 1); // 补签也给1分
|
||||
return plugin.getConfig2().msg("backcheckin_success");
|
||||
} catch (Exception e) {
|
||||
plugin.getDb().addPoints(name, 10);
|
||||
return "§c[Sdf1_login] §f日期格式错误,请用 yyyy-MM-dd";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,433 @@
|
||||
package Sdf1_login;
|
||||
// 配置文件
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.regex.*;
|
||||
|
||||
public class ConfigManager {
|
||||
|
||||
private final Map<String, String> messages =
|
||||
new LinkedHashMap<>();
|
||||
private final Map<String, String> smtp = new LinkedHashMap<>();
|
||||
private final File dataFolder;
|
||||
|
||||
// 配置项
|
||||
public int afkTimeout = 300;
|
||||
public int loginTimeout = 120;
|
||||
public boolean afkEnabled = true;
|
||||
public Set<String> afkWhitelist =
|
||||
new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
|
||||
public String adminTag = "admin";
|
||||
public String adminPassword = "qweasd";
|
||||
|
||||
|
||||
public ConfigManager(File dataFolder) {
|
||||
this.dataFolder = dataFolder;
|
||||
}
|
||||
|
||||
// ===== 消息配置 =====
|
||||
|
||||
public void loadMessages() {
|
||||
messages.clear();
|
||||
File f = new File(dataFolder, "设置.txt");
|
||||
if (!f.exists()) writeDefaultMessages();
|
||||
try {
|
||||
BufferedReader r = new BufferedReader(
|
||||
new InputStreamReader(
|
||||
new FileInputStream(f),
|
||||
StandardCharsets.UTF_8));
|
||||
String line;
|
||||
while ((line = r.readLine()) != null) {
|
||||
if (line.startsWith("\uFEFF"))
|
||||
line = line.substring(1);
|
||||
String t = line.trim();
|
||||
if (t.isEmpty() || t.startsWith("#")) continue;
|
||||
int eq = t.indexOf('=');
|
||||
if (eq < 0) continue;
|
||||
messages.put(t.substring(0, eq).trim(),
|
||||
t.substring(eq + 1).trim());
|
||||
}
|
||||
r.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
ensureMessageDefaults();
|
||||
}
|
||||
|
||||
|
||||
private void ensureMessageDefaults() {
|
||||
putDefault("not_registered",
|
||||
"§c§l[Sdf1_login] §f你还没有注册,请使用 /reg <密码> 注册");
|
||||
putDefault("not_logged_in",
|
||||
"§c§l[Sdf1_login] §f请先使用 /login <密码> 登录");
|
||||
putDefault("login_success",
|
||||
"§a§l[Sdf1_login] §f登录成功!欢迎回来 {user}");
|
||||
putDefault("register_success",
|
||||
"§a§l[Sdf1_login] §f注册成功!请使用 /login <密码> 登录");
|
||||
putDefault("login_failed",
|
||||
"§c§l[Sdf1_login] §f密码错误!");
|
||||
putDefault("already_registered",
|
||||
"§c§l[Sdf1_login] §f你已经注册过了");
|
||||
putDefault("already_logged_in",
|
||||
"§a§l[Sdf1_login] §f你已经登录了");
|
||||
putDefault("password_invalid",
|
||||
"§c§l[Sdf1_login] §f密码不符合要求!需要6-20位,包含大写字母+小写字母+数字");
|
||||
putDefault("password_format_hint",
|
||||
"§7密码要求: 6-20位,至少1个大写字母+1个小写字母+1个数字");
|
||||
putDefault("auto_login_ip",
|
||||
"§a§l[Sdf1_login] §f检测到同IP近期登录,已自动登录!欢迎回来 {user}");
|
||||
putDefault("email_verify_sent",
|
||||
"§a§l[Sdf1_login] §f验证码已发送到 {email},请输入6位验证码:");
|
||||
putDefault("email_verify_success",
|
||||
"§a§l[Sdf1_login] §f邮箱验证成功,已绑定!");
|
||||
putDefault("email_verify_failed",
|
||||
"§c§l[Sdf1_login] §f验证码错误,请重试");
|
||||
putDefault("smtp_verify_sent",
|
||||
"§a§l[Sdf1_login] §f验证码已发送到 {email},请输入6位验证码:");
|
||||
putDefault("smtp_verify_success",
|
||||
"§a§l[Sdf1_login] §fSMTP配置验证成功!");
|
||||
putDefault("smtp_verify_failed",
|
||||
"§c§l[Sdf1_login] §f验证码错误,请重试");
|
||||
putDefault("login_timeout",
|
||||
"§c§l[Sdf1_login] §f登录超时,已被踢出");
|
||||
putDefault("login_required",
|
||||
"§c§l[Sdf1_login] §f你还没有登录,请先 /login <密码>");
|
||||
putDefault("reset_no_email",
|
||||
"§c§l[Sdf1_login] §f你还没有绑定邮箱,无法通过邮件重置密码");
|
||||
putDefault("reset_sent",
|
||||
"§a§l[Sdf1_login] §f临时密码已发送到你的邮箱,请查收");
|
||||
putDefault("reset_temp_used",
|
||||
"§a§l[Sdf1_login] §f临时密码已生效,旧密码已失效");
|
||||
putDefault("reset_failed",
|
||||
"§c§l[Sdf1_login] §f重置失败,请联系管理员");
|
||||
putDefault("email_set",
|
||||
"§a§l[Sdf1_login] §f邮箱已设置为: {email}");
|
||||
putDefault("email_invalid",
|
||||
"§c§l[Sdf1_login] §f邮箱格式不正确");
|
||||
putDefault("checkin_success",
|
||||
"§a§l[Sdf1_login] §f签到成功!获得 {points} 积分(连续{streak}天,倍率x{multi})");
|
||||
putDefault("checkin_already",
|
||||
"§c§l[Sdf1_login] §f今天已经签到过了");
|
||||
putDefault("backcheckin_success",
|
||||
"§a§l[Sdf1_login] §f补签成功!消耗10积分");
|
||||
putDefault("backcheckin_no_point",
|
||||
"§c§l[Sdf1_login] §f积分不足,补签需要10积分");
|
||||
putDefault("backcheckin_expired",
|
||||
"§c§l[Sdf1_login] §f只能补签3天内的签到");
|
||||
putDefault("points_insufficient",
|
||||
"§c§l[Sdf1_login] §f积分不足!当前: {points}");
|
||||
putDefault("points_purchase_success",
|
||||
"§a§l[Sdf1_login] §f兑换成功!消耗 {count} 积分");
|
||||
putDefault("invite_code_generated",
|
||||
"§a§l[Sdf1_login] §f你的邀请码: {code}");
|
||||
putDefault("invite_bound",
|
||||
"§a§l[Sdf1_login] §f你已被 {user} 邀请!");
|
||||
putDefault("invite_referral_bonus",
|
||||
"§a§l[Sdf1_login] §f邀请返点: +{points} 积分");
|
||||
putDefault("gift_claimed",
|
||||
"§a§l[Sdf1_login] §f礼包领取成功!");
|
||||
putDefault("gift_not_ready",
|
||||
"§c§l[Sdf1_login] §f礼包条件未满足");
|
||||
putDefault("gift_all_claimed",
|
||||
"§a§l[Sdf1_login] §f所有礼包已领取完毕!");
|
||||
putDefault("admin_no_permission",
|
||||
"§c§l[Sdf1_login] §f无权限");
|
||||
putDefault("admin_set_password",
|
||||
"§a§l[Sdf1_login] §f已为 {user} 设置新密码");
|
||||
putDefault("admin_delete_confirm",
|
||||
"§c§l[Sdf1_login] §f请输入要删除的玩家名和你的密码进行确认");
|
||||
putDefault("admin_delete_success",
|
||||
"§a§l[Sdf1_login] §f玩家 {user} 已删除");
|
||||
putDefault("admin_delete_failed",
|
||||
"§c§l[Sdf1_login] §f删除失败:验证不通过");
|
||||
putDefault("admin_points_set",
|
||||
"§a§l[Sdf1_login] §f已设置 {user} 积分为 {points}");
|
||||
putDefault("afk_warning",
|
||||
"§e§l[Sdf1_login] §f你已挂机 {duration},将在 {remain} 后被踢出");
|
||||
putDefault("afk_kicked",
|
||||
"§c§l[Sdf1_login] §f因挂机过久被踢出");
|
||||
putDefault("afk_set_time",
|
||||
"§a§l[Sdf1_login] §f挂机踢出时长已设为 {duration}");
|
||||
putDefault("afk_set_enabled",
|
||||
"§a§l[Sdf1_login] §f挂机踢出已开启");
|
||||
putDefault("afk_set_disabled",
|
||||
"§a§l[Sdf1_login] §f挂机踢出已关闭");
|
||||
putDefault("afk_whitelist_added",
|
||||
"§a§l[Sdf1_login] §f已将 {user} 加入防踢白名单");
|
||||
putDefault("afk_whitelist_removed",
|
||||
"§a§l[Sdf1_login] §f已将 {user} 移出防踢白名单");
|
||||
}
|
||||
|
||||
private void putDefault(String key, String value) {
|
||||
messages.putIfAbsent(key, value);
|
||||
}
|
||||
|
||||
|
||||
public String msg(String key, Object... kvPairs) {
|
||||
String template = messages.getOrDefault(key, key);
|
||||
for (int i = 0; i < kvPairs.length - 1; i += 2) {
|
||||
String varName = kvPairs[i].toString();
|
||||
String value = kvPairs[i + 1] != null
|
||||
? kvPairs[i + 1].toString() : "";
|
||||
String[] open = {"{", "[", "(", "【", "(", "《", "<"};
|
||||
String[] close = {"}", "]", ")", "】", ")", "》", ">"};
|
||||
for (int j = 0; j < open.length; j++) {
|
||||
template = template.replace(
|
||||
open[j] + varName + close[j], value);
|
||||
}
|
||||
}
|
||||
template = template.replace("\\n", "\n");
|
||||
template = template.replace("<br>", "\n");
|
||||
return template;
|
||||
}
|
||||
|
||||
private void writeDefaultMessages() {
|
||||
File f = new File(dataFolder, "设置.txt");
|
||||
try (PrintWriter pw = new PrintWriter(
|
||||
new OutputStreamWriter(
|
||||
new FileOutputStream(f),
|
||||
StandardCharsets.UTF_8))) {
|
||||
pw.println("# ==========================================");
|
||||
pw.println("# Sdf1_login 消息提示配置");
|
||||
pw.println("# ==========================================");
|
||||
pw.println("# 变量支持: {变量} [变量] (变量) 【变量】");
|
||||
pw.println("# 可用变量: [user] [duration] [count]");
|
||||
pw.println("# [code] [points] [stage]");
|
||||
pw.println("# [email] [url] [reason]");
|
||||
pw.println();
|
||||
pw.println("# ===== 登录系统 =====");
|
||||
pw.println("not_registered=§c§l[Sdf1_login] §f你还没有注册,请使用 /reg <密码> 注册");
|
||||
pw.println("not_logged_in=§c§l[Sdf1_login] §f请先使用 /login <密码> 登录");
|
||||
pw.println("login_success=§a§l[Sdf1_login] §f登录成功!欢迎回来 [user]");
|
||||
pw.println("register_success=§a§l[Sdf1_login] §f注册成功!请使用 /login <密码> 登录");
|
||||
pw.println("login_failed=§c§l[Sdf1_login] §f密码错误!");
|
||||
pw.println("already_registered=§c§l[Sdf1_login] §f你已经注册过了");
|
||||
pw.println("already_logged_in=§a§l[Sdf1_login] §f你已经登录了");
|
||||
pw.println("password_invalid=§c§l[Sdf1_login] §f密码不符合要求!需要6-20位,包含大写字母+小写字母+数字");
|
||||
pw.println("password_format_hint=§7密码要求: 6-20位,至少1个大写字母+1个小写字母+1个数字");
|
||||
pw.println();
|
||||
pw.println("# ===== IP自动登录 =====");
|
||||
pw.println("auto_login_ip=§a§l[Sdf1_login] §f检测到同IP近期登录,已自动登录!欢迎回来 [user]");
|
||||
pw.println();
|
||||
pw.println("# ===== 邮箱验证 =====");
|
||||
pw.println("email_verify_sent=§a§l[Sdf1_login] §f验证码已发送到 [email],请输入6位验证码:");
|
||||
pw.println("email_verify_success=§a§l[Sdf1_login] §f邮箱验证成功,已绑定!");
|
||||
pw.println("email_verify_failed=§c§l[Sdf1_login] §f验证码错误,请重试");
|
||||
pw.println();
|
||||
pw.println("# ===== SMTP验证 =====");
|
||||
pw.println("smtp_verify_sent=§a§l[Sdf1_login] §f验证码已发送到 [email],请输入6位验证码:");
|
||||
pw.println("smtp_verify_success=§a§l[Sdf1_login] §fSMTP配置验证成功!");
|
||||
pw.println("smtp_verify_failed=§c§l[Sdf1_login] §f验证码错误,请重试");
|
||||
pw.println("login_timeout=§c§l[Sdf1_login] §f登录超时,已被踢出");
|
||||
pw.println("login_required=§c§l[Sdf1_login] §f你还没有登录,请先 /login <密码>");
|
||||
pw.println();
|
||||
pw.println("# ===== 密码重置 =====");
|
||||
pw.println("reset_no_email=§c§l[Sdf1_login] §f你还没有绑定邮箱,无法通过邮件重置密码");
|
||||
pw.println("reset_sent=§a§l[Sdf1_login] §f临时密码已发送到你的邮箱,请查收");
|
||||
pw.println("reset_temp_used=§a§l[Sdf1_login] §f临时密码已生效,旧密码已失效");
|
||||
pw.println("reset_failed=§c§l[Sdf1_login] §f重置失败,请联系管理员");
|
||||
pw.println();
|
||||
pw.println("# ===== 邮箱 =====");
|
||||
pw.println("email_set=§a§l[Sdf1_login] §f邮箱已设置为: [email]");
|
||||
pw.println("email_invalid=§c§l[Sdf1_login] §f邮箱格式不正确");
|
||||
pw.println();
|
||||
pw.println("# ===== 签到 =====");
|
||||
pw.println("checkin_success=§a§l[Sdf1_login] §f签到成功!获得 [points] 积分(连续[streak]天,倍率x[multi])");
|
||||
pw.println("checkin_already=§c§l[Sdf1_login] §f今天已经签到过了");
|
||||
pw.println("backcheckin_success=§a§l[Sdf1_login] §f补签成功!消耗10积分");
|
||||
pw.println("backcheckin_no_point=§c§l[Sdf1_login] §f积分不足,补签需要10积分");
|
||||
pw.println("backcheckin_expired=§c§l[Sdf1_login] §f只能补签3天内的签到");
|
||||
pw.println();
|
||||
pw.println("# ===== 积分 =====");
|
||||
pw.println("points_insufficient=§c§l[Sdf1_login] §f积分不足!当前: [points]");
|
||||
pw.println("points_purchase_success=§a§l[Sdf1_login] §f兑换成功!消耗 [count] 积分");
|
||||
pw.println();
|
||||
pw.println("# ===== 邀请 =====");
|
||||
pw.println("invite_code_generated=§a§l[Sdf1_login] §f你的邀请码: [code]");
|
||||
pw.println("invite_bound=§a§l[Sdf1_login] §f你已被 [user] 邀请!");
|
||||
pw.println("invite_referral_bonus=§a§l[Sdf1_login] §f邀请返点: +[points] 积分");
|
||||
pw.println();
|
||||
pw.println("# ===== 新人礼包 =====");
|
||||
pw.println("gift_claimed=§a§l[Sdf1_login] §f礼包领取成功!");
|
||||
pw.println("gift_not_ready=§c§l[Sdf1_login] §f礼包条件未满足");
|
||||
pw.println("gift_all_claimed=§a§l[Sdf1_login] §f所有礼包已领取完毕!");
|
||||
pw.println();
|
||||
pw.println("# ===== 管理员 =====");
|
||||
pw.println("admin_no_permission=§c§l[Sdf1_login] §f无权限");
|
||||
pw.println("admin_set_password=§a§l[Sdf1_login] §f已为 [user] 设置新密码");
|
||||
pw.println("admin_delete_confirm=§c§l[Sdf1_login] §f请输入要删除的玩家名和你的密码进行确认");
|
||||
pw.println("admin_delete_success=§a§l[Sdf1_login] §f玩家 [user] 已删除");
|
||||
pw.println("admin_delete_failed=§c§l[Sdf1_login] §f删除失败:验证不通过");
|
||||
pw.println("admin_points_set=§a§l[Sdf1_login] §f已设置 [user] 积分为 [points]");
|
||||
pw.println("管理密码=qweasd");
|
||||
pw.println();
|
||||
pw.println("# ===== 挂机踢出 =====");
|
||||
pw.println("afk_warning=§e§l[Sdf1_login] §f你已挂机 [duration],将在 [remain] 后被踢出");
|
||||
pw.println("afk_kicked=§c§l[Sdf1_login] §f因挂机过久被踢出");
|
||||
pw.println("afk_set_time=§a§l[Sdf1_login] §f挂机踢出时长已设为 [duration]");
|
||||
pw.println("afk_set_enabled=§a§l[Sdf1_login] §f挂机踢出已开启");
|
||||
pw.println("afk_set_disabled=§a§l[Sdf1_login] §f挂机踢出已关闭");
|
||||
pw.println("afk_whitelist_added=§a§l[Sdf1_login] §f已将 [user] 加入防踢白名单");
|
||||
pw.println("afk_whitelist_removed=§a§l[Sdf1_login] §f已将 [user] 移出防踢白名单");
|
||||
pw.println();
|
||||
pw.println("# ===== 帮助 =====");
|
||||
pw.println("help_header=§e[Sdf1_login] §f命令列表:");
|
||||
pw.println("help_1=§a/sdf1_login §7- 打开面板");
|
||||
pw.println("help_2=§a/reg <密码> §7- 注册");
|
||||
pw.println("help_3=§a/login <密码> §7- 登录");
|
||||
pw.println("help_4=§a/sdf1_login reset §7- 重置密码");
|
||||
pw.println("help_5=§a/sdf1_login email <邮箱> §7- 绑定邮箱");
|
||||
pw.println("help_6=§a/sdf1_login set <玩家> <密码> §7- [管理员] 设置密码");
|
||||
pw.println("help_7=§a/sdf1_login del <玩家> §7- [管理员] 删除用户");
|
||||
pw.println("help_8=§a/sdf1_login kick on/off [分钟] §7- [管理员] 挂机设置");
|
||||
pw.println("help_9=§a/sdf1_login add/remove <玩家> §7- [管理员] 防踢白名单");
|
||||
pw.println("help_10=§a/sdf1_login get <玩家> §7- [管理员] 查看礼包进度");
|
||||
pw.println("help_11=§a/sdf1_login reload §7- [管理员] 重载配置");
|
||||
} catch (IOException ignored) {}
|
||||
}
|
||||
|
||||
// ===== SMTP配置 =====
|
||||
|
||||
public void loadSmtp() {
|
||||
smtp.clear();
|
||||
File f = new File(dataFolder, "smtp.txt");
|
||||
if (!f.exists()) writeDefaultSmtp();
|
||||
try {
|
||||
BufferedReader r = new BufferedReader(
|
||||
new InputStreamReader(
|
||||
new FileInputStream(f),
|
||||
StandardCharsets.UTF_8));
|
||||
String line;
|
||||
while ((line = r.readLine()) != null) {
|
||||
if (line.startsWith("\uFEFF"))
|
||||
line = line.substring(1);
|
||||
String t = line.trim();
|
||||
if (t.isEmpty() || t.startsWith("#")) continue;
|
||||
int eq = t.indexOf('=');
|
||||
if (eq < 0) continue;
|
||||
smtp.put(t.substring(0, eq).trim(),
|
||||
t.substring(eq + 1).trim());
|
||||
}
|
||||
r.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void writeDefaultSmtp() {
|
||||
File f = new File(dataFolder, "smtp.txt");
|
||||
try (PrintWriter pw = new PrintWriter(
|
||||
new OutputStreamWriter(
|
||||
new FileOutputStream(f),
|
||||
StandardCharsets.UTF_8))) {
|
||||
pw.println("# ==========================================");
|
||||
pw.println("# Sdf1_login SMTP邮件配置");
|
||||
pw.println("# ==========================================");
|
||||
pw.println();
|
||||
pw.println("# SMTP服务器地址");
|
||||
pw.println("smtp地址=smtp.qq.com");
|
||||
pw.println();
|
||||
pw.println("# 端口号(SSL默认465)");
|
||||
pw.println("smtp端口=465");
|
||||
pw.println();
|
||||
pw.println("# 发件人邮箱地址");
|
||||
pw.println("发件邮箱=your@email.com");
|
||||
pw.println();
|
||||
pw.println("# 邮箱授权码(非登录密码)");
|
||||
pw.println("授权码=");
|
||||
pw.println();
|
||||
pw.println("# 测试接收邮箱(留空则发给发件人)");
|
||||
pw.println("测试邮箱=");
|
||||
} catch (IOException ignored) {}
|
||||
}
|
||||
|
||||
public String getSmtp(String key) {
|
||||
return smtp.getOrDefault(key, "");
|
||||
}
|
||||
|
||||
public void setSmtp(String key, String value) {
|
||||
smtp.put(key, value);
|
||||
saveSmtp();
|
||||
}
|
||||
|
||||
public Map<String, String> getSmtpAll() {
|
||||
return new LinkedHashMap<>(smtp);
|
||||
}
|
||||
public void saveSettings() {
|
||||
File f = new File(dataFolder, "url.txt");
|
||||
try (PrintWriter pw = new PrintWriter(
|
||||
new OutputStreamWriter(
|
||||
new FileOutputStream(f),
|
||||
StandardCharsets.UTF_8))) {
|
||||
pw.println("管理标签: " + adminTag);
|
||||
pw.println("管理密码: " + adminPassword);
|
||||
pw.println("挂机踢出: "
|
||||
+ (afkEnabled ? "true" : "false"));
|
||||
pw.println("挂机时长: " + (afkTimeout / 60));
|
||||
pw.println("登录超时: " + loginTimeout);
|
||||
} catch (IOException ignored) {}
|
||||
}
|
||||
|
||||
private void saveSmtp() {
|
||||
File f = new File(dataFolder, "smtp.txt");
|
||||
try (PrintWriter pw = new PrintWriter(
|
||||
new OutputStreamWriter(
|
||||
new FileOutputStream(f),
|
||||
StandardCharsets.UTF_8))) {
|
||||
pw.println("# ==========================================");
|
||||
pw.println("# Sdf1_login SMTP邮件配置");
|
||||
pw.println("# ==========================================");
|
||||
pw.println();
|
||||
pw.println("smtp地址=" + smtp.getOrDefault("smtp地址", "smtp.qq.com"));
|
||||
pw.println("smtp端口=" + smtp.getOrDefault("smtp端口", "465"));
|
||||
pw.println("发件邮箱=" + smtp.getOrDefault("发件邮箱", ""));
|
||||
pw.println("授权码=" + smtp.getOrDefault("授权码", ""));
|
||||
pw.println("测试邮箱=" + smtp.getOrDefault("测试邮箱", ""));
|
||||
} catch (IOException ignored) {}
|
||||
}
|
||||
|
||||
// ===== 其他配置 =====
|
||||
|
||||
public void loadSettings() {
|
||||
File f = new File(dataFolder, "url.txt");
|
||||
if (!f.exists()) return;
|
||||
try {
|
||||
BufferedReader r = new BufferedReader(
|
||||
new InputStreamReader(
|
||||
new FileInputStream(f),
|
||||
StandardCharsets.UTF_8));
|
||||
String line;
|
||||
while ((line = r.readLine()) != null) {
|
||||
String t = line.trim();
|
||||
if (t.isEmpty() || t.startsWith("#")) continue;
|
||||
String[] kv = t.split(":", 2);
|
||||
if (kv.length < 2) continue;
|
||||
String k = kv[0].trim();
|
||||
String v = kv[1].trim();
|
||||
switch (k) {
|
||||
case "管理标签":
|
||||
adminTag = v; break;
|
||||
case "管理密码":
|
||||
adminPassword = v; break;
|
||||
case "挂机踢出":
|
||||
afkEnabled = v.equalsIgnoreCase("true")
|
||||
|| v.equalsIgnoreCase("开启");
|
||||
break;
|
||||
case "挂机时长":
|
||||
try {
|
||||
afkTimeout = Integer.parseInt(v) * 60;
|
||||
} catch (Exception ignored) {}
|
||||
break;
|
||||
case "登录超时":
|
||||
try {
|
||||
loginTimeout = Integer.parseInt(v);
|
||||
} catch (Exception ignored) {}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
r.close();
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
package Sdf1_login;
|
||||
|
||||
import java.io.File;
|
||||
import java.sql.*;
|
||||
import java.util.*;
|
||||
|
||||
//全部数据库操作
|
||||
public class DatabaseManager {
|
||||
|
||||
private Connection db;
|
||||
private final File dataFolder;
|
||||
|
||||
public DatabaseManager(File dataFolder) {
|
||||
this.dataFolder = dataFolder;
|
||||
}
|
||||
|
||||
public void init() {
|
||||
try {
|
||||
Class.forName("org.sqlite.JDBC");
|
||||
File dbFile = new File(dataFolder, "login.db");
|
||||
db = DriverManager.getConnection(
|
||||
"jdbc:sqlite:" + dbFile.getAbsolutePath());
|
||||
Statement st = db.createStatement();
|
||||
st.execute("PRAGMA journal_mode=WAL");
|
||||
st.execute("CREATE TABLE IF NOT EXISTS users ("
|
||||
+ "player_name TEXT PRIMARY KEY,"
|
||||
+ "password_hash TEXT NOT NULL,"
|
||||
+ "password_salt TEXT NOT NULL,"
|
||||
+ "email TEXT DEFAULT '',"
|
||||
+ "register_time INTEGER DEFAULT 0,"
|
||||
+ "last_login_time INTEGER DEFAULT 0,"
|
||||
+ "total_online_time INTEGER DEFAULT 0,"
|
||||
+ "last_online_check INTEGER DEFAULT 0,"
|
||||
+ "is_logged_in INTEGER DEFAULT 0,"
|
||||
+ "blocks_broken INTEGER DEFAULT 0,"
|
||||
+ "blocks_placed INTEGER DEFAULT 0,"
|
||||
+ "gift_stage INTEGER DEFAULT 0,"
|
||||
+ "gift_claimed TEXT DEFAULT '',"
|
||||
+ "invite_code TEXT DEFAULT '',"
|
||||
+ "invited_by TEXT DEFAULT '',"
|
||||
+ "monthly_invite_count INTEGER DEFAULT 0,"
|
||||
+ "invite_month TEXT DEFAULT '',"
|
||||
+ "points INTEGER DEFAULT 0,"
|
||||
+ "checkin_streak INTEGER DEFAULT 0,"
|
||||
+ "last_checkin_date TEXT DEFAULT '',"
|
||||
+ "total_checkin_days INTEGER DEFAULT 0,"
|
||||
+ "temp_password TEXT DEFAULT '',"
|
||||
+ "temp_pw_expire INTEGER DEFAULT 0,"
|
||||
+ "temp_pw_used INTEGER DEFAULT 0)");
|
||||
addColumnIfMissing("tasks_completed", "TEXT DEFAULT ''");
|
||||
addColumnIfMissing("temp_password", "TEXT DEFAULT ''");
|
||||
addColumnIfMissing("temp_pw_expire", "INTEGER DEFAULT 0");
|
||||
addColumnIfMissing("temp_pw_used", "INTEGER DEFAULT 0");
|
||||
st.close();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(
|
||||
"[Sdf1_login] DB初始化失败: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
try {
|
||||
if (db != null && !db.isClosed()) db.close();
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
|
||||
public Connection getDb() { return db; }
|
||||
private void addColumnIfMissing(String col, String def) {
|
||||
try {
|
||||
db.createStatement().executeUpdate(
|
||||
"ALTER TABLE users ADD COLUMN "
|
||||
+ col + " " + def);
|
||||
} catch (SQLException ignored) {}
|
||||
}
|
||||
|
||||
// ===== 用户操作 =====
|
||||
|
||||
public boolean userExists(String name) {
|
||||
try {
|
||||
PreparedStatement ps = db.prepareStatement(
|
||||
"SELECT 1 FROM users WHERE player_name=?");
|
||||
ps.setString(1, name);
|
||||
boolean r = ps.executeQuery().next();
|
||||
ps.close();
|
||||
return r;
|
||||
} catch (SQLException e) { return false; }
|
||||
}
|
||||
|
||||
public void createUser(String name, String hash, String salt) {
|
||||
try {
|
||||
PreparedStatement ps = db.prepareStatement(
|
||||
"INSERT INTO users "
|
||||
+ "(player_name,password_hash,"
|
||||
+ "password_salt,register_time) "
|
||||
+ "VALUES (?,?,?,?)");
|
||||
ps.setString(1, name);
|
||||
ps.setString(2, hash);
|
||||
ps.setString(3, salt);
|
||||
ps.setLong(4, System.currentTimeMillis());
|
||||
ps.executeUpdate();
|
||||
ps.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, Object> getUser(String name) {
|
||||
Map<String, Object> r = new LinkedHashMap<>();
|
||||
try {
|
||||
PreparedStatement ps = db.prepareStatement(
|
||||
"SELECT * FROM users WHERE player_name=?");
|
||||
ps.setString(1, name);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
if (rs.next()) {
|
||||
for (int i = 1;
|
||||
i <= rs.getMetaData().getColumnCount(); i++) {
|
||||
r.put(rs.getMetaData().getColumnName(i),
|
||||
rs.getObject(i));
|
||||
}
|
||||
}
|
||||
rs.close();
|
||||
ps.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
public void setField(String name, String field, Object val) {
|
||||
try {
|
||||
PreparedStatement ps = db.prepareStatement(
|
||||
"UPDATE users SET " + field
|
||||
+ "=? WHERE player_name=?");
|
||||
if (val instanceof Integer)
|
||||
ps.setInt(1, (Integer) val);
|
||||
else if (val instanceof Long)
|
||||
ps.setLong(1, (Long) val);
|
||||
else
|
||||
ps.setString(1, String.valueOf(val));
|
||||
ps.setString(2, name);
|
||||
ps.executeUpdate();
|
||||
ps.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public Object getField(String name, String field) {
|
||||
try {
|
||||
PreparedStatement ps = db.prepareStatement(
|
||||
"SELECT " + field
|
||||
+ " FROM users WHERE player_name=?");
|
||||
ps.setString(1, name);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
Object val = rs.next() ? rs.getObject(1) : null;
|
||||
rs.close();
|
||||
ps.close();
|
||||
return val;
|
||||
} catch (SQLException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean checkPassword(String name, String hash) {
|
||||
try {
|
||||
PreparedStatement ps = db.prepareStatement(
|
||||
"SELECT password_hash FROM users "
|
||||
+ "WHERE player_name=?");
|
||||
ps.setString(1, name);
|
||||
ResultSet rs = ps.executeQuery();
|
||||
if (rs.next()) {
|
||||
String stored = rs.getString("password_hash");
|
||||
rs.close();
|
||||
ps.close();
|
||||
return stored.equals(hash);
|
||||
}
|
||||
rs.close();
|
||||
ps.close();
|
||||
return false;
|
||||
} catch (SQLException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean checkPasswordOrTemp(
|
||||
String name, String hash) {
|
||||
if (checkPassword(name, hash)) return true;
|
||||
Map<String, Object> user = getUser(name);
|
||||
if (user.isEmpty()) return false;
|
||||
String tempHash = (String) user.getOrDefault(
|
||||
"temp_password", "");
|
||||
long expire = ((Number) user.getOrDefault(
|
||||
"temp_pw_expire", 0L)).longValue();
|
||||
if (tempHash == null || tempHash.isEmpty())
|
||||
return false;
|
||||
if (System.currentTimeMillis() > expire)
|
||||
return false;
|
||||
return tempHash.equals(hash);
|
||||
}
|
||||
|
||||
public boolean isUsingTempPassword(String name) {
|
||||
String h = (String) getField(name, "temp_password");
|
||||
return h != null && !h.isEmpty();
|
||||
}
|
||||
|
||||
public void setLoggedIn(String name, boolean val) {
|
||||
setField(name, "is_logged_in", val ? 1 : 0);
|
||||
}
|
||||
|
||||
public void addPoints(String name, int amount) {
|
||||
int cur = ((Number) getField(name, "points"))
|
||||
.intValue();
|
||||
setField(name, "points", cur + amount);
|
||||
}
|
||||
|
||||
public boolean deductPoints(String name, int amount) {
|
||||
int cur = ((Number) getField(name, "points"))
|
||||
.intValue();
|
||||
if (cur < amount) return false;
|
||||
setField(name, "points", cur - amount);
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> getAllUsers() {
|
||||
List<Map<String, Object>> list = new ArrayList<>();
|
||||
try {
|
||||
Statement st = db.createStatement();
|
||||
ResultSet rs = st.executeQuery(
|
||||
"SELECT * FROM users");
|
||||
while (rs.next()) {
|
||||
Map<String, Object> row = new LinkedHashMap<>();
|
||||
for (int i = 1;
|
||||
i <= rs.getMetaData().getColumnCount(); i++) {
|
||||
row.put(
|
||||
rs.getMetaData().getColumnName(i),
|
||||
rs.getObject(i));
|
||||
}
|
||||
list.add(row);
|
||||
}
|
||||
rs.close();
|
||||
st.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public void deleteUser(String name) {
|
||||
try {
|
||||
PreparedStatement ps = db.prepareStatement(
|
||||
"DELETE FROM users WHERE player_name=?");
|
||||
ps.setString(1, name);
|
||||
ps.executeUpdate();
|
||||
ps.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
package Sdf1_login;
|
||||
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
|
||||
public class EmailManager {
|
||||
|
||||
private final ConfigManager config;
|
||||
|
||||
public EmailManager(ConfigManager config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
public void sendTempPassword(String toEmail,
|
||||
String playerName,
|
||||
String tempPassword) {
|
||||
String subject = "[Sdf1_login] 密码重置 - "
|
||||
+ playerName;
|
||||
String body = "玩家: " + playerName
|
||||
+ "\r\n临时密码: " + tempPassword
|
||||
+ "\r\n有效期: 5分钟"
|
||||
+ "\r\n使用后自动失效"
|
||||
+ "\r\n请尽快登录并修改密码";
|
||||
sendEmail(toEmail, subject, body);
|
||||
}
|
||||
|
||||
public void sendVerifyCode(String toEmail,
|
||||
String playerName,
|
||||
String code) {
|
||||
String subject = "[Sdf1_login] 验证码 - "
|
||||
+ playerName;
|
||||
String body = "玩家: " + playerName
|
||||
+ "\r\n验证码: " + code
|
||||
+ "\r\n有效期: 5分钟";
|
||||
sendEmail(toEmail, subject, body);
|
||||
}
|
||||
|
||||
private void sendEmail(String toEmail,
|
||||
String subject, String body) {
|
||||
new Thread(() -> {
|
||||
try {
|
||||
String host = config.getSmtp("smtp地址");
|
||||
int port = 465;
|
||||
try {
|
||||
port = Integer.parseInt(
|
||||
config.getSmtp("smtp端口"));
|
||||
} catch (Exception ignored) {}
|
||||
String from = config.getSmtp("发件邮箱");
|
||||
String pass = config.getSmtp("授权码");
|
||||
|
||||
if (host.isEmpty() || from.isEmpty()
|
||||
|| pass.isEmpty()) return;
|
||||
if (toEmail == null || toEmail.isEmpty())
|
||||
return;
|
||||
|
||||
SSLSocketFactory factory =
|
||||
(SSLSocketFactory) SSLSocketFactory
|
||||
.getDefault();
|
||||
SSLSocket socket = (SSLSocket) factory
|
||||
.createSocket(host, port);
|
||||
|
||||
BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(
|
||||
socket.getInputStream(),
|
||||
StandardCharsets.UTF_8));
|
||||
BufferedWriter writer = new BufferedWriter(
|
||||
new OutputStreamWriter(
|
||||
socket.getOutputStream(),
|
||||
StandardCharsets.UTF_8));
|
||||
|
||||
readResponse(reader);
|
||||
sendCmd(writer, "EHLO localhost");
|
||||
readResponse(reader);
|
||||
sendCmd(writer, "AUTH LOGIN");
|
||||
readResponse(reader);
|
||||
sendCmd(writer, Base64.getEncoder()
|
||||
.encodeToString(from.getBytes(
|
||||
StandardCharsets.UTF_8)));
|
||||
readResponse(reader);
|
||||
sendCmd(writer, Base64.getEncoder()
|
||||
.encodeToString(pass.getBytes(
|
||||
StandardCharsets.UTF_8)));
|
||||
readResponse(reader);
|
||||
sendCmd(writer,
|
||||
"MAIL FROM:<" + from + ">");
|
||||
readResponse(reader);
|
||||
sendCmd(writer,
|
||||
"RCPT TO:<" + toEmail + ">");
|
||||
readResponse(reader);
|
||||
sendCmd(writer, "DATA");
|
||||
readResponse(reader);
|
||||
|
||||
String encodedSubject = Base64.getEncoder()
|
||||
.encodeToString(subject.getBytes(
|
||||
StandardCharsets.UTF_8));
|
||||
|
||||
sendCmd(writer, "From: " + from);
|
||||
sendCmd(writer, "To: " + toEmail);
|
||||
sendCmd(writer, "Subject: =?UTF-8?B?"
|
||||
+ encodedSubject + "?=");
|
||||
sendCmd(writer, "MIME-Version: 1.0");
|
||||
sendCmd(writer,
|
||||
"Content-Type: text/plain;"
|
||||
+ " charset=UTF-8");
|
||||
sendCmd(writer, "");
|
||||
sendCmd(writer, body);
|
||||
sendCmd(writer, ".");
|
||||
readResponse(reader);
|
||||
sendCmd(writer, "QUIT");
|
||||
readResponse(reader);
|
||||
|
||||
socket.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private void sendCmd(BufferedWriter w, String cmd)
|
||||
throws IOException {
|
||||
w.write(cmd + "\r\n");
|
||||
w.flush();
|
||||
}
|
||||
|
||||
private String readResponse(BufferedReader r)
|
||||
throws IOException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String line;
|
||||
while ((line = r.readLine()) != null) {
|
||||
sb.append(line).append("\n");
|
||||
if (line.length() < 4) break;
|
||||
if (line.charAt(3) == ' ') break;
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,338 @@
|
||||
package Sdf1_login;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.*;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import java.io.File;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class GUIManager {
|
||||
|
||||
private final Main plugin;
|
||||
|
||||
public static final String T_MAIN =
|
||||
"§6§l登录面板";
|
||||
public static final String T_MY_INFO =
|
||||
"§a§l我的信息";
|
||||
public static final String T_ADMIN =
|
||||
"§c§l管理员面板";
|
||||
public static final String T_USER_MGMT =
|
||||
"§e§l用户管理";
|
||||
public static final String T_INVITE =
|
||||
"§b§l邀请数据";
|
||||
public static final String T_TASK_CENTER = "§d§l任务中心";
|
||||
public static final String T_GIFT_STAGES = "§d§l新人任务";
|
||||
|
||||
|
||||
public GUIManager(Main plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public void openMain(Player p) {
|
||||
Inventory g = Bukkit.createInventory(
|
||||
null, 27, T_MAIN);
|
||||
fillBg(g);
|
||||
g.setItem(10, mkItem(Material.PLAYER_HEAD,
|
||||
"§a§l我的信息"));
|
||||
g.setItem(12, mkItem(Material.PAPER,
|
||||
"§b§l邀请数据"));
|
||||
g.setItem(14, mkItem(Material.EMERALD,
|
||||
"§d§l积分商城"));
|
||||
g.setItem(16, mkItem(Material.CHEST,
|
||||
"§e§l新人礼包"));
|
||||
if (isAdmin(p)) {
|
||||
g.setItem(22, mkItem(Material.REDSTONE_BLOCK,
|
||||
"§c§l管理员面板"));
|
||||
}
|
||||
p.openInventory(g);
|
||||
}
|
||||
|
||||
public void openMyInfo(Player p) {
|
||||
Inventory g = Bukkit.createInventory(
|
||||
null, 27, T_MY_INFO);
|
||||
fillBg(g);
|
||||
Map<String, Object> user = plugin.getDb()
|
||||
.getUser(p.getName());
|
||||
boolean checkedIn = plugin.getCheckIn()
|
||||
.isCheckedInToday(p.getName());
|
||||
Material checkMat = checkedIn
|
||||
? Material.EMERALD : Material.COAL;
|
||||
String checkName = checkedIn
|
||||
? "§a已签到" : "§7点击签到";
|
||||
int streak = ((Number) user.getOrDefault(
|
||||
"checkin_streak", 0)).intValue();
|
||||
int points = ((Number) user.getOrDefault(
|
||||
"points", 0)).intValue();
|
||||
int totalDays = ((Number) user.getOrDefault(
|
||||
"total_checkin_days", 0)).intValue();
|
||||
String email = (String) user.getOrDefault(
|
||||
"email", "");
|
||||
g.setItem(4, mkItem(Material.NAME_TAG,
|
||||
"§e" + p.getName()));
|
||||
g.setItem(10, mkItem(checkMat, checkName,
|
||||
"§7连续签到: " + streak + "天",
|
||||
"§7累计签到: " + totalDays + "天",
|
||||
"§7当前积分: " + points));
|
||||
g.setItem(11, mkItem(Material.PAPER,
|
||||
"§e修改密码", "§7双击修改密码"));
|
||||
g.setItem(12, mkItem(Material.CLOCK,
|
||||
"§e补签", "§7消耗10积分补签3天内"));
|
||||
g.setItem(13, mkItem(Material.ENDER_PEARL,
|
||||
"§e邀请码",
|
||||
"§7你的邀请码: "
|
||||
+ user.getOrDefault(
|
||||
"invite_code", "无"),
|
||||
"§7点击生成/查看"));
|
||||
g.setItem(14, mkItem(Material.BOOK,
|
||||
"§e邮箱: "
|
||||
+ (email.isEmpty()
|
||||
? "未绑定" : email),
|
||||
"§7点击绑定邮箱"));
|
||||
g.setItem(15, mkItem(Material.EMERALD_BLOCK,
|
||||
"§a§l积分商城",
|
||||
"§7当前: " + points + "积分"));
|
||||
g.setItem(26, mkItem(Material.ARROW, "§7返回"));
|
||||
p.openInventory(g);
|
||||
}
|
||||
|
||||
public void openAdmin(Player p) {
|
||||
Inventory g = Bukkit.createInventory(
|
||||
null, 27, T_ADMIN);
|
||||
fillBg(g);
|
||||
g.setItem(4, mkItem(Material.NAME_TAG,
|
||||
"§e§l管理员面板"));
|
||||
g.setItem(10, mkItem(Material.REDSTONE,
|
||||
"§6SMTP配置",
|
||||
"§7配置邮件服务器"));
|
||||
g.setItem(11, mkItem(Material.ARMOR_STAND,
|
||||
"§a用户管理",
|
||||
"§7管理玩家数据"));
|
||||
g.setItem(12, mkItem(Material.PLAYER_HEAD,
|
||||
"§a§l我的信息"));
|
||||
g.setItem(14, mkItem(Material.EMERALD,
|
||||
"§d§l积分商城"));
|
||||
boolean afkOn = plugin.getConfig2().afkEnabled;
|
||||
int afkMin = plugin.getConfig2().afkTimeout / 60;
|
||||
g.setItem(16, mkItem(
|
||||
afkOn ? Material.REDSTONE_TORCH
|
||||
: Material.TORCH,
|
||||
afkOn ? "§a§l挂机踢出: 开启"
|
||||
: "§c§l挂机踢出: 关闭",
|
||||
"§7左键: 切换开关",
|
||||
"§7右键: 设置时长",
|
||||
"§7当前: " + afkMin + "分钟"));
|
||||
g.setItem(22, mkItem(Material.ARROW, "§7返回"));
|
||||
p.openInventory(g);
|
||||
}
|
||||
|
||||
|
||||
public void openInvite(Player p) {
|
||||
Inventory g = Bukkit.createInventory(
|
||||
null, 27, T_INVITE);
|
||||
fillBg(g);
|
||||
String code = (String) plugin.getDb()
|
||||
.getField(p.getName(), "invite_code");
|
||||
if (code == null || code.isEmpty()) {
|
||||
code = plugin.getInvite().generateCode(p);
|
||||
}
|
||||
int monthly = ((Number) plugin.getDb()
|
||||
.getField(p.getName(),
|
||||
"monthly_invite_count"))
|
||||
.intValue();
|
||||
g.setItem(10, mkItem(Material.PAPER,
|
||||
"§b§l我的邀请码",
|
||||
"§7" + code,
|
||||
"",
|
||||
"§e点击刷新邀请码"));
|
||||
g.setItem(12, mkItem(Material.PLAYER_HEAD,
|
||||
"§a本月邀请",
|
||||
"§7已邀请 §e" + monthly + " §7人"));
|
||||
g.setItem(16, mkItem(Material.BOOK,
|
||||
"§e输入邀请码",
|
||||
"§7绑定他人的邀请码",
|
||||
"",
|
||||
"§e点击输入"));
|
||||
g.setItem(26, mkItem(Material.ARROW, "§7返回"));
|
||||
p.openInventory(g);
|
||||
}
|
||||
|
||||
|
||||
private String getInviteRate(int count) {
|
||||
if (count > 15) return "60%";
|
||||
if (count > 10) return "35%";
|
||||
if (count > 5) return "15%";
|
||||
if (count > 0) return "10%";
|
||||
return "0%";
|
||||
}
|
||||
|
||||
public void openUserManagement(Player p) {
|
||||
Inventory g = Bukkit.createInventory(
|
||||
null, 54, T_USER_MGMT);
|
||||
fillBg(g);
|
||||
List<Map<String, Object>> users = plugin.getDb()
|
||||
.getAllUsers();
|
||||
int slot = 0;
|
||||
for (Map<String, Object> user : users) {
|
||||
if (slot >= 45) break;
|
||||
String name = (String) user.get("player_name");
|
||||
int pts = ((Number) user.getOrDefault(
|
||||
"points", 0)).intValue();
|
||||
int stage = ((Number) user.getOrDefault(
|
||||
"gift_stage", 0)).intValue();
|
||||
g.setItem(slot, mkItem(Material.PLAYER_HEAD,
|
||||
"§e" + name,
|
||||
"§7积分: " + pts
|
||||
+ " 礼包阶段: " + stage,
|
||||
"§7双击管理"));
|
||||
slot++;
|
||||
}
|
||||
g.setItem(53, mkItem(Material.ARROW, "§7返回"));
|
||||
p.openInventory(g);
|
||||
}
|
||||
|
||||
public void openTaskCenter(Player p) {
|
||||
Inventory g = Bukkit.createInventory(
|
||||
null, 27, T_TASK_CENTER);
|
||||
fillBg(g);
|
||||
g.setItem(10, mkItem(Material.BOOK,
|
||||
"§e§l新人任务",
|
||||
"§7完成新手阶段获取奖励"));
|
||||
g.setItem(12, mkItem(Material.COMPASS,
|
||||
"§b§l主线任务",
|
||||
"§7查看主线任务列表"));
|
||||
g.setItem(14, mkItem(Material.PAPER,
|
||||
"§a§l支线任务",
|
||||
"§7查看支线任务列表"));
|
||||
g.setItem(16, mkItem(Material.EMERALD,
|
||||
"§d§l每日签到",
|
||||
"§7签到获取积分"));
|
||||
g.setItem(22, mkItem(Material.ARROW, "§7返回"));
|
||||
p.openInventory(g);
|
||||
}
|
||||
public void openGiftStages(Player p) {
|
||||
Inventory g = Bukkit.createInventory(
|
||||
null, 27, T_GIFT_STAGES);
|
||||
fillBg(g);
|
||||
int max = plugin.getGift().getMaxStage();
|
||||
for (int i = 0; i < max && i < 9; i++) {
|
||||
int stage = i + 1;
|
||||
boolean claimed = plugin.getDb()
|
||||
.getField(p.getName(),
|
||||
"gift_claimed") != null
|
||||
&& ((String) plugin.getDb()
|
||||
.getField(p.getName(),
|
||||
"gift_claimed"))
|
||||
.contains("[" + stage + "]");
|
||||
boolean can = plugin.getGift()
|
||||
.canClaim(p, stage);
|
||||
Material mat = claimed ? Material.LIME_WOOL
|
||||
: (can ? Material.YELLOW_WOOL
|
||||
: Material.RED_WOOL);
|
||||
String status = claimed ? "§a已领取"
|
||||
: (can ? "§e可领取" : "§c未达标");
|
||||
g.setItem(10 + i, mkItem(mat,
|
||||
"§e第" + stage + "阶段",
|
||||
"§7" + status));
|
||||
}
|
||||
g.setItem(26, mkItem(Material.ARROW, "§7返回"));
|
||||
p.openInventory(g);
|
||||
}
|
||||
|
||||
public void openTaskList(Player p, String folderName) {
|
||||
File folder = new File(
|
||||
plugin.getDataFolder(), folderName);
|
||||
List<Map<String, Object>> tasks =
|
||||
plugin.getGift()
|
||||
.loadTasksFromFolder(folder);
|
||||
int size = Math.max(54,
|
||||
((tasks.size() / 9) + 1) * 9);
|
||||
if (size > 54) size = 54;
|
||||
Inventory g = Bukkit.createInventory(null, size,
|
||||
"§d§l" + folderName);
|
||||
fillBg(g);
|
||||
for (int i = 0; i < tasks.size()
|
||||
&& i < 45; i++) {
|
||||
Map<String, Object> task = tasks.get(i);
|
||||
String name = (String) task.get("name");
|
||||
List<String> conds =
|
||||
(List<String>) task.get("conditions");
|
||||
List<String> rewards =
|
||||
(List<String>) task.get("rewards");
|
||||
List<String> lore = new ArrayList<>();
|
||||
lore.add("§7───── 条件 ─────");
|
||||
if (conds != null)
|
||||
for (String c : conds)
|
||||
lore.add("§e" + c);
|
||||
lore.add("");
|
||||
lore.add("§7───── 奖励 ─────");
|
||||
if (rewards != null)
|
||||
for (String r2 : rewards)
|
||||
lore.add("§a" + r2);
|
||||
g.setItem(i, mkItem(Material.BOOK,
|
||||
"§e" + name,
|
||||
lore.toArray(new String[0])));
|
||||
}
|
||||
if (tasks.isEmpty()) {
|
||||
g.setItem(22, mkItem(Material.BARRIER,
|
||||
"§7暂无任务"));
|
||||
}
|
||||
g.setItem(size - 1,
|
||||
mkItem(Material.ARROW, "§7返回"));
|
||||
p.openInventory(g);
|
||||
}
|
||||
|
||||
|
||||
public void openUserDetail(Player p, String target) {
|
||||
Inventory g = Bukkit.createInventory(
|
||||
null, 27, "§e§l管理: " + target);
|
||||
fillBg(g);
|
||||
Map<String, Object> user = plugin.getDb()
|
||||
.getUser(target);
|
||||
int pts = ((Number) user.getOrDefault(
|
||||
"points", 0)).intValue();
|
||||
String email = (String) user.getOrDefault(
|
||||
"email", "");
|
||||
g.setItem(4, mkItem(Material.NAME_TAG,
|
||||
"§e" + target));
|
||||
g.setItem(10, mkItem(Material.EMERALD,
|
||||
"§a积分: " + pts,
|
||||
"§7点击设置积分"));
|
||||
g.setItem(11, mkItem(Material.PAPER,
|
||||
"§e邮箱: "
|
||||
+ (email.isEmpty() ? "无" : email)));
|
||||
g.setItem(12, mkItem(Material.BOOK,
|
||||
"§e设置密码"));
|
||||
g.setItem(16, mkItem(Material.BARRIER,
|
||||
"§c删除用户", "§7二次验证删除"));
|
||||
g.setItem(22, mkItem(Material.ARROW, "§7返回"));
|
||||
p.openInventory(g);
|
||||
}
|
||||
|
||||
private boolean isAdmin(Player p) {
|
||||
return p.getScoreboardTags().contains(
|
||||
plugin.getConfig2().adminTag);
|
||||
}
|
||||
|
||||
private void fillBg(Inventory inv) {
|
||||
ItemStack gl = mkItem(
|
||||
Material.GRAY_STAINED_GLASS_PANE, " ");
|
||||
for (int i = 0; i < inv.getSize(); i++)
|
||||
inv.setItem(i, gl);
|
||||
}
|
||||
|
||||
private ItemStack mkItem(Material mat, String name,
|
||||
String... lore) {
|
||||
ItemStack it = new ItemStack(mat);
|
||||
ItemMeta im = it.getItemMeta();
|
||||
if (im != null) {
|
||||
im.setDisplayName(name);
|
||||
if (lore.length > 0)
|
||||
im.setLore(Arrays.asList(lore));
|
||||
it.setItemMeta(im);
|
||||
}
|
||||
return it;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,296 @@
|
||||
package Sdf1_login;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
|
||||
public class GiftManager {
|
||||
|
||||
private final File giftFolder;
|
||||
private final Main plugin;
|
||||
private final List<Map<String, Object>> stages =
|
||||
new ArrayList<>();
|
||||
|
||||
public GiftManager(Main plugin) {
|
||||
this.plugin = plugin;
|
||||
this.giftFolder = new File(
|
||||
plugin.getDataFolder(), "新人礼包");
|
||||
}
|
||||
|
||||
public void loadStages() {
|
||||
stages.clear();
|
||||
if (!giftFolder.exists()) {
|
||||
giftFolder.mkdirs();
|
||||
writeDefaultStages();
|
||||
}
|
||||
File[] files = giftFolder.listFiles(
|
||||
(dir, name) -> name.endsWith(".md"));
|
||||
if (files == null) return;
|
||||
Arrays.sort(files);
|
||||
for (File f : files) {
|
||||
stages.add(parseMarkdown(f));
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Object> parseMarkdown(File f) {
|
||||
Map<String, Object> stage = new LinkedHashMap<>();
|
||||
stage.put("stage", stages.size() + 1);
|
||||
stage.put("name",
|
||||
f.getName().replace(".md", ""));
|
||||
stage.put("rewards", new ArrayList<String>());
|
||||
try {
|
||||
BufferedReader r = new BufferedReader(
|
||||
new InputStreamReader(
|
||||
new FileInputStream(f),
|
||||
StandardCharsets.UTF_8));
|
||||
String line;
|
||||
List<String> rewards = new ArrayList<>();
|
||||
while ((line = r.readLine()) != null) {
|
||||
if (line.contains("礼包内容")
|
||||
|| line.contains("奖励")
|
||||
|| line.contains("任务内容")) {
|
||||
int colon = line.indexOf(':');
|
||||
if (colon < 0)
|
||||
colon = line.indexOf('\uff1a');
|
||||
if (colon >= 0) {
|
||||
rewards.add(
|
||||
line.substring(colon + 1)
|
||||
.trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
stage.put("rewards", rewards);
|
||||
r.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return stage;
|
||||
}
|
||||
private void writeStage(String name, String content) {
|
||||
File f = new File(giftFolder, name);
|
||||
if (f.exists()) return;
|
||||
try (PrintWriter pw = new PrintWriter(
|
||||
new OutputStreamWriter(
|
||||
new FileOutputStream(f),
|
||||
StandardCharsets.UTF_8))) {
|
||||
pw.print(content);
|
||||
} catch (IOException ignored) {}
|
||||
}
|
||||
|
||||
private void writeDefaultStages() {
|
||||
writeStage("第一阶段.md",
|
||||
"# 第一阶段\n\n## 领取条件\n\n- 无前级条件\n\n## 礼包内容\n\n- 皮革套装 × 1\n- 木制武器工具套装 × 1\n");
|
||||
writeStage("第二阶段.md",
|
||||
"# 第二阶段\n\n## 领取条件\n\n- 在线时长>1小时\n\n## 礼包内容\n\n- 500 游戏币\n");
|
||||
writeStage("第三阶段.md",
|
||||
"# 第三阶段\n\n## 领取条件\n\n- 破坏100个方块\n- 在线>1天\n\n## 礼包内容\n\n- 1500 游戏币\n- 铁质工具套装 × 1\n- 铁质防具 × 1\n");
|
||||
writeStage("第四阶段.md",
|
||||
"# 第四阶段\n\n## 领取条件\n\n- 破坏/放置300方块\n- 在线≥7天\n\n## 礼包内容\n\n- 钻石套装 × 1\n- 2000 游戏币\n");
|
||||
}
|
||||
|
||||
/** 从任意文件夹加载任务列表 */
|
||||
public List<Map<String, Object>> loadTasksFromFolder(
|
||||
File folder) {
|
||||
List<Map<String, Object>> tasks =
|
||||
new ArrayList<>();
|
||||
if (!folder.exists() || !folder.isDirectory())
|
||||
return tasks;
|
||||
File[] files = folder.listFiles(
|
||||
(dir, name) -> name.endsWith(".md"));
|
||||
if (files == null) return tasks;
|
||||
Arrays.sort(files);
|
||||
for (File f : files) {
|
||||
Map<String, Object> task =
|
||||
new LinkedHashMap<>();
|
||||
task.put("name",
|
||||
f.getName().replace(".md", ""));
|
||||
task.put("conditions",
|
||||
new ArrayList<String>());
|
||||
task.put("rewards",
|
||||
new ArrayList<String>());
|
||||
try {
|
||||
BufferedReader r = new BufferedReader(
|
||||
new InputStreamReader(
|
||||
new FileInputStream(f),
|
||||
StandardCharsets.UTF_8));
|
||||
String line;
|
||||
List<String> current = null;
|
||||
while ((line = r.readLine()) != null) {
|
||||
if (line.startsWith("# ")) {
|
||||
task.put("name",
|
||||
line.substring(2)
|
||||
.trim());
|
||||
} else if (line.contains("条件")
|
||||
|| line.contains("领取条件")
|
||||
|| line.contains("完成条件")) {
|
||||
current = (List<String>) task
|
||||
.get("conditions");
|
||||
} else if (line.contains("内容")
|
||||
|| line.contains("奖励")
|
||||
|| line.contains("任务内容")
|
||||
|| line.contains("礼包内容")) {
|
||||
current = (List<String>) task
|
||||
.get("rewards");
|
||||
} else if (line.startsWith("- ")
|
||||
&& current != null) {
|
||||
current.add(line.substring(2)
|
||||
.trim());
|
||||
}
|
||||
}
|
||||
r.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
tasks.add(task);
|
||||
}
|
||||
return tasks;
|
||||
}
|
||||
|
||||
public boolean canClaim(Player p, int stage) {
|
||||
Map<String, Object> user = plugin.getDb()
|
||||
.getUser(p.getName());
|
||||
if (user.isEmpty()) return false;
|
||||
int curStage = ((Number) user.getOrDefault(
|
||||
"gift_stage", 0)).intValue();
|
||||
if (stage > curStage + 1) return false;
|
||||
if (stage < 1 || stage > stages.size())
|
||||
return false;
|
||||
String claimed = (String) user.getOrDefault(
|
||||
"gift_claimed", "");
|
||||
if (claimed.contains("[" + stage + "]"))
|
||||
return false;
|
||||
|
||||
// debug 绕过:检查 tasks_completed
|
||||
String completed = (String) user.getOrDefault(
|
||||
"tasks_completed", "");
|
||||
if (completed.contains("[" + stage + "]"))
|
||||
return true;
|
||||
|
||||
// 正常条件检查
|
||||
long dbTime = ((Number) user.getOrDefault(
|
||||
"total_online_time", 0L)).longValue();
|
||||
long sessionTime = 0;
|
||||
Object lastObj = plugin.getDb().getField(
|
||||
p.getName(), "last_online_check");
|
||||
if (lastObj != null && plugin.getLoggedIn()
|
||||
.contains(p.getName())) {
|
||||
long last = ((Number) lastObj).longValue();
|
||||
if (last > 0) {
|
||||
sessionTime =
|
||||
System.currentTimeMillis() - last;
|
||||
}
|
||||
}
|
||||
long onlineTime = dbTime + sessionTime;
|
||||
int broken = ((Number) user.getOrDefault(
|
||||
"blocks_broken", 0)).intValue();
|
||||
int placed = ((Number) user.getOrDefault(
|
||||
"blocks_placed", 0)).intValue();
|
||||
|
||||
switch (stage) {
|
||||
case 1: return true;
|
||||
case 2: return onlineTime >= 3600000;
|
||||
case 3: return broken >= 100
|
||||
&& onlineTime >= 86400000L;
|
||||
case 4: return (broken + placed) >= 300
|
||||
&& onlineTime >= 604800000L;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void claimReward(Player p, int stage) {
|
||||
if (!canClaim(p, stage)) return;
|
||||
Map<String, Object> user = plugin.getDb()
|
||||
.getUser(p.getName());
|
||||
String claimed = (String) user.getOrDefault(
|
||||
"gift_claimed", "");
|
||||
claimed += "[" + stage + "]";
|
||||
plugin.getDb().setField(
|
||||
p.getName(), "gift_claimed", claimed);
|
||||
plugin.getDb().setField(
|
||||
p.getName(), "gift_stage",
|
||||
Math.max(stage,
|
||||
((Number) user.getOrDefault(
|
||||
"gift_stage", 0))
|
||||
.intValue()));
|
||||
|
||||
if (stage == 1) {
|
||||
giveItem(p, Material.LEATHER_HELMET);
|
||||
giveItem(p, Material.LEATHER_CHESTPLATE);
|
||||
giveItem(p, Material.LEATHER_LEGGINGS);
|
||||
giveItem(p, Material.LEATHER_BOOTS);
|
||||
giveItem(p, Material.WOODEN_SWORD);
|
||||
giveItem(p, Material.WOODEN_PICKAXE);
|
||||
giveItem(p, Material.WOODEN_AXE);
|
||||
giveItem(p, Material.WOODEN_SHOVEL);
|
||||
} else if (stage == 2) {
|
||||
if (plugin.getEconomy() != null) {
|
||||
double before = plugin.getEconomy()
|
||||
.getBalance(p);
|
||||
plugin.getEconomy()
|
||||
.depositPlayer(p, 500);
|
||||
double after = plugin.getEconomy()
|
||||
.getBalance(p);
|
||||
p.sendMessage("§a§l[礼包] §f游戏币: §e"
|
||||
+ (long) before
|
||||
+ "§f → §a" + (long) after);
|
||||
}
|
||||
} else if (stage == 3) {
|
||||
if (plugin.getEconomy() != null) {
|
||||
double before = plugin.getEconomy()
|
||||
.getBalance(p);
|
||||
plugin.getEconomy()
|
||||
.depositPlayer(p, 1500);
|
||||
double after = plugin.getEconomy()
|
||||
.getBalance(p);
|
||||
p.sendMessage("§a§l[礼包] §f游戏币: §e"
|
||||
+ (long) before
|
||||
+ "§f → §a" + (long) after);
|
||||
}
|
||||
giveItem(p, Material.IRON_PICKAXE);
|
||||
giveItem(p, Material.IRON_AXE);
|
||||
giveItem(p, Material.IRON_SWORD);
|
||||
giveItem(p, Material.IRON_HELMET);
|
||||
giveItem(p, Material.IRON_CHESTPLATE);
|
||||
giveItem(p, Material.IRON_LEGGINGS);
|
||||
giveItem(p, Material.IRON_BOOTS);
|
||||
} else if (stage == 4) {
|
||||
if (plugin.getEconomy() != null) {
|
||||
double before = plugin.getEconomy()
|
||||
.getBalance(p);
|
||||
plugin.getEconomy()
|
||||
.depositPlayer(p, 2000);
|
||||
double after = plugin.getEconomy()
|
||||
.getBalance(p);
|
||||
p.sendMessage("§a§l[礼包] §f游戏币: §e"
|
||||
+ (long) before
|
||||
+ "§f → §a" + (long) after);
|
||||
}
|
||||
giveItem(p, Material.DIAMOND_HELMET);
|
||||
giveItem(p, Material.DIAMOND_CHESTPLATE);
|
||||
giveItem(p, Material.DIAMOND_LEGGINGS);
|
||||
giveItem(p, Material.DIAMOND_BOOTS);
|
||||
}
|
||||
p.sendMessage(plugin.getConfig2().msg(
|
||||
"gift_claimed"));
|
||||
}
|
||||
|
||||
private void giveItem(Player p, Material mat) {
|
||||
p.getInventory().addItem(new ItemStack(mat));
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> getStages() {
|
||||
return stages;
|
||||
}
|
||||
|
||||
public int getMaxStage() {
|
||||
return stages.size();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package Sdf1_login;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
|
||||
public class InviteManager {
|
||||
|
||||
private final Main plugin;
|
||||
|
||||
public InviteManager(Main plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public String generateCode(Player p) {
|
||||
String code = PasswordUtils.generateInviteCode();
|
||||
plugin.getDb().setField(
|
||||
p.getName(), "invite_code", code);
|
||||
return code;
|
||||
}
|
||||
|
||||
public void onInviteeCheckIn(String inviteeName) {
|
||||
String invitedBy = (String) plugin.getDb()
|
||||
.getField(inviteeName, "invited_by");
|
||||
if (invitedBy == null || invitedBy.isEmpty()) return;
|
||||
|
||||
String month = new SimpleDateFormat("yyyy-MM")
|
||||
.format(new Date());
|
||||
String lastMonth = (String) plugin.getDb()
|
||||
.getField(invitedBy, "invite_month");
|
||||
|
||||
if (!month.equals(lastMonth)) {
|
||||
plugin.getDb().setField(
|
||||
invitedBy, "invite_month", month);
|
||||
plugin.getDb().setField(
|
||||
invitedBy, "monthly_invite_count", 0);
|
||||
}
|
||||
|
||||
int count = ((Number) plugin.getDb()
|
||||
.getField(invitedBy,
|
||||
"monthly_invite_count")).intValue();
|
||||
plugin.getDb().setField(
|
||||
invitedBy, "monthly_invite_count", count + 1);
|
||||
|
||||
double rate = 0.10;
|
||||
if (count + 1 > 15) {
|
||||
rate = 0.60;
|
||||
} else if (count + 1 > 10) {
|
||||
rate = 0.35;
|
||||
} else if (count + 1 > 5) {
|
||||
rate = 0.15;
|
||||
}
|
||||
|
||||
int bonus = (int) (1 * rate);
|
||||
if (bonus > 0) {
|
||||
plugin.getDb().addPoints(invitedBy, bonus);
|
||||
Player inviterPlayer = Bukkit
|
||||
.getPlayerExact(invitedBy);
|
||||
if (inviterPlayer != null
|
||||
&& inviterPlayer.isOnline()) {
|
||||
inviterPlayer.sendMessage(
|
||||
plugin.getConfig2().msg(
|
||||
"invite_referral_bonus",
|
||||
"points",
|
||||
String.valueOf(bonus)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> getAllInviteData() {
|
||||
List<Map<String, Object>> result = new ArrayList<>();
|
||||
for (Map<String, Object> user
|
||||
: plugin.getDb().getAllUsers()) {
|
||||
String code = (String) user.getOrDefault(
|
||||
"invite_code", "");
|
||||
if (!code.isEmpty()) {
|
||||
Map<String, Object> row = new LinkedHashMap<>();
|
||||
row.put("player",
|
||||
user.get("player_name"));
|
||||
row.put("code", code);
|
||||
row.put("monthly",
|
||||
user.getOrDefault(
|
||||
"monthly_invite_count", 0));
|
||||
result.add(row);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
package Sdf1_login;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public class LoginManager {
|
||||
|
||||
private final Main plugin;
|
||||
|
||||
public LoginManager(Main plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public boolean handleRegister(Player p,
|
||||
String[] args) {
|
||||
String name = p.getName();
|
||||
if (plugin.getDb().userExists(name)) {
|
||||
p.sendMessage(plugin.getConfig2().msg(
|
||||
"already_registered"));
|
||||
return true;
|
||||
}
|
||||
if (args.length < 1) {
|
||||
p.sendMessage("§c用法: /reg <密码>");
|
||||
p.sendMessage(plugin.getConfig2().msg(
|
||||
"password_format_hint"));
|
||||
return true;
|
||||
}
|
||||
String pwd = args[0];
|
||||
if (!PasswordUtils.validate(pwd)) {
|
||||
p.sendMessage(plugin.getConfig2().msg(
|
||||
"password_invalid"));
|
||||
p.sendMessage(plugin.getConfig2().msg(
|
||||
"password_format_hint"));
|
||||
return true;
|
||||
}
|
||||
String salt = PasswordUtils.generateSalt();
|
||||
String hash = PasswordUtils.hash(pwd, salt);
|
||||
plugin.getDb().createUser(name, hash, salt);
|
||||
plugin.getLoggedIn().add(name);
|
||||
plugin.getDb().setLoggedIn(name, true);
|
||||
plugin.getDb().setField(name,
|
||||
"last_login_time",
|
||||
System.currentTimeMillis());
|
||||
plugin.getDb().setField(name,
|
||||
"last_online_check",
|
||||
System.currentTimeMillis());
|
||||
plugin.restoreInventory(p);
|
||||
plugin.recordIPLogin(p);
|
||||
p.sendMessage(plugin.getConfig2().msg(
|
||||
"register_success", "user", name));
|
||||
try {
|
||||
org.bukkit.plugin.Plugin cy =
|
||||
org.bukkit.Bukkit
|
||||
.getPluginManager()
|
||||
.getPlugin("CY_beibao");
|
||||
if (cy != null && cy.isEnabled()) {
|
||||
java.lang.reflect.Method m =
|
||||
cy.getClass().getMethod(
|
||||
"onSdf1Activation",
|
||||
String.class,
|
||||
int.class,
|
||||
int.class);
|
||||
m.invoke(cy, name, 9, 0);
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean handleLogin(Player p,
|
||||
String[] args) {
|
||||
String name = p.getName();
|
||||
if (plugin.getLoggedIn().contains(name)) {
|
||||
p.sendMessage(plugin.getConfig2().msg(
|
||||
"already_logged_in"));
|
||||
return true;
|
||||
}
|
||||
if (!plugin.getDb().userExists(name)) {
|
||||
p.sendMessage(plugin.getConfig2().msg(
|
||||
"not_registered"));
|
||||
return true;
|
||||
}
|
||||
if (args.length < 1) {
|
||||
p.sendMessage("§c用法: /login <密码>");
|
||||
return true;
|
||||
}
|
||||
String pwd = args[0];
|
||||
String salt = (String) plugin.getDb()
|
||||
.getField(name, "password_salt");
|
||||
if (salt == null) {
|
||||
p.sendMessage(
|
||||
"§c[Sdf1_login] §f数据异常,请联系管理员");
|
||||
return true;
|
||||
}
|
||||
String hash = PasswordUtils.hash(pwd, salt);
|
||||
if (plugin.getDb().checkPasswordOrTemp(
|
||||
name, hash)) {
|
||||
plugin.getLoggedIn().add(name);
|
||||
plugin.getDb().setLoggedIn(name, true);
|
||||
plugin.getDb().setField(name,
|
||||
"last_login_time",
|
||||
System.currentTimeMillis());
|
||||
plugin.getDb().setField(name,
|
||||
"last_online_check",
|
||||
System.currentTimeMillis());
|
||||
plugin.restoreInventory(p);
|
||||
plugin.recordIPLogin(p);
|
||||
p.sendMessage(plugin.getConfig2().msg(
|
||||
"login_success", "user", name));
|
||||
if (!plugin.getDb().checkPassword(
|
||||
name, hash)) {
|
||||
plugin.getNeedsPasswordChange()
|
||||
.add(name);
|
||||
p.sendMessage(
|
||||
"§c§l[警告] §f您使用的是临时密码,请尽快修改密码!");
|
||||
p.sendMessage(
|
||||
"§7用法: /sdf1_login pw <旧密码> <新密码> <确认新密码>");
|
||||
}
|
||||
try {
|
||||
org.bukkit.plugin.Plugin cy =
|
||||
org.bukkit.Bukkit
|
||||
.getPluginManager()
|
||||
.getPlugin(
|
||||
"CY_beibao");
|
||||
if (cy != null && cy.isEnabled()) {
|
||||
java.lang.reflect.Method m =
|
||||
cy.getClass().getMethod(
|
||||
"onSdf1Activation",
|
||||
String.class,
|
||||
int.class,
|
||||
int.class);
|
||||
m.invoke(cy, name, 0, 0);
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
} else {
|
||||
p.sendMessage(plugin.getConfig2().msg(
|
||||
"login_failed"));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean handleReset(Player p) {
|
||||
String name = p.getName();
|
||||
String email = (String) plugin.getDb()
|
||||
.getField(name, "email");
|
||||
if (email == null || email.isEmpty()) {
|
||||
p.sendMessage(plugin.getConfig2().msg(
|
||||
"reset_no_email"));
|
||||
return true;
|
||||
}
|
||||
String tempPwd =
|
||||
PasswordUtils.generateTempPassword();
|
||||
String mainSalt = (String) plugin.getDb()
|
||||
.getField(name, "password_salt");
|
||||
String hash =
|
||||
PasswordUtils.hash(tempPwd, mainSalt);
|
||||
plugin.getDb().setField(name,
|
||||
"temp_password", hash);
|
||||
plugin.getDb().setField(name,
|
||||
"temp_pw_expire",
|
||||
System.currentTimeMillis()
|
||||
+ 300000L);
|
||||
plugin.getEmail().sendTempPassword(
|
||||
email, name, tempPwd);
|
||||
p.sendMessage(plugin.getConfig2().msg(
|
||||
"reset_sent"));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,72 @@
|
||||
package Sdf1_login;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Base64;
|
||||
|
||||
public class PasswordUtils {
|
||||
|
||||
private static final SecureRandom RANDOM =
|
||||
new SecureRandom();
|
||||
|
||||
public static String generateSalt() {
|
||||
byte[] salt = new byte[16];
|
||||
RANDOM.nextBytes(salt);
|
||||
return Base64.getEncoder().encodeToString(salt);
|
||||
}
|
||||
|
||||
public static String hash(String password, String salt) {
|
||||
try {
|
||||
MessageDigest md =
|
||||
MessageDigest.getInstance("SHA-256");
|
||||
md.update(Base64.getDecoder().decode(salt));
|
||||
byte[] hash =
|
||||
md.digest(password.getBytes("UTF-8"));
|
||||
return Base64.getEncoder().encodeToString(hash);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean validate(String password) {
|
||||
if (password == null) return false;
|
||||
if (password.length() < 6 || password.length() > 20)
|
||||
return false;
|
||||
boolean hasUpper = false;
|
||||
boolean hasLower = false;
|
||||
boolean hasDigit = false;
|
||||
for (char c : password.toCharArray()) {
|
||||
if (Character.isUpperCase(c)) hasUpper = true;
|
||||
if (Character.isLowerCase(c)) hasLower = true;
|
||||
if (Character.isDigit(c)) hasDigit = true;
|
||||
}
|
||||
return hasUpper && hasLower && hasDigit;
|
||||
}
|
||||
|
||||
public static String generateTempPassword() {
|
||||
String chars =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||
StringBuilder sb = new StringBuilder(8);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
sb.append(chars.charAt(
|
||||
RANDOM.nextInt(chars.length())));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String generateInviteCode() {
|
||||
String chars =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
StringBuilder sb = new StringBuilder(6);
|
||||
for (int i = 0; i < 6; i++) {
|
||||
sb.append(chars.charAt(
|
||||
RANDOM.nextInt(chars.length())));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String generateVerifyCode() {
|
||||
int code = 100000 + RANDOM.nextInt(900000);
|
||||
return String.valueOf(code);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,328 @@
|
||||
package Sdf1_login;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
|
||||
public class PointsManager {
|
||||
|
||||
private final Main plugin;
|
||||
private Object cyInstance = null;
|
||||
private Method cyActivationMethod = null;
|
||||
private Method cyGetShopItemsMethod = null;
|
||||
|
||||
private final List<Map<String, Object>> cyItems =
|
||||
new ArrayList<>();
|
||||
|
||||
public PointsManager(Main plugin) {
|
||||
this.plugin = plugin;
|
||||
discoverCY();
|
||||
}
|
||||
|
||||
private void discoverCY() {
|
||||
try {
|
||||
org.bukkit.plugin.Plugin p = Bukkit
|
||||
.getPluginManager()
|
||||
.getPlugin("CY_beibao");
|
||||
if (p != null && p.isEnabled()) {
|
||||
cyInstance = p;
|
||||
cyGetShopItemsMethod = p.getClass()
|
||||
.getMethod("onSdf1GetShopItems");
|
||||
cyActivationMethod = p.getClass()
|
||||
.getMethod("onSdf1Activation",
|
||||
String.class,
|
||||
int.class, int.class);
|
||||
plugin.getLogger().info(
|
||||
"[Sdf1_login] CY_beibao 联控已连接");
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
|
||||
// ===== CY 商品拉取 =====
|
||||
|
||||
private void refreshCYItems() {
|
||||
cyItems.clear();
|
||||
if (cyInstance == null
|
||||
|| cyGetShopItemsMethod == null) return;
|
||||
try {
|
||||
Object result =
|
||||
cyGetShopItemsMethod.invoke(cyInstance);
|
||||
if (result == null) return;
|
||||
String data = result.toString();
|
||||
if (data.isEmpty()) return;
|
||||
String[] items = data.split(";");
|
||||
for (String itemStr : items) {
|
||||
String[] p = itemStr.split("\\|");
|
||||
if (p.length < 8) continue;
|
||||
Map<String, Object> item =
|
||||
new LinkedHashMap<>();
|
||||
item.put("id", p[0]);
|
||||
item.put("name", p[1]);
|
||||
item.put("days",
|
||||
Integer.parseInt(p[2]));
|
||||
item.put("slots",
|
||||
Integer.parseInt(p[3]));
|
||||
item.put("price",
|
||||
Integer.parseInt(p[4]));
|
||||
item.put("stock",
|
||||
Integer.parseInt(p[5]));
|
||||
item.put("icon", p[6]);
|
||||
item.put("lifetime",
|
||||
"1".equals(p[7]));
|
||||
cyItems.add(item);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().warning(
|
||||
"[Sdf1_login] 拉取CY商品失败: "
|
||||
+ e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void activateCY(String name,
|
||||
int slots, int days) {
|
||||
try {
|
||||
if (cyInstance == null
|
||||
|| cyActivationMethod == null) return;
|
||||
cyActivationMethod.invoke(
|
||||
cyInstance, name, slots, days);
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().warning(
|
||||
"[Sdf1_login] CY激活失败: "
|
||||
+ e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 积分查询 =====
|
||||
|
||||
public int getPoints(String name) {
|
||||
return ((Number) plugin.getDb()
|
||||
.getField(name, "points")).intValue();
|
||||
}
|
||||
|
||||
// ===== 主面板 =====
|
||||
|
||||
public Inventory createShopGUI(Player p) {
|
||||
int points = getPoints(p.getName());
|
||||
Inventory inv = Bukkit.createInventory(
|
||||
null, 54,
|
||||
"§d§l积分商城 | 当前积分: " + points);
|
||||
fillBg(inv);
|
||||
inv.setItem(10, mkItem(Material.EMERALD,
|
||||
"§a游戏币充值",
|
||||
"§7消耗积分兑换游戏币",
|
||||
"§7100积分 = $1000",
|
||||
"",
|
||||
"§e点击兑换"));
|
||||
inv.setItem(12, mkItem(Material.ENDER_CHEST,
|
||||
"§b背包空间",
|
||||
"§7消耗积分兑换背包格子",
|
||||
"§750积分 = 9格永久空间",
|
||||
"",
|
||||
"§e点击兑换"));
|
||||
inv.setItem(14, mkItem(Material.EXPERIENCE_BOTTLE,
|
||||
"§e经验瓶",
|
||||
"§7消耗积分兑换经验",
|
||||
"§720积分 = 10级经验",
|
||||
"",
|
||||
"§e点击兑换"));
|
||||
if (cyInstance != null) {
|
||||
inv.setItem(31, mkItem(Material.CHEST,
|
||||
"§6背包商城",
|
||||
"§7来自CY_beibao的商品",
|
||||
"",
|
||||
"§e点击查看"));
|
||||
}
|
||||
inv.setItem(49, mkItem(Material.ARROW, "§7返回"));
|
||||
return inv;
|
||||
}
|
||||
|
||||
// ===== CY 商品面板 =====
|
||||
|
||||
public Inventory createCYShopGUI(Player p) {
|
||||
refreshCYItems();
|
||||
int points = getPoints(p.getName());
|
||||
int size = Math.max(54,
|
||||
((cyItems.size() / 9) + 1) * 9);
|
||||
if (size > 54) size = 54;
|
||||
Inventory inv = Bukkit.createInventory(
|
||||
null, size,
|
||||
"§d§lCY背包商城 | 当前积分: " + points);
|
||||
fillBg(inv);
|
||||
for (int i = 0; i < cyItems.size()
|
||||
&& i < 45; i++) {
|
||||
Map<String, Object> item = cyItems.get(i);
|
||||
String name = (String) item.get("name");
|
||||
int price = ((Number) item.get("price"))
|
||||
.intValue();
|
||||
int stock = ((Number) item.get("stock"))
|
||||
.intValue();
|
||||
int days = ((Number) item.get("days"))
|
||||
.intValue();
|
||||
int slots = ((Number) item.get("slots"))
|
||||
.intValue();
|
||||
boolean lifetime = (boolean) item
|
||||
.get("lifetime");
|
||||
|
||||
List<String> lore = new ArrayList<>();
|
||||
lore.add("§7价格: §e" + price + " 积分");
|
||||
if (lifetime) {
|
||||
lore.add("§7格子: §e终身 +" + slots);
|
||||
} else {
|
||||
lore.add("§7格子: §e+" + slots);
|
||||
lore.add("§7时间: §e" + days + "天");
|
||||
}
|
||||
if (stock == 0) {
|
||||
lore.add("§7库存: §a无限");
|
||||
} else if (stock == -1) {
|
||||
lore.add("§7库存: §c已下架");
|
||||
} else {
|
||||
lore.add("§7库存: §e" + stock);
|
||||
}
|
||||
lore.add("");
|
||||
if (points >= price && stock != -1) {
|
||||
lore.add("§e点击购买");
|
||||
} else if (stock == -1) {
|
||||
lore.add("§c已下架");
|
||||
} else {
|
||||
lore.add("§c积分不足");
|
||||
}
|
||||
inv.setItem(i, mkItem(Material.CHEST,
|
||||
"§a" + name,
|
||||
lore.toArray(new String[0])));
|
||||
}
|
||||
inv.setItem(size - 1,
|
||||
mkItem(Material.ARROW, "§7返回"));
|
||||
return inv;
|
||||
}
|
||||
|
||||
// ===== 主面板点击 =====
|
||||
|
||||
public void handleClick(Player p, int slot) {
|
||||
int points = getPoints(p.getName());
|
||||
switch (slot) {
|
||||
case 10:
|
||||
if (points >= 100) {
|
||||
plugin.getDb().deductPoints(
|
||||
p.getName(), 100);
|
||||
if (plugin.getEconomy() != null) {
|
||||
plugin.getEconomy()
|
||||
.depositPlayer(p, 1000);
|
||||
}
|
||||
p.sendMessage(plugin.getConfig2().msg(
|
||||
"points_purchase_success",
|
||||
"count", "100"));
|
||||
} else {
|
||||
p.sendMessage(plugin.getConfig2().msg(
|
||||
"points_insufficient",
|
||||
"points",
|
||||
String.valueOf(points)));
|
||||
}
|
||||
break;
|
||||
case 12:
|
||||
if (points >= 50) {
|
||||
plugin.getDb().deductPoints(
|
||||
p.getName(), 50);
|
||||
activateCY(p.getName(), 9, 0);
|
||||
p.sendMessage(plugin.getConfig2().msg(
|
||||
"points_purchase_success",
|
||||
"count", "50"));
|
||||
} else {
|
||||
p.sendMessage(plugin.getConfig2().msg(
|
||||
"points_insufficient",
|
||||
"points",
|
||||
String.valueOf(points)));
|
||||
}
|
||||
break;
|
||||
case 14:
|
||||
if (points >= 20) {
|
||||
plugin.getDb().deductPoints(
|
||||
p.getName(), 20);
|
||||
p.setLevel(p.getLevel() + 10);
|
||||
p.sendMessage(plugin.getConfig2().msg(
|
||||
"points_purchase_success",
|
||||
"count", "20"));
|
||||
} else {
|
||||
p.sendMessage(plugin.getConfig2().msg(
|
||||
"points_insufficient",
|
||||
"points",
|
||||
String.valueOf(points)));
|
||||
}
|
||||
break;
|
||||
case 31:
|
||||
if (cyInstance != null) {
|
||||
p.openInventory(
|
||||
createCYShopGUI(p));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ===== CY 商品面板点击 =====
|
||||
|
||||
public void handleCYClick(Player p, int slot) {
|
||||
if (slot < 0 || slot >= cyItems.size()
|
||||
|| slot >= 45) return;
|
||||
Map<String, Object> item = cyItems.get(slot);
|
||||
int price = ((Number) item.get("price"))
|
||||
.intValue();
|
||||
int stock = ((Number) item.get("stock"))
|
||||
.intValue();
|
||||
int days = ((Number) item.get("days"))
|
||||
.intValue();
|
||||
int slots = ((Number) item.get("slots"))
|
||||
.intValue();
|
||||
boolean lifetime = (boolean) item
|
||||
.get("lifetime");
|
||||
|
||||
if (stock == -1) {
|
||||
p.sendMessage(
|
||||
"§c[Sdf1_login] §f该商品已下架");
|
||||
return;
|
||||
}
|
||||
int points = getPoints(p.getName());
|
||||
if (points < price) {
|
||||
p.sendMessage(plugin.getConfig2().msg(
|
||||
"points_insufficient",
|
||||
"points",
|
||||
String.valueOf(points)));
|
||||
return;
|
||||
}
|
||||
|
||||
plugin.getDb().deductPoints(p.getName(), price);
|
||||
int actDays = lifetime ? 0 : days;
|
||||
activateCY(p.getName(), slots, actDays);
|
||||
|
||||
String tl = lifetime
|
||||
? "终身" : (days + "天");
|
||||
p.sendMessage("§a[Sdf1_login] §f购买成功!获得 "
|
||||
+ tl + " +" + slots + "格 背包空间");
|
||||
}
|
||||
|
||||
// ===== 工具 =====
|
||||
|
||||
private void fillBg(Inventory inv) {
|
||||
ItemStack gl = mkItem(
|
||||
Material.GRAY_STAINED_GLASS_PANE, " ");
|
||||
for (int i = 0; i < inv.getSize(); i++)
|
||||
inv.setItem(i, gl);
|
||||
}
|
||||
|
||||
private ItemStack mkItem(Material mat, String name,
|
||||
String... lore) {
|
||||
ItemStack it = new ItemStack(mat);
|
||||
ItemMeta im = it.getItemMeta();
|
||||
if (im != null) {
|
||||
im.setDisplayName(name);
|
||||
if (lore.length > 0)
|
||||
im.setLore(Arrays.asList(lore));
|
||||
it.setItemMeta(im);
|
||||
}
|
||||
return it;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
name: Sdf1_login
|
||||
version: 1.0
|
||||
main: Sdf1_login.Main
|
||||
api-version: '1.21'
|
||||
commands:
|
||||
sdf1_login:
|
||||
description: Sdf1_login主命令
|
||||
usage: /sdf1_login
|
||||
reg:
|
||||
description: 注册
|
||||
usage: /reg <密码>
|
||||
login:
|
||||
description: 登录
|
||||
usage: /login <密码>
|
||||
l:
|
||||
description: 登录快捷版
|
||||
usage: /l <密码>
|
||||
注册:
|
||||
description: 注册(中文)
|
||||
usage: /注册 <密码>
|
||||
登录:
|
||||
description: 登录(中文)
|
||||
usage: /登录 <密码>
|
||||
玩家信息:
|
||||
description: 打开面板(中文)
|
||||
usage: /玩家信息
|
||||
改密码:
|
||||
description: 修改密码(中文)
|
||||
usage: /改密码 <旧密码> <新密码> <确认新密码>
|
||||
设置邮箱:
|
||||
description: 设置邮箱(中文)
|
||||
usage: /设置邮箱 <邮箱地址>
|
||||
撤销:
|
||||
description: 撤销密码修改(中文)
|
||||
usage: /撤销 <旧密码>
|
||||
删除:
|
||||
description: 删除玩家(中文)
|
||||
usage: /删除 <玩家> <密码>
|
||||
找回密码:
|
||||
description: 找回密码(中文)
|
||||
usage: /找回密码
|
||||
debug:
|
||||
description: 调试指令
|
||||
usage: /debug <玩家> take.<阶段>
|
||||
签到:
|
||||
description: 每日签到(中文)
|
||||
usage: /签到
|
||||
Reference in New Issue
Block a user