fix: AreaGUIManager硬编码URL改为从插件配置读取
- AreaGUIManager删除用户组时通知PHP同步,URL和密钥之前写死为caoyuan.ypshidifu.cn - 现在改为从webManager.getWebBaseUrl()和getSecretKey()读取,支持其他服主自定义web后端 - extractCodeFromInput增强支持#code=fragment形式(上一轮修复) - verifyMinecraftAuthCode加debug日志便于排查 - deploy_sftp.py备份后缀改为.jar.old/.old.N(不以.jar结尾,避免MC加载冲突) - .gitignore添加hs_err_pid*.log
This commit is contained in:
+1
-1
@@ -81,4 +81,4 @@ lpt5
|
|||||||
lpt6
|
lpt6
|
||||||
lpt7
|
lpt7
|
||||||
lpt8
|
lpt8
|
||||||
lpt9
|
lpt9hs_err_pid*.log
|
||||||
|
|||||||
+127
@@ -0,0 +1,127 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
SFTP deployment script for Minecraft plugin JAR
|
||||||
|
Uploads JAR to sfe4-connect.simpfun.cn:2046
|
||||||
|
"""
|
||||||
|
|
||||||
|
import paramiko
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# SFTP Configuration
|
||||||
|
SFTP_HOST = "sfe4-connect.simpfun.cn"
|
||||||
|
SFTP_PORT = 2046
|
||||||
|
SFTP_USER = "sfe3192491.df7d2f7f"
|
||||||
|
SFTP_PASS = "5350427807"
|
||||||
|
|
||||||
|
# Remote paths
|
||||||
|
REMOTE_JAR_PATH = "/plugins/Sdf1_login.jar"
|
||||||
|
|
||||||
|
# Local JAR path
|
||||||
|
LOCAL_JAR_PATH = r"D:\桌面\源码\mc_plugn\Sdf1_login\target\Sdf1_login.jar"
|
||||||
|
|
||||||
|
def backup_remote_file(sftp, remote_path):
|
||||||
|
"""Backup existing file by renaming to .old.N"""
|
||||||
|
try:
|
||||||
|
# Check if file exists
|
||||||
|
sftp.stat(remote_path)
|
||||||
|
|
||||||
|
# Find next available .jar.old / .jar.old.N suffix
|
||||||
|
# ★ 备份名必须脱离 .jar 后缀(如 Sdf1_login.jar.old / .old.1),
|
||||||
|
# 否则 MC 服务器会加载所有 .jar 文件导致插件冲突。
|
||||||
|
base, ext = os.path.splitext(remote_path) # base="/plugins/Sdf1_login", ext=".jar"
|
||||||
|
suffix = 1
|
||||||
|
while True:
|
||||||
|
backup_path = f"{base}{ext}.old{f'.{suffix}' if suffix > 1 else ''}"
|
||||||
|
try:
|
||||||
|
sftp.stat(backup_path)
|
||||||
|
suffix += 1
|
||||||
|
except FileNotFoundError:
|
||||||
|
# Found available backup name
|
||||||
|
sftp.rename(remote_path, backup_path)
|
||||||
|
print(f" Backed up existing file to: {backup_path}")
|
||||||
|
return True
|
||||||
|
except FileNotFoundError:
|
||||||
|
# File doesn't exist, no backup needed
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
print(f" Warning: Could not backup file: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print("=" * 60)
|
||||||
|
print("SFTP Deployment to sfe4-connect.simpfun.cn")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
# Check if local JAR exists
|
||||||
|
if not os.path.exists(LOCAL_JAR_PATH):
|
||||||
|
print(f"❌ Local JAR not found: {LOCAL_JAR_PATH}")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
print(f"\nLocal JAR: {LOCAL_JAR_PATH}")
|
||||||
|
print(f"Remote path: {REMOTE_JAR_PATH}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Create SSH client
|
||||||
|
print(f"\nConnecting to SFTP: {SFTP_HOST}:{SFTP_PORT}")
|
||||||
|
ssh = paramiko.SSHClient()
|
||||||
|
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
|
|
||||||
|
# Connect
|
||||||
|
ssh.connect(
|
||||||
|
hostname=SFTP_HOST,
|
||||||
|
port=SFTP_PORT,
|
||||||
|
username=SFTP_USER,
|
||||||
|
password=SFTP_PASS,
|
||||||
|
timeout=30
|
||||||
|
)
|
||||||
|
|
||||||
|
# Open SFTP session
|
||||||
|
sftp = ssh.open_sftp()
|
||||||
|
print("Connected successfully!")
|
||||||
|
|
||||||
|
# Backup existing JAR
|
||||||
|
print("\nBacking up existing JAR...")
|
||||||
|
backup_remote_file(sftp, REMOTE_JAR_PATH)
|
||||||
|
|
||||||
|
# Upload new JAR
|
||||||
|
print("\nUploading new JAR...")
|
||||||
|
sftp.put(LOCAL_JAR_PATH, REMOTE_JAR_PATH)
|
||||||
|
print(f" Uploaded: {REMOTE_JAR_PATH}")
|
||||||
|
|
||||||
|
# Verify upload
|
||||||
|
remote_stat = sftp.stat(REMOTE_JAR_PATH)
|
||||||
|
local_size = os.path.getsize(LOCAL_JAR_PATH)
|
||||||
|
|
||||||
|
print(f"\nVerification:")
|
||||||
|
print(f" Local size: {local_size} bytes")
|
||||||
|
print(f" Remote size: {remote_stat.st_size} bytes")
|
||||||
|
|
||||||
|
if remote_stat.st_size == local_size:
|
||||||
|
print(" ✅ Size match - upload verified!")
|
||||||
|
else:
|
||||||
|
print(" ⚠️ Size mismatch - upload may be incomplete")
|
||||||
|
|
||||||
|
# Close connections
|
||||||
|
sftp.close()
|
||||||
|
ssh.close()
|
||||||
|
|
||||||
|
print("\n" + "=" * 60)
|
||||||
|
print("✅ SFTP deployment completed successfully!")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
except paramiko.AuthenticationException:
|
||||||
|
print("\n❌ Authentication failed. Check username/password.")
|
||||||
|
return 1
|
||||||
|
except paramiko.SSHException as e:
|
||||||
|
print(f"\n❌ SSH connection failed: {e}")
|
||||||
|
return 1
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\n❌ Deployment failed: {e}")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
@@ -1337,15 +1337,16 @@ public class AreaGUIManager implements Listener {
|
|||||||
String groupName = names.get(raw);
|
String groupName = names.get(raw);
|
||||||
if (p.hasPermission("sdf1.admin")) {
|
if (p.hasPermission("sdf1.admin")) {
|
||||||
ugm.deleteGroupConfig(groupName);
|
ugm.deleteGroupConfig(groupName);
|
||||||
// ★ 通知PHP同步
|
// ★ 通知PHP同步(从插件配置读取地址和密钥,不写死)
|
||||||
try {
|
try {
|
||||||
java.net.URL url = new java.net.URL("https://caoyuan.ypshidifu.cn/plugin/api/land_api.php");
|
String landApiUrl = plugin.webManager.getWebBaseUrl() + "/api/land_api.php";
|
||||||
|
java.net.URL url = new java.net.URL(landApiUrl);
|
||||||
java.net.HttpURLConnection conn = (java.net.HttpURLConnection) url.openConnection();
|
java.net.HttpURLConnection conn = (java.net.HttpURLConnection) url.openConnection();
|
||||||
conn.setRequestMethod("POST");
|
conn.setRequestMethod("POST");
|
||||||
conn.setDoOutput(true);
|
conn.setDoOutput(true);
|
||||||
conn.setConnectTimeout(5000);
|
conn.setConnectTimeout(5000);
|
||||||
conn.setReadTimeout(5000);
|
conn.setReadTimeout(5000);
|
||||||
String body = "action=delete_user_group&name=" + groupName + "&secret=sdf1_web_comm_2026_ypshidifu";
|
String body = "action=delete_user_group&name=" + groupName + "&secret=" + java.net.URLEncoder.encode(plugin.webManager.getSecretKey(), "UTF-8");
|
||||||
java.io.OutputStream os = conn.getOutputStream();
|
java.io.OutputStream os = conn.getOutputStream();
|
||||||
os.write(body.getBytes());
|
os.write(body.getBytes());
|
||||||
os.flush();
|
os.flush();
|
||||||
|
|||||||
@@ -1612,12 +1612,26 @@ public class Main extends JavaPlugin
|
|||||||
input = input.trim();
|
input = input.trim();
|
||||||
|
|
||||||
// 如果包含code=参数,从中提取
|
// 如果包含code=参数,从中提取
|
||||||
if (input.contains("code=") || input.startsWith("http")) {
|
if (input.contains("code=") || input.startsWith("http") || input.contains("#")) {
|
||||||
try {
|
try {
|
||||||
// 尝试解析URL中的query参数
|
// ★ 优先处理 #code= fragment形式(Microsoft公共客户端OAuth返回格式)
|
||||||
|
// URL如: https://login.live.com/oauth20_desktop.srf#code=M.C512_BAY.2_xxx
|
||||||
|
if (input.contains("#code=")) {
|
||||||
|
int hashIdx = input.indexOf("#code=");
|
||||||
|
String afterHash = input.substring(hashIdx + 6); // "#code=" 之后的内容
|
||||||
|
// code到下一个&或字符串结尾
|
||||||
|
int ampIdx = afterHash.indexOf('&');
|
||||||
|
String rawCode = ampIdx >= 0 ? afterHash.substring(0, ampIdx) : afterHash;
|
||||||
|
return java.net.URLDecoder.decode(rawCode, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试解析URL中的query参数 (?code=xxx)
|
||||||
int qIdx = input.indexOf('?');
|
int qIdx = input.indexOf('?');
|
||||||
if (qIdx >= 0) {
|
if (qIdx >= 0) {
|
||||||
String query = input.substring(qIdx + 1);
|
String query = input.substring(qIdx + 1);
|
||||||
|
// 去掉fragment部分
|
||||||
|
int hashQIdx = query.indexOf('#');
|
||||||
|
if (hashQIdx >= 0) query = query.substring(0, hashQIdx);
|
||||||
for (String param : query.split("&")) {
|
for (String param : query.split("&")) {
|
||||||
String[] kv = param.split("=", 2);
|
String[] kv = param.split("=", 2);
|
||||||
if (kv.length == 2 && kv[0].equals("code")) {
|
if (kv.length == 2 && kv[0].equals("code")) {
|
||||||
@@ -1626,11 +1640,11 @@ public class Main extends JavaPlugin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// 解析失败,继续尝试其他方式
|
getLogger().warning("[正版验证] extractCodeFromInput解析异常: " + e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 直接作为授权码返回
|
// 直接作为授权码返回(纯code)
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5162,7 +5162,14 @@ public class WebManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String sessionId = sessionData[0];
|
String sessionId = sessionData[0];
|
||||||
String secret = java.net.URLEncoder.encode(secretKey, StandardCharsets.UTF_8);
|
String secret = secretKey; // ★ 不再URLEncoder.encode,直接发送原始密钥
|
||||||
|
|
||||||
|
// ★ 调试日志:记录code提取结果
|
||||||
|
plugin.getLogger().info("[正版验证] /mscode 提交 - 玩家: " + playerName);
|
||||||
|
plugin.getLogger().info("[正版验证] sessionId: " + sessionId);
|
||||||
|
plugin.getLogger().info("[正版验证] code(前80字符): " + code.substring(0, Math.min(80, code.length())));
|
||||||
|
plugin.getLogger().info("[正版验证] code长度: " + code.length());
|
||||||
|
plugin.getLogger().info("[正版验证] secret: " + secret);
|
||||||
|
|
||||||
webExecutor.submit(() -> {
|
webExecutor.submit(() -> {
|
||||||
try {
|
try {
|
||||||
@@ -5174,7 +5181,10 @@ public class WebManager {
|
|||||||
postData.addProperty("code", code);
|
postData.addProperty("code", code);
|
||||||
postData.addProperty("secret", secret);
|
postData.addProperty("secret", secret);
|
||||||
|
|
||||||
String response = doPost(url, postData.toString());
|
String postBody = postData.toString();
|
||||||
|
plugin.getLogger().info("[正版验证] POST body(前200字符): " + postBody.substring(0, Math.min(200, postBody.length())));
|
||||||
|
|
||||||
|
String response = doPost(url, postBody);
|
||||||
if (response == null) {
|
if (response == null) {
|
||||||
player.sendMessage("§c无法连接Web后端");
|
player.sendMessage("§c无法连接Web后端");
|
||||||
return;
|
return;
|
||||||
|
|||||||
Reference in New Issue
Block a user