293 lines
6.8 KiB
Go
293 lines
6.8 KiB
Go
package bot
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"unsafe"
|
|
|
|
"github.com/Tnze/go-mc/chat"
|
|
"github.com/Tnze/go-mc/data/packetid"
|
|
"github.com/Tnze/go-mc/net"
|
|
pk "github.com/Tnze/go-mc/net/packet"
|
|
"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
|
|
FeatureFlags []string
|
|
}
|
|
|
|
type ConfigErr struct {
|
|
Stage string
|
|
Err error
|
|
}
|
|
|
|
func (l ConfigErr) Error() string {
|
|
return "bot: configuration error: [" + l.Stage + "] " + l.Err.Error()
|
|
}
|
|
|
|
func (l ConfigErr) Unwrap() error {
|
|
return l.Err
|
|
}
|
|
|
|
func (c *Client) joinConfiguration(conn *net.Conn) error {
|
|
receiving := "config custom payload"
|
|
for {
|
|
var p pk.Packet
|
|
if err := conn.ReadPacket(&p); err != nil {
|
|
return ConfigErr{receiving, err}
|
|
}
|
|
|
|
switch packetid.ClientboundPacketID(p.ID) {
|
|
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)
|
|
if err != nil {
|
|
return ConfigErr{"custom payload", err}
|
|
}
|
|
// TODO: Provide configuration custom data handling interface
|
|
|
|
case packetid.ClientboundConfigDisconnect:
|
|
var reason chat.Message
|
|
err := p.Scan(&reason)
|
|
if err != nil {
|
|
return ConfigErr{"disconnect", err}
|
|
}
|
|
return ConfigErr{"disconnect", DisconnectErr(reason)}
|
|
|
|
case packetid.ClientboundConfigFinishConfiguration:
|
|
err := conn.WritePacket(pk.Marshal(
|
|
packetid.ServerboundConfigFinishConfiguration,
|
|
))
|
|
if err != nil {
|
|
return ConfigErr{"finish config", err}
|
|
}
|
|
return nil
|
|
|
|
case packetid.ClientboundConfigKeepAlive:
|
|
var keepAliveID pk.Long
|
|
err := p.Scan(&keepAliveID)
|
|
if err != nil {
|
|
return ConfigErr{"keep alive", err}
|
|
}
|
|
// send it back
|
|
err = conn.WritePacket(pk.Marshal(
|
|
packetid.ServerboundConfigKeepAlive,
|
|
keepAliveID,
|
|
))
|
|
if err != nil {
|
|
return ConfigErr{"keep alive", err}
|
|
}
|
|
|
|
case packetid.ClientboundConfigPing:
|
|
var pingID pk.Int
|
|
err := p.Scan(&pingID)
|
|
if err != nil {
|
|
return ConfigErr{"ping", err}
|
|
}
|
|
// send it back
|
|
err = conn.WritePacket(pk.Marshal(
|
|
packetid.ServerboundConfigPong,
|
|
pingID,
|
|
))
|
|
if err != nil {
|
|
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}
|
|
// }
|
|
|
|
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,
|
|
&PromptMessage,
|
|
)
|
|
if err != nil {
|
|
return ConfigErr{"resource pack", err}
|
|
}
|
|
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))))
|
|
if err != nil {
|
|
return ConfigErr{"update enabled features", err}
|
|
}
|
|
|
|
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{}
|
|
}
|