add support for EnableFeature and CustomReportDetails

This commit is contained in:
Tnze
2024-06-16 00:42:16 +08:00
parent ee23172e0a
commit 40b32dfdd7
3 changed files with 66 additions and 19 deletions

View File

@ -26,12 +26,17 @@ type Client struct {
Events Events Events Events
// Login plugins // Login plugins
LoginPlugin map[string]func(data []byte) ([]byte, error) LoginPlugin map[string]CustomPayloadHandler
// Configuration handler // Configuration handler
ConfigHandler ConfigHandler
CustomReportDetails map[string]string
} }
// CustomPayloadHandler is a function handling custom payload
type CustomPayloadHandler func(data []byte) ([]byte, error)
func (c *Client) Close() error { func (c *Client) Close() error {
return c.Conn.Close() return c.Conn.Close()
} }
@ -47,7 +52,9 @@ func NewClient() *Client {
return &Client{ return &Client{
Auth: Auth{Name: "Steve"}, Auth: Auth{Name: "Steve"},
Events: Events{handlers: make([][]PacketHandler, packetid.ClientboundPacketIDGuard)}, Events: Events{handlers: make([][]PacketHandler, packetid.ClientboundPacketIDGuard)},
LoginPlugin: make(map[string]CustomPayloadHandler),
ConfigHandler: NewDefaultConfigHandler(), ConfigHandler: NewDefaultConfigHandler(),
CustomReportDetails: make(map[string]string),
} }
} }

View File

@ -1,9 +1,8 @@
package bot package bot
import ( import (
"fmt" "bytes"
"io" "io"
"unsafe"
"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"
@ -16,6 +15,8 @@ type ConfigHandler interface {
GetCookie(key pk.Identifier) []byte GetCookie(key pk.Identifier) []byte
SetCookie(key pk.Identifier, payload []byte) SetCookie(key pk.Identifier, payload []byte)
EnableFeature(features []pk.Identifier)
PushResourcePack(res ResourcePack) PushResourcePack(res ResourcePack)
PopResourcePack(id pk.UUID) PopResourcePack(id pk.UUID)
PopAllResourcePack() PopAllResourcePack()
@ -33,7 +34,6 @@ type ResourcePack struct {
type ConfigData struct { type ConfigData struct {
Registries registry.NetworkCodec Registries registry.NetworkCodec
FeatureFlags []string
} }
type ConfigErr struct { type ConfigErr struct {
@ -50,11 +50,10 @@ func (l ConfigErr) Unwrap() error {
} }
func (c *Client) joinConfiguration(conn *net.Conn) error { func (c *Client) joinConfiguration(conn *net.Conn) error {
receiving := "config custom payload"
for { for {
var p pk.Packet var p pk.Packet
if err := conn.ReadPacket(&p); err != nil { if err := conn.ReadPacket(&p); err != nil {
return ConfigErr{receiving, err} return ConfigErr{"config custom payload", err}
} }
switch packetid.ClientboundPacketID(p.ID) { switch packetid.ClientboundPacketID(p.ID) {
@ -81,14 +80,24 @@ func (c *Client) joinConfiguration(conn *net.Conn) error {
return ConfigErr{"custom payload", err} return ConfigErr{"custom payload", err}
} }
// TODO: Provide configuration custom data handling interface // TODO: Provide configuration custom data handling interface
//
// There are two types of Custom packet.
// One for Login stage, the other for config and play stage.
// The first one called "Custom Query", and the second one called "Custom Payload".
// We can know the different by their name, the "query" is one request to one response, paired.
// But the second one can be sent in any order.
//
// And the custome payload packet seems to be same in config stage and play stage.
// How do we provide API for that?
case packetid.ClientboundConfigDisconnect: case packetid.ClientboundConfigDisconnect:
const ErrStage = "disconnect"
var reason chat.Message var reason chat.Message
err := p.Scan(&reason) err := p.Scan(&reason)
if err != nil { if err != nil {
return ConfigErr{"disconnect", err} return ConfigErr{ErrStage, err}
} }
return ConfigErr{"disconnect", DisconnectErr(reason)} return ConfigErr{ErrStage, DisconnectErr(reason)}
case packetid.ClientboundConfigFinishConfiguration: case packetid.ClientboundConfigFinishConfiguration:
err := conn.WritePacket(pk.Marshal( err := conn.WritePacket(pk.Marshal(
@ -100,10 +109,11 @@ func (c *Client) joinConfiguration(conn *net.Conn) error {
return nil return nil
case packetid.ClientboundConfigKeepAlive: case packetid.ClientboundConfigKeepAlive:
const ErrStage = "keep alive"
var keepAliveID pk.Long var keepAliveID pk.Long
err := p.Scan(&keepAliveID) err := p.Scan(&keepAliveID)
if err != nil { if err != nil {
return ConfigErr{"keep alive", err} return ConfigErr{ErrStage, err}
} }
// send it back // send it back
err = conn.WritePacket(pk.Marshal( err = conn.WritePacket(pk.Marshal(
@ -111,7 +121,7 @@ func (c *Client) joinConfiguration(conn *net.Conn) error {
keepAliveID, keepAliveID,
)) ))
if err != nil { if err != nil {
return ConfigErr{"keep alive", err} return ConfigErr{ErrStage, err}
} }
case packetid.ClientboundConfigPing: case packetid.ClientboundConfigPing:
@ -138,7 +148,7 @@ func (c *Client) joinConfiguration(conn *net.Conn) error {
// return ConfigErr{"registry data", err} // return ConfigErr{"registry data", err}
// } // }
case packetid.ClientboundConfigResourcePackPop: // TODO case packetid.ClientboundConfigResourcePackPop:
var id pk.Option[pk.UUID, *pk.UUID] var id pk.Option[pk.UUID, *pk.UUID]
err := p.Scan(&id) err := p.Scan(&id)
if err != nil { if err != nil {
@ -187,21 +197,25 @@ func (c *Client) joinConfiguration(conn *net.Conn) error {
if err != nil { if err != nil {
return ConfigErr{"store cookie", err} return ConfigErr{"store cookie", err}
} }
// TODO: trnasfer to the server // TODO: trnasfer to the specific server
// How does it work? Just connect the new server, and re-start at handshake?
case packetid.ClientboundConfigUpdateEnabledFeatures: case packetid.ClientboundConfigUpdateEnabledFeatures:
err := p.Scan(pk.Array((*[]pk.Identifier)(unsafe.Pointer(&c.ConfigData.FeatureFlags)))) features := []pk.Identifier{}
err := p.Scan(pk.Array(&features))
if err != nil { if err != nil {
return ConfigErr{"update enabled features", err} return ConfigErr{"update enabled features", err}
} }
c.ConfigHandler.EnableFeature(features)
case packetid.ClientboundConfigUpdateTags: case packetid.ClientboundConfigUpdateTags:
// TODO: Handle Tags // TODO: Handle Tags
case packetid.ClientboundConfigSelectKnownPacks: case packetid.ClientboundConfigSelectKnownPacks:
const ErrStage = "select known packs"
packs := []DataPack{} packs := []DataPack{}
err := p.Scan(pk.Array(&packs)) err := p.Scan(pk.Array(&packs))
if err != nil { if err != nil {
return ConfigErr{"select known packs", err} return ConfigErr{ErrStage, err}
} }
knwonPacks := c.ConfigHandler.SelectDataPacks(packs) knwonPacks := c.ConfigHandler.SelectDataPacks(packs)
err = conn.WritePacket(pk.Marshal( err = conn.WritePacket(pk.Marshal(
@ -209,11 +223,32 @@ func (c *Client) joinConfiguration(conn *net.Conn) error {
pk.Array(knwonPacks), pk.Array(knwonPacks),
)) ))
if err != nil { if err != nil {
return ConfigErr{"select known packs", err} return ConfigErr{ErrStage, err}
} }
case packetid.ClientboundConfigCustomReportDetails: case packetid.ClientboundConfigCustomReportDetails:
const ErrStage = "custom report details"
var length pk.VarInt
var title, description pk.String
r := bytes.NewReader(p.Data)
_, err := length.ReadFrom(r)
if err != nil {
return ConfigErr{ErrStage, err}
}
for i := 0; i < int(length); i++ {
_, err = title.ReadFrom(r)
if err != nil {
return ConfigErr{ErrStage, err}
}
_, err = description.ReadFrom(r)
if err != nil {
return ConfigErr{ErrStage, err}
}
c.CustomReportDetails[string(title)] = string(description)
}
case packetid.ClientboundConfigServerLinks: case packetid.ClientboundConfigServerLinks:
// TODO
} }
} }
} }
@ -270,6 +305,8 @@ func (d *DefaultConfigHandler) SetCookie(key pk.Identifier, payload []byte) {
d.cookies[key] = payload d.cookies[key] = payload
} }
func (d *DefaultConfigHandler) EnableFeature(features []pk.Identifier) {}
func (d *DefaultConfigHandler) PushResourcePack(res ResourcePack) { func (d *DefaultConfigHandler) PushResourcePack(res ResourcePack) {
d.resourcesPack = append(d.resourcesPack, res) d.resourcesPack = append(d.resourcesPack, res)
} }

View File

@ -58,6 +58,9 @@ func (a Ary[LEN]) ReadFrom(r io.Reader) (n int64, err error) {
} else { } else {
n += nn n += nn
} }
if Len < 0 {
return n, errors.New("array length less than zero")
}
array := reflect.ValueOf(a.Ary) array := reflect.ValueOf(a.Ary)
for array.Kind() == reflect.Ptr { for array.Kind() == reflect.Ptr {