新增 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: 类型定义
90 lines
3.1 KiB
Python
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"),
|
|
}
|