ForcePilot/backend/package/yuxi/channel/extensions/minecraft/status.py
Kris b3b6c95094 feat(channel): 添加 Minecraft 渠道扩展
新增 Minecraft 渠道扩展,支持在 Yuxi 平台中集成 Minecraft 游戏服务器渠道。

包含以下功能模块:
- client: Minecraft 客户端封装
- config: 渠道配置管理
- gateway: SSE/WebSocket 网关接入
- outbound: 外发消息管理
- streaming: 流式消息处理
- pairing: 用户配对与绑定
- security: 安全校验
- auth: 认证管理
- accounts: 账户管理
- dedupe: 消息去重
- monitor: 渠道状态监控
- status: 会话状态管理
- protocol: Minecraft 协议处理
- rcon: RCON 远程控制
- keepalive: 连接保活
- version_adapter: 版本适配
- setup: 初始化设置
- types: 类型定义
2026-05-21 11:26:52 +08:00

90 lines
3.1 KiB
Python

import asyncio
import logging
import struct
import time
from yuxi.channel.extensions.minecraft.client import McClient
from yuxi.channel.extensions.minecraft.protocol import write_varint, write_string, read_string, write_long
logger = logging.getLogger(__name__)
async def probe_mc_server(
host: str,
port: int,
version: str = "auto",
) -> dict:
result = {
"reachable": False,
"latency_ms": None,
"version_name": None,
"protocol_version": None,
"players_online": 0,
"players_max": 0,
}
try:
client = McClient(host, port)
await client.connect()
from yuxi.channel.extensions.minecraft.version_adapter import get_adapter, DEFAULT_PROTOCOL
adapter = get_adapter(version)
protocol_version = adapter.version if adapter else DEFAULT_PROTOCOL
handshake_data = write_varint(protocol_version) + write_string(host) + struct.pack(">H", port) + write_varint(1)
await client.send_raw(write_varint(len(handshake_data)) + write_varint(0x00) + handshake_data)
client.state = "STATUS"
await client.send_raw(write_varint(1) + write_varint(0x00))
packet = await asyncio.wait_for(client.recv_packet(), timeout=5.0)
if packet.packet_id == 0x00:
json_str, _ = read_string(packet.data, 0)
import json
status = json.loads(json_str)
version_info = status.get("version", {})
players_info = status.get("players", {})
result["version_name"] = version_info.get("name")
result["protocol_version"] = version_info.get("protocol")
result["players_online"] = players_info.get("online", 0)
result["players_max"] = players_info.get("max", 0)
ping_start = time.time() * 1000
ping_data = write_long(int(ping_start))
await client.send_packet(0x01, ping_data)
pong = await asyncio.wait_for(client.recv_packet(), timeout=5.0)
if pong.packet_id == 0x01:
result["latency_ms"] = round(time.time() * 1000 - ping_start)
result["reachable"] = True
logger.debug("MC probe success for %s:%d (latency=%s)", host, port, result["latency_ms"])
await client.disconnect()
return result
await client.disconnect()
return result
except Exception as e:
logger.warning("MC probe failed for %s:%d: %s", host, port, e)
return result
def build_mc_status_summary(snapshot: object) -> dict:
if snapshot is None:
return {
"channel": "minecraft",
"account_id": "default",
"connected": False,
}
return {
"channel": "minecraft",
"account_id": getattr(snapshot, "account_id", "default"),
"host": getattr(snapshot, "host", ""),
"username": getattr(snapshot, "username", ""),
"connected": getattr(snapshot, "connected", False),
"version": getattr(snapshot, "version", "auto"),
"auth_mode": getattr(snapshot, "auth_mode", "offline"),
}