Update to 1.16.3 + add a few new event callbacks.

This commit is contained in:
Tom
2020-09-11 17:58:15 -07:00
parent 5bff8bcb2b
commit 0ec82d90a7
6 changed files with 122 additions and 23 deletions

View File

@ -1,6 +1,6 @@
# Go-MC # Go-MC
![Version](https://img.shields.io/badge/Minecraft-1.16.2-blue.svg) ![Version](https://img.shields.io/badge/Minecraft-1.16.3-blue.svg)
![Protocol](https://img.shields.io/badge/Protocol-751-blue.svg) ![Protocol](https://img.shields.io/badge/Protocol-753-blue.svg)
[![GoDoc](https://godoc.org/github.com/Tnze/go-mc?status.svg)](https://godoc.org/github.com/Tnze/go-mc) [![GoDoc](https://godoc.org/github.com/Tnze/go-mc?status.svg)](https://godoc.org/github.com/Tnze/go-mc)
[![Go Report Card](https://goreportcard.com/badge/github.com/Tnze/go-mc)](https://goreportcard.com/report/github.com/Tnze/go-mc) [![Go Report Card](https://goreportcard.com/badge/github.com/Tnze/go-mc)](https://goreportcard.com/report/github.com/Tnze/go-mc)
[![Build Status](https://travis-ci.org/Tnze/go-mc.svg?branch=master)](https://travis-ci.org/Tnze/go-mc) [![Build Status](https://travis-ci.org/Tnze/go-mc.svg?branch=master)](https://travis-ci.org/Tnze/go-mc)
@ -105,4 +105,4 @@ Originally it's all right to write a bot with only `go-mc/net` package. But cons
理论上讲,只用`go-mc/net`包实现一个bot是完全可行的但是为了节省大家从头去理解MC握手、登录、加密等协议的过程`go-mc/bot`中我已经把这些都实现了,只不过它不是跨版本的。你可以直接使用,或者作为自己实现的参考。 理论上讲,只用`go-mc/net`包实现一个bot是完全可行的但是为了节省大家从头去理解MC握手、登录、加密等协议的过程`go-mc/bot`中我已经把这些都实现了,只不过它不是跨版本的。你可以直接使用,或者作为自己实现的参考。
Now, go and have a look at the example! Now, go and have a look at the example!

View File

@ -14,6 +14,7 @@ type Client struct {
player.Player player.Player
PlayInfo PlayInfo
ServInfo
abilities PlayerAbilities abilities PlayerAbilities
settings Settings settings Settings
Wd world.World //the map data Wd world.World //the map data
@ -49,16 +50,21 @@ func NewClient() (c *Client) {
//PlayInfo content player info in server. //PlayInfo content player info in server.
type PlayInfo struct { type PlayInfo struct {
Gamemode int //游戏模式 Gamemode int //游戏模式
Hardcore bool //是否是极限模式 Hardcore bool //是否是极限模式
Dimension int //维度 Dimension int //维度
Difficulty int //难度 Difficulty int //难度
ViewDistance int //视距 ViewDistance int //视距
ReducedDebugInfo bool //减少调试信息 ReducedDebugInfo bool //减少调试信息
WorldName string //当前世界的名字 WorldName string //当前世界的名字
IsDebug bool //调试 IsDebug bool //调试
IsFlat bool //超平坦世界 IsFlat bool //超平坦世界
// SpawnPosition Position //主世界出生点 SpawnPosition Position //主世界出生点
}
// ServInfo contains information about the server implementation.
type ServInfo struct {
Brand string
} }
// PlayerAbilities defines what player can do. // PlayerAbilities defines what player can do.

View File

@ -8,7 +8,30 @@ import (
pk "github.com/Tnze/go-mc/net/packet" pk "github.com/Tnze/go-mc/net/packet"
) )
type seenPacketFlags uint8
// Valid seenPacketFlags values.
const (
seenJoinGame seenPacketFlags = 1 << iota
seenServerDifficulty
seenPlayerAbilities
seenPlayerInventory
seenUpdateLight
seenChunkData
seenPlayerPositionAndLook
seenSpawnPos
// gameReadyMinPackets are the minimum set of packets that must be seen, for
// the GameReady callback to be invoked.
gameReadyMinPackets = seenJoinGame | seenChunkData | seenUpdateLight |
seenPlayerAbilities | seenPlayerInventory | seenServerDifficulty |
seenPlayerPositionAndLook | seenSpawnPos
)
type eventBroker struct { type eventBroker struct {
seenPackets seenPacketFlags
isReady bool
GameStart func() error GameStart func() error
ChatMsg func(msg chat.Message, pos byte, sender uuid.UUID) error ChatMsg func(msg chat.Message, pos byte, sender uuid.UUID) error
Disconnect func(reason chat.Message) error Disconnect func(reason chat.Message) error
@ -28,7 +51,29 @@ type eventBroker struct {
WindowsItem func(id byte, slots []entity.Slot) error WindowsItem func(id byte, slots []entity.Slot) error
WindowsItemChange func(id byte, slotID int, slot entity.Slot) error WindowsItemChange func(id byte, slotID int, slot entity.Slot) error
// ReceivePacket will be called when new packet arrive. // ServerDifficultyChange is called whenever the gamemode of the server changes.
// Default handler will run only if pass == false. // At time of writing (1.16.3), difficulty values of 0, 1, 2, and 3 correspond
// to peaceful, easy, normal, and hard respectively.
ServerDifficultyChange func(difficulty int) error
// GameReady is called after the client has joined the server and successfully
// received player state. Additionally, the server has begun sending world
// state (such as lighting and chunk information).
//
// Use this callback as a signal as to when your bot should start 'doing'
// things.
GameReady func() error
// ReceivePacket will be called when new packets arrive.
// The default handler will run only if pass == false.
ReceivePacket func(p pk.Packet) (pass bool, err error) ReceivePacket func(p pk.Packet) (pass bool, err error)
} }
func (b *eventBroker) updateSeenPackets(f seenPacketFlags) error {
b.seenPackets |= f
if (^b.seenPackets)&gameReadyMinPackets == 0 && b.GameReady != nil && !b.isReady {
b.isReady = true
return b.GameReady()
}
return nil
}

View File

@ -71,12 +71,33 @@ func (c *Client) handlePacket(p pk.Packet) (disconnect bool, err error) {
if err == nil && c.Events.GameStart != nil { if err == nil && c.Events.GameStart != nil {
err = c.Events.GameStart() err = c.Events.GameStart()
} }
_ = c.conn.WritePacket(
//PluginMessage packet (serverbound) - sending minecraft brand.
pk.Marshal(
data.PluginMessageServerbound,
pk.Identifier("minecraft:brand"),
pk.String(c.settings.Brand),
),
)
if err2 := c.Events.updateSeenPackets(seenJoinGame); err == nil {
err = err2
}
case data.PluginMessageClientbound: case data.PluginMessageClientbound:
err = handlePluginPacket(c, p) err = handlePluginPacket(c, p)
case data.ServerDifficulty: case data.ServerDifficulty:
err = handleServerDifficultyPacket(c, p) err = handleServerDifficultyPacket(c, p)
if err == nil && c.Events.ServerDifficultyChange != nil {
err = c.Events.ServerDifficultyChange(c.Difficulty)
}
if err2 := c.Events.updateSeenPackets(seenServerDifficulty); err == nil {
err = err2
}
case data.SpawnPosition: case data.SpawnPosition:
err = handleSpawnPositionPacket(c, p) err = handleSpawnPositionPacket(c, p)
if err2 := c.Events.updateSeenPackets(seenSpawnPos); err == nil {
err = err2
}
case data.PlayerAbilitiesClientbound: case data.PlayerAbilitiesClientbound:
err = handlePlayerAbilitiesPacket(c, p) err = handlePlayerAbilitiesPacket(c, p)
_ = c.conn.WritePacket( _ = c.conn.WritePacket(
@ -91,13 +112,24 @@ func (c *Client) handlePacket(p pk.Packet) (disconnect bool, err error) {
pk.VarInt(c.settings.MainHand), pk.VarInt(c.settings.MainHand),
), ),
) )
if err2 := c.Events.updateSeenPackets(seenPlayerAbilities); err == nil {
err = err2
}
case data.HeldItemChangeClientbound: case data.HeldItemChangeClientbound:
err = handleHeldItemPacket(c, p) err = handleHeldItemPacket(c, p)
case data.UpdateLight:
err = c.Events.updateSeenPackets(seenUpdateLight)
case data.ChunkData: case data.ChunkData:
err = handleChunkDataPacket(c, p) err = handleChunkDataPacket(c, p)
if err2 := c.Events.updateSeenPackets(seenChunkData); err == nil {
err = err2
}
case data.PlayerPositionAndLookClientbound: case data.PlayerPositionAndLookClientbound:
err = handlePlayerPositionAndLookPacket(c, p) err = handlePlayerPositionAndLookPacket(c, p)
sendPlayerPositionAndLookPacket(c) // to confirm the position sendPlayerPositionAndLookPacket(c) // to confirm the position
if err2 := c.Events.updateSeenPackets(seenPlayerPositionAndLook); err == nil {
err = err2
}
case data.DeclareRecipes: case data.DeclareRecipes:
// handleDeclareRecipesPacket(g, reader) // handleDeclareRecipesPacket(g, reader)
case data.EntityLookAndRelativeMove: case data.EntityLookAndRelativeMove:
@ -395,6 +427,16 @@ func handlePluginPacket(c *Client, p pk.Packet) error {
if err := p.Scan(&Channel, &Data); err != nil { if err := p.Scan(&Channel, &Data); err != nil {
return err return err
} }
switch Channel {
case "minecraft:brand":
var brandRaw pk.String
if err := brandRaw.Decode(bytes.NewReader(Data)); err != nil {
return err
}
c.ServInfo.Brand = string(brandRaw)
}
if c.Events.PluginMessage != nil { if c.Events.PluginMessage != nil {
return c.Events.PluginMessage(string(Channel), []byte(Data)) return c.Events.PluginMessage(string(Channel), []byte(Data))
} }
@ -417,8 +459,8 @@ func handleSpawnPositionPacket(c *Client, p pk.Packet) error {
if err != nil { if err != nil {
return err return err
} }
// c.SpawnPosition.X, c.SpawnPosition.Y, c.SpawnPosition.Z = c.SpawnPosition.X, c.SpawnPosition.Y, c.SpawnPosition.Z =
// pos.X, pos.Y, pos.Z pos.X, pos.Y, pos.Z
return nil return nil
} }
@ -598,11 +640,7 @@ func handleKeepAlivePacket(c *Client, p pk.Packet) error {
)) ))
} }
func handleWindowItemsPacket(c *Client, p pk.Packet) (err error) { func handleWindowItemsPacket(c *Client, p pk.Packet) error {
if c.Events.WindowsItem == nil {
return nil
}
r := bytes.NewReader(p.Data) r := bytes.NewReader(p.Data)
var ( var (
windowID pk.Byte windowID pk.Byte
@ -623,6 +661,14 @@ func handleWindowItemsPacket(c *Client, p pk.Packet) (err error) {
slots = append(slots, slot) slots = append(slots, slot)
} }
if windowID == 0 { // Window ID 0 is the players' inventory.
if err := c.Events.updateSeenPackets(seenPlayerInventory); err != nil {
return err
}
}
if c.Events.WindowsItem == nil {
return nil
}
return c.Events.WindowsItem(byte(windowID), slots) return c.Events.WindowsItem(byte(windowID), slots)
} }

View File

@ -14,7 +14,7 @@ import (
) )
// ProtocolVersion , the protocol version number of minecraft net protocol // ProtocolVersion , the protocol version number of minecraft net protocol
const ProtocolVersion = 751 const ProtocolVersion = 753
// JoinServer connect a Minecraft server for playing the game. // JoinServer connect a Minecraft server for playing the game.
func (c *Client) JoinServer(addr string, port int) (err error) { func (c *Client) JoinServer(addr string, port int) (err error) {

View File

@ -9,6 +9,7 @@ type Settings struct {
DisplayedSkinParts uint8 //皮肤显示 DisplayedSkinParts uint8 //皮肤显示
MainHand int //主手 MainHand int //主手
ReceiveMap bool //接收地图数据 ReceiveMap bool //接收地图数据
Brand string // The brand string presented to the server.
} }
/* /*
@ -33,4 +34,5 @@ var DefaultSettings = Settings{
DisplayedSkinParts: Jacket | LeftSleeve | RightSleeve | LeftPantsLeg | RightPantsLeg | Hat, DisplayedSkinParts: Jacket | LeftSleeve | RightSleeve | LeftPantsLeg | RightPantsLeg | Hat,
MainHand: 1, MainHand: 1,
ReceiveMap: true, ReceiveMap: true,
Brand: "vanilla",
} }