update 1.19.2 bot, chat support

This commit is contained in:
Tnze
2022-11-26 15:58:31 +08:00
parent 6a3589ad61
commit 7814e7b1ab
20 changed files with 532 additions and 136 deletions

View File

@ -20,7 +20,7 @@
// - HealthChange // - HealthChange
// - Death // - Death
// //
// You must manully attach the [EventsListener] to the [Client] as needed. // You must manually attach the [EventsListener] to the [Client] as needed.
package basic package basic
import ( import (
@ -38,22 +38,23 @@ type Player struct {
isSpawn bool isSpawn bool
} }
func NewPlayer(c *bot.Client, settings Settings) *Player { func NewPlayer(c *bot.Client, settings Settings, events EventsListener) *Player {
b := &Player{c: c, Settings: settings} p := &Player{c: c, Settings: settings}
c.Events.AddListener( c.Events.AddListener(
bot.PacketHandler{Priority: 0, ID: packetid.ClientboundLogin, F: b.handleLoginPacket}, bot.PacketHandler{Priority: 0, ID: packetid.ClientboundLogin, F: p.handleLoginPacket},
bot.PacketHandler{Priority: 0, ID: packetid.ClientboundKeepAlive, F: b.handleKeepAlivePacket}, bot.PacketHandler{Priority: 0, ID: packetid.ClientboundKeepAlive, F: p.handleKeepAlivePacket},
bot.PacketHandler{Priority: 0, ID: packetid.ClientboundPlayerPosition, F: b.handlePlayerPosition}, bot.PacketHandler{Priority: 0, ID: packetid.ClientboundPlayerPosition, F: p.handlePlayerPosition},
bot.PacketHandler{Priority: 0, ID: packetid.ClientboundRespawn, F: b.handleRespawnPacket}, bot.PacketHandler{Priority: 0, ID: packetid.ClientboundRespawn, F: p.handleRespawnPacket},
) )
return b events.attach(p)
return p
} }
func (p *Player) Respawn() error { func (p *Player) Respawn() error {
const PerformRespawn = 0 const PerformRespawn = 0
err := p.c.Conn.WritePacket(pk.Marshal( err := p.c.Conn.WritePacket(pk.Marshal(
packetid.ServerboundClientCommand, int32(packetid.ServerboundClientCommand),
pk.VarInt(PerformRespawn), pk.VarInt(PerformRespawn),
)) ))
if err != nil { if err != nil {

View File

@ -5,35 +5,30 @@ import (
"github.com/Tnze/go-mc/chat" "github.com/Tnze/go-mc/chat"
"github.com/Tnze/go-mc/data/packetid" "github.com/Tnze/go-mc/data/packetid"
pk "github.com/Tnze/go-mc/net/packet" pk "github.com/Tnze/go-mc/net/packet"
"io"
) )
type EventsListener struct { type EventsListener struct {
GameStart func() error GameStart func() error
ChatMsg func(c *PlayerMessage) error
SystemMsg func(c chat.Message, overlay bool) error SystemMsg func(c chat.Message, overlay bool) error
Disconnect func(reason chat.Message) error Disconnect func(reason chat.Message) error
HealthChange func(health float32) error HealthChange func(health float32) error
Death func() error Death func() error
} }
// Attach your event listener to the client. // attach your event listener to the client.
// The functions are copied when attaching, and modify on [EventListener] doesn't affect after that. // The functions are copied when attaching, and modify on [EventListener] doesn't affect after that.
func (e EventsListener) Attach(c *bot.Client) { func (e EventsListener) attach(p *Player) {
if e.GameStart != nil { if e.GameStart != nil {
attachJoinGameHandler(c, e.GameStart) attachJoinGameHandler(p.c, e.GameStart)
}
if e.ChatMsg != nil {
attachPlayerMsg(c, e.ChatMsg)
} }
if e.SystemMsg != nil { if e.SystemMsg != nil {
attachSystemMsg(c, e.SystemMsg) attachSystemMsg(p.c, e.SystemMsg)
} }
if e.Disconnect != nil { if e.Disconnect != nil {
attachDisconnect(c, e.Disconnect) attachDisconnect(p.c, e.Disconnect)
} }
if e.HealthChange != nil || e.Death != nil { if e.HealthChange != nil || e.Death != nil {
attachUpdateHealth(c, e.HealthChange, e.Death) attachUpdateHealth(p.c, e.HealthChange, e.Death)
} }
} }
@ -46,60 +41,6 @@ func attachJoinGameHandler(c *bot.Client, handler func() error) {
}) })
} }
type PlayerMessage struct {
}
func (p *PlayerMessage) ReadFrom(r io.Reader) (n int64, err error) {
// SignedMessageHeader
// MessageSignature
// SignedMessageBody
// Optional<Component>
// FilterMask
panic("implement me")
}
type ChatType struct {
ID int32
Name chat.Message
TargetName *chat.Message
}
func (c *ChatType) ReadFrom(r io.Reader) (n int64, err error) {
var hasTargetName pk.Boolean
n1, err := (*pk.VarInt)(&c.ID).ReadFrom(r)
if err != nil {
return n1, err
}
n2, err := c.Name.ReadFrom(r)
if err != nil {
return n1 + n2, err
}
n3, err := hasTargetName.ReadFrom(r)
if err != nil {
return n1 + n2 + n3, err
}
if hasTargetName {
c.TargetName = new(chat.Message)
n4, err := c.TargetName.ReadFrom(r)
return n1 + n2 + n3 + n4, err
}
return n1 + n2 + n3, nil
}
func attachPlayerMsg(c *bot.Client, handler func(c *PlayerMessage) error) {
c.Events.AddListener(bot.PacketHandler{
Priority: 64, ID: packetid.ClientboundPlayerChat,
F: func(p pk.Packet) error {
var message PlayerMessage
var chatType ChatType
if err := p.Scan(&message, &chatType); err != nil {
return Error{err}
}
return handler(&message)
},
})
}
func attachSystemMsg(c *bot.Client, handler func(c chat.Message, overlay bool) error) { func attachSystemMsg(c *bot.Client, handler func(c chat.Message, overlay bool) error) {
c.Events.AddListener(bot.PacketHandler{ c.Events.AddListener(bot.PacketHandler{
Priority: 64, ID: packetid.ClientboundSystemChat, Priority: 64, ID: packetid.ClientboundSystemChat,

View File

@ -1,6 +1,7 @@
package basic package basic
import ( import (
"github.com/Tnze/go-mc/chat"
"unsafe" "unsafe"
"github.com/Tnze/go-mc/data/packetid" "github.com/Tnze/go-mc/data/packetid"
@ -46,9 +47,14 @@ type Dimension struct {
MonsterSpawnBlockLightLimit int32 `nbt:"monster_spawn_block_light_limit"` MonsterSpawnBlockLightLimit int32 `nbt:"monster_spawn_block_light_limit"`
} }
type ChatType struct {
Chat chat.Decoration `nbt:"chat"`
Narration chat.Decoration `nbt:"narration"`
}
type RegistryCodec struct { type RegistryCodec struct {
// What is Below? (wiki.vg) // What is Below? (wiki.vg)
ChatType Registry[nbt.RawMessage] `nbt:"minecraft:chat_type"` ChatType Registry[ChatType] `nbt:"minecraft:chat_type"`
DimensionType Registry[Dimension] `nbt:"minecraft:dimension_type"` DimensionType Registry[Dimension] `nbt:"minecraft:dimension_type"`
WorldGenBiome Registry[nbt.RawMessage] `nbt:"minecraft:worldgen/biome"` WorldGenBiome Registry[nbt.RawMessage] `nbt:"minecraft:worldgen/biome"`
} }
@ -71,6 +77,18 @@ func (r *Registry[E]) Find(name string) *E {
return nil return nil
} }
func (r *Registry[E]) FindByID(id int32) *E {
if id >= 0 && id < int32(len(r.Value)) && r.Value[id].ID == id {
return &r.Value[id].Element
}
for i := range r.Value {
if r.Value[i].ID == id {
return &r.Value[i].Element
}
}
return nil
}
type PlayerInfo struct { type PlayerInfo struct {
EID int32 // The player's Entity ID (EID). EID int32 // The player's Entity ID (EID).
Hardcore bool // Is hardcore Hardcore bool // Is hardcore
@ -106,7 +124,7 @@ func (p *Player) handleLoginPacket(packet pk.Packet) error {
return Error{err} return Error{err}
} }
err = p.c.Conn.WritePacket(pk.Marshal( //PluginMessage packet err = p.c.Conn.WritePacket(pk.Marshal( //PluginMessage packet
packetid.ServerboundCustomPayload, int32(packetid.ServerboundCustomPayload),
pk.Identifier("minecraft:brand"), pk.Identifier("minecraft:brand"),
pk.String(p.Settings.Brand), pk.String(p.Settings.Brand),
)) ))
@ -115,7 +133,7 @@ func (p *Player) handleLoginPacket(packet pk.Packet) error {
} }
err = p.c.Conn.WritePacket(pk.Marshal( err = p.c.Conn.WritePacket(pk.Marshal(
packetid.ServerboundClientInformation, // Client settings int32(packetid.ServerboundClientInformation), // Client settings
pk.String(p.Settings.Locale), pk.String(p.Settings.Locale),
pk.Byte(p.Settings.ViewDistance), pk.Byte(p.Settings.ViewDistance),
pk.VarInt(p.Settings.ChatMode), pk.VarInt(p.Settings.ChatMode),

View File

@ -12,7 +12,7 @@ func (p Player) handleKeepAlivePacket(packet pk.Packet) error {
} }
// Response // Response
err := p.c.Conn.WritePacket(pk.Packet{ err := p.c.Conn.WritePacket(pk.Packet{
ID: packetid.ServerboundKeepAlive, ID: int32(packetid.ServerboundKeepAlive),
Data: packet.Data, Data: packet.Data,
}) })
if err != nil { if err != nil {

168
bot/chat/chat.go Normal file
View File

@ -0,0 +1,168 @@
package chat
import (
"fmt"
"io"
"time"
"github.com/Tnze/go-mc/bot"
"github.com/Tnze/go-mc/bot/basic"
"github.com/Tnze/go-mc/chat"
"github.com/Tnze/go-mc/data/packetid"
pk "github.com/Tnze/go-mc/net/packet"
"github.com/google/uuid"
)
type Chat struct {
c *bot.Client
p *basic.Player
}
func NewChat(c *bot.Client, p *basic.Player, events EventsHandler) *Chat {
attachPlayerMsg(c, p, events.PlayerChatMessage)
return &Chat{c, p}
}
func attachPlayerMsg(c *bot.Client, p *basic.Player, handler func(msg chat.Message) error) {
c.Events.AddListener(bot.PacketHandler{
Priority: 64, ID: packetid.ClientboundPlayerChat,
F: func(packet pk.Packet) error {
var message PlayerMessage
if err := packet.Scan(&message); err != nil {
return err
}
var content chat.Message
if message.content.formatted != nil {
content = *message.content.formatted
} else {
content = chat.Text(message.content.plainMsg)
}
ct := p.WorldInfo.RegistryCodec.ChatType.FindByID(message.chatType.ID)
if ct == nil {
return fmt.Errorf("chat type %d not found", message.chatType.ID)
}
msg := (*chat.Type)(&message.chatType).Decorate(content, &ct.Chat)
return handler(msg)
},
})
}
type PlayerMessage struct {
// SignedMessageHeader
signature []byte
sender uuid.UUID
// MessageSignature
msgSignature []byte
// SignedMessageBody
content msgContent
timestamp time.Time
salt int64
prevMessages []prevMsg
// Optional<Component>
unsignedContent *chat.Message
// FilterMask
filterType int32
filterSet pk.BitSet
// ChatType
chatType chatType
}
func (p *PlayerMessage) String() string {
return p.content.plainMsg
}
func (p *PlayerMessage) ReadFrom(r io.Reader) (n int64, err error) {
var hasMsgSign, hasUnsignedContent pk.Boolean
var timestamp pk.Long
var unsignedContent chat.Message
n, err = pk.Tuple{
&hasMsgSign, pk.Opt{Has: &hasMsgSign,
Field: (*pk.ByteArray)(&p.signature),
},
(*pk.UUID)(&p.sender),
(*pk.ByteArray)(&p.msgSignature),
&p.content,
&timestamp,
(*pk.Long)(&p.salt),
pk.Array(&p.prevMessages),
&hasUnsignedContent, pk.Opt{Has: &hasUnsignedContent,
Field: &unsignedContent,
},
(*pk.VarInt)(&p.filterType),
pk.Opt{
Has: func() bool { return p.filterType == 2 },
Field: &p.filterSet,
},
&p.chatType,
}.ReadFrom(r)
if err != nil {
return
}
p.timestamp = time.UnixMilli(int64(timestamp))
if hasUnsignedContent {
p.unsignedContent = &unsignedContent
}
return n, err
}
type msgContent struct {
plainMsg string
formatted *chat.Message
}
func (m *msgContent) ReadFrom(r io.Reader) (n int64, err error) {
var hasFormatted pk.Boolean
n1, err := (*pk.String)(&m.plainMsg).ReadFrom(r)
if err != nil {
return n1, err
}
n2, err := hasFormatted.ReadFrom(r)
if err != nil {
return n1 + n2, err
}
if hasFormatted {
m.formatted = new(chat.Message)
n3, err := m.formatted.ReadFrom(r)
return n1 + n2 + n3, err
}
return n1 + n2, err
}
type prevMsg struct {
sender uuid.UUID
signature []byte
}
func (p *prevMsg) ReadFrom(r io.Reader) (n int64, err error) {
return pk.Tuple{
(*pk.UUID)(&p.sender),
(*pk.ByteArray)(&p.signature),
}.ReadFrom(r)
}
type chatType chat.Type
func (c *chatType) ReadFrom(r io.Reader) (n int64, err error) {
var hasTargetName pk.Boolean
n1, err := (*pk.VarInt)(&c.ID).ReadFrom(r)
if err != nil {
return n1, err
}
n2, err := c.SenderName.ReadFrom(r)
if err != nil {
return n1 + n2, err
}
n3, err := hasTargetName.ReadFrom(r)
if err != nil {
return n1 + n2 + n3, err
}
if hasTargetName {
c.TargetName = new(chat.Message)
n4, err := c.TargetName.ReadFrom(r)
return n1 + n2 + n3 + n4, err
}
return n1 + n2 + n3, nil
}

7
bot/chat/events.go Normal file
View File

@ -0,0 +1,7 @@
package chat
import "github.com/Tnze/go-mc/chat"
type EventsHandler struct {
PlayerChatMessage func(msg chat.Message) error
}

View File

@ -1,6 +1,7 @@
package bot package bot
import ( import (
"github.com/Tnze/go-mc/data/packetid"
"github.com/Tnze/go-mc/net" "github.com/Tnze/go-mc/net"
"github.com/Tnze/go-mc/yggdrasil/user" "github.com/Tnze/go-mc/yggdrasil/user"
"github.com/google/uuid" "github.com/google/uuid"
@ -33,7 +34,7 @@ func (c *Client) Close() error {
func NewClient() *Client { func NewClient() *Client {
return &Client{ return &Client{
Auth: Auth{Name: "Steve"}, Auth: Auth{Name: "Steve"},
Events: Events{handlers: make(map[int32]*handlerHeap)}, Events: Events{handlers: make(map[packetid.ClientboundPacketID]*handlerHeap)},
} }
} }

View File

@ -1,12 +1,13 @@
package bot package bot
import ( import (
"github.com/Tnze/go-mc/data/packetid"
pk "github.com/Tnze/go-mc/net/packet" pk "github.com/Tnze/go-mc/net/packet"
) )
type Events struct { type Events struct {
generic *handlerHeap // for every packet generic *handlerHeap // for every packet
handlers map[int32]*handlerHeap // for specific packet id only handlers map[packetid.ClientboundPacketID]*handlerHeap // for specific packet id only
} }
func (e *Events) AddListener(listeners ...PacketHandler) { func (e *Events) AddListener(listeners ...PacketHandler) {
@ -36,7 +37,7 @@ func (e *Events) AddGeneric(listeners ...PacketHandler) {
type PacketHandlerFunc func(p pk.Packet) error type PacketHandlerFunc func(p pk.Packet) error
type PacketHandler struct { type PacketHandler struct {
ID int32 ID packetid.ClientboundPacketID
Priority int Priority int
F func(p pk.Packet) error F func(p pk.Packet) error
} }

View File

@ -2,6 +2,8 @@ package bot
import ( import (
"fmt" "fmt"
"github.com/Tnze/go-mc/data/packetid"
pk "github.com/Tnze/go-mc/net/packet" pk "github.com/Tnze/go-mc/net/packet"
) )
@ -24,12 +26,12 @@ func (c *Client) HandleGame() error {
} }
type PacketHandlerError struct { type PacketHandlerError struct {
ID int32 ID packetid.ClientboundPacketID
Err error Err error
} }
func (d PacketHandlerError) Error() string { func (d PacketHandlerError) Error() string {
return fmt.Sprintf("handle packet 0x%X error: %v", d.ID, d.Err) return fmt.Sprintf("handle packet %v error: %v", d.ID, d.Err)
} }
func (d PacketHandlerError) Unwrap() error { func (d PacketHandlerError) Unwrap() error {
@ -37,18 +39,19 @@ func (d PacketHandlerError) Unwrap() error {
} }
func (c *Client) handlePacket(p pk.Packet) (err error) { func (c *Client) handlePacket(p pk.Packet) (err error) {
packetID := packetid.ClientboundPacketID(p.ID)
if c.Events.generic != nil { if c.Events.generic != nil {
for _, handler := range *c.Events.generic { for _, handler := range *c.Events.generic {
if err = handler.F(p); err != nil { if err = handler.F(p); err != nil {
return PacketHandlerError{ID: p.ID, Err: err} return PacketHandlerError{ID: packetID, Err: err}
} }
} }
} }
if listeners := c.Events.handlers[p.ID]; listeners != nil { if listeners := c.Events.handlers[packetID]; listeners != nil {
for _, handler := range *listeners { for _, handler := range *listeners {
err = handler.F(p) err = handler.F(p)
if err != nil { if err != nil {
return PacketHandlerError{ID: p.ID, Err: err} return PacketHandlerError{ID: packetID, Err: err}
} }
} }
} }

51
chat/decoration.go Normal file
View File

@ -0,0 +1,51 @@
package chat
type Decoration struct {
TranslationKey string `nbt:"translation_key"`
Parameters []string `nbt:"parameters"`
Style struct {
Bold bool `nbt:"bold"`
Italic bool `nbt:"italic"`
UnderLined bool `nbt:"underlined"`
StrikeThrough bool `nbt:"strikethrough"`
Obfuscated bool `nbt:"obfuscated"`
Color string `nbt:"color"`
Insertion string `nbt:"insertion"`
Font string `nbt:"font"`
} `nbt:"style"`
}
type Type struct {
ID int32
SenderName Message
TargetName *Message
}
func (t *Type) Decorate(content Message, d *Decoration) (msg Message) {
with := make([]Message, len(d.Parameters))
for i, para := range d.Parameters {
switch para {
case "sender":
with[i] = t.SenderName
case "target":
with[i] = *t.TargetName
case "content":
with[i] = content
default:
with[i] = Text("<nil>")
}
}
return Message{
Translate: d.TranslationKey,
With: with,
Bold: d.Style.Bold,
Italic: d.Style.Italic,
UnderLined: d.Style.UnderLined,
StrikeThrough: d.Style.StrikeThrough,
Obfuscated: d.Style.Obfuscated,
Font: d.Style.Font,
Color: d.Style.Color,
Insertion: d.Style.Insertion,
}
}

View File

@ -22,10 +22,8 @@ import (
pk "github.com/Tnze/go-mc/net/packet" pk "github.com/Tnze/go-mc/net/packet"
) )
type Type int32
const ( const (
Chat Type = iota Chat = iota
System System
GameInfo GameInfo
SayCommand SayCommand
@ -76,9 +74,9 @@ type Message struct {
ClickEvent *ClickEvent `json:"clickEvent,omitempty"` ClickEvent *ClickEvent `json:"clickEvent,omitempty"`
HoverEvent *HoverEvent `json:"hoverEvent,omitempty"` HoverEvent *HoverEvent `json:"hoverEvent,omitempty"`
Translate string `json:"translate,omitempty"` Translate string `json:"translate,omitempty"`
With []json.RawMessage `json:"with,omitempty"` With []Message `json:"with,omitempty"`
Extra []Message `json:"extra,omitempty"` Extra []Message `json:"extra,omitempty"`
} }
// Same as Message, but "Text" is omitempty // Same as Message, but "Text" is omitempty
@ -98,9 +96,9 @@ type translateMsg struct {
ClickEvent *ClickEvent `json:"clickEvent,omitempty"` ClickEvent *ClickEvent `json:"clickEvent,omitempty"`
HoverEvent *HoverEvent `json:"hoverEvent,omitempty"` HoverEvent *HoverEvent `json:"hoverEvent,omitempty"`
Translate string `json:"translate"` Translate string `json:"translate"`
With []json.RawMessage `json:"with,omitempty"` With []Message `json:"with,omitempty"`
Extra []Message `json:"extra,omitempty"` Extra []Message `json:"extra,omitempty"`
} }
type jsonMsg Message type jsonMsg Message
@ -180,10 +178,7 @@ func Text(str string) Message {
func TranslateMsg(key string, with ...Message) (m Message) { func TranslateMsg(key string, with ...Message) (m Message) {
m.Translate = key m.Translate = key
m.With = make([]json.RawMessage, len(with)) m.With = with
for i, v := range with {
m.With[i], _ = json.Marshal(v)
}
return return
} }
@ -250,9 +245,7 @@ func (m Message) ClearString() string {
if m.Translate != "" { if m.Translate != "" {
args := make([]interface{}, len(m.With)) args := make([]interface{}, len(m.With))
for i, v := range m.With { for i, v := range m.With {
var arg Message args[i] = v.ClearString()
_ = arg.UnmarshalJSON(v) //ignore error
args[i] = arg.ClearString()
} }
_, _ = fmt.Fprintf(&msg, translateMap[m.Translate], args...) _, _ = fmt.Fprintf(&msg, translateMap[m.Translate], args...)
@ -297,9 +290,7 @@ func (m Message) String() string {
if m.Translate != "" { if m.Translate != "" {
args := make([]interface{}, len(m.With)) args := make([]interface{}, len(m.With))
for i, v := range m.With { for i, v := range m.With {
var arg Message args[i] = v
_ = arg.UnmarshalJSON(v) //ignore error
args[i] = arg
} }
_, _ = fmt.Fprintf(&msg, translateMap[m.Translate], args...) _, _ = fmt.Fprintf(&msg, translateMap[m.Translate], args...)

View File

@ -0,0 +1,130 @@
// Code generated by "stringer -type ClientboundPacketID"; DO NOT EDIT.
package packetid
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[ClientboundAddEntity-0]
_ = x[ClientboundAddExperienceOrb-1]
_ = x[ClientboundAddPlayer-2]
_ = x[ClientboundAnimate-3]
_ = x[ClientboundAwardStats-4]
_ = x[ClientboundBlockChangedAck-5]
_ = x[ClientboundBlockDestruction-6]
_ = x[ClientboundBlockEntityData-7]
_ = x[ClientboundBlockEvent-8]
_ = x[ClientboundBlockUpdate-9]
_ = x[ClientboundBossEvent-10]
_ = x[ClientboundChangeDifficulty-11]
_ = x[ClientboundChatPreview-12]
_ = x[ClientboundClearTitles-13]
_ = x[ClientboundCommandSuggestions-14]
_ = x[ClientboundCommands-15]
_ = x[ClientboundContainerClose-16]
_ = x[ClientboundContainerSetContent-17]
_ = x[ClientboundContainerSetData-18]
_ = x[ClientboundContainerSetSlot-19]
_ = x[ClientboundCooldown-20]
_ = x[ClientboundCustomChatCompletions-21]
_ = x[ClientboundCustomPayload-22]
_ = x[ClientboundCustomSound-23]
_ = x[ClientboundDeleteChat-24]
_ = x[ClientboundDisconnect-25]
_ = x[ClientboundEntityEvent-26]
_ = x[ClientboundExplode-27]
_ = x[ClientboundForgetLevelChunk-28]
_ = x[ClientboundGameEvent-29]
_ = x[ClientboundHorseScreenOpen-30]
_ = x[ClientboundInitializeBorder-31]
_ = x[ClientboundKeepAlive-32]
_ = x[ClientboundLevelChunkWithLight-33]
_ = x[ClientboundLevelEvent-34]
_ = x[ClientboundLevelParticles-35]
_ = x[ClientboundLightUpdate-36]
_ = x[ClientboundLogin-37]
_ = x[ClientboundMapItemData-38]
_ = x[ClientboundMerchantOffers-39]
_ = x[ClientboundMoveEntityPos-40]
_ = x[ClientboundMoveEntityPosRot-41]
_ = x[ClientboundMoveEntityRot-42]
_ = x[ClientboundMoveVehicle-43]
_ = x[ClientboundOpenBook-44]
_ = x[ClientboundOpenScreen-45]
_ = x[ClientboundOpenSignEditor-46]
_ = x[ClientboundPing-47]
_ = x[ClientboundPlaceGhostRecipe-48]
_ = x[ClientboundPlayerAbilities-49]
_ = x[ClientboundPlayerChatHeader-50]
_ = x[ClientboundPlayerChat-51]
_ = x[ClientboundPlayerCombatEnd-52]
_ = x[ClientboundPlayerCombatEnter-53]
_ = x[ClientboundPlayerCombatKill-54]
_ = x[ClientboundPlayerInfo-55]
_ = x[ClientboundPlayerLookAt-56]
_ = x[ClientboundPlayerPosition-57]
_ = x[ClientboundRecipe-58]
_ = x[ClientboundRemoveEntities-59]
_ = x[ClientboundRemoveMobEffect-60]
_ = x[ClientboundResourcePack-61]
_ = x[ClientboundRespawn-62]
_ = x[ClientboundRotateHead-63]
_ = x[ClientboundSectionBlocksUpdate-64]
_ = x[ClientboundSelectAdvancementsTab-65]
_ = x[ClientboundServerData-66]
_ = x[ClientboundSetActionBarText-67]
_ = x[ClientboundSetBorderCenter-68]
_ = x[ClientboundSetBorderLerpSize-69]
_ = x[ClientboundSetBorderSize-70]
_ = x[ClientboundSetBorderWarningDelay-71]
_ = x[ClientboundSetBorderWarningDistance-72]
_ = x[ClientboundSetCamera-73]
_ = x[ClientboundSetCarriedItem-74]
_ = x[ClientboundSetChunkCacheCenter-75]
_ = x[ClientboundSetChunkCacheRadius-76]
_ = x[ClientboundSetDefaultSpawnPosition-77]
_ = x[ClientboundSetDisplayChatPreview-78]
_ = x[ClientboundSetDisplayObjective-79]
_ = x[ClientboundSetEntityData-80]
_ = x[ClientboundSetEntityLink-81]
_ = x[ClientboundSetEntityMotion-82]
_ = x[ClientboundSetEquipment-83]
_ = x[ClientboundSetExperience-84]
_ = x[ClientboundSetHealth-85]
_ = x[ClientboundSetObjective-86]
_ = x[ClientboundSetPassengers-87]
_ = x[ClientboundSetPlayerTeam-88]
_ = x[ClientboundSetScore-89]
_ = x[ClientboundSetSimulationDistance-90]
_ = x[ClientboundSetSubtitleText-91]
_ = x[ClientboundSetTime-92]
_ = x[ClientboundSetTitleText-93]
_ = x[ClientboundSetTitlesAnimation-94]
_ = x[ClientboundSoundEntity-95]
_ = x[ClientboundSound-96]
_ = x[ClientboundStopSound-97]
_ = x[ClientboundSystemChat-98]
_ = x[ClientboundTabList-99]
_ = x[ClientboundTagQuery-100]
_ = x[ClientboundTakeItemEntity-101]
_ = x[ClientboundTeleportEntity-102]
_ = x[ClientboundUpdateAdvancements-103]
_ = x[ClientboundUpdateAttributes-104]
_ = x[ClientboundUpdateMobEffect-105]
_ = x[ClientboundUpdateRecipes-106]
_ = x[ClientboundUpdateTags-107]
}
const _ClientboundPacketID_name = "ClientboundAddEntityClientboundAddExperienceOrbClientboundAddPlayerClientboundAnimateClientboundAwardStatsClientboundBlockChangedAckClientboundBlockDestructionClientboundBlockEntityDataClientboundBlockEventClientboundBlockUpdateClientboundBossEventClientboundChangeDifficultyClientboundChatPreviewClientboundClearTitlesClientboundCommandSuggestionsClientboundCommandsClientboundContainerCloseClientboundContainerSetContentClientboundContainerSetDataClientboundContainerSetSlotClientboundCooldownClientboundCustomChatCompletionsClientboundCustomPayloadClientboundCustomSoundClientboundDeleteChatClientboundDisconnectClientboundEntityEventClientboundExplodeClientboundForgetLevelChunkClientboundGameEventClientboundHorseScreenOpenClientboundInitializeBorderClientboundKeepAliveClientboundLevelChunkWithLightClientboundLevelEventClientboundLevelParticlesClientboundLightUpdateClientboundLoginClientboundMapItemDataClientboundMerchantOffersClientboundMoveEntityPosClientboundMoveEntityPosRotClientboundMoveEntityRotClientboundMoveVehicleClientboundOpenBookClientboundOpenScreenClientboundOpenSignEditorClientboundPingClientboundPlaceGhostRecipeClientboundPlayerAbilitiesClientboundPlayerChatHeaderClientboundPlayerChatClientboundPlayerCombatEndClientboundPlayerCombatEnterClientboundPlayerCombatKillClientboundPlayerInfoClientboundPlayerLookAtClientboundPlayerPositionClientboundRecipeClientboundRemoveEntitiesClientboundRemoveMobEffectClientboundResourcePackClientboundRespawnClientboundRotateHeadClientboundSectionBlocksUpdateClientboundSelectAdvancementsTabClientboundServerDataClientboundSetActionBarTextClientboundSetBorderCenterClientboundSetBorderLerpSizeClientboundSetBorderSizeClientboundSetBorderWarningDelayClientboundSetBorderWarningDistanceClientboundSetCameraClientboundSetCarriedItemClientboundSetChunkCacheCenterClientboundSetChunkCacheRadiusClientboundSetDefaultSpawnPositionClientboundSetDisplayChatPreviewClientboundSetDisplayObjectiveClientboundSetEntityDataClientboundSetEntityLinkClientboundSetEntityMotionClientboundSetEquipmentClientboundSetExperienceClientboundSetHealthClientboundSetObjectiveClientboundSetPassengersClientboundSetPlayerTeamClientboundSetScoreClientboundSetSimulationDistanceClientboundSetSubtitleTextClientboundSetTimeClientboundSetTitleTextClientboundSetTitlesAnimationClientboundSoundEntityClientboundSoundClientboundStopSoundClientboundSystemChatClientboundTabListClientboundTagQueryClientboundTakeItemEntityClientboundTeleportEntityClientboundUpdateAdvancementsClientboundUpdateAttributesClientboundUpdateMobEffectClientboundUpdateRecipesClientboundUpdateTags"
var _ClientboundPacketID_index = [...]uint16{0, 20, 47, 67, 85, 106, 132, 159, 185, 206, 228, 248, 275, 297, 319, 348, 367, 392, 422, 449, 476, 495, 527, 551, 573, 594, 615, 637, 655, 682, 702, 728, 755, 775, 805, 826, 851, 873, 889, 911, 936, 960, 987, 1011, 1033, 1052, 1073, 1098, 1113, 1140, 1166, 1193, 1214, 1240, 1268, 1295, 1316, 1339, 1364, 1381, 1406, 1432, 1455, 1473, 1494, 1524, 1556, 1577, 1604, 1630, 1658, 1682, 1714, 1749, 1769, 1794, 1824, 1854, 1888, 1920, 1950, 1974, 1998, 2024, 2047, 2071, 2091, 2114, 2138, 2162, 2181, 2213, 2239, 2257, 2280, 2309, 2331, 2347, 2367, 2388, 2406, 2425, 2450, 2475, 2504, 2531, 2557, 2581, 2602}
func (i ClientboundPacketID) String() string {
if i < 0 || i >= ClientboundPacketID(len(_ClientboundPacketID_index)-1) {
return "ClientboundPacketID(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _ClientboundPacketID_name[_ClientboundPacketID_index[i]:_ClientboundPacketID_index[i+1]]
}

View File

@ -1,5 +1,11 @@
package packetid package packetid
//go:generate stringer -type ClientboundPacketID
//go:generate stringer -type ServerboundPacketID
type ClientboundPacketID int32
type ServerboundPacketID int32
// Login Clientbound // Login Clientbound
const ( const (
LoginDisconnect = iota LoginDisconnect = iota
@ -30,7 +36,7 @@ const (
// Game Clientbound // Game Clientbound
const ( const (
ClientboundAddEntity = iota ClientboundAddEntity ClientboundPacketID = iota
ClientboundAddExperienceOrb ClientboundAddExperienceOrb
ClientboundAddPlayer ClientboundAddPlayer
ClientboundAnimate ClientboundAnimate
@ -142,7 +148,7 @@ const (
// Game Serverbound // Game Serverbound
const ( const (
ServerboundAcceptTeleportation = iota ServerboundAcceptTeleportation ServerboundPacketID = iota
ServerboundBlockEntityTagQuery ServerboundBlockEntityTagQuery
ServerboundChangeDifficulty ServerboundChangeDifficulty
ServerboundChatAck ServerboundChatAck

View File

@ -0,0 +1,73 @@
// Code generated by "stringer -type ServerboundPacketID"; DO NOT EDIT.
package packetid
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[ServerboundAcceptTeleportation-0]
_ = x[ServerboundBlockEntityTagQuery-1]
_ = x[ServerboundChangeDifficulty-2]
_ = x[ServerboundChatAck-3]
_ = x[ServerboundChatCommand-4]
_ = x[ServerboundChat-5]
_ = x[ServerboundChatPreview-6]
_ = x[ServerboundClientCommand-7]
_ = x[ServerboundClientInformation-8]
_ = x[ServerboundCommandSuggestion-9]
_ = x[ServerboundContainerButtonClick-10]
_ = x[ServerboundContainerClick-11]
_ = x[ServerboundContainerClose-12]
_ = x[ServerboundCustomPayload-13]
_ = x[ServerboundEditBook-14]
_ = x[ServerboundEntityTagQuery-15]
_ = x[ServerboundInteract-16]
_ = x[ServerboundJigsawGenerate-17]
_ = x[ServerboundKeepAlive-18]
_ = x[ServerboundLockDifficulty-19]
_ = x[ServerboundMovePlayerPos-20]
_ = x[ServerboundMovePlayerPosRot-21]
_ = x[ServerboundMovePlayerRot-22]
_ = x[ServerboundMovePlayerStatusOnly-23]
_ = x[ServerboundMoveVehicle-24]
_ = x[ServerboundPaddleBoat-25]
_ = x[ServerboundPickItem-26]
_ = x[ServerboundPlaceRecipe-27]
_ = x[ServerboundPlayerAbilities-28]
_ = x[ServerboundPlayerAction-29]
_ = x[ServerboundPlayerCommand-30]
_ = x[ServerboundPlayerInput-31]
_ = x[ServerboundPong-32]
_ = x[ServerboundRecipeBookChangeSettings-33]
_ = x[ServerboundRecipeBookSeenRecipe-34]
_ = x[ServerboundRenameItem-35]
_ = x[ServerboundResourcePack-36]
_ = x[ServerboundSeenAdvancements-37]
_ = x[ServerboundSelectTrade-38]
_ = x[ServerboundSetBeacon-39]
_ = x[ServerboundSetCarriedItem-40]
_ = x[ServerboundSetCommandBlock-41]
_ = x[ServerboundSetCommandMinecart-42]
_ = x[ServerboundSetCreativeModeSlot-43]
_ = x[ServerboundSetJigsawBlock-44]
_ = x[ServerboundSetStructureBlock-45]
_ = x[ServerboundSignUpdate-46]
_ = x[ServerboundSwing-47]
_ = x[ServerboundTeleportToEntity-48]
_ = x[ServerboundUseItemOn-49]
_ = x[ServerboundUseItem-50]
}
const _ServerboundPacketID_name = "ServerboundAcceptTeleportationServerboundBlockEntityTagQueryServerboundChangeDifficultyServerboundChatAckServerboundChatCommandServerboundChatServerboundChatPreviewServerboundClientCommandServerboundClientInformationServerboundCommandSuggestionServerboundContainerButtonClickServerboundContainerClickServerboundContainerCloseServerboundCustomPayloadServerboundEditBookServerboundEntityTagQueryServerboundInteractServerboundJigsawGenerateServerboundKeepAliveServerboundLockDifficultyServerboundMovePlayerPosServerboundMovePlayerPosRotServerboundMovePlayerRotServerboundMovePlayerStatusOnlyServerboundMoveVehicleServerboundPaddleBoatServerboundPickItemServerboundPlaceRecipeServerboundPlayerAbilitiesServerboundPlayerActionServerboundPlayerCommandServerboundPlayerInputServerboundPongServerboundRecipeBookChangeSettingsServerboundRecipeBookSeenRecipeServerboundRenameItemServerboundResourcePackServerboundSeenAdvancementsServerboundSelectTradeServerboundSetBeaconServerboundSetCarriedItemServerboundSetCommandBlockServerboundSetCommandMinecartServerboundSetCreativeModeSlotServerboundSetJigsawBlockServerboundSetStructureBlockServerboundSignUpdateServerboundSwingServerboundTeleportToEntityServerboundUseItemOnServerboundUseItem"
var _ServerboundPacketID_index = [...]uint16{0, 30, 60, 87, 105, 127, 142, 164, 188, 216, 244, 275, 300, 325, 349, 368, 393, 412, 437, 457, 482, 506, 533, 557, 588, 610, 631, 650, 672, 698, 721, 745, 767, 782, 817, 848, 869, 892, 919, 941, 961, 986, 1012, 1041, 1071, 1096, 1124, 1145, 1161, 1188, 1208, 1226}
func (i ServerboundPacketID) String() string {
if i < 0 || i >= ServerboundPacketID(len(_ServerboundPacketID_index)-1) {
return "ServerboundPacketID(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _ServerboundPacketID_name[_ServerboundPacketID_index[i]:_ServerboundPacketID_index[i+1]]
}

View File

@ -1,6 +1,7 @@
package main package main
import ( import (
botchat "github.com/Tnze/go-mc/bot/chat"
"log" "log"
"time" "time"
@ -17,8 +18,9 @@ import (
const timeout = 45 const timeout = 45
var ( var (
c *bot.Client c *bot.Client
p *basic.Player p *basic.Player
bc *botchat.Chat
watch chan time.Time watch chan time.Time
) )
@ -26,16 +28,16 @@ var (
func main() { func main() {
//log.SetOutput(colorable.NewColorableStdout()) // optional for colorable output //log.SetOutput(colorable.NewColorableStdout()) // optional for colorable output
c = bot.NewClient() c = bot.NewClient()
p = basic.NewPlayer(c, basic.DefaultSettings) p = basic.NewPlayer(c, basic.DefaultSettings, basic.EventsListener{
//Register event handlers
basic.EventsListener{
GameStart: onGameStart, GameStart: onGameStart,
ChatMsg: onChatMsg,
SystemMsg: onSystemMsg, SystemMsg: onSystemMsg,
Disconnect: onDisconnect, Disconnect: onDisconnect,
Death: onDeath, Death: onDeath,
}.Attach(c) })
bc = botchat.NewChat(c, p, botchat.EventsHandler{PlayerChatMessage: onChatMsg})
//Register event handlers
c.Events.AddListener(soundListener) c.Events.AddListener(soundListener)
//Login //Login
@ -107,13 +109,13 @@ func onSound(id int, category int, x, y, z float64, volume, pitch float32) error
return nil return nil
} }
func onChatMsg(c *basic.PlayerMessage) error { func onChatMsg(c chat.Message) error {
log.Println("Chat:", c.SignedMessage) log.Println("Chat:", c)
return nil return nil
} }
func onSystemMsg(c chat.Message, pos byte) error { func onSystemMsg(c chat.Message, overlay bool) error {
log.Printf("System: %v, Location: %v", c, pos) log.Printf("System: %v, Overlay: %v", c, overlay)
return nil return nil
} }

View File

@ -12,6 +12,7 @@ import (
"github.com/Tnze/go-mc/bot" "github.com/Tnze/go-mc/bot"
"github.com/Tnze/go-mc/bot/basic" "github.com/Tnze/go-mc/bot/basic"
botchat "github.com/Tnze/go-mc/bot/chat"
"github.com/Tnze/go-mc/bot/screen" "github.com/Tnze/go-mc/bot/screen"
"github.com/Tnze/go-mc/bot/world" "github.com/Tnze/go-mc/bot/world"
"github.com/Tnze/go-mc/chat" "github.com/Tnze/go-mc/chat"
@ -27,6 +28,7 @@ var accessToken = flag.String("token", "", "AccessToken")
var client *bot.Client var client *bot.Client
var player *basic.Player var player *basic.Player
var chatHandler *botchat.Chat
var worldManager *world.World var worldManager *world.World
var screenManager *screen.Manager var screenManager *screen.Manager
@ -39,15 +41,16 @@ func main() {
UUID: *playerID, UUID: *playerID,
AsTk: *accessToken, AsTk: *accessToken,
} }
player = basic.NewPlayer(client, basic.DefaultSettings) player = basic.NewPlayer(client, basic.DefaultSettings, basic.EventsListener{
basic.EventsListener{
GameStart: onGameStart, GameStart: onGameStart,
ChatMsg: onChatMsg,
SystemMsg: onSystemMsg, SystemMsg: onSystemMsg,
Disconnect: onDisconnect, Disconnect: onDisconnect,
HealthChange: onHealthChange, HealthChange: onHealthChange,
Death: onDeath, Death: onDeath,
}.Attach(client) })
chatHandler = botchat.NewChat(client, player, botchat.EventsHandler{
PlayerChatMessage: onPlayerMsg,
})
worldManager = world.NewWorld(client, player, world.EventsListener{ worldManager = world.NewWorld(client, player, world.EventsListener{
LoadChunk: onChunkLoad, LoadChunk: onChunkLoad,
UnloadChunk: onChunkUnload, UnloadChunk: onChunkUnload,
@ -103,13 +106,13 @@ func onGameStart() error {
return nil //if err isn't nil, HandleGame() will return it. return nil //if err isn't nil, HandleGame() will return it.
} }
func onChatMsg(c *basic.PlayerMessage) error { func onPlayerMsg(msg chat.Message) error {
log.Println("Chat:", c.SignedMessage.String()) log.Printf("Player: %v", msg)
return nil return nil
} }
func onSystemMsg(c chat.Message, pos byte) error { func onSystemMsg(c chat.Message, overlay bool) error {
log.Printf("System: %v, Location: %v", c, pos) log.Printf("System: %v, Overlay: %v", c, overlay)
return nil return nil
} }

View File

@ -28,7 +28,7 @@ func main() {
//log.SetOutput(colorable.NewColorableStdout()) //log.SetOutput(colorable.NewColorableStdout())
client = bot.NewClient() client = bot.NewClient()
client.Auth.Name = "Daze" client.Auth.Name = "Daze"
player = basic.NewPlayer(client, basic.DefaultSettings) player = basic.NewPlayer(client, basic.DefaultSettings, basic.EventsListener{})
client.Events.AddListener(bot.PacketHandler{ client.Events.AddListener(bot.PacketHandler{
ID: packetid.ClientboundCommands, ID: packetid.ClientboundCommands,
Priority: 50, Priority: 50,

View File

@ -44,11 +44,10 @@ func newIndividual(id int, name string) (i *individual) {
i.id = id i.id = id
i.client = bot.NewClient() i.client = bot.NewClient()
i.client.Auth.Name = name i.client.Auth.Name = name
i.player = basic.NewPlayer(i.client, basic.DefaultSettings) i.player = basic.NewPlayer(i.client, basic.DefaultSettings, basic.EventsListener{
basic.EventsListener{
GameStart: i.onGameStart, GameStart: i.onGameStart,
Disconnect: onDisconnect, Disconnect: onDisconnect,
}.Attach(i.client) })
return return
} }

View File

@ -17,12 +17,12 @@ type Packet struct {
} }
// Marshal generate Packet with the ID and Fields // Marshal generate Packet with the ID and Fields
func Marshal(id int32, fields ...FieldEncoder) (pk Packet) { func Marshal[ID ~int32 | int](id ID, fields ...FieldEncoder) (pk Packet) {
var pb Builder var pb Builder
for _, v := range fields { for _, v := range fields {
pb.WriteField(v) pb.WriteField(v)
} }
return pb.Packet(id) return pb.Packet(int32(id))
} }
// Scan decode the packet and fill data into fields // Scan decode the packet and fill data into fields

View File

@ -2,6 +2,7 @@ package packet
import ( import (
"errors" "errors"
"fmt"
"io" "io"
"reflect" "reflect"
) )
@ -130,10 +131,10 @@ func (t Tuple) WriteTo(w io.Writer) (n int64, err error) {
// ReadFrom read Tuple from io.Reader, panic when any of field don't implement FieldDecoder // ReadFrom read Tuple from io.Reader, panic when any of field don't implement FieldDecoder
func (t Tuple) ReadFrom(r io.Reader) (n int64, err error) { func (t Tuple) ReadFrom(r io.Reader) (n int64, err error) {
for _, v := range t { for i, v := range t {
nn, err := v.(FieldDecoder).ReadFrom(r) nn, err := v.(FieldDecoder).ReadFrom(r)
if err != nil { if err != nil {
return n, err return n, fmt.Errorf("decode tuple[%d] error: %w", i, err)
} }
n += nn n += nn
} }