summaryrefslogtreecommitdiff
path: root/classic/packets.go
diff options
context:
space:
mode:
authorraven <citrons@mondecitronne.com>2026-03-20 14:29:52 -0500
committerraven <citrons@mondecitronne.com>2026-03-20 14:29:52 -0500
commitc3d63652a4b80add587ee17f5c9f3773417203ad (patch)
treee26e1d36f912f1bca210e1aaa3e314668d0b010b /classic/packets.go
initial commit
Diffstat (limited to 'classic/packets.go')
-rw-r--r--classic/packets.go243
1 files changed, 243 insertions, 0 deletions
diff --git a/classic/packets.go b/classic/packets.go
new file mode 100644
index 0000000..c1f650e
--- /dev/null
+++ b/classic/packets.go
@@ -0,0 +1,243 @@
+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
+)
+
+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) 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: return &SpawnPlayer {}
+ case 0x08: return &SetPosFacing {}
+ 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 {}
+ default: return nil
+ }
+}
+
+func readPacket(rd io.Reader, client 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)
+ 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) (Packet, error) {
+ return readPacket(rd, false)
+}
+
+func CReadPacket(rd io.Reader) (Packet, error) {
+ return readPacket(rd, true)
+}
+
+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)
+}