693 lines
15 KiB
Go
693 lines
15 KiB
Go
package bot
|
|
|
|
import (
|
|
// "bytes"
|
|
// "math"
|
|
// "time"
|
|
"fmt"
|
|
|
|
"github.com/Tnze/go-mc/chat"
|
|
"github.com/Tnze/go-mc/data"
|
|
pk "github.com/Tnze/go-mc/net/packet"
|
|
// "github.com/Tnze/gomcbot/world/entity"
|
|
)
|
|
|
|
// //GetPosition return the player's position
|
|
// func (p *Player) GetPosition() (x, y, z float64) {
|
|
// return p.X, p.Y, p.Z
|
|
// }
|
|
|
|
// //GetBlockPos return the position of the Block at player's feet
|
|
// func (p *Player) GetBlockPos() (x, y, z int) {
|
|
// return int(math.Floor(p.X)), int(math.Floor(p.Y)), int(math.Floor(p.Z))
|
|
// }
|
|
|
|
// HandleGame recive server packet and response them correctly.
|
|
// Note that HandleGame will block if you don't recive from Events.
|
|
func (c *Client) HandleGame() error {
|
|
for {
|
|
//Read packets
|
|
p, err := c.conn.ReadPacket()
|
|
if err != nil {
|
|
return fmt.Errorf("bot: read packet fail: %v", err)
|
|
}
|
|
//handle packets
|
|
disconnect, err := c.handlePacket(p)
|
|
if err != nil {
|
|
return fmt.Errorf("handle packet 0x%X error: %v", p.ID, err)
|
|
}
|
|
if disconnect {
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c *Client) handlePacket(p pk.Packet) (disconnect bool, err error) {
|
|
switch p.ID {
|
|
case data.JoinGame:
|
|
err = handleJoinGamePacket(c, p)
|
|
|
|
if err == nil && c.Events.GameStart != nil {
|
|
err = c.Events.GameStart()
|
|
}
|
|
case data.PluginMessageClientbound:
|
|
err = handlePluginPacket(c, p)
|
|
case data.ServerDifficulty:
|
|
err = handleServerDifficultyPacket(c, p)
|
|
case data.SpawnPosition:
|
|
err = handleSpawnPositionPacket(c, p)
|
|
case data.PlayerAbilitiesClientbound:
|
|
err = handlePlayerAbilitiesPacket(c, p)
|
|
c.conn.WritePacket(
|
|
//ClientSettings packet (serverbound)
|
|
pk.Marshal(
|
|
data.ClientSettings,
|
|
pk.String(c.settings.Locale),
|
|
pk.Byte(c.settings.ViewDistance),
|
|
pk.VarInt(c.settings.ChatMode),
|
|
pk.Boolean(c.settings.ChatColors),
|
|
pk.UnsignedByte(c.settings.DisplayedSkinParts),
|
|
pk.VarInt(c.settings.MainHand),
|
|
),
|
|
)
|
|
case data.HeldItemChangeClientbound:
|
|
err = handleHeldItemPacket(c, p)
|
|
case data.ChunkData:
|
|
////err = handleChunkDataPacket(c, p)
|
|
case data.PlayerPositionAndLookClientbound:
|
|
err = handlePlayerPositionAndLookPacket(c, p)
|
|
sendPlayerPositionAndLookPacket(c) // to confirm the position
|
|
case 0x5A:
|
|
// handleDeclareRecipesPacket(g, reader)
|
|
case 0x29:
|
|
// err = handleEntityLookAndRelativeMove(g, reader)
|
|
case 0x3B:
|
|
// handleEntityHeadLookPacket(g, reader)
|
|
case 0x28:
|
|
// err = handleEntityRelativeMovePacket(g, reader)
|
|
case data.KeepAliveClientbound:
|
|
err = handleKeepAlivePacket(c, p)
|
|
case 0x2B:
|
|
//handleEntityPacket(g, reader)
|
|
case 0x05:
|
|
// err = handleSpawnPlayerPacket(g, reader)
|
|
case data.WindowItems:
|
|
err = handleWindowItemsPacket(c, p)
|
|
case data.UpdateHealth:
|
|
//// err = handleUpdateHealthPacket(c, p)
|
|
case data.ChatMessageClientbound:
|
|
err = handleChatMessagePacket(c, p)
|
|
case data.BlockChange:
|
|
////err = handleBlockChangePacket(c, p)
|
|
case data.MultiBlockChange:
|
|
////err = handleMultiBlockChangePacket(c, p)
|
|
case 0x1A:
|
|
err = handleDisconnectPacket(c, p)
|
|
disconnect = true
|
|
case 0x16:
|
|
// err = handleSetSlotPacket(g, reader)
|
|
case 0x51:
|
|
// err = handleSoundEffect(g, reader)
|
|
default:
|
|
// fmt.Printf("ignore pack id %X\n", p.ID)
|
|
}
|
|
return
|
|
}
|
|
|
|
// func handleSoundEffect(g *Client, r *bytes.Reader) error {
|
|
// SoundID, err := pk.UnpackVarInt(r)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// SoundCategory, err := pk.UnpackVarInt(r)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
|
|
// x, err := pk.UnpackInt32(r)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// y, err := pk.UnpackInt32(r)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// z, err := pk.UnpackInt32(r)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// Volume, err := pk.UnpackFloat(r)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// Pitch, err := pk.UnpackFloat(r)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// g.events <- SoundEffectEvent{SoundID, SoundCategory, float64(x) / 8, float64(y) / 8, float64(z) / 8, Volume, Pitch}
|
|
|
|
// return nil
|
|
// }
|
|
|
|
func handleDisconnectPacket(c *Client, p pk.Packet) error {
|
|
var rowReason pk.String
|
|
err := p.Scan(&rowReason)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if c.Events.Disconnect != nil {
|
|
var reason chat.Message
|
|
err := reason.UnmarshalJSON([]byte(rowReason))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return c.Events.Disconnect(reason)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// func handleSetSlotPacket(g *Client, r *bytes.Reader) error {
|
|
// windowID, err := r.ReadByte()
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// slot, err := pk.UnpackInt16(r)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// slotData, err := unpackSolt(r)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
|
|
// switch int8(windowID) {
|
|
// case 0:
|
|
// if slot < 32 || slot > 44 {
|
|
// // return fmt.Errorf("slot out of range")
|
|
// break
|
|
// }
|
|
// fallthrough
|
|
// case -2:
|
|
// g.player.Inventory[slot] = slotData
|
|
// g.events <- InventoryChangeEvent(slot)
|
|
// }
|
|
// return nil
|
|
// }
|
|
|
|
// func handleMultiBlockChangePacket(c *Client, p pk.Packet) error {
|
|
// if !c.settings.ReciveMap {
|
|
// return nil
|
|
// }
|
|
|
|
// var cX, cY pk.Int
|
|
|
|
// err := p.Scan(&cX, &cY)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
|
|
// c := g.wd.chunks[chunkLoc{int(cX), int(cY)}]
|
|
// if c != nil {
|
|
// RecordCount, err := pk.UnpackVarInt(r)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
|
|
// for i := int32(0); i < RecordCount; i++ {
|
|
// xz, err := r.ReadByte()
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// y, err := r.ReadByte()
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// BlockID, err := pk.UnpackVarInt(r)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// x, z := xz>>4, xz&0x0F
|
|
|
|
// c.sections[y/16].blocks[x][y%16][z] = Block{id: uint(BlockID)}
|
|
// }
|
|
// }
|
|
|
|
// return nil
|
|
// }
|
|
|
|
// func handleBlockChangePacket(c *Client, p pk.Packet) error {
|
|
// if !c.settings.ReciveMap {
|
|
// return nil
|
|
// }
|
|
// var pos pk.Position
|
|
// err := p.Scan(&pos)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
|
|
// c := g.wd.chunks[chunkLoc{x >> 4, z >> 4}]
|
|
// if c != nil {
|
|
// id, err := pk.UnpackVarInt(r)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// c.sections[y/16].blocks[x&15][y&15][z&15] = Block{id: uint(id)}
|
|
// }
|
|
|
|
// return nil
|
|
// }
|
|
|
|
func handleChatMessagePacket(c *Client, p pk.Packet) error {
|
|
var (
|
|
s pk.String
|
|
pos pk.Byte
|
|
)
|
|
|
|
if err := p.Scan(&s, &pos); err != nil {
|
|
return err
|
|
}
|
|
|
|
var cm chat.Message
|
|
err := cm.UnmarshalJSON([]byte(s))
|
|
|
|
if err == nil && c.Events.ChatMsg != nil {
|
|
err = c.Events.ChatMsg(cm)
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
// func handleUpdateHealthPacket(c *Client, p pk.Packet) (err error) {
|
|
// var (
|
|
// Health pk.Float
|
|
// Food pk.VarInt
|
|
// FoodSaturation pk.Float
|
|
// )
|
|
|
|
// err = p.Scan(&Health, &Food, &FoodSaturation)
|
|
// if err != nil {
|
|
// return
|
|
// }
|
|
|
|
// c.player.Health = Health
|
|
// c.player.Food = Food
|
|
// c.player.FoodSaturation = FoodSaturation
|
|
|
|
// if c.player.Health < 1 { //player is dead
|
|
// sendPlayerPositionAndLookPacket(c)
|
|
// time.Sleep(time.Second * 2) //wait for 2 sec make it more like a human
|
|
// sendClientStatusPacket(c, 0) //status 0 means perform respawn
|
|
// }
|
|
// return
|
|
// }
|
|
|
|
func handleJoinGamePacket(c *Client, p pk.Packet) error {
|
|
var (
|
|
eid pk.Int
|
|
gamemode pk.UnsignedByte
|
|
dimension pk.Int
|
|
maxPlayers pk.UnsignedByte
|
|
levelType pk.String
|
|
viewDistance pk.VarInt
|
|
rdi pk.Boolean
|
|
)
|
|
err := p.Scan(&eid, &gamemode, &dimension, &maxPlayers, &levelType, &rdi)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
c.EntityID = int(eid)
|
|
c.Gamemode = int(gamemode & 0x7)
|
|
c.Hardcore = gamemode&0x8 != 0
|
|
c.Dimension = int(dimension)
|
|
c.LevelType = string(levelType)
|
|
c.ViewDistance = int(viewDistance)
|
|
c.ReducedDebugInfo = bool(rdi)
|
|
return nil
|
|
}
|
|
|
|
func handlePluginPacket(c *Client, p pk.Packet) error {
|
|
// fmt.Println("Plugin Packet: ", p)
|
|
return nil
|
|
}
|
|
|
|
func handleServerDifficultyPacket(c *Client, p pk.Packet) error {
|
|
var difficulty pk.Byte
|
|
err := p.Scan(&difficulty)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
c.Difficulty = int(difficulty)
|
|
return nil
|
|
}
|
|
|
|
func handleSpawnPositionPacket(c *Client, p pk.Packet) error {
|
|
var pos pk.Position
|
|
err := p.Scan(&pos)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// c.SpawnPosition.X, c.SpawnPosition.Y, c.SpawnPosition.Z =
|
|
// pos.X, pos.Y, pos.Z
|
|
return nil
|
|
}
|
|
|
|
func handlePlayerAbilitiesPacket(g *Client, p pk.Packet) error {
|
|
var (
|
|
flags pk.Byte
|
|
flySpeed pk.Float
|
|
viewMod pk.Float
|
|
)
|
|
err := p.Scan(&flags, &flySpeed, &viewMod)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
g.abilities.Flags = int8(flags)
|
|
g.abilities.FlyingSpeed = float32(flySpeed)
|
|
g.abilities.FieldofViewModifier = float32(viewMod)
|
|
return nil
|
|
}
|
|
|
|
func handleHeldItemPacket(c *Client, p pk.Packet) error {
|
|
var hi pk.Byte
|
|
if err := p.Scan(&hi); err != nil {
|
|
return err
|
|
}
|
|
c.HeldItem = int(hi)
|
|
return nil
|
|
}
|
|
|
|
// func handleChunkDataPacket(g *Client, p pk.Packet) error {
|
|
// if !g.settings.ReciveMap {
|
|
// return nil
|
|
// }
|
|
|
|
// c, x, y, err := unpackChunkDataPacket(p, g.Info.Dimension == 0)
|
|
// g.wd.chunks[chunkLoc{x, y}] = c
|
|
// return err
|
|
// }
|
|
|
|
// var isSpawn bool
|
|
|
|
func handlePlayerPositionAndLookPacket(c *Client, p pk.Packet) error {
|
|
var (
|
|
x, y, z pk.Double
|
|
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
|
|
}
|
|
|
|
if flags&0x01 == 0 {
|
|
c.X = float64(x)
|
|
} else {
|
|
c.X += float64(x)
|
|
}
|
|
if flags&0x02 == 0 {
|
|
c.Y = float64(y)
|
|
} else {
|
|
c.Y += float64(y)
|
|
}
|
|
if flags&0x04 == 0 {
|
|
c.Z = float64(z)
|
|
} else {
|
|
c.Z += float64(z)
|
|
}
|
|
if flags&0x08 == 0 {
|
|
c.Yaw = float32(yaw)
|
|
} else {
|
|
c.Yaw += float32(yaw)
|
|
}
|
|
if flags&0x10 == 0 {
|
|
c.Pitch = float32(pitch)
|
|
} else {
|
|
c.Pitch += float32(pitch)
|
|
}
|
|
|
|
//Confirm
|
|
return c.conn.WritePacket(pk.Marshal(
|
|
data.TeleportConfirm,
|
|
pk.VarInt(TeleportID),
|
|
))
|
|
}
|
|
|
|
// func handleDeclareRecipesPacket(g *Client, r *bytes.Reader) {
|
|
// //Ignore Declare Recipes Packet
|
|
|
|
// // NumRecipes, index := pk.UnpackVarInt(p.Data)
|
|
// // for i := 0; i < int(NumRecipes); i++ {
|
|
// // RecipeID, len := pk.UnpackString(p.Data[index:])
|
|
// // index += len
|
|
// // Type, len := pk.UnpackString(p.Data[index:])
|
|
// // index += len
|
|
// // switch Type {
|
|
// // case "crafting_shapeless":
|
|
// // }
|
|
// // }
|
|
// }
|
|
|
|
// func handleEntityLookAndRelativeMove(g *Client, r *bytes.Reader) error {
|
|
// ID, err := pk.UnpackVarInt(r)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// E := g.wd.Entities[ID]
|
|
// if E != nil {
|
|
// P, ok := E.(*Player)
|
|
// if !ok {
|
|
// return nil
|
|
// }
|
|
// DeltaX, err := pk.UnpackInt16(r)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// DeltaY, err := pk.UnpackInt16(r)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// DeltaZ, err := pk.UnpackInt16(r)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
|
|
// yaw, err := r.ReadByte()
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
|
|
// pitch, err := r.ReadByte()
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// P.Yaw += float32(yaw) * (1.0 / 256)
|
|
// P.Pitch += float32(pitch) * (1.0 / 256)
|
|
|
|
// og, err := r.ReadByte()
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// P.OnGround = og != 0x00
|
|
|
|
// P.X += float64(DeltaX) / 128
|
|
// P.Y += float64(DeltaY) / 128
|
|
// P.Z += float64(DeltaZ) / 128
|
|
// }
|
|
// return nil
|
|
// }
|
|
|
|
// func handleEntityHeadLookPacket(g *Client, r *bytes.Reader) {
|
|
|
|
// }
|
|
|
|
// func handleEntityRelativeMovePacket(g *Client, r *bytes.Reader) error {
|
|
// ID, err := pk.UnpackVarInt(r)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// E := g.wd.Entities[ID]
|
|
// if E != nil {
|
|
// P, ok := E.(*Player)
|
|
// if !ok {
|
|
// return nil
|
|
// }
|
|
// DeltaX, err := pk.UnpackInt16(r)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// DeltaY, err := pk.UnpackInt16(r)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// DeltaZ, err := pk.UnpackInt16(r)
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
|
|
// og, err := r.ReadByte()
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
// P.OnGround = og != 0x00
|
|
|
|
// P.X += float64(DeltaX) / 128
|
|
// P.Y += float64(DeltaY) / 128
|
|
// P.Z += float64(DeltaZ) / 128
|
|
// }
|
|
// return nil
|
|
// }
|
|
|
|
func handleKeepAlivePacket(c *Client, p pk.Packet) error {
|
|
var KeepAliveID pk.Long
|
|
if err := p.Scan(&KeepAliveID); err != nil {
|
|
return err
|
|
}
|
|
//Response
|
|
return c.conn.WritePacket(pk.Marshal(
|
|
data.KeepAliveServerbound,
|
|
KeepAliveID,
|
|
))
|
|
}
|
|
|
|
// func handleEntityPacket(g *Client, r *bytes.Reader) {
|
|
// // initialize an entity.
|
|
// }
|
|
|
|
// func handleSpawnPlayerPacket(g *Client, r *bytes.Reader) (err error) {
|
|
// np := new(Player)
|
|
// np.entityID, err = pk.UnpackVarInt(r)
|
|
// if err != nil {
|
|
// return
|
|
// }
|
|
// np.UUID[0], err = pk.UnpackInt64(r)
|
|
// if err != nil {
|
|
// return
|
|
// }
|
|
// np.UUID[1], err = pk.UnpackInt64(r)
|
|
// if err != nil {
|
|
// return
|
|
// }
|
|
// np.X, err = pk.UnpackDouble(r)
|
|
// if err != nil {
|
|
// return
|
|
// }
|
|
// np.Y, err = pk.UnpackDouble(r)
|
|
// if err != nil {
|
|
// return
|
|
// }
|
|
// np.Z, err = pk.UnpackDouble(r)
|
|
// if err != nil {
|
|
// return
|
|
// }
|
|
|
|
// yaw, err := r.ReadByte()
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
|
|
// pitch, err := r.ReadByte()
|
|
// if err != nil {
|
|
// return err
|
|
// }
|
|
|
|
// np.Yaw = float32(yaw) * (1.0 / 256)
|
|
// np.Pitch = float32(pitch) * (1.0 / 256)
|
|
|
|
// g.wd.Entities[np.entityID] = np //把该玩家添加到全局实体表里面
|
|
// return nil
|
|
// }
|
|
|
|
func handleWindowItemsPacket(g *Client, p pk.Packet) (err error) {
|
|
// var (
|
|
// WindowID pk.Byte
|
|
// solts entity.Solt
|
|
// )
|
|
// err = p.Scan(&WindowID, &solts)
|
|
// if err != nil {
|
|
// return
|
|
// }
|
|
|
|
// switch WindowID {
|
|
// case 0: //is player inventory
|
|
// g.Inventory = solts
|
|
// }
|
|
return nil
|
|
}
|
|
|
|
func sendPlayerPositionAndLookPacket(c *Client) {
|
|
c.conn.WritePacket(pk.Marshal(
|
|
data.PlayerPositionAndLookServerbound,
|
|
pk.Double(c.X),
|
|
pk.Double(c.Y),
|
|
pk.Double(c.Z),
|
|
pk.Float(c.Yaw),
|
|
pk.Float(c.Pitch),
|
|
pk.Boolean(c.OnGround),
|
|
))
|
|
}
|
|
|
|
// func sendPlayerLookPacket(g *Client) {
|
|
// var data []byte
|
|
// data = append(data, pk.PackFloat(g.player.Yaw)...)
|
|
// data = append(data, pk.PackFloat(g.player.Pitch)...)
|
|
// data = append(data, pk.PackBoolean(g.player.OnGround))
|
|
// g.sendChan <- pk.Packet{
|
|
// ID: 0x12,
|
|
// Data: data,
|
|
// }
|
|
// }
|
|
|
|
// func sendPlayerPositionPacket(g *Client) {
|
|
// var data []byte
|
|
// data = append(data, pk.PackDouble(g.player.X)...)
|
|
// data = append(data, pk.PackDouble(g.player.Y)...)
|
|
// data = append(data, pk.PackDouble(g.player.Z)...)
|
|
// data = append(data, pk.PackBoolean(g.player.OnGround))
|
|
|
|
// g.sendChan <- pk.Packet{
|
|
// ID: 0x10,
|
|
// Data: data,
|
|
// }
|
|
// }
|
|
|
|
// func sendClientStatusPacket(g *Client, status int32) {
|
|
// data := pk.PackVarInt(status)
|
|
// g.sendChan <- pk.Packet{
|
|
// ID: 0x03,
|
|
// Data: data,
|
|
// }
|
|
// }
|
|
|
|
// //hand could be 0: main hand, 1: off hand
|
|
// func sendAnimationPacket(g *Client, hand int32) {
|
|
// data := pk.PackVarInt(hand)
|
|
// g.sendChan <- pk.Packet{
|
|
// ID: 0x27,
|
|
// Data: data,
|
|
// }
|
|
// }
|
|
|
|
// func sendPlayerDiggingPacket(g *Client, status int32, x, y, z int, face Face) {
|
|
// data := pk.PackVarInt(status)
|
|
// data = append(data, pk.PackPosition(x, y, z)...)
|
|
// data = append(data, byte(face))
|
|
|
|
// g.sendChan <- pk.Packet{
|
|
// ID: 0x18,
|
|
// Data: data,
|
|
// }
|
|
// }
|
|
|
|
// func sendUseItemPacket(g *Client, hand int32) {
|
|
// data := pk.PackVarInt(hand)
|
|
// g.sendChan <- pk.Packet{
|
|
// ID: 0x2A,
|
|
// Data: data,
|
|
// }
|
|
// }
|