// Package netproto 网络协议 v1 编解码(网络协议.md §1–§3)。 // // 帧格式:长度 u32(大端)| 消息ID u16 | 压缩标志 u8 | payload; // 整数:定长大端 / 变长 varint(LEB128);字符串:varint 长度 + UTF-8。 package netproto import ( "encoding/binary" "errors" "fmt" "math" ) // 消息 ID(网络协议.md §3 消息目录)。 const ( MsgHandshake = 0x01 // C→S 握手 MsgLoginRequest = 0x02 // C→S 登录 MsgLoginResponse = 0x03 // S→C 登录结果 MsgChunkData = 0x04 // S→C 区块数据 MsgBlockChange = 0x05 // S→C 方块变更 MsgPlayerMove = 0x06 // C→S 玩家移动 MsgEntityMove = 0x07 // S→C 实体移动 MsgChatMessage = 0x09 // 双向 聊天 MsgInventorySync = 0x0A // 双向 背包 MsgHeartbeat = 0x0E // 双向 心跳 MsgDisconnect = 0x0F // 双向 断开 ) // 压缩标志(网络协议.md §1)。 const ( flagCompressed = 1 // payload 为 zstd 压缩块 ) // Frame 通用帧。 type Frame struct { MsgID uint16 Flags uint8 Payload []byte } // MaxFrameSize 单帧上限 1 MiB(防御性,网络协议.md §1)。 const MaxFrameSize = 1 << 20 // ErrIncomplete 半包错误:数据不足以组成完整帧(调用方应等待更多数据)。 var ErrIncomplete = errors.New("netproto: 帧数据不完整") // EncodeFrame 序列化一帧为字节流。 // 帧头 7 字节:长度 u32(= 消息ID 2 + 标志 1 + 载荷,不含长度字段本身)+ 消息ID u16 + 压缩标志 u8。 func EncodeFrame(f Frame) []byte { out := make([]byte, 7+len(f.Payload)) binary.BigEndian.PutUint32(out[0:4], uint32(3+len(f.Payload))) binary.BigEndian.PutUint16(out[4:6], f.MsgID) out[6] = f.Flags copy(out[7:], f.Payload) return out } // DecodeFrame 从字节流解析一帧,返回帧与剩余字节。 // 数据不足时返回 ErrIncomplete(可用 errors.Is 判定半包,等待更多数据)。 func DecodeFrame(data []byte) (Frame, []byte, error) { if len(data) < 4 { return Frame{}, nil, fmt.Errorf("%w(帧头不足 %d 字节)", ErrIncomplete, len(data)) } length := binary.BigEndian.Uint32(data[0:4]) if length > MaxFrameSize { return Frame{}, nil, fmt.Errorf("netproto: 帧长度 %d 超上限", length) } if int(length)+4 > len(data) { return Frame{}, nil, fmt.Errorf("%w(需 %d 字节,剩 %d)", ErrIncomplete, length+4, len(data)) } f := Frame{ MsgID: binary.BigEndian.Uint16(data[4:6]), Flags: data[6], Payload: data[7 : 4+int(length)], } return f, data[4+int(length):], nil } // ---- 基础读写器(网络协议.md §2 序列化规则)---- // Writer 字节写入器。 type Writer struct{ B []byte } // U8 写无符号 8 位。 func (w *Writer) U8(v uint8) { w.B = append(w.B, v) } // U16 写无符号 16 位(大端)。 func (w *Writer) U16(v uint16) { w.B = binary.BigEndian.AppendUint16(w.B, v) } // U32 写无符号 32 位(大端)。 func (w *Writer) U32(v uint32) { w.B = binary.BigEndian.AppendUint32(w.B, v) } // I32 写有符号 32 位(大端)。 func (w *Writer) I32(v int32) { w.U32(uint32(v)) } // F32 写 float32(位模式)。 func (w *Writer) F32(v float32) { w.U32(math.Float32bits(v)) } // Varint 写变长整数(LEB128,无符号)。 func (w *Writer) Varint(v uint32) { for v >= 0x80 { w.B = append(w.B, byte(v)|0x80) v >>= 7 } w.B = append(w.B, byte(v)) } // Str 写字符串(varint 长度 + UTF-8)。 func (w *Writer) Str(s string) { w.Varint(uint32(len(s))) w.B = append(w.B, s...) } // Bytes 写字节串(varint 长度 + 数据)。 func (w *Writer) Bytes(b []byte) { w.Varint(uint32(len(b))) w.B = append(w.B, b...) } // Reader 字节读取器。 type Reader struct { B []byte Off int Err error } // NewReader 创建读取器。 func NewReader(b []byte) *Reader { return &Reader{B: b} } // U8 读无符号 8 位。 func (r *Reader) U8() uint8 { if r.Err != nil { return 0 } if r.Off+1 > len(r.B) { r.Err = fmt.Errorf("netproto: 读取越界") return 0 } v := r.B[r.Off] r.Off++ return v } // U16 读无符号 16 位(大端)。 func (r *Reader) U16() uint16 { if r.Err != nil { return 0 } if r.Off+2 > len(r.B) { r.Err = fmt.Errorf("netproto: 读取越界") return 0 } v := binary.BigEndian.Uint16(r.B[r.Off:]) r.Off += 2 return v } // U32 读无符号 32 位(大端)。 func (r *Reader) U32() uint32 { if r.Err != nil { return 0 } if r.Off+4 > len(r.B) { r.Err = fmt.Errorf("netproto: 读取越界") return 0 } v := binary.BigEndian.Uint32(r.B[r.Off:]) r.Off += 4 return v } // I32 读有符号 32 位。 func (r *Reader) I32() int32 { return int32(r.U32()) } // F32 读 float32。 func (r *Reader) F32() float32 { return math.Float32frombits(r.U32()) } // Varint 读变长整数(LEB128)。 func (r *Reader) Varint() uint32 { if r.Err != nil { return 0 } var v uint32 for shift := 0; shift < 35; shift += 7 { if r.Off >= len(r.B) { r.Err = fmt.Errorf("netproto: varint 越界") return 0 } b := r.B[r.Off] r.Off++ v |= uint32(b&0x7F) << shift if b&0x80 == 0 { return v } } r.Err = fmt.Errorf("netproto: varint 超长") return 0 } // Str 读字符串(varint 长度 + UTF-8)。 func (r *Reader) Str() string { n := r.Varint() if r.Err != nil { return "" } if n > MaxFrameSize || r.Off+int(n) > len(r.B) { r.Err = fmt.Errorf("netproto: 字符串越界(长度 %d)", n) return "" } s := string(r.B[r.Off : r.Off+int(n)]) r.Off += int(n) return s } // Bytes 读字节串。 func (r *Reader) Bytes() []byte { n := r.Varint() if r.Err != nil { return nil } if n > MaxFrameSize || r.Off+int(n) > len(r.B) { r.Err = fmt.Errorf("netproto: 字节串越界(长度 %d)", n) return nil } b := r.B[r.Off : r.Off+int(n)] r.Off += int(n) return b } // ---- 常用消息载荷(网络协议.md §3)---- // BlockChange 方块变更(0x05)。 type BlockChange struct { X, Y, Z int32 BlockID uint16 Meta uint8 } // Encode 编码方块变更。 func (m BlockChange) Encode() []byte { w := &Writer{} w.I32(m.X) w.I32(m.Y) w.I32(m.Z) w.U16(m.BlockID) w.U8(m.Meta) return w.B } // DecodeBlockChange 解码方块变更。 func DecodeBlockChange(b []byte) (BlockChange, error) { r := NewReader(b) m := BlockChange{ X: r.I32(), Y: r.I32(), Z: r.I32(), BlockID: r.U16(), Meta: r.U8(), } return m, r.Err } // PlayerMove 玩家移动(0x06,20Hz)。 type PlayerMove struct { X, Y, Z float32 Yaw, Pitch float32 OnGround bool } // Encode 编码玩家移动。 func (m PlayerMove) Encode() []byte { w := &Writer{} w.F32(m.X) w.F32(m.Y) w.F32(m.Z) w.F32(m.Yaw) w.F32(m.Pitch) if m.OnGround { w.U8(1) } else { w.U8(0) } return w.B } // DecodePlayerMove 解码玩家移动。 func DecodePlayerMove(b []byte) (PlayerMove, error) { r := NewReader(b) m := PlayerMove{ X: r.F32(), Y: r.F32(), Z: r.F32(), Yaw: r.F32(), Pitch: r.F32(), } m.OnGround = r.U8() != 0 return m, r.Err } // Handshake 握手(0x01)。 type Handshake struct { ProtocolVersion int ClientID string } // Encode 编码握手。 func (m Handshake) Encode() []byte { w := &Writer{} w.Varint(uint32(m.ProtocolVersion)) w.Str(m.ClientID) return w.B } // DecodeHandshake 解码握手。 func DecodeHandshake(b []byte) (Handshake, error) { r := NewReader(b) m := Handshake{ProtocolVersion: int(r.Varint()), ClientID: r.Str()} return m, r.Err }