Add generic event

This commit is contained in:
Tnze
2021-02-27 14:49:11 +08:00
parent 3b83aaf8ae
commit 8e7ac43bf5
10 changed files with 316 additions and 195 deletions

View File

@ -1,5 +1,5 @@
# Go-MC # Go-MC
![Version](https://img.shields.io/badge/Minecraft-1.16.4-blue.svg) ![Version](https://img.shields.io/badge/Minecraft-1.16.5-blue.svg)
![Protocol](https://img.shields.io/badge/Protocol-754-blue.svg) ![Protocol](https://img.shields.io/badge/Protocol-754-blue.svg)
[![GoDoc](https://godoc.org/github.com/Tnze/go-mc?status.svg)](https://godoc.org/github.com/Tnze/go-mc) [![GoDoc](https://godoc.org/github.com/Tnze/go-mc?status.svg)](https://godoc.org/github.com/Tnze/go-mc)
[![Go Report Card](https://goreportcard.com/badge/github.com/Tnze/go-mc)](https://goreportcard.com/report/github.com/Tnze/go-mc) [![Go Report Card](https://goreportcard.com/badge/github.com/Tnze/go-mc)](https://goreportcard.com/report/github.com/Tnze/go-mc)

View File

@ -12,6 +12,7 @@ type Player struct {
PlayerInfo PlayerInfo
WorldInfo WorldInfo
isSpawn bool
} }
func NewPlayer(c *bot.Client, settings Settings) *Player { func NewPlayer(c *bot.Client, settings Settings) *Player {
@ -19,14 +20,33 @@ func NewPlayer(c *bot.Client, settings Settings) *Player {
c.Events.AddListener( c.Events.AddListener(
bot.PacketHandler{Priority: 0, ID: packetid.Login, F: b.handleJoinGamePacket}, bot.PacketHandler{Priority: 0, ID: packetid.Login, F: b.handleJoinGamePacket},
bot.PacketHandler{Priority: 0, ID: packetid.KeepAliveClientbound, F: b.handleKeepAlivePacket}, bot.PacketHandler{Priority: 0, ID: packetid.KeepAliveClientbound, F: b.handleKeepAlivePacket},
bot.PacketHandler{Priority: 0, ID: packetid.PositionClientbound, F: b.handlePlayerPositionAndLook},
) )
return b return b
} }
func (p *Player) Respawn() error { func (p *Player) Respawn() error {
const PerformRespawn = 0 const PerformRespawn = 0
return p.c.Conn.WritePacket(pk.Marshal(
err := p.c.Conn.WritePacket(pk.Marshal(
packetid.ClientCommand, packetid.ClientCommand,
pk.VarInt(PerformRespawn), pk.VarInt(PerformRespawn),
)) ))
if err != nil {
return Error{err}
}
return nil
}
type Error struct {
Err error
}
func (e Error) Error() string {
return "bot/basic: " + e.Err.Error()
}
func (e Error) Unwrap() error {
return e.Err
} }

View File

@ -37,7 +37,7 @@ func (e *EventsListener) onDisconnect(p pk.Packet) error {
if e.Disconnect != nil { if e.Disconnect != nil {
var reason chat.Message var reason chat.Message
if err := p.Scan(&reason); err != nil { if err := p.Scan(&reason); err != nil {
return err return Error{err}
} }
return e.Disconnect(reason) return e.Disconnect(reason)
} }
@ -51,7 +51,7 @@ func (e *EventsListener) onChatMsg(p pk.Packet) error {
var sender pk.UUID var sender pk.UUID
if err := p.Scan(&msg, &pos, &sender); err != nil { if err := p.Scan(&msg, &pos, &sender); err != nil {
return err return Error{err}
} }
return e.ChatMsg(msg, byte(pos), uuid.UUID(sender)) return e.ChatMsg(msg, byte(pos), uuid.UUID(sender))
@ -66,7 +66,7 @@ func (e *EventsListener) onUpdateHealth(p pk.Packet) error {
var foodSaturation pk.Float var foodSaturation pk.Float
if err := p.Scan(&health, &food, &foodSaturation); err != nil { if err := p.Scan(&health, &food, &foodSaturation); err != nil {
return err return Error{err}
} }
if e.HealthChange != nil { if e.HealthChange != nil {
if err := e.HealthChange(float32(health)); err != nil { if err := e.HealthChange(float32(health)); err != nil {

View File

@ -1,9 +1,10 @@
package basic package basic
import ( import (
"unsafe"
"github.com/Tnze/go-mc/data/packetid" "github.com/Tnze/go-mc/data/packetid"
pk "github.com/Tnze/go-mc/net/packet" pk "github.com/Tnze/go-mc/net/packet"
"unsafe"
) )
// WorldInfo content player info in server. // WorldInfo content player info in server.
@ -38,7 +39,7 @@ type ServInfo struct {
func (p *Player) handleJoinGamePacket(packet pk.Packet) error { func (p *Player) handleJoinGamePacket(packet pk.Packet) error {
var WorldCount pk.VarInt var WorldCount pk.VarInt
var WorldNames = []pk.Identifier{} var WorldNames = make([]pk.Identifier, 0)
err := packet.Scan( err := packet.Scan(
(*pk.Int)(&p.EID), (*pk.Int)(&p.EID),
(*pk.Boolean)(&p.Hardcore), (*pk.Boolean)(&p.Hardcore),
@ -58,7 +59,7 @@ func (p *Player) handleJoinGamePacket(packet pk.Packet) error {
(*pk.Boolean)(&p.IsFlat), (*pk.Boolean)(&p.IsFlat),
) )
if err != nil { if err != nil {
return err return Error{err}
} }
// This line should work "like" the following code but without copy things // This line should work "like" the following code but without copy things
@ -74,10 +75,10 @@ func (p *Player) handleJoinGamePacket(packet pk.Packet) error {
pk.String(p.Settings.Brand), pk.String(p.Settings.Brand),
)) ))
if err != nil { if err != nil {
return err return Error{err}
} }
return p.c.Conn.WritePacket(pk.Marshal( err = p.c.Conn.WritePacket(pk.Marshal(
packetid.Settings, // Client settings packetid.Settings, // Client settings
pk.String(p.Settings.Locale), pk.String(p.Settings.Locale),
pk.Byte(p.Settings.ViewDistance), pk.Byte(p.Settings.ViewDistance),
@ -86,4 +87,8 @@ func (p *Player) handleJoinGamePacket(packet pk.Packet) error {
pk.UnsignedByte(p.Settings.DisplayedSkinParts), pk.UnsignedByte(p.Settings.DisplayedSkinParts),
pk.VarInt(p.Settings.MainHand), pk.VarInt(p.Settings.MainHand),
)) ))
if err != nil {
return Error{err}
}
return nil
} }

View File

@ -3,16 +3,59 @@ package basic
import ( import (
"github.com/Tnze/go-mc/data/packetid" "github.com/Tnze/go-mc/data/packetid"
pk "github.com/Tnze/go-mc/net/packet" pk "github.com/Tnze/go-mc/net/packet"
"log"
) )
func (p Player) handleKeepAlivePacket(packet pk.Packet) error { func (p Player) handleKeepAlivePacket(packet pk.Packet) error {
var KeepAliveID pk.Long var KeepAliveID pk.Long
if err := packet.Scan(&KeepAliveID); err != nil { if err := packet.Scan(&KeepAliveID); err != nil {
return err return Error{err}
} }
// Response // Response
return p.c.Conn.WritePacket(pk.Marshal( err := p.c.Conn.WritePacket(pk.Packet{
packetid.KeepAliveServerbound, ID: packetid.KeepAliveServerbound,
KeepAliveID, Data: packet.Data,
)) })
if err != nil {
return Error{err}
}
return nil
}
func (p *Player) handlePlayerPositionAndLook(packet pk.Packet) error {
var (
X, Y, Z pk.Double
Yaw, Pitch pk.Float
Flags pk.Byte
TeleportID pk.VarInt
)
if err := packet.Scan(&X, &Y, &Z, &Yaw, &Pitch, &Flags, &TeleportID); err != nil {
return Error{err}
}
// Teleport Confirm
err := p.c.Conn.WritePacket(pk.Marshal(
packetid.TeleportConfirm,
TeleportID,
))
if err != nil {
return Error{err}
}
if !p.isSpawn {
// PlayerPositionAndRotation to confirm the spawn position
err = p.c.Conn.WritePacket(pk.Marshal(
packetid.PositionLook,
X, Y-1.62, Z,
Yaw, Pitch,
pk.Boolean(true),
))
if err != nil {
return Error{err}
}
p.isSpawn = true
log.Print("Position confirmed")
}
return nil
} }

View File

@ -5,7 +5,8 @@ import (
) )
type Events struct { type Events struct {
handlers map[int32]*handlerHeap generic *handlerHeap // for every packet
handlers map[int32]*handlerHeap // for specific packet id only
} }
func (e *Events) AddListener(listeners ...PacketHandler) { func (e *Events) AddListener(listeners ...PacketHandler) {
@ -21,6 +22,18 @@ func (e *Events) AddListener(listeners ...PacketHandler) {
} }
} }
// AddGeneric adds listeners like AddListener, but the packet ID is ignored.
// Generic listener is always called before specific packet listener.
func (e *Events) AddGeneric(listeners ...PacketHandler) {
for _, l := range listeners {
if e.generic == nil {
e.generic = &handlerHeap{l}
} else {
e.generic.Push(l)
}
}
}
type PacketHandlerFunc func(p pk.Packet) error type PacketHandlerFunc func(p pk.Packet) error
type PacketHandler struct { type PacketHandler struct {
ID int32 ID int32

View File

@ -37,6 +37,13 @@ func (d PacketHandlerError) Unwrap() error {
} }
func (c *Client) handlePacket(p pk.Packet) (err error) { func (c *Client) handlePacket(p pk.Packet) (err error) {
if c.Events.generic != nil {
for _, handler := range *c.Events.generic {
if err = handler.F(p); err != nil {
return PacketHandlerError{ID: p.ID, Err: err}
}
}
}
if listeners := c.Events.handlers[p.ID]; listeners != nil { if listeners := c.Events.handlers[p.ID]; listeners != nil {
for _, handler := range *listeners { for _, handler := range *listeners {
err = handler.F(p) err = handler.F(p)

View File

@ -1,9 +1,14 @@
// Daze could join an offline-mode server as client.
// Just standing there and do nothing. Automatically reborn after five seconds of death.
//
// BUG(Tnze): Kick by Disconnect: Time Out
package main package main
import ( import (
"errors" "errors"
"flag" "flag"
"log" "log"
"time"
"github.com/google/uuid" "github.com/google/uuid"
@ -20,6 +25,7 @@ var player *basic.Player
func main() { func main() {
flag.Parse() flag.Parse()
client = bot.NewClient() client = bot.NewClient()
client.Auth.Name = "Daze"
player = basic.NewPlayer(client, basic.DefaultSettings) player = basic.NewPlayer(client, basic.DefaultSettings)
basic.EventsListener{ basic.EventsListener{
GameStart: onGameStart, GameStart: onGameStart,
@ -35,19 +41,22 @@ func main() {
} }
log.Println("Login success") log.Println("Login success")
//Register event handlers
//JoinGame //JoinGame
for { for {
if err = client.HandleGame(); err != nil { if err = client.HandleGame(); err == nil {
var interErr *bot.PacketHandlerError panic("HandleGame never return nil")
if errors.As(err, &interErr) { }
log.Print("Internal bugs: ", interErr)
if err2 := new(bot.PacketHandlerError); errors.As(err, err2) {
if err := new(DisconnectErr); errors.As(err2, err) {
log.Print("Disconnect: ", err.Reason)
return
} else { } else {
log.Fatal(err) // print and ignore the error
log.Print(err2)
} }
} else { } else {
break log.Fatal(err)
} }
} }
} }
@ -55,7 +64,14 @@ func main() {
func onDeath() error { func onDeath() error {
log.Println("Died and Respawned") log.Println("Died and Respawned")
// If we exclude Respawn(...) then the player won't press the "Respawn" button upon death // If we exclude Respawn(...) then the player won't press the "Respawn" button upon death
return player.Respawn() go func() {
time.Sleep(time.Second * 5)
err := player.Respawn()
if err != nil {
log.Print(err)
}
}()
return nil
} }
func onGameStart() error { func onGameStart() error {
@ -63,12 +79,20 @@ func onGameStart() error {
return nil //if err isn't nil, HandleGame() will return it. return nil //if err isn't nil, HandleGame() will return it.
} }
func onChatMsg(c chat.Message, pos byte, uuid uuid.UUID) error { func onChatMsg(c chat.Message, _ byte, _ uuid.UUID) error {
log.Println("Chat:", c.ClearString()) // output chat message without any format code (like color or bold) log.Println("Chat:", c.ClearString()) // output chat message without any format code (like color or bold)
return nil return nil
} }
func onDisconnect(reason chat.Message) error { type DisconnectErr struct {
log.Println("Disconnect:", reason) Reason chat.Message
return nil }
func (d DisconnectErr) Error() string {
return "disconnect: " + d.Reason.String()
}
func onDisconnect(reason chat.Message) error {
// return a error value so that we can stop main loop
return DisconnectErr{Reason: reason}
} }

View File

@ -1,5 +1,3 @@
//+build ignore
// gen_packetIDs.go generates the enumeration of packet IDs used on the wire. // gen_packetIDs.go generates the enumeration of packet IDs used on the wire.
package main package main
@ -8,6 +6,7 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"os" "os"
"strconv"
"text/template" "text/template"
"github.com/iancoleman/strcase" "github.com/iancoleman/strcase"
@ -22,30 +21,30 @@ package packetid
// Login state // Login state
const ( const (
// Clientbound // Clientbound
{{range $Name, $ID := .Login.Clientbound}} {{$Name}} = {{$ID}} {{range $ID, $Name := .Login.Clientbound}} {{$Name}} = {{$ID | printf "%#x"}}
{{end}} {{end}}
// Serverbound // Serverbound
{{range $Name, $ID := .Login.Serverbound}} {{$Name}} = {{$ID}} {{range $ID, $Name := .Login.Serverbound}} {{$Name}} = {{$ID | printf "%#x"}}
{{end}} {{end}}
) )
// Ping state // Ping state
const ( const (
// Clientbound // Clientbound
{{range $Name, $ID := .Status.Clientbound}} {{$Name}} = {{$ID}} {{range $ID, $Name := .Status.Clientbound}} {{$Name}} = {{$ID | printf "%#x"}}
{{end}} {{end}}
// Serverbound // Serverbound
{{range $Name, $ID := .Status.Serverbound}} {{$Name}} = {{$ID}} {{range $ID, $Name := .Status.Serverbound}} {{$Name}} = {{$ID | printf "%#x"}}
{{end}} {{end}}
) )
// Play state // Play state
const ( const (
// Clientbound // Clientbound
{{range $Name, $ID := .Play.Clientbound}} {{$Name}} = {{$ID}} {{range $ID, $Name := .Play.Clientbound}} {{$Name}} = {{$ID | printf "%#x"}}
{{end}} {{end}}
// Serverbound // Serverbound
{{range $Name, $ID := .Play.Serverbound}} {{$Name}} = {{$ID}} {{range $ID, $Name := .Play.Serverbound}} {{$Name}} = {{$ID | printf "%#x"}}
{{end}} {{end}}
) )
` `
@ -69,25 +68,21 @@ func unnest(input map[string]interface{}, keys ...string) (map[string]interface{
} }
type duplexMappings struct { type duplexMappings struct {
Clientbound map[string]string Clientbound map[int32]string
Serverbound map[string]string Serverbound map[int32]string
} }
func (m *duplexMappings) EnsureUniqueNames() { func (m *duplexMappings) EnsureUniqueNames() {
// Assemble a slice of keys to check across both maps, because we cannot // Assemble a slice of keys to check across both maps, because we cannot
// mutate a map while iterating it. // mutate a map while iterating it.
clientKeys := make([]string, 0, len(m.Clientbound)) clientBounds := make(map[string]int32)
for k, _ := range m.Clientbound { for sk, sv := range m.Clientbound {
clientKeys = append(clientKeys, k) clientBounds[sv] = sk
} }
for sk, sv := range m.Serverbound {
for _, k := range clientKeys { if ck, ok := clientBounds[sv]; ok {
if _, alsoServerKey := m.Serverbound[k]; alsoServerKey { m.Clientbound[ck] = sv + "Clientbound"
cVal, sVal := m.Clientbound[k], m.Serverbound[k] m.Serverbound[sk] = sv + "Serverbound"
delete(m.Clientbound, k)
delete(m.Serverbound, k)
m.Clientbound[k+"Clientbound"] = cVal
m.Serverbound[k+"Serverbound"] = sVal
} }
} }
} }
@ -96,8 +91,8 @@ func (m *duplexMappings) EnsureUniqueNames() {
// game state. // game state.
func unpackMapping(data map[string]interface{}, gameState string) (duplexMappings, error) { func unpackMapping(data map[string]interface{}, gameState string) (duplexMappings, error) {
out := duplexMappings{ out := duplexMappings{
Clientbound: make(map[string]string), Clientbound: make(map[int32]string),
Serverbound: make(map[string]string), Serverbound: make(map[int32]string),
} }
info, err := unnest(data, gameState, "toClient", "types") info, err := unnest(data, gameState, "toClient", "types")
@ -107,7 +102,7 @@ func unpackMapping(data map[string]interface{}, gameState string) (duplexMapping
pType := info["packet"].([]interface{})[1].([]interface{})[0].(map[string]interface{})["type"] pType := info["packet"].([]interface{})[1].([]interface{})[0].(map[string]interface{})["type"]
mappings := pType.([]interface{})[1].(map[string]interface{})["mappings"].(map[string]interface{}) mappings := pType.([]interface{})[1].(map[string]interface{})["mappings"].(map[string]interface{})
for k, v := range mappings { for k, v := range mappings {
out.Clientbound[strcase.ToCamel(v.(string))] = k out.Clientbound[mustAtoi(k)] = strcase.ToCamel(v.(string))
} }
info, err = unnest(data, gameState, "toServer", "types") info, err = unnest(data, gameState, "toServer", "types")
if err != nil { if err != nil {
@ -116,12 +111,20 @@ func unpackMapping(data map[string]interface{}, gameState string) (duplexMapping
pType = info["packet"].([]interface{})[1].([]interface{})[0].(map[string]interface{})["type"] pType = info["packet"].([]interface{})[1].([]interface{})[0].(map[string]interface{})["type"]
mappings = pType.([]interface{})[1].(map[string]interface{})["mappings"].(map[string]interface{}) mappings = pType.([]interface{})[1].(map[string]interface{})["mappings"].(map[string]interface{})
for k, v := range mappings { for k, v := range mappings {
out.Serverbound[strcase.ToCamel(v.(string))] = k out.Serverbound[mustAtoi(k)] = strcase.ToCamel(v.(string))
} }
return out, nil return out, nil
} }
func mustAtoi(num string) int32 {
if n, err := strconv.ParseInt(num, 0, 32); err != nil {
panic(err)
} else {
return int32(n)
}
}
type protocolIDs struct { type protocolIDs struct {
Login duplexMappings Login duplexMappings
Play duplexMappings Play duplexMappings
@ -158,7 +161,7 @@ func downloadInfo() (*protocolIDs, error) {
} }
//go:generate go run $GOFILE //go:generate go run $GOFILE
//go:generate go fmt packetid.go //go:generate go fmt ../packetid.go
func main() { func main() {
pIDs, err := downloadInfo() pIDs, err := downloadInfo()
if err != nil { if err != nil {
@ -166,7 +169,7 @@ func main() {
os.Exit(1) os.Exit(1)
} }
f, err := os.Create("packetid.go") f, err := os.Create("../packetid.go")
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err) fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1) os.Exit(1)

View File

@ -2,169 +2,175 @@
package packetid package packetid
// Valid PktID values. // Login state
const ( const (
// Clientbound packets for connections in the login state. // Clientbound
Compress = 0x03 Disconnect = 0x0
Disconnect = 0x00 EncryptionBeginClientbound = 0x1
EncryptionBeginClientbound = 0x01 Success = 0x2
LoginPluginRequest = 0x04 Compress = 0x3
Success = 0x02 LoginPluginRequest = 0x4
// Serverbound packets for connections in the login state // Serverbound
EncryptionBeginServerbound = 0x01 LoginStart = 0x0
LoginPluginResponse = 0x02 EncryptionBeginServerbound = 0x1
LoginStart = 0x00 LoginPluginResponse = 0x2
)
// Clientbound packets for connections in the play state. // Ping state
AbilitiesClientbound = 0x30 const (
AcknowledgePlayerDigging = 0x07 // Clientbound
Advancements = 0x57 ServerInfo = 0x0
Animation = 0x05 PingClientbound = 0x1
AttachEntity = 0x45
BlockAction = 0x0a // Serverbound
BlockBreakAnimation = 0x08 PingStart = 0x0
BlockChange = 0x0b PingServerbound = 0x1
BossBar = 0x0c )
Camera = 0x3e
ChatClientbound = 0x0e // Play state
CloseWindowClientbound = 0x12 const (
Collect = 0x55 // Clientbound
CombatEvent = 0x31 SpawnEntity = 0x0
CraftProgressBar = 0x14 SpawnEntityExperienceOrb = 0x1
CraftRecipeResponse = 0x2f SpawnEntityLiving = 0x2
CustomPayloadClientbound = 0x17 SpawnEntityPainting = 0x3
NamedEntitySpawn = 0x4
Animation = 0x5
Statistics = 0x6
AcknowledgePlayerDigging = 0x7
BlockBreakAnimation = 0x8
TileEntityData = 0x9
BlockAction = 0xa
BlockChange = 0xb
BossBar = 0xc
Difficulty = 0xd
ChatClientbound = 0xe
TabCompleteClientbound = 0xf
DeclareCommands = 0x10 DeclareCommands = 0x10
DeclareRecipes = 0x5a TransactionClientbound = 0x11
Difficulty = 0x0d CloseWindowClientbound = 0x12
Entity = 0x2a WindowItems = 0x13
EntityDestroy = 0x36 CraftProgressBar = 0x14
EntityEffect = 0x59 SetSlot = 0x15
EntityEquipment = 0x47 SetCooldown = 0x16
EntityHeadRotation = 0x3a CustomPayloadClientbound = 0x17
EntityLook = 0x29 NamedSoundEffect = 0x18
EntityMetadata = 0x44
EntityMoveLook = 0x28
EntitySoundEffect = 0x50
EntityStatus = 0x1a
EntityTeleport = 0x56
EntityUpdateAttributes = 0x58
EntityVelocity = 0x46
Experience = 0x48
Explosion = 0x1b
FacePlayer = 0x33
GameStateChange = 0x1d
HeldItemSlotClientbound = 0x3f
KeepAliveClientbound = 0x1f
KickDisconnect = 0x19 KickDisconnect = 0x19
EntityStatus = 0x1a
Explosion = 0x1b
UnloadChunk = 0x1c
GameStateChange = 0x1d
OpenHorseWindow = 0x1e
KeepAliveClientbound = 0x1f
MapChunk = 0x20
WorldEvent = 0x21
WorldParticles = 0x22
UpdateLight = 0x23
Login = 0x24 Login = 0x24
Map = 0x25 Map = 0x25
MapChunk = 0x20 TradeList = 0x26
MultiBlockChange = 0x3b
NamedEntitySpawn = 0x04
NamedSoundEffect = 0x18
NbtQueryResponse = 0x54
OpenBook = 0x2c
OpenHorseWindow = 0x1e
OpenSignEntity = 0x2e
OpenWindow = 0x2d
PlayerInfo = 0x32
PlayerlistHeader = 0x53
PositionClientbound = 0x34
RelEntityMove = 0x27 RelEntityMove = 0x27
EntityMoveLook = 0x28
EntityLook = 0x29
Entity = 0x2a
VehicleMoveClientbound = 0x2b
OpenBook = 0x2c
OpenWindow = 0x2d
OpenSignEntity = 0x2e
CraftRecipeResponse = 0x2f
AbilitiesClientbound = 0x30
CombatEvent = 0x31
PlayerInfo = 0x32
FacePlayer = 0x33
PositionClientbound = 0x34
UnlockRecipes = 0x35
EntityDestroy = 0x36
RemoveEntityEffect = 0x37 RemoveEntityEffect = 0x37
ResourcePackSend = 0x38 ResourcePackSend = 0x38
Respawn = 0x39 Respawn = 0x39
ScoreboardDisplayObjective = 0x43 EntityHeadRotation = 0x3a
ScoreboardObjective = 0x4a MultiBlockChange = 0x3b
ScoreboardScore = 0x4d
SelectAdvancementTab = 0x3c SelectAdvancementTab = 0x3c
SetCooldown = 0x16
SetPassengers = 0x4b
SetSlot = 0x15
SoundEffect = 0x51
SpawnEntity = 0x00
SpawnEntityExperienceOrb = 0x01
SpawnEntityLiving = 0x02
SpawnEntityPainting = 0x03
SpawnPosition = 0x42
Statistics = 0x06
StopSound = 0x52
TabCompleteClientbound = 0x0f
Tags = 0x5b
Teams = 0x4c
TileEntityData = 0x09
Title = 0x4f
TradeList = 0x26
TransactionClientbound = 0x11
UnloadChunk = 0x1c
UnlockRecipes = 0x35
UpdateHealth = 0x49
UpdateLight = 0x23
UpdateTime = 0x4e
UpdateViewDistance = 0x41
UpdateViewPosition = 0x40
VehicleMoveClientbound = 0x2b
WindowItems = 0x13
WorldBorder = 0x3d WorldBorder = 0x3d
WorldEvent = 0x21 Camera = 0x3e
WorldParticles = 0x22 HeldItemSlotClientbound = 0x3f
UpdateViewPosition = 0x40
UpdateViewDistance = 0x41
SpawnPosition = 0x42
ScoreboardDisplayObjective = 0x43
EntityMetadata = 0x44
AttachEntity = 0x45
EntityVelocity = 0x46
EntityEquipment = 0x47
Experience = 0x48
UpdateHealth = 0x49
ScoreboardObjective = 0x4a
SetPassengers = 0x4b
Teams = 0x4c
ScoreboardScore = 0x4d
UpdateTime = 0x4e
Title = 0x4f
EntitySoundEffect = 0x50
SoundEffect = 0x51
StopSound = 0x52
PlayerlistHeader = 0x53
NbtQueryResponse = 0x54
Collect = 0x55
EntityTeleport = 0x56
Advancements = 0x57
EntityUpdateAttributes = 0x58
EntityEffect = 0x59
DeclareRecipes = 0x5a
Tags = 0x5b
// Serverbound packets for connections in the play state. // Serverbound
AbilitiesServerbound = 0x1a TeleportConfirm = 0x0
AdvancementTab = 0x22 QueryBlockNbt = 0x1
ArmAnimation = 0x2c SetDifficulty = 0x2
BlockDig = 0x1b ChatServerbound = 0x3
BlockPlace = 0x2e ClientCommand = 0x4
ChatServerbound = 0x03 Settings = 0x5
ClientCommand = 0x04 TabCompleteServerbound = 0x6
CloseWindowServerbound = 0x0a TransactionServerbound = 0x7
CraftRecipeRequest = 0x19 EnchantItem = 0x8
CustomPayloadServerbound = 0x0b WindowClick = 0x9
DisplayedRecipe = 0x1e CloseWindowServerbound = 0xa
EditBook = 0x0c CustomPayloadServerbound = 0xb
EnchantItem = 0x08 EditBook = 0xc
EntityAction = 0x1c QueryEntityNbt = 0xd
Flying = 0x15 UseEntity = 0xe
GenerateStructure = 0x0f GenerateStructure = 0xf
HeldItemSlotServerbound = 0x25
KeepAliveServerbound = 0x10 KeepAliveServerbound = 0x10
LockDifficulty = 0x11 LockDifficulty = 0x11
Look = 0x14
NameItem = 0x20
PickItem = 0x18
PositionLook = 0x13
PositionServerbound = 0x12 PositionServerbound = 0x12
QueryBlockNbt = 0x01 PositionLook = 0x13
QueryEntityNbt = 0x0d Look = 0x14
Flying = 0x15
VehicleMoveServerbound = 0x16
SteerBoat = 0x17
PickItem = 0x18
CraftRecipeRequest = 0x19
AbilitiesServerbound = 0x1a
BlockDig = 0x1b
EntityAction = 0x1c
SteerVehicle = 0x1d
DisplayedRecipe = 0x1e
RecipeBook = 0x1f RecipeBook = 0x1f
NameItem = 0x20
ResourcePackReceive = 0x21 ResourcePackReceive = 0x21
AdvancementTab = 0x22
SelectTrade = 0x23 SelectTrade = 0x23
SetBeaconEffect = 0x24 SetBeaconEffect = 0x24
SetCreativeSlot = 0x28 HeldItemSlotServerbound = 0x25
SetDifficulty = 0x02
Settings = 0x05
Spectate = 0x2d
SteerBoat = 0x17
SteerVehicle = 0x1d
TabCompleteServerbound = 0x06
TeleportConfirm = 0x00
TransactionServerbound = 0x07
UpdateCommandBlock = 0x26 UpdateCommandBlock = 0x26
UpdateCommandBlockMinecart = 0x27 UpdateCommandBlockMinecart = 0x27
SetCreativeSlot = 0x28
UpdateJigsawBlock = 0x29 UpdateJigsawBlock = 0x29
UpdateSign = 0x2b
UpdateStructureBlock = 0x2a UpdateStructureBlock = 0x2a
UseEntity = 0x0e UpdateSign = 0x2b
ArmAnimation = 0x2c
Spectate = 0x2d
BlockPlace = 0x2e
UseItem = 0x2f UseItem = 0x2f
VehicleMoveServerbound = 0x16
WindowClick = 0x09
// Clientbound packets used to respond to ping/status requests.
PingClientbound = 0x01
ServerInfo = 0x00
// Serverbound packets used to ping or read server status.
PingServerbound = 0x01
PingStart = 0x00
) )