Files
ngzz-mc/internal/netproto/protocol.go

320 lines
7.3 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Package netproto 网络协议 v1 编解码(网络协议.md §1§3
//
// 帧格式:长度 u32大端| 消息ID u16 | 压缩标志 u8 | payload
// 整数:定长大端 / 变长 varintLEB128字符串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 玩家移动0x0620Hz
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
}