bot is nolonger a submodule
This commit is contained in:
1
bot
1
bot
Submodule bot deleted from 1b7712dc24
54
bot/client.go
Normal file
54
bot/client.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package bot
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Tnze/go-mc/bot/world/entity/player"
|
||||||
|
"github.com/Tnze/go-mc/net"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Client is the Object used to access Minecraft server
|
||||||
|
type Client struct {
|
||||||
|
conn *net.Conn
|
||||||
|
Auth
|
||||||
|
|
||||||
|
player.Player
|
||||||
|
PlayInfo
|
||||||
|
abilities PlayerAbilities
|
||||||
|
settings Settings
|
||||||
|
// wd world //the map data
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//NewClient init and return a new Client
|
||||||
|
func NewClient() (c *Client) {
|
||||||
|
c = new(Client)
|
||||||
|
|
||||||
|
//init Client
|
||||||
|
c.settings = DefaultSettings
|
||||||
|
c.Name = "Steve"
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//PlayInfo content player info in server.
|
||||||
|
type PlayInfo struct {
|
||||||
|
Gamemode int //游戏模式
|
||||||
|
Hardcore bool //是否是极限模式
|
||||||
|
Dimension int //维度
|
||||||
|
Difficulty int //难度
|
||||||
|
LevelType string //地图类型
|
||||||
|
ViewDistance int //视距
|
||||||
|
ReducedDebugInfo bool //减少调试信息
|
||||||
|
// SpawnPosition Position //主世界出生点
|
||||||
|
}
|
||||||
|
|
||||||
|
// PlayerAbilities defines what player can do.
|
||||||
|
type PlayerAbilities struct {
|
||||||
|
Flags int8
|
||||||
|
FlyingSpeed float32
|
||||||
|
FieldofViewModifier float32
|
||||||
|
}
|
||||||
|
|
||||||
|
//Position is a 3D vector.
|
||||||
|
type Position struct {
|
||||||
|
X, Y, Z int
|
||||||
|
}
|
664
bot/ingame.go
Normal file
664
bot/ingame.go
Normal file
@ -0,0 +1,664 @@
|
|||||||
|
package bot
|
||||||
|
|
||||||
|
import (
|
||||||
|
// "bytes"
|
||||||
|
// "math"
|
||||||
|
// "time"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"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
|
||||||
|
err = c.handlePacket(p)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("handle packet 0x%X error: %v", p.ID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) handlePacket(p pk.Packet) (err error) {
|
||||||
|
switch p.ID {
|
||||||
|
case data.JoinGame:
|
||||||
|
err = handleJoinGamePacket(c, p)
|
||||||
|
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:
|
||||||
|
// should assumes that the server has already closed the connection by the time the packet arrives.
|
||||||
|
|
||||||
|
err = fmt.Errorf("disconnect")
|
||||||
|
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 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
|
||||||
|
// }
|
||||||
|
|
||||||
|
// cm, err := newChatMsg([]byte(s))
|
||||||
|
// if err != nil {
|
||||||
|
// return err
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
// }
|
||||||
|
// }
|
227
bot/login.go
Normal file
227
bot/login.go
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
package bot
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/sha1"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Tnze/go-mc/net/CFB8"
|
||||||
|
pk "github.com/Tnze/go-mc/net/packet"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Auth includes a account
|
||||||
|
type Auth struct {
|
||||||
|
Name string
|
||||||
|
UUID string
|
||||||
|
AsTk string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加密请求
|
||||||
|
func handleEncryptionRequest(c *Client, pack pk.Packet) error {
|
||||||
|
//创建AES对称加密密钥
|
||||||
|
key, encoStream, decoStream := newSymmetricEncryption()
|
||||||
|
|
||||||
|
//解析EncryptionRequest包
|
||||||
|
var er encryptionRequest
|
||||||
|
if err := pack.Scan(&er); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err := loginAuth(c.AsTk, c.Name, c.Auth.UUID, key, er) //向Mojang验证
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("login fail: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 响应加密请求
|
||||||
|
var p pk.Packet // Encryption Key Response
|
||||||
|
p, err = genEncryptionKeyResponse(key, er.PublicKey, er.VerifyToken)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("gen encryption key response fail: %v", err)
|
||||||
|
}
|
||||||
|
err = c.conn.WritePacket(p)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置连接加密
|
||||||
|
c.conn.SetCipher(encoStream, decoStream)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type encryptionRequest struct {
|
||||||
|
ServerID string
|
||||||
|
PublicKey []byte
|
||||||
|
VerifyToken []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *encryptionRequest) Decode(r io.ByteReader) error {
|
||||||
|
var serverID pk.String
|
||||||
|
if err := serverID.Decode(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var publicKeyLength, verifyTokenLength pk.VarInt
|
||||||
|
|
||||||
|
if err := publicKeyLength.Decode(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
publicKey, err := pk.ReadNBytes(r, int(publicKeyLength))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := verifyTokenLength.Decode(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
verifyToken, err := pk.ReadNBytes(r, int(verifyTokenLength))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
e.ServerID = string(serverID)
|
||||||
|
e.PublicKey = publicKey
|
||||||
|
e.VerifyToken = verifyToken
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// authDigest computes a special SHA-1 digest required for Minecraft web
|
||||||
|
// authentication on Premium servers (online-mode=true).
|
||||||
|
// Source: http://wiki.vg/Protocol_Encryption#Server
|
||||||
|
//
|
||||||
|
// Also many, many thanks to SirCmpwn and his wonderful gist (C#):
|
||||||
|
// https://gist.github.com/SirCmpwn/404223052379e82f91e6
|
||||||
|
func authDigest(serverID string, sharedSecret, publicKey []byte) string {
|
||||||
|
h := sha1.New()
|
||||||
|
h.Write([]byte(serverID))
|
||||||
|
h.Write(sharedSecret)
|
||||||
|
h.Write(publicKey)
|
||||||
|
hash := h.Sum(nil)
|
||||||
|
|
||||||
|
// Check for negative hashes
|
||||||
|
negative := (hash[0] & 0x80) == 0x80
|
||||||
|
if negative {
|
||||||
|
hash = twosComplement(hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim away zeroes
|
||||||
|
res := strings.TrimLeft(fmt.Sprintf("%x", hash), "0")
|
||||||
|
if negative {
|
||||||
|
res = "-" + res
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// little endian
|
||||||
|
func twosComplement(p []byte) []byte {
|
||||||
|
carry := true
|
||||||
|
for i := len(p) - 1; i >= 0; i-- {
|
||||||
|
p[i] = byte(^p[i])
|
||||||
|
if carry {
|
||||||
|
carry = p[i] == 0xff
|
||||||
|
p[i]++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
type profile struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type request struct {
|
||||||
|
AccessToken string `json:"accessToken"`
|
||||||
|
SelectedProfile profile `json:"selectedProfile"`
|
||||||
|
ServerID string `json:"serverId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func loginAuth(AsTk, name, UUID string, shareSecret []byte, er encryptionRequest) error {
|
||||||
|
digest := authDigest(er.ServerID, shareSecret, er.PublicKey)
|
||||||
|
|
||||||
|
client := http.Client{}
|
||||||
|
requestPacket, err := json.Marshal(
|
||||||
|
request{
|
||||||
|
AccessToken: AsTk,
|
||||||
|
SelectedProfile: profile{
|
||||||
|
ID: UUID,
|
||||||
|
Name: name,
|
||||||
|
},
|
||||||
|
ServerID: digest,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("create request packet to authenticate faile: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
PostRequest, err := http.NewRequest(http.MethodPost, "https://sessionserver.mojang.com/session/minecraft/join",
|
||||||
|
bytes.NewReader(requestPacket))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("make request error: %v", err)
|
||||||
|
}
|
||||||
|
PostRequest.Header.Set("User-Agent", "gomcbot")
|
||||||
|
PostRequest.Header.Set("Connection", "keep-alive")
|
||||||
|
resp, err := client.Do(PostRequest)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("post fail: %v", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
body, _ := ioutil.ReadAll(resp.Body)
|
||||||
|
if resp.Status != "204 No Content" {
|
||||||
|
return fmt.Errorf("auth fail: %s", string(body))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AES/CFB8 with random key
|
||||||
|
func newSymmetricEncryption() (key []byte, encoStream, decoStream cipher.Stream) {
|
||||||
|
key = make([]byte, 16)
|
||||||
|
rand.Read(key) //生成密钥
|
||||||
|
|
||||||
|
b, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
decoStream = CFB8.NewCFB8Decrypt(b, key)
|
||||||
|
encoStream = CFB8.NewCFB8Encrypt(b, key)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func genEncryptionKeyResponse(shareSecret, publicKey, verifyToken []byte) (erp pk.Packet, err error) {
|
||||||
|
iPK, err := x509.ParsePKIXPublicKey(publicKey) // Decode Public Key
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("decode public key fail: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rsaKey := iPK.(*rsa.PublicKey)
|
||||||
|
cryptPK, err := rsa.EncryptPKCS1v15(rand.Reader, rsaKey, shareSecret)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("encryption share secret fail: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
verifyT, err := rsa.EncryptPKCS1v15(rand.Reader, rsaKey, verifyToken)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("encryption verfy tokenfail: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var data []byte
|
||||||
|
data = append(data, pk.VarInt(int32(len(cryptPK))).Encode()...)
|
||||||
|
data = append(data, cryptPK...)
|
||||||
|
data = append(data, pk.VarInt(int32(len(verifyT))).Encode()...)
|
||||||
|
data = append(data, verifyT...)
|
||||||
|
erp = pk.Packet{
|
||||||
|
ID: 0x01,
|
||||||
|
Data: data,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
125
bot/mcbot.go
Normal file
125
bot/mcbot.go
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
package bot
|
||||||
|
|
||||||
|
import (
|
||||||
|
// "bufio"
|
||||||
|
// "bytes"
|
||||||
|
"fmt"
|
||||||
|
// "net"
|
||||||
|
|
||||||
|
"github.com/Tnze/go-mc/net"
|
||||||
|
pk "github.com/Tnze/go-mc/net/packet"
|
||||||
|
)
|
||||||
|
|
||||||
|
//ProtocalVersion is the protocal version
|
||||||
|
// 477 for 1.14
|
||||||
|
const ProtocalVersion = 477
|
||||||
|
|
||||||
|
// PingAndList chack server status and list online player
|
||||||
|
// Return a JSON string about server status.
|
||||||
|
// see JSON format at https://wiki.vg/Server_List_Ping#Response
|
||||||
|
func PingAndList(addr string, port int) (string, error) {
|
||||||
|
conn, err := net.DialMC(fmt.Sprintf("%s:%d", addr, port))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
//握手
|
||||||
|
err = conn.WritePacket(
|
||||||
|
//Handshake Packet
|
||||||
|
pk.Marshal(
|
||||||
|
0x00, //Handshake packet ID
|
||||||
|
pk.VarInt(ProtocalVersion), //Protocal version
|
||||||
|
pk.String(addr), //Server's address
|
||||||
|
pk.UnsignedShort(port),
|
||||||
|
pk.Byte(1),
|
||||||
|
))
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("bot: send handshake packect fail: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//请求服务器状态
|
||||||
|
err = conn.WritePacket(pk.Marshal(0))
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("bot: send list packect fail: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
//服务器返回状态
|
||||||
|
recv, err := conn.ReadPacket()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("bot: recv list packect fail: %v", err)
|
||||||
|
}
|
||||||
|
var s pk.String
|
||||||
|
err = recv.Scan(&s)
|
||||||
|
return string(s), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// JoinServer connect a Minecraft server for playing the game.
|
||||||
|
func (c *Client) JoinServer(addr string, port int) (err error) {
|
||||||
|
//Connect
|
||||||
|
c.conn, err = net.DialMC(fmt.Sprintf("%s:%d", addr, port))
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("bot: connect server fail: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//Handshake
|
||||||
|
err = c.conn.WritePacket(
|
||||||
|
//Handshake Packet
|
||||||
|
pk.Marshal(
|
||||||
|
0x00, //Handshake packet ID
|
||||||
|
pk.VarInt(ProtocalVersion), //Protocal version
|
||||||
|
pk.String(addr), //Server's address
|
||||||
|
pk.UnsignedShort(port),
|
||||||
|
pk.Byte(2),
|
||||||
|
))
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("bot: send handshake packect fail: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//Login
|
||||||
|
err = c.conn.WritePacket(
|
||||||
|
//LoginStart Packet
|
||||||
|
pk.Marshal(0, pk.String(c.Name)))
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("bot: send login start packect fail: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
//Recive Packet
|
||||||
|
var pack pk.Packet
|
||||||
|
pack, err = c.conn.ReadPacket()
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("bot: recv packet for Login fail: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
//Handle Packet
|
||||||
|
switch pack.ID {
|
||||||
|
case 0x00: //Disconnect
|
||||||
|
var reason pk.String
|
||||||
|
err = pack.Scan(&reason)
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("bot: read Disconnect message fail: %v", err)
|
||||||
|
} else {
|
||||||
|
err = fmt.Errorf("bot: connect disconnected by server: %s", reason)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
case 0x01: //Encryption Request
|
||||||
|
handleEncryptionRequest(c, pack)
|
||||||
|
case 0x02: //Login Success
|
||||||
|
// uuid, l := pk.UnpackString(pack.Data)
|
||||||
|
// name, _ := unpackString(pack.Data[l:])
|
||||||
|
return //switches the connection state to PLAY.
|
||||||
|
case 0x03: //Set Compression
|
||||||
|
var threshold pk.VarInt
|
||||||
|
if err := pack.Scan(&threshold); err != nil {
|
||||||
|
return fmt.Errorf("bot: set compression fail: %v", err)
|
||||||
|
}
|
||||||
|
c.conn.SetThreshold(int(threshold))
|
||||||
|
case 0x04: //Login Plugin Request
|
||||||
|
fmt.Println("Waring Login Plugin Request")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
36
bot/settings.go
Normal file
36
bot/settings.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package bot
|
||||||
|
|
||||||
|
// Settings of client
|
||||||
|
type Settings struct {
|
||||||
|
Locale string //地区
|
||||||
|
ViewDistance int //视距
|
||||||
|
ChatMode int //聊天模式
|
||||||
|
ChatColors bool //聊天颜色
|
||||||
|
DisplayedSkinParts uint8 //皮肤显示
|
||||||
|
MainHand int //主手
|
||||||
|
ReciveMap bool //接收地图数据
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Used by Settings.DisplayedSkinParts.
|
||||||
|
For each bits set if shows match part.
|
||||||
|
*/
|
||||||
|
const (
|
||||||
|
_ = 1 << iota
|
||||||
|
Jacket
|
||||||
|
LeftSleeve
|
||||||
|
RightSleeve
|
||||||
|
LeftPantsLeg
|
||||||
|
RightPantsLeg
|
||||||
|
Hat
|
||||||
|
)
|
||||||
|
|
||||||
|
//DefaultSettings are the default settings of client
|
||||||
|
var DefaultSettings = Settings{
|
||||||
|
Locale: "zh_CN",
|
||||||
|
ViewDistance: 15,
|
||||||
|
ChatMode: 0,
|
||||||
|
DisplayedSkinParts: Jacket | LeftSleeve | RightSleeve | LeftPantsLeg | RightPantsLeg | Hat,
|
||||||
|
MainHand: 1,
|
||||||
|
ReciveMap: true,
|
||||||
|
}
|
157
bot/world/chunk.go
Normal file
157
bot/world/chunk.go
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
package gomcbot
|
||||||
|
|
||||||
|
// import (
|
||||||
|
// "bytes"
|
||||||
|
// "fmt"
|
||||||
|
// pk "github.com/Tnze/gomcbot/network/packet"
|
||||||
|
// "io"
|
||||||
|
// )
|
||||||
|
|
||||||
|
// func unpackChunkDataPacket(p *pk.Packet, hasSkyLight bool) (c *Chunk, x, y int, err error) {
|
||||||
|
// reader := bytes.NewReader(p.Data)
|
||||||
|
// //区块坐标
|
||||||
|
// X, err := pk.UnpackInt32(reader)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, 0, 0, err
|
||||||
|
// }
|
||||||
|
// Y, err := pk.UnpackInt32(reader)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, 0, 0, err
|
||||||
|
// }
|
||||||
|
// // fmt.Println("Chunk: (", X, ", ", Y, ")") //Debug: Show Chunk loc
|
||||||
|
// fc, err := reader.ReadByte()
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, 0, 0, err
|
||||||
|
// }
|
||||||
|
// FullChunk := fc != 0x00
|
||||||
|
|
||||||
|
// //主掩码
|
||||||
|
// PrimaryBitMask, err := pk.UnpackVarInt(reader)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, 0, 0, err
|
||||||
|
// }
|
||||||
|
|
||||||
|
// //区块数据
|
||||||
|
// Size, err := pk.UnpackVarInt(reader)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, 0, 0, err
|
||||||
|
// }
|
||||||
|
// Data := make([]byte, Size)
|
||||||
|
// _, err = io.ReadAtLeast(reader, Data, int(Size))
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, 0, 0, err
|
||||||
|
// }
|
||||||
|
|
||||||
|
// //实体信息
|
||||||
|
// // NumberofBlockEntities, len := pk.UnpackVarInt(p.Data[index:])
|
||||||
|
// // index += len
|
||||||
|
|
||||||
|
// //解析区块数据
|
||||||
|
// cc, err := readChunkColumn(FullChunk, PrimaryBitMask, bytes.NewReader(Data), hasSkyLight)
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
// return cc, int(X), int(Y), err
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func readChunkColumn(isFull bool, mask int32, data *bytes.Reader, hasSkyLight bool) (*Chunk, error) {
|
||||||
|
// var c Chunk
|
||||||
|
// for sectionY := 0; sectionY < 16; sectionY++ {
|
||||||
|
// if (mask & (1 << uint(sectionY))) != 0 { // Is the given bit set in the mask?
|
||||||
|
// BitsPerBlock, err := data.ReadByte()
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, fmt.Errorf("read BitsPerBlock fail: %v", err)
|
||||||
|
// }
|
||||||
|
// //读调色板
|
||||||
|
// var palette []uint
|
||||||
|
// if BitsPerBlock < 9 {
|
||||||
|
// length, err := pk.UnpackVarInt(data)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, fmt.Errorf("read palette (id len) fail: %v", err)
|
||||||
|
// }
|
||||||
|
// palette = make([]uint, length)
|
||||||
|
|
||||||
|
// for id := uint(0); id < uint(length); id++ {
|
||||||
|
// stateID, err := pk.UnpackVarInt(data)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, fmt.Errorf("read palette (id) fail: %v", err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// palette[id] = uint(stateID)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// //Section数据
|
||||||
|
// DataArrayLength, err := pk.UnpackVarInt(data)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, fmt.Errorf("read DataArrayLength fail: %v", err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// DataArray := make([]int64, DataArrayLength)
|
||||||
|
// for i := 0; i < int(DataArrayLength); i++ {
|
||||||
|
// DataArray[i], err = pk.UnpackInt64(data)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, fmt.Errorf("read DataArray fail: %v", err)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// //用数据填充区块
|
||||||
|
// fillSection(&c.sections[sectionY], perBits(BitsPerBlock), DataArray, palette)
|
||||||
|
|
||||||
|
// //throw BlockLight data
|
||||||
|
// _, err = pk.ReadNBytes(data, 2048)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, fmt.Errorf("read BlockLight fail: %v", err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if hasSkyLight {
|
||||||
|
// //throw SkyLight data
|
||||||
|
// _, err = pk.ReadNBytes(data, 2048)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, fmt.Errorf("read SkyLight fail: %v", err)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if isFull { //need recive Biomes datas
|
||||||
|
// _, err := pk.ReadNBytes(data, 256*4)
|
||||||
|
// if err != nil {
|
||||||
|
// return nil, fmt.Errorf("read Biomes fail: %v", err)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // fmt.Println(c)
|
||||||
|
// return &c, nil
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const defaultBitsPerBlock = 14
|
||||||
|
|
||||||
|
// func perBits(BitsPerBlock byte) uint {
|
||||||
|
// switch {
|
||||||
|
// case BitsPerBlock <= 4:
|
||||||
|
// return 4
|
||||||
|
// case BitsPerBlock < 9:
|
||||||
|
// return uint(BitsPerBlock)
|
||||||
|
// default:
|
||||||
|
// return defaultBitsPerBlock
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func fillSection(s *Section, bpb uint, DataArray []int64, palette []uint) {
|
||||||
|
// mask := uint(1<<bpb - 1)
|
||||||
|
// for n := 0; n < 16*16*16; n++ {
|
||||||
|
// offset := uint(n * int(bpb))
|
||||||
|
// data := uint(DataArray[offset/64])
|
||||||
|
// data >>= offset % 64
|
||||||
|
// if offset%64 > 64-bpb {
|
||||||
|
// l := bpb + offset%64 - 64
|
||||||
|
// data &= uint(DataArray[offset/64+1] << l)
|
||||||
|
// }
|
||||||
|
// data &= mask
|
||||||
|
|
||||||
|
// if bpb < 9 {
|
||||||
|
// s.blocks[n%16][n/(16*16)][n%(16*16)/16].id = palette[data]
|
||||||
|
// } else {
|
||||||
|
// s.blocks[n%16][n/(16*16)][n%(16*16)/16].id = data
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
9
bot/world/entity/entity.go
Normal file
9
bot/world/entity/entity.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package entity
|
||||||
|
|
||||||
|
//Entity is the entity of minecraft
|
||||||
|
type Entity struct {
|
||||||
|
EntityID int //实体ID
|
||||||
|
}
|
||||||
|
|
||||||
|
type Solt struct {
|
||||||
|
}
|
20
bot/world/entity/player/player.go
Normal file
20
bot/world/entity/player/player.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package player
|
||||||
|
|
||||||
|
import "github.com/Tnze/go-mc/bot/world/entity"
|
||||||
|
|
||||||
|
// Player includes the player's status.
|
||||||
|
type Player struct {
|
||||||
|
entity.Entity
|
||||||
|
UUID [2]int64 //128bit UUID
|
||||||
|
|
||||||
|
X, Y, Z float64
|
||||||
|
Yaw, Pitch float32
|
||||||
|
OnGround bool
|
||||||
|
|
||||||
|
HeldItem int //拿着的物品栏位
|
||||||
|
Inventory []entity.Solt
|
||||||
|
|
||||||
|
Health float32 //血量
|
||||||
|
Food int32 //饱食度
|
||||||
|
FoodSaturation float32 //食物饱和度
|
||||||
|
}
|
78
bot/world/motion.go
Normal file
78
bot/world/motion.go
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
package gomcbot
|
||||||
|
|
||||||
|
// import (
|
||||||
|
// "math"
|
||||||
|
// "time"
|
||||||
|
// )
|
||||||
|
|
||||||
|
// // SetPosition method move your character around.
|
||||||
|
// // Server will ignore this if changes too much.
|
||||||
|
// func (g *Client) SetPosition(x, y, z float64, onGround bool) {
|
||||||
|
// g.motion <- func() {
|
||||||
|
// g.player.X, g.player.Y, g.player.Z = x, y, z
|
||||||
|
// g.player.OnGround = onGround
|
||||||
|
// sendPlayerPositionPacket(g) //向服务器更新位置
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // LookAt method turn player's hand and make it look at a point.
|
||||||
|
// func (g *Client) LookAt(x, y, z float64) {
|
||||||
|
// x0, y0, z0 := g.player.X, g.player.Y, g.player.Z
|
||||||
|
// x, y, z = x-x0, y-y0, z-z0
|
||||||
|
|
||||||
|
// r := math.Sqrt(x*x + y*y + z*z)
|
||||||
|
// yaw := -math.Atan2(x, z) / math.Pi * 180
|
||||||
|
// for yaw < 0 {
|
||||||
|
// yaw = 360 + yaw
|
||||||
|
// }
|
||||||
|
// pitch := -math.Asin(y/r) / math.Pi * 180
|
||||||
|
|
||||||
|
// g.LookYawPitch(float32(yaw), float32(pitch))
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // LookYawPitch set player's hand to the direct by yaw and pitch.
|
||||||
|
// // yaw can be [0, 360) and pitch can be (-180, 180).
|
||||||
|
// // if |pitch|>90 the player's hand will be very strange.
|
||||||
|
// func (g *Client) LookYawPitch(yaw, pitch float32) {
|
||||||
|
// g.motion <- func() {
|
||||||
|
// g.player.Yaw, g.player.Pitch = yaw, pitch
|
||||||
|
// sendPlayerLookPacket(g) //向服务器更新朝向
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // SwingHand sent when the player's arm swings.
|
||||||
|
// // if hand is true, swing the main hand
|
||||||
|
// func (g *Client) SwingHand(hand bool) {
|
||||||
|
// if hand {
|
||||||
|
// sendAnimationPacket(g, 0)
|
||||||
|
// } else {
|
||||||
|
// sendAnimationPacket(g, 1)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Dig a block in the position and wait for it's breaked
|
||||||
|
// func (g *Client) Dig(x, y, z int) error {
|
||||||
|
// b := g.GetBlock(x, y, z).id
|
||||||
|
// sendPlayerDiggingPacket(g, 0, x, y, z, Top) //start
|
||||||
|
// sendPlayerDiggingPacket(g, 2, x, y, z, Top) //end
|
||||||
|
|
||||||
|
// for {
|
||||||
|
// time.Sleep(time.Millisecond * 50)
|
||||||
|
// if g.GetBlock(x, y, z).id != b {
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// g.SwingHand(true)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // UseItem use the item in hand.
|
||||||
|
// // if hand is true, swing the main hand
|
||||||
|
// func (g *Client) UseItem(hand bool) {
|
||||||
|
// if hand {
|
||||||
|
// sendUseItemPacket(g, 0)
|
||||||
|
// } else {
|
||||||
|
// sendUseItemPacket(g, 1)
|
||||||
|
// }
|
||||||
|
// }
|
68
bot/world/world.go
Normal file
68
bot/world/world.go
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
package gomcbot
|
||||||
|
|
||||||
|
// //World record all of the things in the world where player at
|
||||||
|
// type world struct {
|
||||||
|
// Entities map[int32]Entity
|
||||||
|
// chunks map[chunkLoc]*Chunk
|
||||||
|
// }
|
||||||
|
|
||||||
|
// //Chunk store a 256*16*16 clolumn blocks
|
||||||
|
// type Chunk struct {
|
||||||
|
// sections [16]Section
|
||||||
|
// }
|
||||||
|
|
||||||
|
// //Section store a 16*16*16 cube blocks
|
||||||
|
// type Section struct {
|
||||||
|
// blocks [16][16][16]Block
|
||||||
|
// }
|
||||||
|
|
||||||
|
// //Block is the base of world
|
||||||
|
// type Block struct {
|
||||||
|
// id uint
|
||||||
|
// }
|
||||||
|
|
||||||
|
// type chunkLoc struct {
|
||||||
|
// X, Y int
|
||||||
|
// }
|
||||||
|
|
||||||
|
// //Entity 表示一个实体
|
||||||
|
// type Entity interface {
|
||||||
|
// EntityID() int32
|
||||||
|
// }
|
||||||
|
|
||||||
|
// //Face is a face of a block
|
||||||
|
// type Face byte
|
||||||
|
|
||||||
|
// // All six faces in a block
|
||||||
|
// const (
|
||||||
|
// Bottom Face = iota
|
||||||
|
// Top
|
||||||
|
// North
|
||||||
|
// South
|
||||||
|
// West
|
||||||
|
// East
|
||||||
|
// )
|
||||||
|
|
||||||
|
// // getBlock return the block in the position (x, y, z)
|
||||||
|
// func (w *world) getBlock(x, y, z int) Block {
|
||||||
|
// c := w.chunks[chunkLoc{x >> 4, z >> 4}]
|
||||||
|
// if c != nil {
|
||||||
|
// cx, cy, cz := x&15, y&15, z&15
|
||||||
|
// /*
|
||||||
|
// n = n&(16-1)
|
||||||
|
|
||||||
|
// is equal to
|
||||||
|
|
||||||
|
// n %= 16
|
||||||
|
// if n < 0 { n += 16 }
|
||||||
|
// */
|
||||||
|
|
||||||
|
// return c.sections[y/16].blocks[cx][cy][cz]
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return Block{id: 0}
|
||||||
|
// }
|
||||||
|
|
||||||
|
// func (b Block) String() string {
|
||||||
|
// return blockNameByID[b.id]
|
||||||
|
// }
|
9
bot/world/world_test.go
Normal file
9
bot/world/world_test.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package gomcbot
|
||||||
|
|
||||||
|
// import "testing"
|
||||||
|
|
||||||
|
// func TestBlockString(t *testing.T) {
|
||||||
|
// for i := uint(0); i < 8598+1; i++ {
|
||||||
|
// t.Log(Block{id: i})
|
||||||
|
// }
|
||||||
|
// }
|
Reference in New Issue
Block a user