新增 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: 类型定义
74 lines
2.5 KiB
Python
74 lines
2.5 KiB
Python
import asyncio
|
|
import logging
|
|
import random
|
|
|
|
from yuxi.channel.extensions.minecraft.protocol import write_bool, write_double
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
PLAYER_POSITION_INTERVAL = 60.0
|
|
KEEP_ALIVE_TIMEOUT = 30.0
|
|
|
|
|
|
class KeepAliveManager:
|
|
def __init__(self, client):
|
|
self.client = client
|
|
self._position_task: asyncio.Task | None = None
|
|
self._timeout_task: asyncio.Task | None = None
|
|
self._last_keep_alive = 0.0
|
|
self._running = False
|
|
|
|
async def start(self) -> None:
|
|
self._running = True
|
|
self._last_keep_alive = asyncio.get_event_loop().time()
|
|
self._position_task = asyncio.create_task(self._position_loop())
|
|
self._timeout_task = asyncio.create_task(self._timeout_monitor())
|
|
logger.info("KeepAliveManager started")
|
|
|
|
async def stop(self) -> None:
|
|
self._running = False
|
|
if self._position_task:
|
|
self._position_task.cancel()
|
|
self._position_task = None
|
|
if self._timeout_task:
|
|
self._timeout_task.cancel()
|
|
self._timeout_task = None
|
|
logger.info("KeepAliveManager stopped")
|
|
|
|
async def _position_loop(self) -> None:
|
|
x, y, z = 0.0, 64.0, 0.0
|
|
|
|
while self._running:
|
|
try:
|
|
await asyncio.sleep(PLAYER_POSITION_INTERVAL + random.uniform(-5, 5))
|
|
|
|
data = write_double(x) + write_double(y) + write_double(z) + write_bool(True)
|
|
await self.client.send_packet(0x1A, data)
|
|
logger.debug("Player position sent: (%.1f, %.1f, %.1f)", x, y, z)
|
|
|
|
except asyncio.CancelledError:
|
|
break
|
|
except Exception:
|
|
logger.exception("Error sending player position")
|
|
|
|
def record_keep_alive(self) -> None:
|
|
self._last_keep_alive = asyncio.get_event_loop().time()
|
|
|
|
@property
|
|
def is_alive(self) -> bool:
|
|
elapsed = asyncio.get_event_loop().time() - self._last_keep_alive
|
|
return elapsed < KEEP_ALIVE_TIMEOUT
|
|
|
|
async def _timeout_monitor(self) -> None:
|
|
while self._running:
|
|
try:
|
|
await asyncio.sleep(KEEP_ALIVE_TIMEOUT)
|
|
if not self.is_alive:
|
|
logger.warning("Keep-Alive timeout detected, connection may be dead")
|
|
if self.client and self.client.writer:
|
|
self.client.writer.close()
|
|
except asyncio.CancelledError:
|
|
break
|
|
except Exception:
|
|
logger.exception("Error in keep-alive timeout monitor")
|