通过python模拟mc客户端向服务器发送请求来获取玩家数据从而达到检测目的,支持BE版本跟Java版本
import tkinter as tk
from tkinter import ttk, messagebox
import socket
import json
import threading
import re
import time
import dns.resolver
import struct
import random
class MinecraftServerChecker:
def __init__(self, root):
self.root = root
self.root.title("Minecraft 服务器状态检查器")
self.root.geometry("800x800")
self.root.resizable(True, True)
self.root.configure(bg="#1f1f1f")
# 当前检测类型:java 或 bedrock
self.server_type = "java"
# 设置主题颜色
self.bg_color = "#1f1f1f"
self.fg_color = "#e0e0e0"
self.accent_color = "#42a5f5"
self.success_color = "#66bb6a"
self.error_color = "#ef5350"
self.loding_color = "#ffffff"
# 加载常用服务器列表
self.popular_servers = [
"mc.hypixel.net",
"mc.fallcraft.top",
"2b2t.org",
"us.mineplex.com",
"n6-16.yxsjmc.cn",
"2b2tmcpe.org",
"2b2tpe.org"
]
# 创建UI元素
self.create_widgets()
def pack_varint(self, value):
"""将整数打包为VarInt格式"""
data = bytearray()
while True:
byte = value & 0x7F
value >>= 7
if value != 0:
byte |= 0x80
data.append(byte)
if value == 0: # 修复这里的中文字符
break
return bytes(data)
def read_varint(self, sock):
"""从socket读取VarInt格式的整数"""
result = 0
shift = 0
while True:
data = sock.recv(1)
if not data:
return None
byte = data[0]
result |= (byte & 0x7F) << shift
shift += 7
if not (byte & 0x80):
break
return result
def create_widgets(self):
# 标题区域
title_frame = tk.Frame(self.root, bg=self.bg_color, padx=15, pady=10)
title_frame.pack(fill=tk.X)
tk.Label(
title_frame,
text="Minecraft 服务器状态查询",
font=("Arial", 16, "bold"),
bg=self.bg_color,
fg=self.fg_color
).pack(side=tk.LEFT, padx=10)
# 版本标签
version_info_frame = tk.Frame(title_frame, bg=self.bg_color)
version_info_frame.pack(side=tk.RIGHT, padx=10, fill=tk.X)
tk.Label(
version_info_frame,
text="检测类型:",
font=("Arial", 9),
bg=self.bg_color,
fg="#90caf9"
).pack(side=tk.LEFT, padx=(0, 5))
# 服务器类型选择框
self.server_type_var = tk.StringVar(value="java")
server_type_menu = ttk.Combobox(
version_info_frame,
textvariable=self.server_type_var,
values=["Java版", "基岩版"],
width=8,
state="readonly"
)
server_type_menu.pack(side=tk.LEFT)
server_type_menu.bind("<<ComboboxSelected>>", self.change_server_type)
# 输入区域
input_frame = tk.Frame(self.root, bg=self.bg_color, padx=15, pady=10)
input_frame.pack(fill=tk.X)
# 服务器地址标签
server_label_frame = tk.Frame(input_frame, bg=self.bg_color)
server_label_frame.pack(fill=tk.X)
tk.Label(
server_label_frame,
text="服务器地址:",
font=("Arial", 11),
bg=self.bg_color,
fg=self.fg_color
).pack(side=tk.LEFT, anchor="w", pady=5)
self.port_info_label = tk.Label(
server_label_frame,
text="(格式: 地址[:端口] SRV解析",
font=("Arial", 8),
bg=self.bg_color,
fg="#9e9e9e"
)
self.port_info_label.pack(side=tk.LEFT, anchor="w", pady=5, padx=(10, 0))
# 地址输入框
server_address_frame = tk.Frame(input_frame, bg=self.bg_color)
server_address_frame.pack(fill=tk.X, pady=5)
# 服务器地址输入框
self.server_entry = ttk.Entry(
server_address_frame,
font=("Arial", 11),
width=45
)
self.server_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 10))
self.server_entry.insert(0, "mc.fallcraft.top")
self.server_entry.bind("<Return>", lambda event: self.start_check_thread())
# 检查按钮
self.check_button = ttk.Button(
server_address_frame,
text="检查状态",
command=self.start_check_thread,
width=12
)
self.check_button.pack(side=tk.RIGHT, padx=(10, 0))
# 信息显示框
self.info_frame = tk.LabelFrame(
input_frame,
text="解析信息",
font=("Arial", 9),
bg="#2d2d2d",
fg="#90caf9",
padx=10,
pady=5
)
self.info_frame.pack(fill=tk.X, pady=(10, 0))
self.info_label = tk.Label(
self.info_frame,
text="就绪",
font=("Arial", 9),
bg="#2d2d2d",
fg="#e0e0e0",
anchor="w"
)
self.info_label.pack(fill=tk.X)
# 常用服务器快捷选项
tk.Label(
input_frame,
text="常用服务器:",
font=("Arial", 9),
bg=self.bg_color,
fg="#9e9e9e"
).pack(anchor="w", pady=(15, 5))
self.popular_buttons_frame = tk.Frame(input_frame, bg=self.bg_color)
self.popular_buttons_frame.pack(fill=tk.X, pady=5)
# 填充流行服务器按钮
for server in self.popular_servers:
btn = tk.Button(
self.popular_buttons_frame,
text=server,
font=("Arial", 8),
bg="#2d2d2d",
fg="#bdbdbd",
activebackground="#3d3d3d",
activeforeground="#e0e0e0",
relief="flat",
command=lambda s=server: self.set_server(s)
)
btn.pack(side=tk.LEFT, padx=3)
# 状态显示区域
status_frame = tk.LabelFrame(
self.root,
text="服务器状态",
font=("Arial", 11, "bold"),
bg=self.bg_color,
fg=self.fg_color,
padx=15,
pady=10
)
status_frame.pack(fill=tk.BOTH, padx=15, pady=(0, 15), expand=True)
# 服务器状态
self.status_display = tk.Frame(status_frame, bg=self.bg_color)
self.status_display.pack(fill=tk.X, pady=(0, 10))
self.status_label = tk.Label(
self.status_display,
text="就绪",
font=("Arial", 12),
bg=self.bg_color,
fg="#bdbdbd"
)
self.status_label.pack(side=tk.LEFT)
self.status_icon = tk.Label(self.status_display, text="📡", font=("Arial", 14), bg=self.loding_color)
self.status_icon.pack(side=tk.RIGHT)
# 详情容器
details_container = tk.Frame(status_frame, bg=self.bg_color)
details_container.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)
# 第一行: 基础信息
info_frame = tk.Frame(details_container, bg=self.bg_color)
info_frame.pack(fill=tk.X, pady=(0, 10))
# 在线玩家信息
tk.Label(
info_frame,
text="在线玩家:",
font=("Arial", 10),
bg=self.bg_color,
fg="#9e9e9e"
).pack(side=tk.LEFT, padx=(0, 5))
self.player_count = tk.Label(
info_frame,
text="0/0",
font=("Arial", 10, "bold"),
bg=self.bg_color,
fg=self.fg_color,
width=10
)
self.player_count.pack(side=tk.LEFT, padx=(0, 20))
# 协议版本信息
tk.Label(
info_frame,
text="协议版本:",
font=("Arial", 10),
bg=self.bg_color,
fg="#9e9e9e"
).pack(side=tk.LEFT, padx=(0, 5))
self.protocol_info = tk.Label(
info_frame,
text="未知",
font=("Arial", 10),
bg=self.bg_color,
fg=self.fg_color,
width=10
)
self.protocol_info.pack(side=tk.LEFT)
# 服务器版本信息
version_frame = tk.Frame(details_container, bg=self.bg_color)
version_frame.pack(fill=tk.X, pady=(0, 10))
tk.Label(
version_frame,
text="服务器版本:",
font=("Arial", 10),
bg=self.bg_color,
fg="#9e9e9e",
anchor="w"
).pack(side=tk.LEFT, anchor="nw", padx=(0, 5))
self.version_info = tk.Text(
version_frame,
height=3,
width=50,
bg="#2d2d2d",
fg=self.fg_color,
font=("Arial", 9),
wrap=tk.WORD,
padx=5,
pady=3,
state=tk.DISABLED
)
self.version_info.pack(side=tk.LEFT, fill=tk.X, expand=True)
# MOTD区域
tk.Label(
details_container,
text="服务器描述:",
font=("Arial", 10),
bg=self.bg_color,
fg="#9e9e9e",
anchor="w"
).pack(anchor="w", pady=(0, 5))
self.motd_text = tk.Text(
details_container,
height=4,
bg="#2d2d2d",
fg=self.fg_color,
font=("Arial", 9),
wrap=tk.WORD,
padx=5,
pady=3,
state=tk.DISABLED
)
self.motd_text.pack(fill=tk.X, pady=(0, 10))
# 玩家列表区域
tk.Label(
details_container,
text="在线玩家列表:",
font=("Arial", 10),
bg=self.bg_color,
fg="#9e9e9e",
anchor="w"
).pack(anchor="w", pady=(0, 5))
player_list_frame = tk.Frame(details_container, bg=self.bg_color)
player_list_frame.pack(fill=tk.BOTH, expand=True)
self.player_list = tk.Listbox(
player_list_frame,
height=6,
bg="#2d2d2d",
fg=self.fg_color,
font=("Arial", 9),
selectbackground=self.accent_color,
selectmode=tk.SINGLE,
highlightthickness=0
)
self.player_list.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar = ttk.Scrollbar(player_list_frame, command=self.player_list.yview)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.player_list.config(yscrollcommand=scrollbar.set)
# 添加提示信息
self.player_list.delete(0, tk.END)
self.player_list.insert(tk.END, "在线玩家 (点击复制):")
self.player_list.itemconfig(0, fg="#4fc3f7")
self.player_list.insert(tk.END, "") # 空行分隔
self.player_list.insert(tk.END, "等待服务器响应...")
self.player_list.itemconfig(2, fg="#9e9e9e")
# 状态栏
self.status_bar = tk.Label(
self.root,
text="就绪 | 支持 Java版 和 Bedrock版 | 输入示例: hypixel.net",
font=("Arial", 8),
bg="#121212",
fg="#757575",
anchor="w",
padx=10
)
self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)
def change_server_type(self, event):
"""更改服务器类型"""
server_type = self.server_type_var.get()
self.server_type = "java" if server_type == "Java版" else "bedrock"
# 更新状态栏提示
if server_type == "Java版":
self.port_info_label.config(text="(格式: 地址[:端口] SRV解析)")
self.status_bar.config(text="就绪 | 支持 Java版 和 Bedrock版 | Java版默认端口25565")
else:
self.port_info_label.config(text="(格式: 地址[:端口] 基岩版默认端口19132)")
self.status_bar.config(text="就绪 | 支持 Java版 和 Bedrock版 | Bedrock版默认端口19132")
# 更新界面提示
self.info_label.config(text=f"当前检测: {server_type} | 准备查询...")
def set_server(self, server):
"""设置服务器地址"""
self.server_entry.delete(0, tk.END)
self.server_entry.insert(0, server)
self.info_label.config(text=f"已选择: {server} | 准备查询...")
def start_check_thread(self):
"""启动一个新线程来检查服务器状态"""
server_address = self.server_entry.get().strip()
if not server_address:
messagebox.showerror("错误", "请输入服务器地址")
return
# 重置UI状态
self.reset_ui()
self.status_label.config(text="连接中...", fg="#00BFFF")
self.status_icon.config(text="⏳", fg="#00BFFF")
self.check_button.config(state=tk.DISABLED)
# 启动查询线程
server_type = self.server_type
thread = threading.Thread(target=self.resolve_and_check_server, args=(server_address, server_type))
thread.daemon = True
thread.start()
def resolve_and_check_server(self, address, server_type):
"""解析SRV记录并检查服务器状态"""
# 默认端口设置
default_port = 25565 if server_type == "java" else 19132
port = default_port
host = address
# 检查地址是否包含端口
if ":" in address:
parts = address.split(":", 1)
host = parts[0]
try:
port = int(parts[1])
except ValueError:
self.root.after(0, lambda: self.show_error("端口格式错误", host, port))
return
# 显示连接信息
self.root.after(0, lambda: self.info_label.config(
text=f"正在连接 {host}:{port} ({'Java版' if server_type == 'java' else '基岩版'})"
))
self.root.after(0, lambda: self.status_bar.config(
text=f"正在查询 {host}:{port}..."
))
# 根据服务器类型进行查询
if server_type == "java":
# Java版服务器支持SRV记录解析
self.root.after(0, lambda: self.info_label.config(text=f"解析 {host} 的SRV记录..."))
try:
# 创建SRV查询名称
srv_name = f"_minecraft._tcp.{host}"
# 查询SRV记录
answer = dns.resolver.resolve(srv_name, "SRV", lifetime=5)
if answer:
# 获取最合适的SRV记录
sorted_answers = sorted(answer, key=lambda x: (x.priority, -x.weight))
srv_record = sorted_answers[0]
# 使用SRV记录中的主机和端口
resolved_host = str(srv_record.target).rstrip('.')
resolved_port = srv_record.port
self.root.after(0, lambda: self.info_label.config(
text=f"找到SRV记录: {srv_name} → {resolved_host}:{resolved_port}")
)
self.root.after(0, lambda: self.status_bar.config(
text=f"使用SRV记录: {resolved_host}:{resolved_port}")
)
host = resolved_host
port = resolved_port
except (dns.resolver.NoAnswer, dns.resolver.NXDOMAIN):
# 没有SRV记录,使用默认端口
pass
except dns.exception.Timeout:
# DNS查询超时,继续尝试
pass
except Exception as e:
self.root.after(0, lambda: self.show_error(f"DNS查询错误: {str(e)}", host, port))
return
# 查询Java版服务器状态
self.check_java_server_status(host, port)
else:
# 查询基岩版服务器状态
self.check_bedrock_server_status(host, port)
def clean_server_address(self, address):
"""清理服务器地址"""
# 移除协议部分
if address.startswith("http://"):
address = address[7:]
elif address.startswith("https://"):
address = address[8:]
# 移除路径部分
if "/" in address:
address = address.split("/")[0]
return address
def reset_ui(self):
"""重置UI元素为初始状态"""
self.player_count.config(text="0/0")
self.protocol_info.config(text="未知")
self.version_info.config(state=tk.NORMAL)
self.version_info.delete(1.0, tk.END)
self.version_info.insert(tk.END, "未知")
self.version_info.config(state=tk.DISABLED)
self.motd_text.config(state=tk.NORMAL)
self.motd_text.delete(1.0, tk.END)
self.motd_text.insert(tk.END, "暂无描述信息")
self.motd_text.config(state=tk.DISABLED)
self.player_list.delete(0, tk.END)
self.player_list.insert(tk.END, "在线玩家 (点击复制):")
self.player_list.itemconfig(0, fg="#4fc3f7")
self.player_list.insert(tk.END, "") # 空行分隔
self.player_list.insert(tk.END, "等待服务器响应...")
self.player_list.itemconfig(2, fg="#9e9e9e")
def check_java_server_status(self, host, port):
"""检查Java版Minecraft服务器状态"""
try:
# 创建TCP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(20) # 较长的超时时间
sock.connect((host, port))
# 构建协议版本 (使用通用协议版本)
protocol_version = self.pack_varint(761) # 1.19.3协议版本,较新服务器都兼容
# 构建握手包
host_encoded = host.encode("utf-8")
host_length = self.pack_varint(len(host_encoded))
port_bytes = port.to_bytes(2, byteorder="big")
# 握手包数据内容
handshake_data = b""
handshake_data += b"\x00" # 包ID: 握手(0)
handshake_data += protocol_version
handshake_data += host_length
handshake_data += host_encoded
handshake_data += port_bytes
handshake_data += b"\x01" # 下一步状态: 状态(1)
# 添加长度前缀
packet_length = self.pack_varint(len(handshake_data))
full_packet = packet_length + handshake_data
# 发送握手包
sock.sendall(full_packet)
# 发送状态请求包
request_packet = self.pack_varint(1) + b"\x00" # 长度为1, 包ID:0
sock.sendall(request_packet)
# 读取响应长度
response_length = self.read_varint(sock)
if response_length is None or response_length < 1:
self.root.after(0, lambda: self.show_error("服务器响应异常", host, port))
return
# 读取完整响应
response_data = b""
while len(response_data) < response_length:
chunk = sock.recv(min(4096, response_length - len(response_data)))
if not chunk:
break
response_data += chunk
# 查找JSON数据
json_start = response_data.find(b"{")
if json_start == -1:
# 尝试寻找JSON开头
json_start = response_data.find(b'{"')
if json_start == -1:
self.root.after(0, lambda: self.show_error("服务器返回非标准响应", host, port))
return
# 解析JSON
try:
json_data = response_data[json_start:].decode("utf-8", errors="ignore")
status = json.loads(json_data)
# 更新UI
self.root.after(0, lambda: self.update_ui_with_status(status, host, port, "java"))
except json.JSONDecodeError as ex:
self.root.after(0, lambda: self.show_error(f"服务器返回无效JSON: {str(ex)}", host, port))
except Exception as ex:
self.root.after(0, lambda: self.show_error(f"解析错误: {str(ex)}", host, port))
except socket.gaierror:
self.root.after(0, lambda: self.show_error(f"无法解析主机名: {host}", host, port))
except socket.timeout:
self.root.after(0, lambda: self.show_error("连接超时(可能防火墙阻止或服务器离线)", host, port))
except ConnectionRefusedError:
self.root.after(0, lambda: self.show_error("连接被拒绝(服务器离线或端口错误)", host, port))
except TimeoutError:
self.root.after(0, lambda: self.show_error("操作超时,请重试", host, port))
except Exception as ex:
self.root.after(0, lambda: self.show_error(f"错误: {str(ex)}", host, port))
finally:
try:
sock.close()
except:
pass
def check_bedrock_server_status(self, host, port):
"""检查基岩版Minecraft服务器状态"""
try:
# 创建UDP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(5.0)
# 构造数据包
# 包ID: 1 (Unconnected Ping)
packet_id = b'\x01'
# 当前时间戳 (8字节,小端)
timestamp = struct.pack('<Q', int(time.time() * 1000))
magic = bytes([0x00, 0xFF, 0xFF, 0x00, 0xFE, 0xFE, 0xFE, 0xFE,
0xFD, 0xFD, 0xFD, 0xFD, 0x12, 0x34, 0x56, 0x78])
# 客户端GUID (8字节)
client_guid = struct.pack('<Q', random.randint(0, 2 ** 64 - 1))
# 组合数据包
packet = packet_id + timestamp + magic + client_guid
# 发送数据包
sock.sendto(packet, (host, port))
# 接收响应
data, addr = sock.recvfrom(2048)
# 解析响应包
if data[0] != 0x1C: # Unconnected Pong
self.root.after(0, lambda: self.show_error("服务器返回无效数据", host, port))
return
# 解析时间戳
timestamp_response = data[1:9]
# 服务器GUID
server_guid = data[9:17]
# 读取字符串信息
# 长度前缀 (大端16位整数)
str_len = struct.unpack('>H', data[17:19])[0]
# 服务器信息字符串
info_bytes = data[19:19 + str_len]
server_info = info_bytes.decode('utf-8', errors='ignore')
# 解析服务器信息
info_parts = server_info.split(';')
# 典型格式:
# [0] - MCPE
# [1] - 服务器名称
# [2] - 协议版本
# [3] - 游戏版本
# [4] - 在线玩家数
# [5] - 最大玩家数
if len(info_parts) < 6:
self.root.after(0, lambda: self.show_error("服务器返回信息不足", host, port))
return
server_name = info_parts[1]
protocol_version = info_parts[2]
game_version = info_parts[3]
online_players = int(info_parts[4])
max_players = int(info_parts[5])
# 创建类似Java版的响应结构
bedrock_status = {
'description': server_name,
'players': {
'online': online_players,
'max': max_players,
'sample': [] # 基岩版不支持玩家列表
},
'version': {
'name': game_version,
'protocol': protocol_version
}
}
# 更新UI
self.root.after(0, lambda: self.update_ui_with_status(bedrock_status, host, port, "bedrock"))
except socket.timeout:
self.root.after(0, lambda: self.show_error("基岩版连接超时", host, port))
except socket.gaierror:
self.root.after(0, lambda: self.show_error(f"无法解析主机名: {host}", host, port))
except ConnectionRefusedError:
self.root.after(0, lambda: self.show_error("连接被拒绝(服务器离线或端口错误)", host, port))
except Exception as ex:
self.root.after(0, lambda: self.show_error(f"错误: {str(ex)}", host, port))
finally:
try:
sock.close()
except:
pass
def clean_motd(self, motd):
"""清理MOTD中的格式代码"""
# 如果MOTD是JSON格式
if isinstance(motd, dict):
# 尝试提取text字段
if "text" in motd:
text = motd["text"]
# 检查是否有额外字段
if "extra" in motd and isinstance(motd["extra"], list):
for extra in motd["extra"]:
if "text" in extra:
text += extra["text"]
return re.sub(r"§[0-9a-fk-or]", "", text)
return str(motd)
elif isinstance(motd, str):
# 处理普通字符串
return re.sub(r"§[0-9a-fk-or]", "", motd)
else:
return str(motd)
def format_version_string(self, version_str):
"""格式化长版本字符串"""
# 如果版本字符串过长
if len(version_str) > 40:
# 尝试分割为多个版本
versions = [v.strip() for v in version_str.split(",")]
# 检查是否可以简化为范围
if len(versions) > 3:
# 提取第一个和最后一个版本号
first_ver = versions[0]
last_ver = versions[-1]
# 创建版本范围字符串
range_str = f"{first_ver} - {last_ver}"
# 计算省略的版本数
omitted = len(versions) - 2
return f"{range_str}\n(包含{len(versions)}个版本, 省略{omitted}个)"
else:
return version_str
return version_str
def update_ui_with_status(self, status, host, port, server_type):
"""使用服务器状态更新UI"""
self.status_label.config(text="在线", fg=self.success_color)
self.status_icon.config(text="✅")
self.check_button.config(state=tk.NORMAL)
server_type_text = "Java版" if server_type == "java" else "基岩版"
self.status_bar.config(text=f"{host}:{port} ({server_type_text}) 在线 | {time.strftime('%H:%M:%S')}")
self.info_label.config(text=f"成功连接 {host}:{port} ({server_type_text})")
# 提取服务器信息
try:
description = status.get('description', {})
motd = self.clean_motd(description) if description else "无描述信息"
players = status.get('players', {})
online = players.get('online', 0)
max_players = players.get('max', 0)
player_list = players.get('sample', [])
version = status.get('version', {})
version_name = version.get('name', '未知')
protocol = version.get('protocol', '未知')
# 更新基本信息
# 处理异常大的玩家数量
if max_players > 1000000:
player_count_text = f"{online}/∞"
else:
player_count_text = f"{online}/{max_players}"
self.player_count.config(text=player_count_text)
self.protocol_info.config(text=protocol)
# 更新版本信息
formatted_version = self.format_version_string(version_name)
self.version_info.config(state=tk.NORMAL)
self.version_info.delete(1.0, tk.END)
self.version_info.insert(tk.END, formatted_version)
self.version_info.config(state=tk.DISABLED)
# 更新MOTD
self.motd_text.config(state=tk.NORMAL)
self.motd_text.delete(1.0, tk.END)
self.motd_text.insert(tk.END, motd)
self.motd_text.config(state=tk.DISABLED)
# 更新玩家列表
self.player_list.delete(0, tk.END)
self.player_list.insert(tk.END, "在线玩家 (点击复制):")
self.player_list.itemconfig(0, fg="#4fc3f7")
self.player_list.insert(tk.END, "") # 空行分隔
if server_type == "java" and online > 0 and player_list:
player_names = [p['name'] for p in player_list]
for i, player in enumerate(player_names):
self.player_list.insert(tk.END, f"{i + 1}. {player}")
# 添加更多提示
total_players = len(player_names)
if total_players < online:
self.player_list.insert(tk.END, "")
self.player_list.insert(tk.END, f"还有其他 {online - total_players} 位玩家在线...")
self.player_list.itemconfig(tk.END, fg="#90a4ae")
# 添加复制功能
self.player_list.bind("<<ListboxSelect>>", self.copy_player_name)
else:
if server_type == "bedrock":
self.player_list.insert(tk.END, "基岩版不支持在线玩家列表")
else:
self.player_list.insert(tk.END, "目前没有玩家在线或服务器未返回玩家数据")
self.player_list.itemconfig(2, fg="#9e9e9e")
except Exception as ex:
self.root.after(0, lambda: self.show_error(f"解析服务器数据时出错: {str(ex)}", host, port))
def copy_player_name(self, event):
"""复制选中的玩家名称"""
widget = event.widget
selection = widget.curselection()
if selection:
index = selection[0]
text = widget.get(index)
# 检查是否是玩家名字行 (不以特殊行开头)
if index > 1 and not text.startswith(" ") and text.strip() != "":
# 提取玩家名字 (移除编号)
player_name = re.sub(r'^\d+\.\s*', '', text)
self.root.clipboard_clear()
self.root.clipboard_append(player_name)
self.status_bar.config(text=f"已复制: {player_name}")
def show_error(self, message, host, port):
"""显示错误信息"""
self.status_label.config(text="离线", fg=self.error_color)
self.status_icon.config(text="❌")
self.check_button.config(state=tk.NORMAL)
self.player_count.config(text="0/0")
self.status_bar.config(text=f"错误: {message} | 按F5重试")
self.info_label.config(text=f"连接错误: {message}", fg="#ff5252")
# 更新MOTD区域显示错误
self.motd_text.config(state=tk.NORMAL)
self.motd_text.delete(1.0, tk.END)
self.motd_text.insert(tk.END, "错误信息:\n\n", "error_title")
self.motd_text.insert(tk.END, f"{message}\n\n", "error_msg")
self.motd_text.insert(tk.END, f"目标: {host}:{port}\n\n", "address")
self.motd_text.insert(tk.END, "可能的解决方案:\n", "subtitle")
self.motd_text.insert(tk.END, "1. 检查服务器地址是否正确\n")
self.motd_text.insert(tk.END, "2. 服务器可能已关闭或防火墙阻止\n")
self.motd_text.insert(tk.END, "3. 确保使用正确的服务器类型\n")
self.motd_text.insert(tk.END, "4. 检查端口设置 (Java:25565, Bedrock:19132)\n")
self.motd_text.insert(tk.END, "5. 检查您的网络连接\n")
self.motd_text.insert(tk.END, "6. 某些服务器需要耐心等待响应")
# 设置文本样式
self.motd_text.tag_configure("error_title", foreground="#ff7043", font=("Arial", 9, "bold"))
self.motd_text.tag_configure("subtitle", foreground="#90a4ae", font=("Arial", 9, "italic"))
self.motd_text.tag_configure("error_msg", foreground="#ff5252")
self.motd_text.tag_configure("address", foreground="#64b5f6")
self.motd_text.config(state=tk.DISABLED)
# 重置玩家列表
self.player_list.delete(0, tk.END)
self.player_list.insert(tk.END, "在线玩家 (点击复制):")
self.player_list.itemconfig(0, fg="#4fc3f7")
self.player_list.insert(tk.END, "") # 空行分隔
self.player_list.insert(tk.END, "服务器离线或不可达")
self.player_list.itemconfig(2, fg=self.error_color)
if __name__ == "__main__":
root = tk.Tk()
app = MinecraftServerChecker(root)
print("hello,world")
# 添加F5刷新功能
root.bind("<F5>", lambda event: app.start_check_thread())
# 添加回车键功能
root.bind("<Return>", lambda event: app.start_check_thread())
root.mainloop()
没有回复内容