From 7814e7b1aba17ce1f5a23537ac6633f3ffecbd50 Mon Sep 17 00:00:00 2001 From: Tnze Date: Sat, 26 Nov 2022 15:58:31 +0800 Subject: [PATCH] update 1.19.2 bot, chat support --- bot/basic/basic.go | 19 +-- bot/basic/events.go | 71 +-------- bot/basic/info.go | 24 ++- bot/basic/keepalive.go | 2 +- bot/chat/chat.go | 168 ++++++++++++++++++++ bot/chat/events.go | 7 + bot/client.go | 3 +- bot/event.go | 7 +- bot/ingame.go | 13 +- chat/decoration.go | 51 ++++++ chat/message.go | 29 ++-- data/packetid/clientboundpacketid_string.go | 130 +++++++++++++++ data/packetid/packetid.go | 10 +- data/packetid/serverboundpacketid_string.go | 73 +++++++++ examples/autofish/autofish.go | 26 +-- examples/daze/daze.go | 19 ++- examples/dazeReadCommand/daze.go | 2 +- examples/pressureTest/main.go | 5 +- net/packet/packet.go | 4 +- net/packet/util.go | 5 +- 20 files changed, 532 insertions(+), 136 deletions(-) create mode 100644 bot/chat/chat.go create mode 100644 bot/chat/events.go create mode 100644 chat/decoration.go create mode 100644 data/packetid/clientboundpacketid_string.go create mode 100644 data/packetid/serverboundpacketid_string.go diff --git a/bot/basic/basic.go b/bot/basic/basic.go index 3aa867c..0397df2 100644 --- a/bot/basic/basic.go +++ b/bot/basic/basic.go @@ -20,7 +20,7 @@ // - HealthChange // - 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 import ( @@ -38,22 +38,23 @@ type Player struct { isSpawn bool } -func NewPlayer(c *bot.Client, settings Settings) *Player { - b := &Player{c: c, Settings: settings} +func NewPlayer(c *bot.Client, settings Settings, events EventsListener) *Player { + p := &Player{c: c, Settings: settings} c.Events.AddListener( - bot.PacketHandler{Priority: 0, ID: packetid.ClientboundLogin, F: b.handleLoginPacket}, - bot.PacketHandler{Priority: 0, ID: packetid.ClientboundKeepAlive, F: b.handleKeepAlivePacket}, - bot.PacketHandler{Priority: 0, ID: packetid.ClientboundPlayerPosition, F: b.handlePlayerPosition}, - bot.PacketHandler{Priority: 0, ID: packetid.ClientboundRespawn, F: b.handleRespawnPacket}, + bot.PacketHandler{Priority: 0, ID: packetid.ClientboundLogin, F: p.handleLoginPacket}, + bot.PacketHandler{Priority: 0, ID: packetid.ClientboundKeepAlive, F: p.handleKeepAlivePacket}, + bot.PacketHandler{Priority: 0, ID: packetid.ClientboundPlayerPosition, F: p.handlePlayerPosition}, + bot.PacketHandler{Priority: 0, ID: packetid.ClientboundRespawn, F: p.handleRespawnPacket}, ) - return b + events.attach(p) + return p } func (p *Player) Respawn() error { const PerformRespawn = 0 err := p.c.Conn.WritePacket(pk.Marshal( - packetid.ServerboundClientCommand, + int32(packetid.ServerboundClientCommand), pk.VarInt(PerformRespawn), )) if err != nil { diff --git a/bot/basic/events.go b/bot/basic/events.go index 1d491d2..51fe268 100644 --- a/bot/basic/events.go +++ b/bot/basic/events.go @@ -5,35 +5,30 @@ import ( "github.com/Tnze/go-mc/chat" "github.com/Tnze/go-mc/data/packetid" pk "github.com/Tnze/go-mc/net/packet" - "io" ) type EventsListener struct { GameStart func() error - ChatMsg func(c *PlayerMessage) error SystemMsg func(c chat.Message, overlay bool) error Disconnect func(reason chat.Message) error HealthChange func(health float32) 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. -func (e EventsListener) Attach(c *bot.Client) { +func (e EventsListener) attach(p *Player) { if e.GameStart != nil { - attachJoinGameHandler(c, e.GameStart) - } - if e.ChatMsg != nil { - attachPlayerMsg(c, e.ChatMsg) + attachJoinGameHandler(p.c, e.GameStart) } if e.SystemMsg != nil { - attachSystemMsg(c, e.SystemMsg) + attachSystemMsg(p.c, e.SystemMsg) } if e.Disconnect != nil { - attachDisconnect(c, e.Disconnect) + attachDisconnect(p.c, e.Disconnect) } 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 - // 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) { c.Events.AddListener(bot.PacketHandler{ Priority: 64, ID: packetid.ClientboundSystemChat, diff --git a/bot/basic/info.go b/bot/basic/info.go index fc870b2..be787d4 100644 --- a/bot/basic/info.go +++ b/bot/basic/info.go @@ -1,6 +1,7 @@ package basic import ( + "github.com/Tnze/go-mc/chat" "unsafe" "github.com/Tnze/go-mc/data/packetid" @@ -46,9 +47,14 @@ type Dimension struct { MonsterSpawnBlockLightLimit int32 `nbt:"monster_spawn_block_light_limit"` } +type ChatType struct { + Chat chat.Decoration `nbt:"chat"` + Narration chat.Decoration `nbt:"narration"` +} + type RegistryCodec struct { // 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"` WorldGenBiome Registry[nbt.RawMessage] `nbt:"minecraft:worldgen/biome"` } @@ -71,6 +77,18 @@ func (r *Registry[E]) Find(name string) *E { 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 { EID int32 // The player's Entity ID (EID). Hardcore bool // Is hardcore @@ -106,7 +124,7 @@ func (p *Player) handleLoginPacket(packet pk.Packet) error { return Error{err} } err = p.c.Conn.WritePacket(pk.Marshal( //PluginMessage packet - packetid.ServerboundCustomPayload, + int32(packetid.ServerboundCustomPayload), pk.Identifier("minecraft: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( - packetid.ServerboundClientInformation, // Client settings + int32(packetid.ServerboundClientInformation), // Client settings pk.String(p.Settings.Locale), pk.Byte(p.Settings.ViewDistance), pk.VarInt(p.Settings.ChatMode), diff --git a/bot/basic/keepalive.go b/bot/basic/keepalive.go index 8029a5b..5539101 100644 --- a/bot/basic/keepalive.go +++ b/bot/basic/keepalive.go @@ -12,7 +12,7 @@ func (p Player) handleKeepAlivePacket(packet pk.Packet) error { } // Response err := p.c.Conn.WritePacket(pk.Packet{ - ID: packetid.ServerboundKeepAlive, + ID: int32(packetid.ServerboundKeepAlive), Data: packet.Data, }) if err != nil { diff --git a/bot/chat/chat.go b/bot/chat/chat.go new file mode 100644 index 0000000..3efeb5d --- /dev/null +++ b/bot/chat/chat.go @@ -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 + 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, + ×tamp, + (*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 +} diff --git a/bot/chat/events.go b/bot/chat/events.go new file mode 100644 index 0000000..60db083 --- /dev/null +++ b/bot/chat/events.go @@ -0,0 +1,7 @@ +package chat + +import "github.com/Tnze/go-mc/chat" + +type EventsHandler struct { + PlayerChatMessage func(msg chat.Message) error +} diff --git a/bot/client.go b/bot/client.go index a790a1b..ae6c3c7 100644 --- a/bot/client.go +++ b/bot/client.go @@ -1,6 +1,7 @@ package bot import ( + "github.com/Tnze/go-mc/data/packetid" "github.com/Tnze/go-mc/net" "github.com/Tnze/go-mc/yggdrasil/user" "github.com/google/uuid" @@ -33,7 +34,7 @@ func (c *Client) Close() error { func NewClient() *Client { return &Client{ Auth: Auth{Name: "Steve"}, - Events: Events{handlers: make(map[int32]*handlerHeap)}, + Events: Events{handlers: make(map[packetid.ClientboundPacketID]*handlerHeap)}, } } diff --git a/bot/event.go b/bot/event.go index 28ca79c..a7936f2 100644 --- a/bot/event.go +++ b/bot/event.go @@ -1,12 +1,13 @@ package bot import ( + "github.com/Tnze/go-mc/data/packetid" pk "github.com/Tnze/go-mc/net/packet" ) type Events struct { - generic *handlerHeap // for every packet - handlers map[int32]*handlerHeap // for specific packet id only + generic *handlerHeap // for every packet + handlers map[packetid.ClientboundPacketID]*handlerHeap // for specific packet id only } func (e *Events) AddListener(listeners ...PacketHandler) { @@ -36,7 +37,7 @@ func (e *Events) AddGeneric(listeners ...PacketHandler) { type PacketHandlerFunc func(p pk.Packet) error type PacketHandler struct { - ID int32 + ID packetid.ClientboundPacketID Priority int F func(p pk.Packet) error } diff --git a/bot/ingame.go b/bot/ingame.go index 3a9751d..1f332fd 100644 --- a/bot/ingame.go +++ b/bot/ingame.go @@ -2,6 +2,8 @@ package bot import ( "fmt" + + "github.com/Tnze/go-mc/data/packetid" pk "github.com/Tnze/go-mc/net/packet" ) @@ -24,12 +26,12 @@ func (c *Client) HandleGame() error { } type PacketHandlerError struct { - ID int32 + ID packetid.ClientboundPacketID Err error } 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 { @@ -37,18 +39,19 @@ func (d PacketHandlerError) Unwrap() error { } func (c *Client) handlePacket(p pk.Packet) (err error) { + packetID := packetid.ClientboundPacketID(p.ID) if c.Events.generic != nil { for _, handler := range *c.Events.generic { 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 { err = handler.F(p) if err != nil { - return PacketHandlerError{ID: p.ID, Err: err} + return PacketHandlerError{ID: packetID, Err: err} } } } diff --git a/chat/decoration.go b/chat/decoration.go new file mode 100644 index 0000000..3f84eb3 --- /dev/null +++ b/chat/decoration.go @@ -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("") + } + } + 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, + } +} diff --git a/chat/message.go b/chat/message.go index c4770af..55c4a81 100644 --- a/chat/message.go +++ b/chat/message.go @@ -22,10 +22,8 @@ import ( pk "github.com/Tnze/go-mc/net/packet" ) -type Type int32 - const ( - Chat Type = iota + Chat = iota System GameInfo SayCommand @@ -76,9 +74,9 @@ type Message struct { ClickEvent *ClickEvent `json:"clickEvent,omitempty"` HoverEvent *HoverEvent `json:"hoverEvent,omitempty"` - Translate string `json:"translate,omitempty"` - With []json.RawMessage `json:"with,omitempty"` - Extra []Message `json:"extra,omitempty"` + Translate string `json:"translate,omitempty"` + With []Message `json:"with,omitempty"` + Extra []Message `json:"extra,omitempty"` } // Same as Message, but "Text" is omitempty @@ -98,9 +96,9 @@ type translateMsg struct { ClickEvent *ClickEvent `json:"clickEvent,omitempty"` HoverEvent *HoverEvent `json:"hoverEvent,omitempty"` - Translate string `json:"translate"` - With []json.RawMessage `json:"with,omitempty"` - Extra []Message `json:"extra,omitempty"` + Translate string `json:"translate"` + With []Message `json:"with,omitempty"` + Extra []Message `json:"extra,omitempty"` } type jsonMsg Message @@ -180,10 +178,7 @@ func Text(str string) Message { func TranslateMsg(key string, with ...Message) (m Message) { m.Translate = key - m.With = make([]json.RawMessage, len(with)) - for i, v := range with { - m.With[i], _ = json.Marshal(v) - } + m.With = with return } @@ -250,9 +245,7 @@ func (m Message) ClearString() string { if m.Translate != "" { args := make([]interface{}, len(m.With)) for i, v := range m.With { - var arg Message - _ = arg.UnmarshalJSON(v) //ignore error - args[i] = arg.ClearString() + args[i] = v.ClearString() } _, _ = fmt.Fprintf(&msg, translateMap[m.Translate], args...) @@ -297,9 +290,7 @@ func (m Message) String() string { if m.Translate != "" { args := make([]interface{}, len(m.With)) for i, v := range m.With { - var arg Message - _ = arg.UnmarshalJSON(v) //ignore error - args[i] = arg + args[i] = v } _, _ = fmt.Fprintf(&msg, translateMap[m.Translate], args...) diff --git a/data/packetid/clientboundpacketid_string.go b/data/packetid/clientboundpacketid_string.go new file mode 100644 index 0000000..ba76a1f --- /dev/null +++ b/data/packetid/clientboundpacketid_string.go @@ -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]] +} diff --git a/data/packetid/packetid.go b/data/packetid/packetid.go index c674a06..465c454 100644 --- a/data/packetid/packetid.go +++ b/data/packetid/packetid.go @@ -1,5 +1,11 @@ package packetid +//go:generate stringer -type ClientboundPacketID +//go:generate stringer -type ServerboundPacketID + +type ClientboundPacketID int32 +type ServerboundPacketID int32 + // Login Clientbound const ( LoginDisconnect = iota @@ -30,7 +36,7 @@ const ( // Game Clientbound const ( - ClientboundAddEntity = iota + ClientboundAddEntity ClientboundPacketID = iota ClientboundAddExperienceOrb ClientboundAddPlayer ClientboundAnimate @@ -142,7 +148,7 @@ const ( // Game Serverbound const ( - ServerboundAcceptTeleportation = iota + ServerboundAcceptTeleportation ServerboundPacketID = iota ServerboundBlockEntityTagQuery ServerboundChangeDifficulty ServerboundChatAck diff --git a/data/packetid/serverboundpacketid_string.go b/data/packetid/serverboundpacketid_string.go new file mode 100644 index 0000000..2edbc44 --- /dev/null +++ b/data/packetid/serverboundpacketid_string.go @@ -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]] +} diff --git a/examples/autofish/autofish.go b/examples/autofish/autofish.go index 811f73b..ffc197b 100644 --- a/examples/autofish/autofish.go +++ b/examples/autofish/autofish.go @@ -1,6 +1,7 @@ package main import ( + botchat "github.com/Tnze/go-mc/bot/chat" "log" "time" @@ -17,8 +18,9 @@ import ( const timeout = 45 var ( - c *bot.Client - p *basic.Player + c *bot.Client + p *basic.Player + bc *botchat.Chat watch chan time.Time ) @@ -26,16 +28,16 @@ var ( func main() { //log.SetOutput(colorable.NewColorableStdout()) // optional for colorable output c = bot.NewClient() - p = basic.NewPlayer(c, basic.DefaultSettings) - - //Register event handlers - basic.EventsListener{ + p = basic.NewPlayer(c, basic.DefaultSettings, basic.EventsListener{ GameStart: onGameStart, - ChatMsg: onChatMsg, SystemMsg: onSystemMsg, Disconnect: onDisconnect, Death: onDeath, - }.Attach(c) + }) + bc = botchat.NewChat(c, p, botchat.EventsHandler{PlayerChatMessage: onChatMsg}) + + //Register event handlers + c.Events.AddListener(soundListener) //Login @@ -107,13 +109,13 @@ func onSound(id int, category int, x, y, z float64, volume, pitch float32) error return nil } -func onChatMsg(c *basic.PlayerMessage) error { - log.Println("Chat:", c.SignedMessage) +func onChatMsg(c chat.Message) error { + log.Println("Chat:", c) return nil } -func onSystemMsg(c chat.Message, pos byte) error { - log.Printf("System: %v, Location: %v", c, pos) +func onSystemMsg(c chat.Message, overlay bool) error { + log.Printf("System: %v, Overlay: %v", c, overlay) return nil } diff --git a/examples/daze/daze.go b/examples/daze/daze.go index 92282aa..12b66ab 100644 --- a/examples/daze/daze.go +++ b/examples/daze/daze.go @@ -12,6 +12,7 @@ import ( "github.com/Tnze/go-mc/bot" "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/world" "github.com/Tnze/go-mc/chat" @@ -27,6 +28,7 @@ var accessToken = flag.String("token", "", "AccessToken") var client *bot.Client var player *basic.Player +var chatHandler *botchat.Chat var worldManager *world.World var screenManager *screen.Manager @@ -39,15 +41,16 @@ func main() { UUID: *playerID, AsTk: *accessToken, } - player = basic.NewPlayer(client, basic.DefaultSettings) - basic.EventsListener{ + player = basic.NewPlayer(client, basic.DefaultSettings, basic.EventsListener{ GameStart: onGameStart, - ChatMsg: onChatMsg, SystemMsg: onSystemMsg, Disconnect: onDisconnect, HealthChange: onHealthChange, Death: onDeath, - }.Attach(client) + }) + chatHandler = botchat.NewChat(client, player, botchat.EventsHandler{ + PlayerChatMessage: onPlayerMsg, + }) worldManager = world.NewWorld(client, player, world.EventsListener{ LoadChunk: onChunkLoad, UnloadChunk: onChunkUnload, @@ -103,13 +106,13 @@ func onGameStart() error { return nil //if err isn't nil, HandleGame() will return it. } -func onChatMsg(c *basic.PlayerMessage) error { - log.Println("Chat:", c.SignedMessage.String()) +func onPlayerMsg(msg chat.Message) error { + log.Printf("Player: %v", msg) return nil } -func onSystemMsg(c chat.Message, pos byte) error { - log.Printf("System: %v, Location: %v", c, pos) +func onSystemMsg(c chat.Message, overlay bool) error { + log.Printf("System: %v, Overlay: %v", c, overlay) return nil } diff --git a/examples/dazeReadCommand/daze.go b/examples/dazeReadCommand/daze.go index 787fb43..38cbddf 100644 --- a/examples/dazeReadCommand/daze.go +++ b/examples/dazeReadCommand/daze.go @@ -28,7 +28,7 @@ func main() { //log.SetOutput(colorable.NewColorableStdout()) client = bot.NewClient() client.Auth.Name = "Daze" - player = basic.NewPlayer(client, basic.DefaultSettings) + player = basic.NewPlayer(client, basic.DefaultSettings, basic.EventsListener{}) client.Events.AddListener(bot.PacketHandler{ ID: packetid.ClientboundCommands, Priority: 50, diff --git a/examples/pressureTest/main.go b/examples/pressureTest/main.go index 17dd627..9e9c7e8 100644 --- a/examples/pressureTest/main.go +++ b/examples/pressureTest/main.go @@ -44,11 +44,10 @@ func newIndividual(id int, name string) (i *individual) { i.id = id i.client = bot.NewClient() i.client.Auth.Name = name - i.player = basic.NewPlayer(i.client, basic.DefaultSettings) - basic.EventsListener{ + i.player = basic.NewPlayer(i.client, basic.DefaultSettings, basic.EventsListener{ GameStart: i.onGameStart, Disconnect: onDisconnect, - }.Attach(i.client) + }) return } diff --git a/net/packet/packet.go b/net/packet/packet.go index 9f72163..b5d86d4 100644 --- a/net/packet/packet.go +++ b/net/packet/packet.go @@ -17,12 +17,12 @@ type Packet struct { } // 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 for _, v := range fields { pb.WriteField(v) } - return pb.Packet(id) + return pb.Packet(int32(id)) } // Scan decode the packet and fill data into fields diff --git a/net/packet/util.go b/net/packet/util.go index 92cf1b0..952f0ee 100644 --- a/net/packet/util.go +++ b/net/packet/util.go @@ -2,6 +2,7 @@ package packet import ( "errors" + "fmt" "io" "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 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) if err != nil { - return n, err + return n, fmt.Errorf("decode tuple[%d] error: %w", i, err) } n += nn }