bot is nolonger a submodule

This commit is contained in:
JunDao
2019-05-01 16:09:36 +08:00
parent 6055f2ca7b
commit 45820c10f5
12 changed files with 1447 additions and 1 deletions

1
bot

Submodule bot deleted from 1b7712dc24

54
bot/client.go Normal file
View 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
View 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
View 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
View 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
View 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
View 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
// }
// }
// }

View File

@ -0,0 +1,9 @@
package entity
//Entity is the entity of minecraft
type Entity struct {
EntityID int //实体ID
}
type Solt struct {
}

View 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
View 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
View 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
View 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})
// }
// }