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