package protocol import ( "spark-server/internal/pkg/errs" "google.golang.org/protobuf/encoding/protowire" ) type AuthRequest struct { Token string DeviceID uint64 AppVersion string Language string } type AuthResponse struct { UserID int64 DeviceID uint64 } type ErrorBody struct { Code int32 Message string } func EncodeAuthRequest(a AuthRequest) []byte { out := appendStringField(nil, 1, a.Token) out = appendVarintField(out, 2, a.DeviceID) out = appendStringField(out, 3, a.AppVersion) out = appendStringField(out, 4, a.Language) return out } func DecodeAuthRequest(body []byte) (AuthRequest, error) { var req AuthRequest for len(body) > 0 { num, typ, tagLen := protowire.ConsumeTag(body) if tagLen < 0 { return AuthRequest{}, errs.ErrInvalidFrame } body = body[tagLen:] val, n, err := consumeField(typ, body) if err != nil { return AuthRequest{}, err } body = body[n:] assignAuth(&req, num, typ, val) } return req, nil } func assignAuth(req *AuthRequest, num protowire.Number, typ protowire.Type, val []byte) { switch { case num == 1 && typ == protowire.BytesType: req.Token = string(val) case num == 2 && typ == protowire.VarintType: v, _ := protowire.ConsumeVarint(val) req.DeviceID = v case num == 3 && typ == protowire.BytesType: req.AppVersion = string(val) case num == 4 && typ == protowire.BytesType: req.Language = string(val) } } func EncodeAuthResponse(a AuthResponse) []byte { out := appendVarintField(nil, 1, uint64(a.UserID)) return appendVarintField(out, 2, a.DeviceID) } func EncodeError(e ErrorBody) []byte { out := appendVarintField(nil, 1, uint64(e.Code)) return appendStringField(out, 2, e.Message) } // DecodeError reads a protobuf-wire error payload. func DecodeError(body []byte) (ErrorBody, error) { var out ErrorBody for len(body) > 0 { num, typ, tagLen := protowire.ConsumeTag(body) if tagLen < 0 { return ErrorBody{}, errs.ErrInvalidFrame } body = body[tagLen:] val, n, err := consumeField(typ, body) if err != nil { return ErrorBody{}, err } body = body[n:] if num == 1 && typ == protowire.VarintType { v, _ := protowire.ConsumeVarint(val) out.Code = int32(v) } if num == 2 && typ == protowire.BytesType { out.Message = string(val) } } return out, nil } func EncodeMsgID(id int64) []byte { return appendVarintField(nil, 1, uint64(id)) } func EncodeKick(reason string) []byte { return appendStringField(nil, 1, reason) } func appendStringField(out []byte, field protowire.Number, s string) []byte { if s == "" { return out } out = protowire.AppendVarint(out, protowire.EncodeTag(field, protowire.BytesType)) return protowire.AppendString(out, s) } func appendVarintField(out []byte, field protowire.Number, v uint64) []byte { if v == 0 { return out } out = protowire.AppendVarint(out, protowire.EncodeTag(field, protowire.VarintType)) return protowire.AppendVarint(out, v) } func consumeField(typ protowire.Type, body []byte) ([]byte, int, error) { switch typ { case protowire.VarintType: v, n := protowire.ConsumeVarint(body) if n < 0 { return nil, 0, errs.ErrInvalidFrame } return protowire.AppendVarint(nil, v), n, nil case protowire.BytesType: v, n := protowire.ConsumeBytes(body) if n < 0 { return nil, 0, errs.ErrInvalidFrame } return append([]byte(nil), v...), n, nil default: n := protowire.ConsumeFieldValue(1, typ, body) if n < 0 { return nil, 0, errs.ErrInvalidFrame } return nil, n, nil } }