package wire import ( "bytes" "encoding/binary" "errors" ) // Writer builds redapricot/Minecraft primitive types into a byte slice. type Writer struct { buf []byte } func NewWriter() *Writer { return &Writer{} } func (w *Writer) U8(v byte) *Writer { w.buf = append(w.buf, v); return w } func (w *Writer) VarInt(v int) *Writer { w.buf = AppendVarInt(w.buf, v); return w } func (w *Writer) U16(v uint16) *Writer { w.buf = append(w.buf, byte(v>>8), byte(v)); return w } func (w *Writer) Bytes(p []byte) *Writer { w.buf = append(w.buf, p...); return w } func (w *Writer) I64(v int64) *Writer { var b [8]byte binary.BigEndian.PutUint64(b[:], uint64(v)) w.buf = append(w.buf, b[:]...) return w } func (w *Writer) String(s string) *Writer { w.VarInt(len(s)) w.buf = append(w.buf, s...) return w } // Out returns the built bytes. func (w *Writer) Out() []byte { return w.buf } // Reader consumes redapricot/Minecraft primitive types from a byte slice. type Reader struct { buf []byte pos int } func NewReader(b []byte) *Reader { return &Reader{buf: b} } var errUnderflow = errors.New("wire: read underflow") func (r *Reader) U8() (byte, error) { if r.pos >= len(r.buf) { return 0, errUnderflow } v := r.buf[r.pos] r.pos++ return v, nil } func (r *Reader) VarInt() (int, error) { br := bytes.NewReader(r.buf[r.pos:]) before := br.Len() v, err := ReadVarInt(br) if err != nil { return 0, err } r.pos += before - br.Len() return v, nil } func (r *Reader) U16() (uint16, error) { if r.pos+2 > len(r.buf) { return 0, errUnderflow } v := binary.BigEndian.Uint16(r.buf[r.pos:]) r.pos += 2 return v, nil } func (r *Reader) I64() (int64, error) { if r.pos+8 > len(r.buf) { return 0, errUnderflow } v := int64(binary.BigEndian.Uint64(r.buf[r.pos:])) r.pos += 8 return v, nil } func (r *Reader) Bytes(n int) ([]byte, error) { if n < 0 || r.pos+n > len(r.buf) { return nil, errUnderflow } out := make([]byte, n) copy(out, r.buf[r.pos:r.pos+n]) r.pos += n return out, nil } func (r *Reader) String() (string, error) { n, err := r.VarInt() if err != nil { return "", err } b, err := r.Bytes(n) if err != nil { return "", err } return string(b), nil } // Remaining returns the unread bytes (a copy is not made). func (r *Reader) Remaining() []byte { return r.buf[r.pos:] } // BuildHandshake produces a full uncompressed Minecraft Handshake packet // (length-prefixed, packet id 0x00). func BuildHandshake(protocolVersion int, address string, port uint16, intent int) []byte { body := NewWriter(). VarInt(0x00). // packet id VarInt(protocolVersion). String(address). U16(port). VarInt(intent). Out() out := AppendVarInt(nil, len(body)) return append(out, body...) }