From ee23172e0a524c94d0d457b2d458c85e2972d9ba Mon Sep 17 00:00:00 2001 From: Tnze Date: Sat, 15 Jun 2024 23:59:23 +0800 Subject: [PATCH] upgrade bot configuration protocol --- bot/client.go | 6 +- bot/configuration.go | 187 +++++++++++++++++++++++++++++++++++++++---- bot/mcbot.go | 2 +- 3 files changed, 176 insertions(+), 19 deletions(-) diff --git a/bot/client.go b/bot/client.go index 26074d6..8c9332c 100644 --- a/bot/client.go +++ b/bot/client.go @@ -18,15 +18,18 @@ type Client struct { Auth Auth // These are filled when login process - Name string UUID uuid.UUID ConfigData // Ingame packet handlers Events Events + // Login plugins LoginPlugin map[string]func(data []byte) ([]byte, error) + + // Configuration handler + ConfigHandler } func (c *Client) Close() error { @@ -44,6 +47,7 @@ func NewClient() *Client { return &Client{ Auth: Auth{Name: "Steve"}, Events: Events{handlers: make([][]PacketHandler, packetid.ClientboundPacketIDGuard)}, + ConfigHandler: NewDefaultConfigHandler(), } } diff --git a/bot/configuration.go b/bot/configuration.go index 4d39331..a65a77c 100644 --- a/bot/configuration.go +++ b/bot/configuration.go @@ -1,6 +1,8 @@ package bot import ( + "fmt" + "io" "unsafe" "github.com/Tnze/go-mc/chat" @@ -10,14 +12,27 @@ import ( "github.com/Tnze/go-mc/registry" ) +type ConfigHandler interface { + GetCookie(key pk.Identifier) []byte + SetCookie(key pk.Identifier, payload []byte) + + PushResourcePack(res ResourcePack) + PopResourcePack(id pk.UUID) + PopAllResourcePack() + + SelectDataPacks(packs []DataPack) []DataPack +} + +type ResourcePack struct { + ID pk.UUID + URL string + Hash string + Forced bool + PromptMessage *chat.Message // Optional +} + type ConfigData struct { Registries registry.NetworkCodec - ResourcePack struct { - URL string - Hash string - Forced bool - PromptMessage *chat.Message // Optional - } FeatureFlags []string } @@ -43,7 +58,22 @@ func (c *Client) joinConfiguration(conn *net.Conn) error { } switch packetid.ClientboundPacketID(p.ID) { - case packetid.ClientboundCustomPayload: + case packetid.ClientboundConfigCookieRequest: + var key pk.Identifier + err := p.Scan(&key) + if err != nil { + return ConfigErr{"cookie request", err} + } + cookieContent := c.ConfigHandler.GetCookie(key) + err = conn.WritePacket(pk.Marshal( + packetid.ServerboundConfigCookieResponse, + pk.ByteArray(cookieContent), + )) + if err != nil { + return ConfigErr{"cookie response", err} + } + + case packetid.ClientboundConfigCustomPayload: var channel pk.Identifier var data pk.PluginMessageData err := p.Scan(&channel, &data) @@ -52,7 +82,7 @@ func (c *Client) joinConfiguration(conn *net.Conn) error { } // TODO: Provide configuration custom data handling interface - case packetid.ClientboundDisconnect: + case packetid.ClientboundConfigDisconnect: var reason chat.Message err := p.Scan(&reason) if err != nil { @@ -99,18 +129,29 @@ func (c *Client) joinConfiguration(conn *net.Conn) error { return ConfigErr{"pong", err} } + case packetid.ClientboundConfigResetChat: + // TODO + case packetid.ClientboundConfigRegistryData: - err := p.Scan(pk.NBT(&c.ConfigData.Registries)) - if err != nil { - return ConfigErr{"registry data", err} - } + // err := p.Scan(pk.NBT(&c.ConfigData.Registries)) + // if err != nil { + // return ConfigErr{"registry data", err} + // } case packetid.ClientboundConfigResourcePackPop: // TODO + var id pk.Option[pk.UUID, *pk.UUID] + err := p.Scan(&id) + if err != nil { + return ConfigErr{"resource pack pop", err} + } + case packetid.ClientboundConfigResourcePackPush: + var id pk.UUID var Url, Hash pk.String var Forced pk.Boolean var PromptMessage pk.Option[chat.Message, *chat.Message] err := p.Scan( + &id, &Url, &Hash, &Forced, @@ -119,12 +160,34 @@ func (c *Client) joinConfiguration(conn *net.Conn) error { if err != nil { return ConfigErr{"resource pack", err} } - c.ConfigData.ResourcePack.URL = string(Url) - c.ConfigData.ResourcePack.Hash = string(Hash) - c.ConfigData.ResourcePack.Forced = bool(Forced) - if PromptMessage.Has { - c.ConfigData.ResourcePack.PromptMessage = &PromptMessage.Val + res := ResourcePack{ + ID: id, + URL: string(Url), + Hash: string(Hash), + Forced: bool(Forced), } + if PromptMessage.Has { + res.PromptMessage = &PromptMessage.Val + } + c.ConfigHandler.PushResourcePack(res) + + case packetid.ClientboundConfigStoreCookie: + var key pk.Identifier + var payload pk.ByteArray + err := p.Scan(&key, &payload) + if err != nil { + return ConfigErr{"store cookie", err} + } + c.ConfigHandler.SetCookie(key, []byte(payload)) + + case packetid.ClientboundConfigTransfer: + var host pk.String + var port pk.VarInt + err := p.Scan(&host, &port) + if err != nil { + return ConfigErr{"store cookie", err} + } + // TODO: trnasfer to the server case packetid.ClientboundConfigUpdateEnabledFeatures: err := p.Scan(pk.Array((*[]pk.Identifier)(unsafe.Pointer(&c.ConfigData.FeatureFlags)))) @@ -134,6 +197,96 @@ func (c *Client) joinConfiguration(conn *net.Conn) error { case packetid.ClientboundConfigUpdateTags: // TODO: Handle Tags + case packetid.ClientboundConfigSelectKnownPacks: + packs := []DataPack{} + err := p.Scan(pk.Array(&packs)) + if err != nil { + return ConfigErr{"select known packs", err} + } + knwonPacks := c.ConfigHandler.SelectDataPacks(packs) + err = conn.WritePacket(pk.Marshal( + packetid.ServerboundConfigSelectKnownPacks, + pk.Array(knwonPacks), + )) + if err != nil { + return ConfigErr{"select known packs", err} + } + + case packetid.ClientboundConfigCustomReportDetails: + case packetid.ClientboundConfigServerLinks: } } } + +type DataPack struct { + Namespace string + ID string + Version string +} + +func (d DataPack) WriteTo(w io.Writer) (n int64, err error) { + n, err = pk.String(d.Namespace).WriteTo(w) + if err != nil { + return n, err + } + n1, err := pk.String(d.ID).WriteTo(w) + if err != nil { + return n + n1, err + } + n2, err := pk.String(d.Version).WriteTo(w) + return n + n1 + n2, err +} + +func (d *DataPack) ReadFrom(r io.Reader) (n int64, err error) { + n, err = (*pk.String)(&d.Namespace).ReadFrom(r) + if err != nil { + return n, err + } + n1, err := (*pk.String)(&d.ID).ReadFrom(r) + if err != nil { + return n + n1, err + } + n2, err := (*pk.String)(&d.Version).ReadFrom(r) + return n + n1 + n2, err +} + +type DefaultConfigHandler struct { + cookies map[pk.Identifier][]byte + resourcesPack []ResourcePack +} + +func NewDefaultConfigHandler() *DefaultConfigHandler { + return &DefaultConfigHandler{ + cookies: make(map[pk.String][]byte), + resourcesPack: make([]ResourcePack, 0), + } +} + +func (d *DefaultConfigHandler) GetCookie(key pk.Identifier) []byte { + return d.cookies[key] +} + +func (d *DefaultConfigHandler) SetCookie(key pk.Identifier, payload []byte) { + d.cookies[key] = payload +} + +func (d *DefaultConfigHandler) PushResourcePack(res ResourcePack) { + d.resourcesPack = append(d.resourcesPack, res) +} + +func (d *DefaultConfigHandler) PopResourcePack(id pk.UUID) { + for i, v := range d.resourcesPack { + if id == v.ID { + d.resourcesPack = append(d.resourcesPack[:i], d.resourcesPack[i+1:]...) + break + } + } +} + +func (d *DefaultConfigHandler) PopAllResourcePack() { + d.resourcesPack = d.resourcesPack[:0] +} + +func (d *DefaultConfigHandler) SelectDataPacks(packs []DataPack) []DataPack { + return []DataPack{} +} diff --git a/bot/mcbot.go b/bot/mcbot.go index b8b87c8..ce27cf9 100644 --- a/bot/mcbot.go +++ b/bot/mcbot.go @@ -19,7 +19,7 @@ import ( // ProtocolVersion is the protocol version number of minecraft net protocol const ( - ProtocolVersion = 765 + ProtocolVersion = 767 DefaultPort = mcnet.DefaultPort )