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, // } // }