You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Python Socket实现离线Minecraft服务器的Bot连接及数据包交互?

问题解答:Minecraft离线服务器Bot连接实现

核心结论:离线模式无需加密流程

连接离线Minecraft服务器时,完全不需要处理加密密钥请求(0x01包)和加密响应,服务器会跳过所有加密、认证步骤,直接返回登录成功数据包(0x02)。你的理解是对的,这一步不需要额外的加密交互。

现有数据包代码检查

你写的握手和登录开始数据包逻辑方向正确,但有一个关键细节缺失:Minecraft的数据包要求整个数据包的长度要先用VarInt编码后放在最前面,你当前的代码只编码了包ID(0x00),没有加上整个包的长度前缀,这会导致服务器无法正确解析数据包。

修正后的握手包发送逻辑:

def send_handshake(sock_con, host, port, version):
    # 构造包内容(不含长度前缀)
    packet_content = struct.pack(">B", 0x00) + varint.encode(version) + \
                     varint.encode(len(host)) + host.encode('utf-8') + \
                     struct.pack(">H", port) + struct.pack(">B", 0x02)
    # 计算长度并编码为VarInt,作为数据包前缀
    packet_length = varint.encode(len(packet_content))
    # 发送完整数据包(长度前缀 + 包内容)
    full_packet = packet_length + packet_content
    print("Handshake Packet Bytes ", full_packet)
    sock_con.send(full_packet)

登录开始包同理,需要添加长度前缀:

def send_login_start(sock_con, name):
    username_bytes = name.encode('utf-8')
    packet_content = struct.pack(">B", 0x00) + varint.encode(len(username_bytes)) + username_bytes
    packet_length = varint.encode(len(packet_content))
    full_packet = packet_length + packet_content
    print("Login Packet Bytes ", full_packet)
    sock_con.send(full_packet)

接收并解析登录成功数据包

发送完握手和登录开始包后,需要从socket读取服务器返回的数据包,流程如下:

  1. 先读取VarInt格式的数据包总长度
  2. 根据长度读取完整的数据包内容
  3. 从内容中读取VarInt格式的包ID:登录成功包的ID是0x02
  4. 解析包内容:包含UUID(离线模式下为基于用户名生成的伪UUID)和用户名

示例接收代码:

def read_varint(sock):
    # 实现Minecraft VarInt读取逻辑
    result = 0
    for i in range(5):
        byte = ord(sock.recv(1))
        result |= (byte & 0x7F) << (7 * i)
        if not byte & 0x80:
            break
    return result

def read_varint_from_bytes(data):
    # 从字节数组读取VarInt的辅助函数
    result = 0
    ptr = 0
    for i in range(5):
        byte = data[ptr]
        ptr += 1
        result |= (byte & 0x7F) << (7 * i)
        if not byte & 0x80:
            break
    return result

def receive_login_success(sock_con):
    # 读取数据包长度
    packet_length = read_varint(sock_con)
    packet_content = sock_con.recv(packet_length)
    
    # 读取包ID
    ptr = 0
    packet_id = 0
    while True:
        byte = packet_content[ptr]
        ptr += 1
        packet_id |= (byte & 0x7F) << (7 * (ptr - 1))
        if not byte & 0x80:
            break
    
    # 处理登录成功包
    if packet_id == 0x02:
        # 解析UUID
        uuid_length = read_varint_from_bytes(packet_content[ptr:])
        ptr += len(varint.encode(uuid_length))
        uuid = packet_content[ptr:ptr+uuid_length].decode('utf-8')
        ptr += uuid_length
        
        # 解析用户名
        username_length = read_varint_from_bytes(packet_content[ptr:])
        ptr += len(varint.encode(username_length))
        username = packet_content[ptr:ptr+username_length].decode('utf-8')
        
        print(f"登录成功:UUID={uuid}, 用户名={username}")
        return True
    else:
        # 收到加密请求包,说明服务器是在线模式
        print("服务器要求加密,无法用离线账号连接")
        return False

后续保活与连接维持

登录成功进入Play状态后,服务器会定期发送保活数据包(0x00包),你需要在收到后原样返回相同的包内容,否则服务器会主动断开连接。

整体流程总结

  1. 创建TCP Socket连接到服务器的host:port
  2. 发送带长度前缀的握手数据包
  3. 发送带长度前缀的登录开始数据包
  4. 读取服务器返回的数据包,确认登录成功
  5. 进入Play状态,循环处理保活包及其他游戏数据包

内容的提问来源于stack exchange,提问作者TheGoldenMine

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 14:13:10