新增 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: 类型定义
445 lines
11 KiB
Python
445 lines
11 KiB
Python
from dataclasses import dataclass, field
|
|
|
|
|
|
@dataclass
|
|
class PacketDef:
|
|
packet_id: int
|
|
name: str
|
|
direction: str
|
|
fields: list[dict] = field(default_factory=list)
|
|
|
|
|
|
@dataclass
|
|
class VersionAdapter:
|
|
version: int
|
|
game_version: str
|
|
clientbound: dict[int, PacketDef] = field(default_factory=dict)
|
|
serverbound: dict[int, PacketDef] = field(default_factory=dict)
|
|
|
|
def get_clientbound(self, packet_id: int) -> PacketDef | None:
|
|
return self.clientbound.get(packet_id)
|
|
|
|
def get_serverbound(self, packet_id: int) -> PacketDef | None:
|
|
return self.serverbound.get(packet_id)
|
|
|
|
def cb(self, name: str) -> int | None:
|
|
for pkt in self.clientbound.values():
|
|
if pkt.name == name:
|
|
return pkt.packet_id
|
|
return None
|
|
|
|
def sb(self, name: str) -> int | None:
|
|
for pkt in self.serverbound.values():
|
|
if pkt.name == name:
|
|
return pkt.packet_id
|
|
return None
|
|
|
|
|
|
V769 = VersionAdapter(version=769, game_version="1.21.4")
|
|
|
|
V769.clientbound[0x00] = PacketDef(
|
|
0x00,
|
|
"status_response",
|
|
"clientbound",
|
|
[
|
|
{"name": "json_response", "type": "string"},
|
|
],
|
|
)
|
|
V769.clientbound[0x01] = PacketDef(
|
|
0x01,
|
|
"pong_response",
|
|
"clientbound",
|
|
[
|
|
{"name": "payload", "type": "long"},
|
|
],
|
|
)
|
|
|
|
V769.clientbound[0x02] = PacketDef(
|
|
0x02,
|
|
"login_success",
|
|
"clientbound",
|
|
[
|
|
{"name": "uuid", "type": "uuid"},
|
|
{"name": "username", "type": "string"},
|
|
{"name": "properties_count", "type": "varint"},
|
|
],
|
|
)
|
|
V769.clientbound[0x01] = PacketDef(
|
|
0x01,
|
|
"encryption_request",
|
|
"clientbound",
|
|
[
|
|
{"name": "server_id", "type": "string"},
|
|
{"name": "public_key_length", "type": "varint"},
|
|
{"name": "verify_token_length", "type": "varint"},
|
|
],
|
|
)
|
|
|
|
V769.clientbound[0x26] = PacketDef(
|
|
0x26,
|
|
"keep_alive",
|
|
"clientbound",
|
|
[
|
|
{"name": "keep_alive_id", "type": "long"},
|
|
],
|
|
)
|
|
V769.clientbound[0x37] = PacketDef(
|
|
0x37,
|
|
"player_chat_message",
|
|
"clientbound",
|
|
[
|
|
{"name": "sender_uuid", "type": "uuid"},
|
|
{"name": "index", "type": "varint"},
|
|
{"name": "message_signature_present", "type": "bool"},
|
|
],
|
|
)
|
|
V769.clientbound[0x6B] = PacketDef(
|
|
0x6B,
|
|
"system_chat_message",
|
|
"clientbound",
|
|
[
|
|
{"name": "content", "type": "chat"},
|
|
{"name": "overlay", "type": "bool"},
|
|
],
|
|
)
|
|
V769.clientbound[0x1D] = PacketDef(
|
|
0x1D,
|
|
"disconnect",
|
|
"clientbound",
|
|
[
|
|
{"name": "reason", "type": "chat"},
|
|
],
|
|
)
|
|
V769.clientbound[0x2B] = PacketDef(
|
|
0x2B,
|
|
"login_play",
|
|
"clientbound",
|
|
[
|
|
{"name": "entity_id", "type": "int"},
|
|
{"name": "hardcore", "type": "bool"},
|
|
{"name": "dimension_count", "type": "varint"},
|
|
{"name": "max_players", "type": "varint"},
|
|
{"name": "view_distance", "type": "varint"},
|
|
{"name": "simulation_distance", "type": "varint"},
|
|
{"name": "reduced_debug_info", "type": "bool"},
|
|
{"name": "enable_respawn_screen", "type": "bool"},
|
|
{"name": "do_limited_crafting", "type": "bool"},
|
|
{"name": "dimension_type", "type": "string"},
|
|
{"name": "dimension_name", "type": "string"},
|
|
{"name": "hashed_seed", "type": "long"},
|
|
{"name": "game_mode", "type": "byte"},
|
|
{"name": "previous_game_mode", "type": "byte"},
|
|
{"name": "is_debug", "type": "bool"},
|
|
{"name": "is_flat", "type": "bool"},
|
|
{"name": "death_location", "type": "optional_string"},
|
|
{"name": "portal_cooldown", "type": "varint"},
|
|
],
|
|
)
|
|
V769.clientbound[0x3E] = PacketDef(
|
|
0x3E,
|
|
"player_info_update",
|
|
"clientbound",
|
|
[
|
|
{"name": "actions", "type": "byte"},
|
|
{"name": "number_of_players", "type": "varint"},
|
|
],
|
|
)
|
|
V769.clientbound[0x45] = PacketDef(0x45, "respawn", "clientbound", [])
|
|
|
|
V769.serverbound[0x18] = PacketDef(
|
|
0x18,
|
|
"keep_alive",
|
|
"serverbound",
|
|
[
|
|
{"name": "keep_alive_id", "type": "long"},
|
|
],
|
|
)
|
|
V769.serverbound[0x05] = PacketDef(
|
|
0x05,
|
|
"chat_message",
|
|
"serverbound",
|
|
[
|
|
{"name": "message", "type": "string"},
|
|
],
|
|
)
|
|
V769.serverbound[0x04] = PacketDef(
|
|
0x04,
|
|
"chat_command",
|
|
"serverbound",
|
|
[
|
|
{"name": "command", "type": "string"},
|
|
],
|
|
)
|
|
V769.serverbound[0x1A] = PacketDef(
|
|
0x1A,
|
|
"set_player_position",
|
|
"serverbound",
|
|
[
|
|
{"name": "x", "type": "double"},
|
|
{"name": "feet_y", "type": "double"},
|
|
{"name": "z", "type": "double"},
|
|
{"name": "on_ground", "type": "bool"},
|
|
],
|
|
)
|
|
V769.serverbound[0x1B] = PacketDef(
|
|
0x1B,
|
|
"set_player_position_rotation",
|
|
"serverbound",
|
|
[
|
|
{"name": "x", "type": "double"},
|
|
{"name": "feet_y", "type": "double"},
|
|
{"name": "z", "type": "double"},
|
|
{"name": "yaw", "type": "float"},
|
|
{"name": "pitch", "type": "float"},
|
|
{"name": "on_ground", "type": "bool"},
|
|
],
|
|
)
|
|
|
|
V763 = VersionAdapter(version=763, game_version="1.20.4")
|
|
|
|
V763.clientbound[0x00] = PacketDef(
|
|
0x00,
|
|
"status_response",
|
|
"clientbound",
|
|
[
|
|
{"name": "json_response", "type": "string"},
|
|
],
|
|
)
|
|
V763.clientbound[0x01] = PacketDef(
|
|
0x01,
|
|
"pong_response",
|
|
"clientbound",
|
|
[
|
|
{"name": "payload", "type": "long"},
|
|
],
|
|
)
|
|
V763.clientbound[0x26] = PacketDef(
|
|
0x26,
|
|
"keep_alive",
|
|
"clientbound",
|
|
[
|
|
{"name": "keep_alive_id", "type": "long"},
|
|
],
|
|
)
|
|
V763.clientbound[0x38] = PacketDef(0x38, "player_chat_message", "clientbound", [])
|
|
V763.clientbound[0x64] = PacketDef(
|
|
0x64,
|
|
"system_chat_message",
|
|
"clientbound",
|
|
[
|
|
{"name": "content", "type": "chat"},
|
|
{"name": "overlay", "type": "bool"},
|
|
],
|
|
)
|
|
V763.clientbound[0x1A] = PacketDef(
|
|
0x1A,
|
|
"disconnect",
|
|
"clientbound",
|
|
[
|
|
{"name": "reason", "type": "chat"},
|
|
],
|
|
)
|
|
V763.serverbound[0x12] = PacketDef(
|
|
0x12,
|
|
"keep_alive",
|
|
"serverbound",
|
|
[
|
|
{"name": "keep_alive_id", "type": "long"},
|
|
],
|
|
)
|
|
V763.serverbound[0x05] = PacketDef(
|
|
0x05,
|
|
"chat_message",
|
|
"serverbound",
|
|
[
|
|
{"name": "message", "type": "string"},
|
|
],
|
|
)
|
|
V763.serverbound[0x04] = PacketDef(
|
|
0x04,
|
|
"chat_command",
|
|
"serverbound",
|
|
[
|
|
{"name": "command", "type": "string"},
|
|
],
|
|
)
|
|
|
|
V767 = VersionAdapter(version=767, game_version="1.21-1.21.1")
|
|
V767.clientbound = dict(V769.clientbound)
|
|
V767.serverbound = dict(V769.serverbound)
|
|
|
|
V768 = VersionAdapter(version=768, game_version="1.21.2-1.21.3")
|
|
V768.clientbound = dict(V769.clientbound)
|
|
V768.serverbound = dict(V769.serverbound)
|
|
|
|
V770 = VersionAdapter(version=770, game_version="1.21.5")
|
|
V770.clientbound = {
|
|
0x00: PacketDef(
|
|
0x00,
|
|
"status_response",
|
|
"clientbound",
|
|
[
|
|
{"name": "json_response", "type": "string"},
|
|
],
|
|
),
|
|
0x01: PacketDef(
|
|
0x01,
|
|
"pong_response",
|
|
"clientbound",
|
|
[
|
|
{"name": "payload", "type": "long"},
|
|
],
|
|
),
|
|
0x02: PacketDef(
|
|
0x02,
|
|
"login_success",
|
|
"clientbound",
|
|
[
|
|
{"name": "uuid", "type": "uuid"},
|
|
{"name": "username", "type": "string"},
|
|
{"name": "properties_count", "type": "varint"},
|
|
],
|
|
),
|
|
0x26: PacketDef(
|
|
0x26,
|
|
"keep_alive",
|
|
"clientbound",
|
|
[
|
|
{"name": "keep_alive_id", "type": "long"},
|
|
],
|
|
),
|
|
0x3C: PacketDef(
|
|
0x3C,
|
|
"player_chat_message",
|
|
"clientbound",
|
|
[
|
|
{"name": "sender_uuid", "type": "uuid"},
|
|
{"name": "index", "type": "varint"},
|
|
{"name": "message_signature_present", "type": "bool"},
|
|
],
|
|
),
|
|
0x6C: PacketDef(
|
|
0x6C,
|
|
"system_chat_message",
|
|
"clientbound",
|
|
[
|
|
{"name": "content", "type": "chat"},
|
|
{"name": "overlay", "type": "bool"},
|
|
],
|
|
),
|
|
0x1C: PacketDef(
|
|
0x1C,
|
|
"disconnect",
|
|
"clientbound",
|
|
[
|
|
{"name": "reason", "type": "chat"},
|
|
],
|
|
),
|
|
0x2B: PacketDef(
|
|
0x2B,
|
|
"login_play",
|
|
"clientbound",
|
|
[
|
|
{"name": "entity_id", "type": "int"},
|
|
{"name": "hardcore", "type": "bool"},
|
|
{"name": "dimension_count", "type": "varint"},
|
|
{"name": "max_players", "type": "varint"},
|
|
{"name": "view_distance", "type": "varint"},
|
|
{"name": "simulation_distance", "type": "varint"},
|
|
{"name": "reduced_debug_info", "type": "bool"},
|
|
{"name": "enable_respawn_screen", "type": "bool"},
|
|
{"name": "do_limited_crafting", "type": "bool"},
|
|
{"name": "dimension_type", "type": "string"},
|
|
{"name": "dimension_name", "type": "string"},
|
|
{"name": "hashed_seed", "type": "long"},
|
|
{"name": "game_mode", "type": "byte"},
|
|
{"name": "previous_game_mode", "type": "byte"},
|
|
{"name": "is_debug", "type": "bool"},
|
|
{"name": "is_flat", "type": "bool"},
|
|
{"name": "death_location", "type": "optional_string"},
|
|
{"name": "portal_cooldown", "type": "varint"},
|
|
],
|
|
),
|
|
0x3E: PacketDef(
|
|
0x3E,
|
|
"player_info_update",
|
|
"clientbound",
|
|
[
|
|
{"name": "actions", "type": "byte"},
|
|
{"name": "number_of_players", "type": "varint"},
|
|
],
|
|
),
|
|
0x45: PacketDef(0x45, "respawn", "clientbound", []),
|
|
}
|
|
V770.serverbound = {
|
|
0x18: PacketDef(
|
|
0x18,
|
|
"keep_alive",
|
|
"serverbound",
|
|
[
|
|
{"name": "keep_alive_id", "type": "long"},
|
|
],
|
|
),
|
|
0x06: PacketDef(
|
|
0x06,
|
|
"chat_message",
|
|
"serverbound",
|
|
[
|
|
{"name": "message", "type": "string"},
|
|
],
|
|
),
|
|
0x05: PacketDef(
|
|
0x05,
|
|
"chat_command",
|
|
"serverbound",
|
|
[
|
|
{"name": "command", "type": "string"},
|
|
],
|
|
),
|
|
0x1A: PacketDef(
|
|
0x1A,
|
|
"set_player_position",
|
|
"serverbound",
|
|
[
|
|
{"name": "x", "type": "double"},
|
|
{"name": "feet_y", "type": "double"},
|
|
{"name": "z", "type": "double"},
|
|
{"name": "on_ground", "type": "bool"},
|
|
],
|
|
),
|
|
0x1B: PacketDef(
|
|
0x1B,
|
|
"set_player_position_rotation",
|
|
"serverbound",
|
|
[
|
|
{"name": "x", "type": "double"},
|
|
{"name": "feet_y", "type": "double"},
|
|
{"name": "z", "type": "double"},
|
|
{"name": "yaw", "type": "float"},
|
|
{"name": "pitch", "type": "float"},
|
|
{"name": "on_ground", "type": "bool"},
|
|
],
|
|
),
|
|
}
|
|
|
|
PROTOCOL_REGISTRY: dict[int, VersionAdapter] = {
|
|
763: V763,
|
|
767: V767,
|
|
768: V768,
|
|
769: V769,
|
|
770: V770,
|
|
}
|
|
|
|
DEFAULT_PROTOCOL = 769
|
|
|
|
|
|
def get_adapter(version: int | str) -> VersionAdapter:
|
|
if isinstance(version, str) and version == "auto":
|
|
return V769
|
|
if isinstance(version, str):
|
|
try:
|
|
version = int(version)
|
|
except ValueError:
|
|
return V769
|
|
return PROTOCOL_REGISTRY.get(version, V769)
|