Phase8
Some checks failed
CI / Node — lint, typecheck, test, build (push) Failing after 9s
CI / Go — node-agent tests (push) Failing after 9s
CI / Go — edge-gateway build (push) Successful in 17s

This commit is contained in:
smueller
2026-06-26 13:33:03 +02:00
parent ab21f53cdd
commit b335f6a497
39 changed files with 1477 additions and 20 deletions

View File

@@ -0,0 +1,174 @@
package mc
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"strings"
)
type Handshake struct {
ProtocolVersion int32
ServerAddress string
ServerPort uint16
NextState int32
Raw []byte
}
func ReadHandshake(r io.Reader) (*Handshake, error) {
raw, err := readPacket(r)
if err != nil {
return nil, err
}
reader := newReader(raw)
packetID, err := reader.readVarInt()
if err != nil {
return nil, err
}
if packetID != 0 {
return nil, fmt.Errorf("unexpected packet id %d", packetID)
}
protocolVersion, err := reader.readVarInt()
if err != nil {
return nil, err
}
serverAddress, err := reader.readString()
if err != nil {
return nil, err
}
portBytes, err := reader.readBytes(2)
if err != nil {
return nil, err
}
serverPort := binary.BigEndian.Uint16(portBytes)
nextState, err := reader.readVarInt()
if err != nil {
return nil, err
}
return &Handshake{
ProtocolVersion: protocolVersion,
ServerAddress: serverAddress,
ServerPort: serverPort,
NextState: nextState,
Raw: raw,
}, nil
}
func ExtractSlug(hostname, playDomain string) string {
host := strings.ToLower(strings.Split(hostname, "\x00")[0])
domain := strings.ToLower(playDomain)
suffix := "." + domain
if !strings.HasSuffix(host, suffix) {
return ""
}
slug := strings.TrimSuffix(host, suffix)
if slug == "" || strings.Contains(slug, ".") {
return ""
}
return slug
}
func readPacket(r io.Reader) ([]byte, error) {
length, err := readVarIntFrom(r)
if err != nil {
return nil, err
}
if length <= 0 || length > 1<<20 {
return nil, fmt.Errorf("invalid packet length %d", length)
}
payload := make([]byte, length)
if _, err := io.ReadFull(r, payload); err != nil {
return nil, err
}
return payload, nil
}
type packetReader struct {
buf *bytes.Reader
}
func newReader(data []byte) *packetReader {
return &packetReader{buf: bytes.NewReader(data)}
}
func (p *packetReader) readVarInt() (int32, error) {
return readVarIntFrom(p.buf)
}
func (p *packetReader) readString() (string, error) {
length, err := p.readVarInt()
if err != nil {
return "", err
}
if length < 0 || length > 255 {
return "", fmt.Errorf("invalid string length %d", length)
}
data, err := p.readBytes(int(length))
if err != nil {
return "", err
}
return string(data), nil
}
func (p *packetReader) readBytes(n int) ([]byte, error) {
out := make([]byte, n)
if _, err := io.ReadFull(p.buf, out); err != nil {
return nil, err
}
return out, nil
}
func readVarIntFrom(r io.Reader) (int32, error) {
var numRead int
var result int32
for {
var value [1]byte
if _, err := io.ReadFull(r, value[:]); err != nil {
return 0, err
}
result |= int32(value[0]&0x7F) << (7 * numRead)
numRead++
if numRead > 5 {
return 0, fmt.Errorf("varint too big")
}
if value[0]&0x80 == 0 {
break
}
}
return result, nil
}
func WritePacket(w io.Writer, payload []byte) error {
if err := writeVarIntTo(w, int32(len(payload))); err != nil {
return err
}
_, err := w.Write(payload)
return err
}
func writeVarIntTo(w io.Writer, value int32) error {
for {
temp := byte(value & 0x7F)
value >>= 7
if value != 0 {
temp |= 0x80
}
if _, err := w.Write([]byte{temp}); err != nil {
return err
}
if value == 0 {
break
}
}
return nil
}