Move decode of complex packets into their own package
This commit is contained in:
329
bot/ingame.go
329
bot/ingame.go
@ -2,18 +2,15 @@ package bot
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
|
||||||
"github.com/Tnze/go-mc/bot/world"
|
"github.com/Tnze/go-mc/bot/world"
|
||||||
"github.com/Tnze/go-mc/bot/world/entity"
|
|
||||||
"github.com/Tnze/go-mc/chat"
|
"github.com/Tnze/go-mc/chat"
|
||||||
"github.com/Tnze/go-mc/data"
|
"github.com/Tnze/go-mc/data"
|
||||||
"github.com/Tnze/go-mc/nbt"
|
|
||||||
pk "github.com/Tnze/go-mc/net/packet"
|
pk "github.com/Tnze/go-mc/net/packet"
|
||||||
|
"github.com/Tnze/go-mc/net/ptypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
// //GetPosition return the player's position
|
// //GetPosition return the player's position
|
||||||
@ -166,44 +163,32 @@ func (c *Client) handlePacket(p pk.Packet) (disconnect bool, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handleSoundEffect(c *Client, p pk.Packet) error {
|
func handleSoundEffect(c *Client, p pk.Packet) error {
|
||||||
var (
|
var s ptypes.SoundEffect
|
||||||
SoundID pk.VarInt
|
if err := s.Decode(p); err != nil {
|
||||||
SoundCategory pk.VarInt
|
|
||||||
x, y, z pk.Int
|
|
||||||
Volume, Pitch pk.Float
|
|
||||||
)
|
|
||||||
err := p.Scan(&SoundID, &SoundCategory, &x, &y, &z, &Volume, &Pitch)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Events.SoundPlay != nil {
|
if c.Events.SoundPlay != nil {
|
||||||
err = c.Events.SoundPlay(
|
return c.Events.SoundPlay(
|
||||||
data.SoundNames[SoundID], int(SoundCategory),
|
data.SoundNames[s.Sound], int(s.Category),
|
||||||
float64(x)/8, float64(y)/8, float64(z)/8,
|
float64(s.X)/8, float64(s.Y)/8, float64(s.Z)/8,
|
||||||
float32(Volume), float32(Pitch))
|
float32(s.Volume), float32(s.Pitch))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleNamedSoundEffect(c *Client, p pk.Packet) error {
|
func handleNamedSoundEffect(c *Client, p pk.Packet) error {
|
||||||
var (
|
var s ptypes.NamedSoundEffect
|
||||||
SoundName pk.String
|
if err := s.Decode(p); err != nil {
|
||||||
SoundCategory pk.VarInt
|
|
||||||
x, y, z pk.Int
|
|
||||||
Volume, Pitch pk.Float
|
|
||||||
)
|
|
||||||
err := p.Scan(&SoundName, &SoundCategory, &x, &y, &z, &Volume, &Pitch)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Events.SoundPlay != nil {
|
if c.Events.SoundPlay != nil {
|
||||||
err = c.Events.SoundPlay(
|
return c.Events.SoundPlay(
|
||||||
string(SoundName), int(SoundCategory),
|
string(s.Sound), int(s.Category),
|
||||||
float64(x)/8, float64(y)/8, float64(z)/8,
|
float64(s.X)/8, float64(s.Y)/8, float64(s.Z)/8,
|
||||||
float32(Volume), float32(Pitch))
|
float32(s.Volume), float32(s.Pitch))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -227,16 +212,12 @@ func handleSetSlotPacket(c *Client, p pk.Packet) error {
|
|||||||
if c.Events.WindowsItemChange == nil {
|
if c.Events.WindowsItemChange == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var (
|
var pkt ptypes.SetSlot
|
||||||
windowID pk.Byte
|
if err := pkt.Decode(p); err != nil {
|
||||||
slotI pk.Short
|
|
||||||
slot entity.Slot
|
|
||||||
)
|
|
||||||
if err := p.Scan(&windowID, &slotI, &slot); err != nil && !errors.Is(err, nbt.ErrEND) {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Events.WindowsItemChange(byte(windowID), int(slotI), slot)
|
return c.Events.WindowsItemChange(byte(pkt.WindowID), int(pkt.Slot), pkt.SlotData)
|
||||||
}
|
}
|
||||||
|
|
||||||
// func handleMultiBlockChangePacket(c *Client, p pk.Packet) error {
|
// func handleMultiBlockChangePacket(c *Client, p pk.Packet) error {
|
||||||
@ -303,136 +284,80 @@ func handleSetSlotPacket(c *Client, p pk.Packet) error {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
func handleChatMessagePacket(c *Client, p pk.Packet) (err error) {
|
func handleChatMessagePacket(c *Client, p pk.Packet) (err error) {
|
||||||
var (
|
var msg ptypes.ChatMessageClientbound
|
||||||
s chat.Message
|
if err := msg.Decode(p); err != nil {
|
||||||
pos pk.Byte
|
|
||||||
sender pk.UUID
|
|
||||||
)
|
|
||||||
|
|
||||||
err = p.Scan(&s, &pos, &sender)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Events.ChatMsg != nil {
|
if c.Events.ChatMsg != nil {
|
||||||
err = c.Events.ChatMsg(s, byte(pos), uuid.UUID(sender))
|
return c.Events.ChatMsg(msg.S, byte(msg.Pos), uuid.UUID(msg.Sender))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleUpdateHealthPacket(c *Client, p pk.Packet) error {
|
||||||
|
var pkt ptypes.UpdateHealth
|
||||||
|
if err := pkt.Decode(p); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleUpdateHealthPacket(c *Client, p pk.Packet) (err error) {
|
c.Health = float32(pkt.Health)
|
||||||
var (
|
c.Food = int32(pkt.Food)
|
||||||
Health pk.Float
|
c.FoodSaturation = float32(pkt.FoodSaturation)
|
||||||
Food pk.VarInt
|
|
||||||
FoodSaturation pk.Float
|
|
||||||
)
|
|
||||||
|
|
||||||
err = p.Scan(&Health, &Food, &FoodSaturation)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Health = float32(Health)
|
|
||||||
c.Food = int32(Food)
|
|
||||||
c.FoodSaturation = float32(FoodSaturation)
|
|
||||||
|
|
||||||
if c.Events.HealthChange != nil {
|
if c.Events.HealthChange != nil {
|
||||||
err = c.Events.HealthChange()
|
if err := c.Events.HealthChange(); err != nil {
|
||||||
if err != nil {
|
return err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if c.Health < 1 { //player is dead
|
if c.Health < 1 { //player is dead
|
||||||
sendPlayerPositionAndLookPacket(c)
|
sendPlayerPositionAndLookPacket(c)
|
||||||
if c.Events.Die != nil {
|
if c.Events.Die != nil {
|
||||||
err = c.Events.Die()
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleJoinGamePacket(c *Client, p pk.Packet) error {
|
if err := c.Events.Die(); err != nil {
|
||||||
var (
|
|
||||||
eid pk.Int
|
|
||||||
hardcore pk.Boolean
|
|
||||||
gamemode pk.UnsignedByte
|
|
||||||
previousGm pk.UnsignedByte
|
|
||||||
worldCount pk.VarInt
|
|
||||||
worldNames pk.Identifier
|
|
||||||
_ pk.NBT
|
|
||||||
//dimensionCodec pk.NBT
|
|
||||||
dimension pk.Int
|
|
||||||
worldName pk.Identifier
|
|
||||||
hashedSeed pk.Long
|
|
||||||
maxPlayers pk.VarInt
|
|
||||||
viewDistance pk.VarInt
|
|
||||||
rdi pk.Boolean // Reduced Debug Info
|
|
||||||
ers pk.Boolean // Enable respawn screen
|
|
||||||
isDebug pk.Boolean
|
|
||||||
isFlat pk.Boolean
|
|
||||||
)
|
|
||||||
err := p.Scan(&eid, &hardcore, &gamemode, &previousGm, &worldCount, &worldNames, &dimension, &worldName,
|
|
||||||
&hashedSeed, &maxPlayers, &rdi, &ers, &isDebug, &isFlat)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
c.EntityID = int(eid)
|
}
|
||||||
c.Gamemode = int(gamemode & 0x7)
|
|
||||||
c.Hardcore = gamemode&0x8 != 0
|
|
||||||
c.Dimension = int(dimension)
|
|
||||||
c.WorldName = string(worldName)
|
|
||||||
c.ViewDistance = int(viewDistance)
|
|
||||||
c.ReducedDebugInfo = bool(rdi)
|
|
||||||
c.IsDebug = bool(isDebug)
|
|
||||||
c.IsFlat = bool(isFlat)
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// The PluginMessageData only used in recive PluginMessage packet.
|
func handleJoinGamePacket(c *Client, p pk.Packet) error {
|
||||||
// When decode it, read to end.
|
var pkt ptypes.JoinGame
|
||||||
type pluginMessageData []byte
|
if err := pkt.Decode(p); err != nil {
|
||||||
|
|
||||||
//Encode a PluginMessageData
|
|
||||||
func (p pluginMessageData) Encode() []byte {
|
|
||||||
return []byte(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
//Decode a PluginMessageData
|
|
||||||
func (p *pluginMessageData) Decode(r pk.DecodeReader) error {
|
|
||||||
data, err := ioutil.ReadAll(r)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
*p = data
|
|
||||||
|
c.EntityID = int(pkt.PlayerEntity)
|
||||||
|
c.Gamemode = int(pkt.Gamemode & 0x7)
|
||||||
|
c.Hardcore = pkt.Gamemode&0x8 != 0
|
||||||
|
c.Dimension = int(pkt.Dimension)
|
||||||
|
c.WorldName = string(pkt.WorldName)
|
||||||
|
c.ViewDistance = int(pkt.ViewDistance)
|
||||||
|
c.ReducedDebugInfo = bool(pkt.RDI)
|
||||||
|
c.IsDebug = bool(pkt.IsDebug)
|
||||||
|
c.IsFlat = bool(pkt.IsFlat)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handlePluginPacket(c *Client, p pk.Packet) error {
|
func handlePluginPacket(c *Client, p pk.Packet) error {
|
||||||
var (
|
var msg ptypes.PluginMessage
|
||||||
Channel pk.Identifier
|
if err := msg.Decode(p); err != nil {
|
||||||
Data pluginMessageData
|
|
||||||
)
|
|
||||||
if err := p.Scan(&Channel, &Data); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
switch Channel {
|
switch msg.Channel {
|
||||||
case "minecraft:brand":
|
case "minecraft:brand":
|
||||||
var brandRaw pk.String
|
var brandRaw pk.String
|
||||||
if err := brandRaw.Decode(bytes.NewReader(Data)); err != nil {
|
if err := brandRaw.Decode(bytes.NewReader(msg.Data)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.ServInfo.Brand = string(brandRaw)
|
c.ServInfo.Brand = string(brandRaw)
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Events.PluginMessage != nil {
|
if c.Events.PluginMessage != nil {
|
||||||
return c.Events.PluginMessage(string(Channel), []byte(Data))
|
return c.Events.PluginMessage(string(msg.Channel), []byte(msg.Data))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -492,133 +417,56 @@ func handleChunkDataPacket(c *Client, p pk.Packet) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var pkt ptypes.ChunkData
|
||||||
X, Z pk.Int
|
if err := pkt.Decode(p); err != nil {
|
||||||
FullChunk pk.Boolean
|
|
||||||
PrimaryBitMask pk.VarInt
|
|
||||||
Heightmaps struct{}
|
|
||||||
Biomes = biomesData{fullChunk: (*bool)(&FullChunk)}
|
|
||||||
Data chunkData
|
|
||||||
BlockEntities blockEntities
|
|
||||||
)
|
|
||||||
if err := p.Scan(&X, &Z, &FullChunk, &PrimaryBitMask, pk.NBT{V: &Heightmaps}, &Biomes, &Data, &BlockEntities); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
chunk, err := world.DecodeChunkColumn(int32(PrimaryBitMask), Data)
|
|
||||||
|
chunk, err := world.DecodeChunkColumn(int32(pkt.PrimaryBitMask), pkt.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("decode chunk column fail: %w", err)
|
return fmt.Errorf("decode chunk column: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Wd.LoadChunk(int(X), int(Z), chunk)
|
c.Wd.LoadChunk(int(pkt.X), int(pkt.Z), chunk)
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
type biomesData struct {
|
|
||||||
fullChunk *bool
|
|
||||||
data []pk.VarInt
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *biomesData) Decode(r pk.DecodeReader) error {
|
|
||||||
if b.fullChunk == nil || !*b.fullChunk {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var nobd pk.VarInt // Number of Biome Datums
|
|
||||||
if err := nobd.Decode(r); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
b.data = make([]pk.VarInt, nobd)
|
|
||||||
|
|
||||||
for i := 0; i < int(nobd); i++ {
|
|
||||||
var d pk.VarInt
|
|
||||||
if err := d.Decode(r); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
b.data[i] = d
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type chunkData []byte
|
|
||||||
type blockEntities []blockEntitie
|
|
||||||
type blockEntitie struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode implement net.packet.FieldDecoder
|
|
||||||
func (c *chunkData) Decode(r pk.DecodeReader) error {
|
|
||||||
var Size pk.VarInt
|
|
||||||
if err := Size.Decode(r); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*c = make([]byte, Size)
|
|
||||||
if _, err := r.Read(*c); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode implement net.packet.FieldDecoder
|
|
||||||
func (b *blockEntities) Decode(r pk.DecodeReader) error {
|
|
||||||
var nobe pk.VarInt // Number of BlockEntities
|
|
||||||
if err := nobe.Decode(r); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*b = make(blockEntities, nobe)
|
|
||||||
decoder := nbt.NewDecoder(r)
|
|
||||||
for i := 0; i < int(nobe); i++ {
|
|
||||||
if err := decoder.Decode(&(*b)[i]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handlePlayerPositionAndLookPacket(c *Client, p pk.Packet) error {
|
func handlePlayerPositionAndLookPacket(c *Client, p pk.Packet) error {
|
||||||
var (
|
var pkt ptypes.PositionAndLookClientbound
|
||||||
x, y, z pk.Double
|
if err := pkt.Decode(p); err != nil {
|
||||||
yaw, pitch pk.Float
|
|
||||||
flags pk.Byte
|
|
||||||
TeleportID pk.VarInt
|
|
||||||
)
|
|
||||||
|
|
||||||
err := p.Scan(&x, &y, &z, &yaw, &pitch, &flags, &TeleportID)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if flags&0x01 == 0 {
|
if pkt.RelativeX() {
|
||||||
c.X = float64(x)
|
c.X = float64(pkt.X)
|
||||||
} else {
|
} else {
|
||||||
c.X += float64(x)
|
c.X += float64(pkt.X)
|
||||||
}
|
}
|
||||||
if flags&0x02 == 0 {
|
if pkt.RelativeY() {
|
||||||
c.Y = float64(y)
|
c.Y = float64(pkt.Y)
|
||||||
} else {
|
} else {
|
||||||
c.Y += float64(y)
|
c.Y += float64(pkt.Y)
|
||||||
}
|
}
|
||||||
if flags&0x04 == 0 {
|
if pkt.RelativeZ() {
|
||||||
c.Z = float64(z)
|
c.Z = float64(pkt.Z)
|
||||||
} else {
|
} else {
|
||||||
c.Z += float64(z)
|
c.Z += float64(pkt.Z)
|
||||||
}
|
}
|
||||||
if flags&0x08 == 0 {
|
if pkt.RelativeYaw() {
|
||||||
c.Yaw = float32(yaw)
|
c.Yaw = float32(pkt.Yaw)
|
||||||
} else {
|
} else {
|
||||||
c.Yaw += float32(yaw)
|
c.Yaw += float32(pkt.Yaw)
|
||||||
}
|
}
|
||||||
if flags&0x10 == 0 {
|
if pkt.RelativePitch() {
|
||||||
c.Pitch = float32(pitch)
|
c.Pitch = float32(pkt.Pitch)
|
||||||
} else {
|
} else {
|
||||||
c.Pitch += float32(pitch)
|
c.Pitch += float32(pkt.Pitch)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Confirm
|
//Confirm
|
||||||
return c.conn.WritePacket(pk.Marshal(
|
return c.conn.WritePacket(pk.Marshal(
|
||||||
data.TeleportConfirm,
|
data.TeleportConfirm,
|
||||||
pk.VarInt(TeleportID),
|
pk.VarInt(pkt.TeleportID),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -635,35 +483,20 @@ func handleKeepAlivePacket(c *Client, p pk.Packet) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handleWindowItemsPacket(c *Client, p pk.Packet) error {
|
func handleWindowItemsPacket(c *Client, p pk.Packet) error {
|
||||||
r := bytes.NewReader(p.Data)
|
var pkt ptypes.WindowItems
|
||||||
var (
|
if err := pkt.Decode(p); err != nil {
|
||||||
windowID pk.Byte
|
|
||||||
count pk.Short
|
|
||||||
slots []entity.Slot
|
|
||||||
)
|
|
||||||
if err := windowID.Decode(r); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := count.Decode(r); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
for i := 0; i < int(count); i++ {
|
|
||||||
var slot entity.Slot
|
|
||||||
if err := slot.Decode(r); err != nil && !errors.Is(err, nbt.ErrEND) {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
slots = append(slots, slot)
|
|
||||||
}
|
|
||||||
|
|
||||||
if windowID == 0 { // Window ID 0 is the players' inventory.
|
if pkt.WindowID == 0 { // Window ID 0 is the players' inventory.
|
||||||
if err := c.Events.updateSeenPackets(seenPlayerInventory); err != nil {
|
if err := c.Events.updateSeenPackets(seenPlayerInventory); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if c.Events.WindowsItem == nil {
|
if c.Events.WindowsItem != nil {
|
||||||
return nil
|
return c.Events.WindowsItem(byte(pkt.WindowID), pkt.Slots)
|
||||||
}
|
}
|
||||||
return c.Events.WindowsItem(byte(windowID), slots)
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleSetExperience(c *Client, p pk.Packet) (err error) {
|
func handleSetExperience(c *Client, p pk.Packet) (err error) {
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/Tnze/go-mc/data"
|
"github.com/Tnze/go-mc/data"
|
||||||
pk "github.com/Tnze/go-mc/net/packet"
|
pk "github.com/Tnze/go-mc/net/packet"
|
||||||
|
"github.com/Tnze/go-mc/net/ptypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SwingArm swing player's arm.
|
// SwingArm swing player's arm.
|
||||||
@ -83,12 +84,11 @@ func (c *Client) Chat(msg string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// PluginMessage is used by mods and plugins to send their data.
|
// PluginMessage is used by mods and plugins to send their data.
|
||||||
func (c *Client) PluginMessage(channal string, msg []byte) error {
|
func (c *Client) PluginMessage(channel string, msg []byte) error {
|
||||||
return c.conn.WritePacket(pk.Marshal(
|
return c.conn.WritePacket((&ptypes.PluginMessage{
|
||||||
data.CustomPayloadServerbound,
|
Channel: pk.Identifier(channel),
|
||||||
pk.Identifier(channal),
|
Data: ptypes.PluginData(msg),
|
||||||
pluginMessageData(msg),
|
}).Encode())
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UseBlock is used to place or use a block.
|
// UseBlock is used to place or use a block.
|
||||||
|
110
net/ptypes/chunk.go
Normal file
110
net/ptypes/chunk.go
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
// Package ptypes implements encoding and decoding for high-level packets.
|
||||||
|
package ptypes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/Tnze/go-mc/nbt"
|
||||||
|
pk "github.com/Tnze/go-mc/net/packet"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ChunkData is a clientbound packet which describes a chunk.
|
||||||
|
type ChunkData struct {
|
||||||
|
X, Z pk.Int
|
||||||
|
FullChunk pk.Boolean
|
||||||
|
PrimaryBitMask pk.VarInt
|
||||||
|
Heightmaps struct{}
|
||||||
|
Biomes biomesData
|
||||||
|
Data chunkData
|
||||||
|
BlockEntities blockEntities
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ChunkData) Decode(pkt pk.Packet) error {
|
||||||
|
r := bytes.NewReader(pkt.Data)
|
||||||
|
if err := p.X.Decode(r); err != nil {
|
||||||
|
return fmt.Errorf("X: %v", err)
|
||||||
|
}
|
||||||
|
if err := p.Z.Decode(r); err != nil {
|
||||||
|
return fmt.Errorf("Z: %v", err)
|
||||||
|
}
|
||||||
|
if err := p.FullChunk.Decode(r); err != nil {
|
||||||
|
return fmt.Errorf("full chunk: %v", err)
|
||||||
|
}
|
||||||
|
if err := p.PrimaryBitMask.Decode(r); err != nil {
|
||||||
|
return fmt.Errorf("bit mask: %v", err)
|
||||||
|
}
|
||||||
|
if err := (pk.NBT{V: &p.Heightmaps}).Decode(r); err != nil {
|
||||||
|
return fmt.Errorf("heightmaps: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Biome data is only present for full chunks.
|
||||||
|
if p.FullChunk {
|
||||||
|
if err := p.Biomes.Decode(r); err != nil {
|
||||||
|
return fmt.Errorf("heightmaps: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := p.Data.Decode(r); err != nil {
|
||||||
|
return fmt.Errorf("data: %v", err)
|
||||||
|
}
|
||||||
|
if err := p.BlockEntities.Decode(r); err != nil {
|
||||||
|
return fmt.Errorf("block entities: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type biomesData struct {
|
||||||
|
data []pk.VarInt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *biomesData) Decode(r pk.DecodeReader) error {
|
||||||
|
var nobd pk.VarInt // Number of Biome Datums
|
||||||
|
if err := nobd.Decode(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
b.data = make([]pk.VarInt, nobd)
|
||||||
|
|
||||||
|
for i := 0; i < int(nobd); i++ {
|
||||||
|
var d pk.VarInt
|
||||||
|
if err := d.Decode(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
b.data[i] = d
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type chunkData []byte
|
||||||
|
type blockEntities []blockEntity
|
||||||
|
type blockEntity struct{}
|
||||||
|
|
||||||
|
// Decode implement net.packet.FieldDecoder
|
||||||
|
func (c *chunkData) Decode(r pk.DecodeReader) error {
|
||||||
|
var sz pk.VarInt
|
||||||
|
if err := sz.Decode(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*c = make([]byte, sz)
|
||||||
|
if _, err := r.Read(*c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode implement net.packet.FieldDecoder
|
||||||
|
func (b *blockEntities) Decode(r pk.DecodeReader) error {
|
||||||
|
var sz pk.VarInt // Number of BlockEntities
|
||||||
|
if err := sz.Decode(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*b = make(blockEntities, sz)
|
||||||
|
decoder := nbt.NewDecoder(r)
|
||||||
|
for i := 0; i < int(sz); i++ {
|
||||||
|
if err := decoder.Decode(&(*b)[i]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
54
net/ptypes/inventory.go
Normal file
54
net/ptypes/inventory.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
// Package ptypes implements encoding and decoding for high-level packets.
|
||||||
|
package ptypes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/Tnze/go-mc/bot/world/entity"
|
||||||
|
"github.com/Tnze/go-mc/nbt"
|
||||||
|
pk "github.com/Tnze/go-mc/net/packet"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetSlot is a clientbound packet which configures an inventory slot.
|
||||||
|
// A window ID of -1 represents the cursor, and a window ID of 0 represents
|
||||||
|
// the players inventory.
|
||||||
|
type SetSlot struct {
|
||||||
|
WindowID pk.Byte
|
||||||
|
Slot pk.Short
|
||||||
|
SlotData entity.Slot
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SetSlot) Decode(pkt pk.Packet) error {
|
||||||
|
if err := pkt.Scan(&p.WindowID, &p.Slot, &p.SlotData); err != nil && !errors.Is(err, nbt.ErrEND) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WindowItems is a clientbound packet describing the contents of multiple
|
||||||
|
// inventory slots in a window/inventory.
|
||||||
|
type WindowItems struct {
|
||||||
|
WindowID pk.Byte
|
||||||
|
Slots []entity.Slot
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *WindowItems) Decode(pkt pk.Packet) error {
|
||||||
|
r := bytes.NewReader(pkt.Data)
|
||||||
|
if err := p.WindowID.Decode(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var count pk.Short
|
||||||
|
if err := count.Decode(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Slots = make([]entity.Slot, int(count))
|
||||||
|
for i := 0; i < int(count); i++ {
|
||||||
|
if err := p.Slots[i].Decode(r); err != nil && !errors.Is(err, nbt.ErrEND) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
91
net/ptypes/misc.go
Normal file
91
net/ptypes/misc.go
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
package ptypes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
|
"github.com/Tnze/go-mc/chat"
|
||||||
|
"github.com/Tnze/go-mc/data"
|
||||||
|
pk "github.com/Tnze/go-mc/net/packet"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SoundEffect is a clientbound packet used to play a specific sound ID
|
||||||
|
// on the client.
|
||||||
|
type SoundEffect struct {
|
||||||
|
Sound pk.VarInt
|
||||||
|
Category pk.VarInt
|
||||||
|
X, Y, Z pk.Int
|
||||||
|
Volume, Pitch pk.Float
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *SoundEffect) Decode(pkt pk.Packet) error {
|
||||||
|
return pkt.Scan(&p.Sound, &p.Category, &p.X, &p.Y, &p.Z, &p.Volume, &p.Pitch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NamedSoundEffect is a clientbound packet used to play a sound with the
|
||||||
|
// specified name on the client.
|
||||||
|
type NamedSoundEffect struct {
|
||||||
|
Sound pk.String
|
||||||
|
Category pk.VarInt
|
||||||
|
X, Y, Z pk.Int
|
||||||
|
Volume, Pitch pk.Float
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *NamedSoundEffect) Decode(pkt pk.Packet) error {
|
||||||
|
return pkt.Scan(&p.Sound, &p.Category, &p.X, &p.Y, &p.Z, &p.Volume, &p.Pitch)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChatMessageClientbound represents a chat message forwarded by the server.
|
||||||
|
type ChatMessageClientbound struct {
|
||||||
|
S chat.Message
|
||||||
|
Pos pk.Byte
|
||||||
|
Sender pk.UUID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ChatMessageClientbound) Decode(pkt pk.Packet) error {
|
||||||
|
return pkt.Scan(&p.S, &p.Pos, &p.Sender)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateHealth encodes player health/food information from the server.
|
||||||
|
type UpdateHealth struct {
|
||||||
|
Health pk.Float
|
||||||
|
Food pk.VarInt
|
||||||
|
FoodSaturation pk.Float
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *UpdateHealth) Decode(pkt pk.Packet) error {
|
||||||
|
return pkt.Scan(&p.Health, &p.Food, &p.FoodSaturation)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginData encodes the custom data encoded in a plugin message.
|
||||||
|
type PluginData []byte
|
||||||
|
|
||||||
|
func (p PluginData) Encode() []byte {
|
||||||
|
return []byte(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PluginData) Decode(r pk.DecodeReader) error {
|
||||||
|
data, err := ioutil.ReadAll(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*p = data
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PluginMessage represents a packet with a customized payload.
|
||||||
|
type PluginMessage struct {
|
||||||
|
Channel pk.Identifier
|
||||||
|
Data PluginData
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PluginMessage) Decode(pkt pk.Packet) error {
|
||||||
|
return pkt.Scan(&p.Channel, &p.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PluginMessage) Encode() pk.Packet {
|
||||||
|
return pk.Marshal(
|
||||||
|
data.CustomPayloadServerbound,
|
||||||
|
p.Channel,
|
||||||
|
p.Data,
|
||||||
|
)
|
||||||
|
}
|
35
net/ptypes/motion.go
Normal file
35
net/ptypes/motion.go
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Package ptypes implements encoding and decoding for high-level packets.
|
||||||
|
package ptypes
|
||||||
|
|
||||||
|
import (
|
||||||
|
pk "github.com/Tnze/go-mc/net/packet"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PositionAndLookClientbound describes the location and orientation of
|
||||||
|
// the player.
|
||||||
|
type PositionAndLookClientbound struct {
|
||||||
|
X, Y, Z pk.Double
|
||||||
|
Yaw, Pitch pk.Float
|
||||||
|
Flags pk.Byte
|
||||||
|
TeleportID pk.VarInt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PositionAndLookClientbound) RelativeX() bool {
|
||||||
|
return p.Flags&0x01 != 0
|
||||||
|
}
|
||||||
|
func (p *PositionAndLookClientbound) RelativeY() bool {
|
||||||
|
return p.Flags&0x02 != 0
|
||||||
|
}
|
||||||
|
func (p *PositionAndLookClientbound) RelativeZ() bool {
|
||||||
|
return p.Flags&0x04 != 0
|
||||||
|
}
|
||||||
|
func (p *PositionAndLookClientbound) RelativeYaw() bool {
|
||||||
|
return p.Flags&0x08 != 0
|
||||||
|
}
|
||||||
|
func (p *PositionAndLookClientbound) RelativePitch() bool {
|
||||||
|
return p.Flags&0x10 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PositionAndLookClientbound) Decode(pkt pk.Packet) error {
|
||||||
|
return pkt.Scan(&p.X, &p.Y, &p.Z, &p.Yaw, &p.Pitch, &p.Flags, &p.TeleportID)
|
||||||
|
}
|
32
net/ptypes/world.go
Normal file
32
net/ptypes/world.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package ptypes
|
||||||
|
|
||||||
|
import (
|
||||||
|
pk "github.com/Tnze/go-mc/net/packet"
|
||||||
|
)
|
||||||
|
|
||||||
|
// JoinGame encodes global/world information from the server.
|
||||||
|
type JoinGame struct {
|
||||||
|
PlayerEntity pk.Int
|
||||||
|
Hardcore pk.Boolean
|
||||||
|
Gamemode pk.UnsignedByte
|
||||||
|
PrevGamemode pk.UnsignedByte
|
||||||
|
WorldCount pk.VarInt
|
||||||
|
WorldNames pk.Identifier
|
||||||
|
//DimensionCodec pk.NBT
|
||||||
|
Dimension pk.Int
|
||||||
|
WorldName pk.Identifier
|
||||||
|
HashedSeed pk.Long
|
||||||
|
maxPlayers pk.VarInt // Now ignored
|
||||||
|
ViewDistance pk.VarInt
|
||||||
|
RDI pk.Boolean // Reduced Debug Info
|
||||||
|
ERS pk.Boolean // Enable respawn screen
|
||||||
|
IsDebug pk.Boolean
|
||||||
|
IsFlat pk.Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *JoinGame) Decode(pkt pk.Packet) error {
|
||||||
|
return pkt.Scan(&p.PlayerEntity, &p.Hardcore, &p.Gamemode, &p.PrevGamemode,
|
||||||
|
&p.WorldCount, &p.WorldNames, &p.Dimension,
|
||||||
|
&p.WorldName, &p.HashedSeed, &p.maxPlayers, &p.ViewDistance,
|
||||||
|
&p.RDI, &p.ERS, &p.IsDebug, &p.IsFlat)
|
||||||
|
}
|
Reference in New Issue
Block a user