diff options
| author | raven <citrons@mondecitronne.com> | 2026-03-20 14:29:52 -0500 |
|---|---|---|
| committer | raven <citrons@mondecitronne.com> | 2026-03-20 14:29:52 -0500 |
| commit | c3d63652a4b80add587ee17f5c9f3773417203ad (patch) | |
| tree | e26e1d36f912f1bca210e1aaa3e314668d0b010b /classic/packets.go | |
initial commit
Diffstat (limited to 'classic/packets.go')
| -rw-r--r-- | classic/packets.go | 243 |
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) +} |
