package classic import ( "io" "fmt" "bytes" "bufio" "encoding/binary" ) type FByte int8 type FShort int16 type String [64]byte type LevelData [1024]byte type Packet interface { PacketId() byte } const ( NonOpUser = 0x00 OpUser = 0x64 ) const ( NoCpe = 0x00 UseCpe = 0x42 ) type PlayerId struct { Version byte Username String VerificationKey String Ext byte } func (p *PlayerId) PacketId() byte { return 0x00 } type ServerId struct { Version byte ServerName String Motd String UserType byte } func (p *ServerId) PacketId() byte { return 0x00 } type Ping struct {} func (p *Ping) PacketId() byte { return 0x01 } type LevelInit struct {} func (p *LevelInit) PacketId() byte { return 0x02 } type LevelDataChunk struct { Length int16 Data LevelData PercentComplete byte } func (p *LevelDataChunk) PacketId() byte { return 0x03 } type LevelFinalize struct { Width int16 Height int16 Length int16 } func (p *LevelFinalize) PacketId() byte { return 0x04 } const ( BlockDestroyed = iota BlockCreated ) type ClientSetBlock struct { X, Y, Z int16 Mode byte Block byte } func (p *ClientSetBlock) PacketId() byte { return 0x05 } type SetBlock struct { X, Y, Z int16 Block byte } func (p *SetBlock) PacketId() byte { return 0x06 } type SpawnPlayer struct { PlayerId int8 Username String X, Y, Z FShort Yaw, Pitch byte } func (p *SpawnPlayer) PacketId() byte { return 0x07 } type SetPosFacing struct { PlayerId int8 X, Y, Z FShort Yaw, Pitch byte } func (p *SetPosFacing) PacketId() byte { return 0x08 } type UpdatePosFacing struct { UpdatePos UpdateFacing } func (p *UpdatePosFacing) PacketId() byte { return 0x09 } type UpdatePos struct { DeltaX, DeltaY, DeltaZ FByte } func (p *UpdatePos) PacketId() byte { return 0x0a } type UpdateFacing struct { Yaw, Pitch byte } func (p *UpdateFacing) PacketId() byte { return 0x0b } type DespawnPlayer struct { PlayerId int8 } func (p *DespawnPlayer) PacketId() byte { return 0x0c } type Message struct { PlayerId int8 Message String } func (p *Message) PacketId() byte { return 0x0d } type DisconnectPlayer struct { Reason String } func (p *DisconnectPlayer) PacketId() byte { return 0x0e } type UpdateUserType struct { Type byte } func (p *UpdateUserType) PacketId() byte { return 0x0f } func createPacketType(packetId byte, client bool, ext map[string]bool) Packet { switch packetId { case 0x00: if client { return &ServerId {} } else { return &PlayerId {} } case 0x01: return &Ping {} case 0x02: return &LevelInit {} case 0x03: return &LevelDataChunk {} case 0x04: return &LevelFinalize {} case 0x05: return &ClientSetBlock {} case 0x06: return &SetBlock {} case 0x07: if !ext["ExtEntityPositions"] { return &SpawnPlayer {} } else { return &SpawnPlayerExt {} } case 0x08: if !ext["ExtEntityPositions"] { return &SetPosFacing {} } else { return &SetPosFacingExt {} } case 0x09: return &UpdatePosFacing {} case 0x0a: return &UpdatePos {} case 0x0b: return &UpdateFacing {} case 0x0c: return &DespawnPlayer {} case 0x0d: return &Message {} case 0x0e: return &DisconnectPlayer {} case 0x0f: return &UpdateUserType {} case 0x10: return &ExtInfo {} case 0x11: return &ExtEntry {} case 0x13: return &CustomBlocksSupportLevel {} default: return nil } } func readPacket( rd io.Reader, client bool, ext map[string]bool) (Packet, error) { var b [1]byte _, err := io.ReadFull(rd, b[:]) if err != nil { return nil, fmt.Errorf("Read packet: %w", err) } packetId := b[0] packet := createPacketType(packetId, client, ext) if packet == nil { return nil, fmt.Errorf("Unknown packet type: 0x%02x", packetId) } err = binary.Read(rd, binary.BigEndian, packet) if err != nil { return nil, fmt.Errorf("Read packet: %w", err) } return packet, nil } func SReadPacket(rd io.Reader, ext map[string]bool) (Packet, error) { return readPacket(rd, false, ext) } func CReadPacket(rd io.Reader, ext map[string]bool) (Packet, error) { return readPacket(rd, true, ext) } func WritePacket(wr io.Writer, packet Packet) error { bw := bufio.NewWriter(wr) bw.WriteByte(packet.PacketId()) err := binary.Write(bw, binary.BigEndian, packet) if err != nil { return fmt.Errorf("Write packet: %w", err) } return bw.Flush() } func PadString(str string) String { var pstr String copy(pstr[:], []byte(str)) if len(str) < 64 { copy(pstr[len(str):], bytes.Repeat([]byte(" "), 64 - len(str))) } return pstr } func UnpadString(pstr String) string { return string(bytes.TrimRight(pstr[:], " ")) } func (s String) String() string { return UnpadString(s) }