From 8e7ac43bf5b1c3574c869bb54fc2bfd3ad5215bc Mon Sep 17 00:00:00 2001 From: Tnze Date: Sat, 27 Feb 2021 14:49:11 +0800 Subject: [PATCH] Add generic event --- README.md | 2 +- bot/basic/basic.go | 22 +- bot/basic/events.go | 6 +- bot/basic/info.go | 15 +- bot/basic/keepalive.go | 53 ++++- bot/event.go | 15 +- bot/ingame.go | 7 + cmd/daze/daze.go | 50 ++-- data/packetid/{ => gen}/gen_packetIDs.go | 57 ++--- data/packetid/packetid.go | 284 ++++++++++++----------- 10 files changed, 316 insertions(+), 195 deletions(-) rename data/packetid/{ => gen}/gen_packetIDs.go (74%) diff --git a/README.md b/README.md index 8be59e0..e1ba300 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # 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) [![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) diff --git a/bot/basic/basic.go b/bot/basic/basic.go index 36791e3..826c0d4 100644 --- a/bot/basic/basic.go +++ b/bot/basic/basic.go @@ -12,6 +12,7 @@ type Player struct { PlayerInfo WorldInfo + isSpawn bool } func NewPlayer(c *bot.Client, settings Settings) *Player { @@ -19,14 +20,33 @@ func NewPlayer(c *bot.Client, settings Settings) *Player { c.Events.AddListener( 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.PositionClientbound, F: b.handlePlayerPositionAndLook}, ) return b } func (p *Player) Respawn() error { const PerformRespawn = 0 - return p.c.Conn.WritePacket(pk.Marshal( + + err := p.c.Conn.WritePacket(pk.Marshal( packetid.ClientCommand, 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 } diff --git a/bot/basic/events.go b/bot/basic/events.go index 08dc67c..dbb5ba3 100644 --- a/bot/basic/events.go +++ b/bot/basic/events.go @@ -37,7 +37,7 @@ func (e *EventsListener) onDisconnect(p pk.Packet) error { if e.Disconnect != nil { var reason chat.Message if err := p.Scan(&reason); err != nil { - return err + return Error{err} } return e.Disconnect(reason) } @@ -51,7 +51,7 @@ func (e *EventsListener) onChatMsg(p pk.Packet) error { var sender pk.UUID if err := p.Scan(&msg, &pos, &sender); err != nil { - return err + return Error{err} } 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 if err := p.Scan(&health, &food, &foodSaturation); err != nil { - return err + return Error{err} } if e.HealthChange != nil { if err := e.HealthChange(float32(health)); err != nil { diff --git a/bot/basic/info.go b/bot/basic/info.go index 243a665..526de3f 100644 --- a/bot/basic/info.go +++ b/bot/basic/info.go @@ -1,9 +1,10 @@ package basic import ( + "unsafe" + "github.com/Tnze/go-mc/data/packetid" pk "github.com/Tnze/go-mc/net/packet" - "unsafe" ) // WorldInfo content player info in server. @@ -38,7 +39,7 @@ type ServInfo struct { func (p *Player) handleJoinGamePacket(packet pk.Packet) error { var WorldCount pk.VarInt - var WorldNames = []pk.Identifier{} + var WorldNames = make([]pk.Identifier, 0) err := packet.Scan( (*pk.Int)(&p.EID), (*pk.Boolean)(&p.Hardcore), @@ -58,7 +59,7 @@ func (p *Player) handleJoinGamePacket(packet pk.Packet) error { (*pk.Boolean)(&p.IsFlat), ) if err != nil { - return err + return Error{err} } // 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), )) 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 pk.String(p.Settings.Locale), pk.Byte(p.Settings.ViewDistance), @@ -86,4 +87,8 @@ func (p *Player) handleJoinGamePacket(packet pk.Packet) error { pk.UnsignedByte(p.Settings.DisplayedSkinParts), pk.VarInt(p.Settings.MainHand), )) + if err != nil { + return Error{err} + } + return nil } diff --git a/bot/basic/keepalive.go b/bot/basic/keepalive.go index 1b424bd..1e033fa 100644 --- a/bot/basic/keepalive.go +++ b/bot/basic/keepalive.go @@ -3,16 +3,59 @@ package basic import ( "github.com/Tnze/go-mc/data/packetid" pk "github.com/Tnze/go-mc/net/packet" + "log" ) func (p Player) handleKeepAlivePacket(packet pk.Packet) error { var KeepAliveID pk.Long if err := packet.Scan(&KeepAliveID); err != nil { - return err + return Error{err} } // Response - return p.c.Conn.WritePacket(pk.Marshal( - packetid.KeepAliveServerbound, - KeepAliveID, - )) + err := p.c.Conn.WritePacket(pk.Packet{ + ID: packetid.KeepAliveServerbound, + 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 } diff --git a/bot/event.go b/bot/event.go index ad741f3..28ca79c 100644 --- a/bot/event.go +++ b/bot/event.go @@ -5,7 +5,8 @@ import ( ) 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) { @@ -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 PacketHandler struct { ID int32 diff --git a/bot/ingame.go b/bot/ingame.go index 1f08f1e..3a9751d 100644 --- a/bot/ingame.go +++ b/bot/ingame.go @@ -37,6 +37,13 @@ func (d PacketHandlerError) Unwrap() 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 { for _, handler := range *listeners { err = handler.F(p) diff --git a/cmd/daze/daze.go b/cmd/daze/daze.go index b998b42..d7f8a32 100644 --- a/cmd/daze/daze.go +++ b/cmd/daze/daze.go @@ -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 import ( "errors" "flag" "log" + "time" "github.com/google/uuid" @@ -20,6 +25,7 @@ var player *basic.Player func main() { flag.Parse() client = bot.NewClient() + client.Auth.Name = "Daze" player = basic.NewPlayer(client, basic.DefaultSettings) basic.EventsListener{ GameStart: onGameStart, @@ -35,19 +41,22 @@ func main() { } log.Println("Login success") - //Register event handlers - //JoinGame for { - if err = client.HandleGame(); err != nil { - var interErr *bot.PacketHandlerError - if errors.As(err, &interErr) { - log.Print("Internal bugs: ", interErr) + if err = client.HandleGame(); err == nil { + panic("HandleGame never return nil") + } + + if err2 := new(bot.PacketHandlerError); errors.As(err, err2) { + if err := new(DisconnectErr); errors.As(err2, err) { + log.Print("Disconnect: ", err.Reason) + return } else { - log.Fatal(err) + // print and ignore the error + log.Print(err2) } } else { - break + log.Fatal(err) } } } @@ -55,7 +64,14 @@ func main() { func onDeath() error { log.Println("Died and Respawned") // 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 { @@ -63,12 +79,20 @@ func onGameStart() error { 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) return nil } -func onDisconnect(reason chat.Message) error { - log.Println("Disconnect:", reason) - return nil +type DisconnectErr struct { + Reason chat.Message +} + +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} } diff --git a/data/packetid/gen_packetIDs.go b/data/packetid/gen/gen_packetIDs.go similarity index 74% rename from data/packetid/gen_packetIDs.go rename to data/packetid/gen/gen_packetIDs.go index 60efa13..870b706 100644 --- a/data/packetid/gen_packetIDs.go +++ b/data/packetid/gen/gen_packetIDs.go @@ -1,5 +1,3 @@ -//+build ignore - // gen_packetIDs.go generates the enumeration of packet IDs used on the wire. package main @@ -8,6 +6,7 @@ import ( "fmt" "net/http" "os" + "strconv" "text/template" "github.com/iancoleman/strcase" @@ -22,30 +21,30 @@ package packetid // Login state const ( // Clientbound -{{range $Name, $ID := .Login.Clientbound}} {{$Name}} = {{$ID}} +{{range $ID, $Name := .Login.Clientbound}} {{$Name}} = {{$ID | printf "%#x"}} {{end}} // Serverbound -{{range $Name, $ID := .Login.Serverbound}} {{$Name}} = {{$ID}} +{{range $ID, $Name := .Login.Serverbound}} {{$Name}} = {{$ID | printf "%#x"}} {{end}} ) // Ping state const ( // Clientbound -{{range $Name, $ID := .Status.Clientbound}} {{$Name}} = {{$ID}} +{{range $ID, $Name := .Status.Clientbound}} {{$Name}} = {{$ID | printf "%#x"}} {{end}} // Serverbound -{{range $Name, $ID := .Status.Serverbound}} {{$Name}} = {{$ID}} +{{range $ID, $Name := .Status.Serverbound}} {{$Name}} = {{$ID | printf "%#x"}} {{end}} ) // Play state const ( // Clientbound -{{range $Name, $ID := .Play.Clientbound}} {{$Name}} = {{$ID}} +{{range $ID, $Name := .Play.Clientbound}} {{$Name}} = {{$ID | printf "%#x"}} {{end}} // Serverbound -{{range $Name, $ID := .Play.Serverbound}} {{$Name}} = {{$ID}} +{{range $ID, $Name := .Play.Serverbound}} {{$Name}} = {{$ID | printf "%#x"}} {{end}} ) ` @@ -69,25 +68,21 @@ func unnest(input map[string]interface{}, keys ...string) (map[string]interface{ } type duplexMappings struct { - Clientbound map[string]string - Serverbound map[string]string + Clientbound map[int32]string + Serverbound map[int32]string } func (m *duplexMappings) EnsureUniqueNames() { // Assemble a slice of keys to check across both maps, because we cannot // mutate a map while iterating it. - clientKeys := make([]string, 0, len(m.Clientbound)) - for k, _ := range m.Clientbound { - clientKeys = append(clientKeys, k) + clientBounds := make(map[string]int32) + for sk, sv := range m.Clientbound { + clientBounds[sv] = sk } - - for _, k := range clientKeys { - if _, alsoServerKey := m.Serverbound[k]; alsoServerKey { - cVal, sVal := m.Clientbound[k], m.Serverbound[k] - delete(m.Clientbound, k) - delete(m.Serverbound, k) - m.Clientbound[k+"Clientbound"] = cVal - m.Serverbound[k+"Serverbound"] = sVal + for sk, sv := range m.Serverbound { + if ck, ok := clientBounds[sv]; ok { + m.Clientbound[ck] = sv + "Clientbound" + m.Serverbound[sk] = sv + "Serverbound" } } } @@ -96,8 +91,8 @@ func (m *duplexMappings) EnsureUniqueNames() { // game state. func unpackMapping(data map[string]interface{}, gameState string) (duplexMappings, error) { out := duplexMappings{ - Clientbound: make(map[string]string), - Serverbound: make(map[string]string), + Clientbound: make(map[int32]string), + Serverbound: make(map[int32]string), } 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"] mappings := pType.([]interface{})[1].(map[string]interface{})["mappings"].(map[string]interface{}) 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") 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"] mappings = pType.([]interface{})[1].(map[string]interface{})["mappings"].(map[string]interface{}) for k, v := range mappings { - out.Serverbound[strcase.ToCamel(v.(string))] = k + out.Serverbound[mustAtoi(k)] = strcase.ToCamel(v.(string)) } 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 { Login duplexMappings Play duplexMappings @@ -158,7 +161,7 @@ func downloadInfo() (*protocolIDs, error) { } //go:generate go run $GOFILE -//go:generate go fmt packetid.go +//go:generate go fmt ../packetid.go func main() { pIDs, err := downloadInfo() if err != nil { @@ -166,7 +169,7 @@ func main() { os.Exit(1) } - f, err := os.Create("packetid.go") + f, err := os.Create("../packetid.go") if err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) os.Exit(1) diff --git a/data/packetid/packetid.go b/data/packetid/packetid.go index 099284f..33eb919 100644 --- a/data/packetid/packetid.go +++ b/data/packetid/packetid.go @@ -2,169 +2,175 @@ package packetid -// Valid PktID values. +// Login state const ( - // Clientbound packets for connections in the login state. - Compress = 0x03 - Disconnect = 0x00 - EncryptionBeginClientbound = 0x01 - LoginPluginRequest = 0x04 - Success = 0x02 + // Clientbound + Disconnect = 0x0 + EncryptionBeginClientbound = 0x1 + Success = 0x2 + Compress = 0x3 + LoginPluginRequest = 0x4 - // Serverbound packets for connections in the login state - EncryptionBeginServerbound = 0x01 - LoginPluginResponse = 0x02 - LoginStart = 0x00 + // Serverbound + LoginStart = 0x0 + EncryptionBeginServerbound = 0x1 + LoginPluginResponse = 0x2 +) - // Clientbound packets for connections in the play state. - AbilitiesClientbound = 0x30 - AcknowledgePlayerDigging = 0x07 - Advancements = 0x57 - Animation = 0x05 - AttachEntity = 0x45 - BlockAction = 0x0a - BlockBreakAnimation = 0x08 - BlockChange = 0x0b - BossBar = 0x0c - Camera = 0x3e - ChatClientbound = 0x0e - CloseWindowClientbound = 0x12 - Collect = 0x55 - CombatEvent = 0x31 - CraftProgressBar = 0x14 - CraftRecipeResponse = 0x2f - CustomPayloadClientbound = 0x17 +// Ping state +const ( + // Clientbound + ServerInfo = 0x0 + PingClientbound = 0x1 + + // Serverbound + PingStart = 0x0 + PingServerbound = 0x1 +) + +// Play state +const ( + // Clientbound + SpawnEntity = 0x0 + SpawnEntityExperienceOrb = 0x1 + SpawnEntityLiving = 0x2 + 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 - DeclareRecipes = 0x5a - Difficulty = 0x0d - Entity = 0x2a - EntityDestroy = 0x36 - EntityEffect = 0x59 - EntityEquipment = 0x47 - EntityHeadRotation = 0x3a - EntityLook = 0x29 - 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 + TransactionClientbound = 0x11 + CloseWindowClientbound = 0x12 + WindowItems = 0x13 + CraftProgressBar = 0x14 + SetSlot = 0x15 + SetCooldown = 0x16 + CustomPayloadClientbound = 0x17 + NamedSoundEffect = 0x18 KickDisconnect = 0x19 + EntityStatus = 0x1a + Explosion = 0x1b + UnloadChunk = 0x1c + GameStateChange = 0x1d + OpenHorseWindow = 0x1e + KeepAliveClientbound = 0x1f + MapChunk = 0x20 + WorldEvent = 0x21 + WorldParticles = 0x22 + UpdateLight = 0x23 Login = 0x24 Map = 0x25 - MapChunk = 0x20 - MultiBlockChange = 0x3b - NamedEntitySpawn = 0x04 - NamedSoundEffect = 0x18 - NbtQueryResponse = 0x54 - OpenBook = 0x2c - OpenHorseWindow = 0x1e - OpenSignEntity = 0x2e - OpenWindow = 0x2d - PlayerInfo = 0x32 - PlayerlistHeader = 0x53 - PositionClientbound = 0x34 + TradeList = 0x26 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 ResourcePackSend = 0x38 Respawn = 0x39 - ScoreboardDisplayObjective = 0x43 - ScoreboardObjective = 0x4a - ScoreboardScore = 0x4d + EntityHeadRotation = 0x3a + MultiBlockChange = 0x3b 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 - WorldEvent = 0x21 - WorldParticles = 0x22 + Camera = 0x3e + 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. - AbilitiesServerbound = 0x1a - AdvancementTab = 0x22 - ArmAnimation = 0x2c - BlockDig = 0x1b - BlockPlace = 0x2e - ChatServerbound = 0x03 - ClientCommand = 0x04 - CloseWindowServerbound = 0x0a - CraftRecipeRequest = 0x19 - CustomPayloadServerbound = 0x0b - DisplayedRecipe = 0x1e - EditBook = 0x0c - EnchantItem = 0x08 - EntityAction = 0x1c - Flying = 0x15 - GenerateStructure = 0x0f - HeldItemSlotServerbound = 0x25 + // Serverbound + TeleportConfirm = 0x0 + QueryBlockNbt = 0x1 + SetDifficulty = 0x2 + ChatServerbound = 0x3 + ClientCommand = 0x4 + Settings = 0x5 + TabCompleteServerbound = 0x6 + TransactionServerbound = 0x7 + EnchantItem = 0x8 + WindowClick = 0x9 + CloseWindowServerbound = 0xa + CustomPayloadServerbound = 0xb + EditBook = 0xc + QueryEntityNbt = 0xd + UseEntity = 0xe + GenerateStructure = 0xf KeepAliveServerbound = 0x10 LockDifficulty = 0x11 - Look = 0x14 - NameItem = 0x20 - PickItem = 0x18 - PositionLook = 0x13 PositionServerbound = 0x12 - QueryBlockNbt = 0x01 - QueryEntityNbt = 0x0d + PositionLook = 0x13 + Look = 0x14 + Flying = 0x15 + VehicleMoveServerbound = 0x16 + SteerBoat = 0x17 + PickItem = 0x18 + CraftRecipeRequest = 0x19 + AbilitiesServerbound = 0x1a + BlockDig = 0x1b + EntityAction = 0x1c + SteerVehicle = 0x1d + DisplayedRecipe = 0x1e RecipeBook = 0x1f + NameItem = 0x20 ResourcePackReceive = 0x21 + AdvancementTab = 0x22 SelectTrade = 0x23 SetBeaconEffect = 0x24 - SetCreativeSlot = 0x28 - SetDifficulty = 0x02 - Settings = 0x05 - Spectate = 0x2d - SteerBoat = 0x17 - SteerVehicle = 0x1d - TabCompleteServerbound = 0x06 - TeleportConfirm = 0x00 - TransactionServerbound = 0x07 + HeldItemSlotServerbound = 0x25 UpdateCommandBlock = 0x26 UpdateCommandBlockMinecart = 0x27 + SetCreativeSlot = 0x28 UpdateJigsawBlock = 0x29 - UpdateSign = 0x2b UpdateStructureBlock = 0x2a - UseEntity = 0x0e + UpdateSign = 0x2b + ArmAnimation = 0x2c + Spectate = 0x2d + BlockPlace = 0x2e 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 )