configuration protocol support draft

This commit is contained in:
Tnze
2023-11-19 02:10:15 +08:00
parent 61916db07a
commit 006f958f43
9 changed files with 184 additions and 106 deletions

View File

@ -41,11 +41,3 @@ To get the first of each primary version: `go get github.com/Tnze/go-mc@v1.19.0`
- Run `go run github.com/Tnze/go-mc/cmd/mcping localhost` to ping and list the localhost mc server. - Run `go run github.com/Tnze/go-mc/cmd/mcping localhost` to ping and list the localhost mc server.
- Run `go run github.com/Tnze/go-mc/cmd/daze` to join the local server at *localhost:25565* as player named Daze on the offline mode. - Run `go run github.com/Tnze/go-mc/cmd/daze` to join the local server at *localhost:25565* as player named Daze on the offline mode.
## Supported Version
As the `go-mc/net` package implements the minecraft network protocol, there is no update between the versions at this
level. So net package actually supports any version. It's just that the ID and content of the package are different
between different versions.
由于`go-mc/net`实现的是MC底层的网络协议而这个协议在MC更新时其实并不会有改动MC更新时其实只是包的ID和内容的定义发生了变化所以net包本身是跨版本的。

67
bot/configuration.go Normal file
View File

@ -0,0 +1,67 @@
package bot
import (
"github.com/Tnze/go-mc/data/packetid"
"github.com/Tnze/go-mc/nbt"
"github.com/Tnze/go-mc/net"
pk "github.com/Tnze/go-mc/net/packet"
)
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 LoginErr{receiving, err}
}
switch packetid.ClientboundPacketID(p.ID) {
case packetid.ClientboundConfigCustomPayload:
var channel pk.Identifier
var data pk.PluginMessageData
err := p.Scan(&channel, &data)
if err != nil {
return LoginErr{"custom payload", err}
}
// TODO: Provide configuration custom data handling interface
case packetid.ClientboundConfigDisconnect:
case packetid.ClientboundConfigFinishConfiguration:
err := conn.WritePacket(pk.Marshal(
packetid.ServerboundConfigFinishConfiguration,
))
if err != nil {
return LoginErr{"finish config", err}
}
return nil
case packetid.ClientboundConfigKeepAlive:
var keepAliveID pk.Long
err := p.Scan(&keepAliveID)
if err != nil {
return LoginErr{"keep alive", err}
}
// send it back
err = conn.WritePacket(pk.Marshal(
packetid.ServerboundConfigKeepAlive,
keepAliveID,
))
if err != nil {
return LoginErr{"keep alive", err}
}
case packetid.ClientboundConfigPing:
case packetid.ClientboundConfigRegistryData:
var registryCodec nbt.RawMessage
err := p.Scan(pk.NBT(&registryCodec))
if err != nil {
return LoginErr{"registry data", err}
}
// TODO: Handle registries
case packetid.ClientboundConfigResourcePack:
case packetid.ClientboundConfigUpdateEnabledFeatures:
case packetid.ClientboundConfigUpdateTags:
}
}
}

View File

@ -15,12 +15,106 @@ import (
"net/http" "net/http"
"strings" "strings"
"github.com/google/uuid"
"github.com/Tnze/go-mc/chat"
"github.com/Tnze/go-mc/data/packetid" "github.com/Tnze/go-mc/data/packetid"
"github.com/Tnze/go-mc/net" "github.com/Tnze/go-mc/net"
"github.com/Tnze/go-mc/net/CFB8" "github.com/Tnze/go-mc/net/CFB8"
pk "github.com/Tnze/go-mc/net/packet" pk "github.com/Tnze/go-mc/net/packet"
) )
func (c *Client) joinLogin(conn *net.Conn) error {
var err error
if c.Auth.UUID != "" {
c.UUID, err = uuid.Parse(c.Auth.UUID)
if err != nil {
return LoginErr{"login start", err}
}
}
err = conn.WritePacket(pk.Marshal(
packetid.ServerboundLoginStart,
pk.String(c.Auth.Name),
pk.UUID(c.UUID),
))
if err != nil {
return LoginErr{"login start", err}
}
receiving := "encrypt start"
for {
// Receive Packet
var p pk.Packet
if err = conn.ReadPacket(&p); err != nil {
return LoginErr{receiving, err}
}
// Handle Packet
switch packetid.ClientboundPacketID(p.ID) {
case packetid.ClientboundLoginDisconnect: // LoginDisconnect
var reason chat.Message
err = p.Scan(&reason)
if err != nil {
return LoginErr{"disconnect", err}
}
return LoginErr{"disconnect", DisconnectErr(reason)}
case packetid.ClientboundLoginEncryptionRequest: // Encryption Request
if err := handleEncryptionRequest(conn, c, p); err != nil {
return LoginErr{"encryption", err}
}
receiving = "set compression"
case packetid.ClientboundLoginSuccess: // Login Success
err := p.Scan(
(*pk.UUID)(&c.UUID),
(*pk.String)(&c.Name),
)
if err != nil {
return LoginErr{"login success", err}
}
err = conn.WritePacket(pk.Marshal(packetid.ServerboundLoginAcknowledged))
if err != nil {
return LoginErr{"login success", err}
}
return nil
case packetid.ClientboundLoginCompression: // Set Compression
var threshold pk.VarInt
if err := p.Scan(&threshold); err != nil {
return LoginErr{"compression", err}
}
conn.SetThreshold(int(threshold))
receiving = "login success"
case packetid.ClientboundLoginPluginRequest: // Login Plugin Request
var (
msgid pk.VarInt
channel pk.Identifier
data pk.PluginMessageData
)
if err := p.Scan(&msgid, &channel, &data); err != nil {
return LoginErr{"Login Plugin", err}
}
var PluginMessageData pk.Option[pk.PluginMessageData, *pk.PluginMessageData]
if handler, ok := c.LoginPlugin[string(channel)]; ok {
PluginMessageData.Has = true
PluginMessageData.Val, err = handler(data)
if err != nil {
return LoginErr{"Login Plugin", err}
}
}
if err := conn.WritePacket(pk.Marshal(
packetid.ServerboundLoginPluginResponse,
msgid, PluginMessageData,
)); err != nil {
return LoginErr{"login Plugin", err}
}
}
}
}
// Auth includes an account // Auth includes an account
type Auth struct { type Auth struct {
Name string Name string
@ -196,7 +290,7 @@ func genEncryptionKeyResponse(shareSecret, publicKey, verifyToken []byte) (erp p
return erp, err return erp, err
} }
return pk.Marshal( return pk.Marshal(
packetid.LoginEncryptionResponse, packetid.ServerboundLoginEncryptionResponse,
pk.ByteArray(cryptPK), pk.ByteArray(cryptPK),
pk.ByteArray(verifyT), pk.ByteArray(verifyT),
), nil ), nil

View File

@ -10,10 +10,7 @@ import (
"net" "net"
"strconv" "strconv"
"github.com/google/uuid"
"github.com/Tnze/go-mc/chat" "github.com/Tnze/go-mc/chat"
"github.com/Tnze/go-mc/data/packetid"
mcnet "github.com/Tnze/go-mc/net" mcnet "github.com/Tnze/go-mc/net"
pk "github.com/Tnze/go-mc/net/packet" pk "github.com/Tnze/go-mc/net/packet"
"github.com/Tnze/go-mc/net/queue" "github.com/Tnze/go-mc/net/queue"
@ -22,7 +19,7 @@ import (
// ProtocolVersion is the protocol version number of minecraft net protocol // ProtocolVersion is the protocol version number of minecraft net protocol
const ( const (
ProtocolVersion = 763 ProtocolVersion = 764
DefaultPort = mcnet.DefaultPort DefaultPort = mcnet.DefaultPort
) )
@ -110,90 +107,18 @@ func (c *Client) join(addr string, options JoinOptions) error {
if err != nil { if err != nil {
return LoginErr{"handshake", err} return LoginErr{"handshake", err}
} }
// Login Start // Login Start
c.UUID, err = uuid.Parse(c.Auth.UUID) if err := c.joinLogin(conn); err != nil {
PlayerUUID := pk.Option[pk.UUID, *pk.UUID]{ return err
Has: err == nil,
Val: pk.UUID(c.UUID),
}
err = conn.WritePacket(pk.Marshal(
packetid.LoginStart,
pk.String(c.Auth.Name),
PlayerUUID,
))
if err != nil {
return LoginErr{"login start", err}
}
receiving := "encrypt start"
for {
// Receive Packet
var p pk.Packet
if err = conn.ReadPacket(&p); err != nil {
return LoginErr{receiving, err}
} }
// Handle Packet // Configuration
switch p.ID { if err := c.joinConfiguration(conn); err != nil {
case packetid.LoginDisconnect: // LoginDisconnect return err
var reason chat.Message
err = p.Scan(&reason)
if err != nil {
return LoginErr{"disconnect", err}
}
return LoginErr{"disconnect", DisconnectErr(reason)}
case packetid.LoginEncryptionRequest: // Encryption Request
if err := handleEncryptionRequest(conn, c, p); err != nil {
return LoginErr{"encryption", err}
}
receiving = "set compression"
case packetid.LoginSuccess: // Login Success
err := p.Scan(
(*pk.UUID)(&c.UUID),
(*pk.String)(&c.Name),
)
if err != nil {
return LoginErr{"login success", err}
} }
c.Conn = warpConn(conn, options.QueueRead, options.QueueWrite) c.Conn = warpConn(conn, options.QueueRead, options.QueueWrite)
return nil return nil
case packetid.LoginCompression: // Set Compression
var threshold pk.VarInt
if err := p.Scan(&threshold); err != nil {
return LoginErr{"compression", err}
}
conn.SetThreshold(int(threshold))
receiving = "login success"
case packetid.LoginPluginRequest: // Login Plugin Request
var (
msgid pk.VarInt
channel pk.Identifier
data pk.PluginMessageData
)
if err := p.Scan(&msgid, &channel, &data); err != nil {
return LoginErr{"Login Plugin", err}
}
var PluginMessageData pk.Option[pk.PluginMessageData, *pk.PluginMessageData]
if handler, ok := c.LoginPlugin[string(channel)]; ok {
PluginMessageData.Has = true
PluginMessageData.Val, err = handler(data)
if err != nil {
return LoginErr{"Login Plugin", err}
}
}
if err := conn.WritePacket(pk.Marshal(
packetid.LoginPluginResponse,
msgid, PluginMessageData,
)); err != nil {
return LoginErr{"login Plugin", err}
}
}
}
} }
type LoginErr struct { type LoginErr struct {

View File

@ -94,7 +94,7 @@ func pingAndList(ctx context.Context, addr string, conn *mcnet.Conn) (data []byt
// LIST // LIST
// 请求服务器状态 // 请求服务器状态
err = conn.WritePacket(pk.Marshal( err = conn.WritePacket(pk.Marshal(
packetid.StatusRequest, packetid.ServerboundStatusRequest,
)) ))
if err != nil { if err != nil {
return nil, 0, fmt.Errorf("bot: send list packect fail: %v", err) return nil, 0, fmt.Errorf("bot: send list packect fail: %v", err)
@ -114,7 +114,7 @@ func pingAndList(ctx context.Context, addr string, conn *mcnet.Conn) (data []byt
// PING // PING
startTime := time.Now() startTime := time.Now()
err = conn.WritePacket(pk.Marshal( err = conn.WritePacket(pk.Marshal(
packetid.StatusPingRequest, packetid.ServerboundStatusPingRequest,
pk.Long(startTime.Unix()), pk.Long(startTime.Unix()),
)) ))
if err != nil { if err != nil {

View File

@ -79,7 +79,7 @@ func Encrypt(conn *net.Conn, name string, serverKey *rsa.PrivateKey) (*Resp, err
func encryptionRequest(conn *net.Conn, publicKey, verifyToken []byte) error { func encryptionRequest(conn *net.Conn, publicKey, verifyToken []byte) error {
return conn.WritePacket(pk.Marshal( return conn.WritePacket(pk.Marshal(
packetid.LoginEncryptionRequest, packetid.ClientboundLoginEncryptionRequest,
pk.String(""), pk.String(""),
pk.ByteArray(publicKey), pk.ByteArray(publicKey),
pk.ByteArray(verifyToken), pk.ByteArray(verifyToken),
@ -92,7 +92,7 @@ func encryptionResponse(conn *net.Conn, serverKey *rsa.PrivateKey, verifyToken [
if err != nil { if err != nil {
return nil, err return nil, err
} }
if p.ID != packetid.LoginEncryptionResponse { if packetid.ServerboundPacketID(p.ID) != packetid.ServerboundLoginEncryptionResponse {
return nil, fmt.Errorf("0x%02X is not Encryption Response", p.ID) return nil, fmt.Errorf("0x%02X is not Encryption Response", p.ID)
} }

View File

@ -96,8 +96,8 @@ func (d *MojangLoginHandler) AcceptLogin(conn *net.Conn, protocol int32) (name s
if err != nil { if err != nil {
return return
} }
if p.ID != packetid.LoginStart { if packetid.ServerboundPacketID(p.ID) != packetid.ServerboundLoginStart {
err = wrongPacketErr{expect: packetid.LoginStart, get: p.ID} err = wrongPacketErr{expect: int32(packetid.ServerboundLoginStart), get: p.ID}
return return
} }
@ -133,7 +133,7 @@ func (d *MojangLoginHandler) AcceptLogin(conn *net.Conn, protocol int32) (name s
// set compression // set compression
if d.Threshold >= 0 { if d.Threshold >= 0 {
err = conn.WritePacket(pk.Marshal( err = conn.WritePacket(pk.Marshal(
packetid.LoginCompression, packetid.ClientboundLoginCompression,
pk.VarInt(d.Threshold), pk.VarInt(d.Threshold),
)) ))
if err != nil { if err != nil {
@ -152,7 +152,7 @@ func (d *MojangLoginHandler) AcceptLogin(conn *net.Conn, protocol int32) (name s
} }
// send login success // send login success
err = conn.WritePacket(pk.Marshal( err = conn.WritePacket(pk.Marshal(
packetid.LoginSuccess, packetid.ClientboundLoginSuccess,
pk.UUID(id), pk.UUID(id),
pk.String(name), pk.String(name),
pk.Array(properties), pk.Array(properties),

View File

@ -59,15 +59,15 @@ func (s *Server) acceptListPing(conn *net.Conn, clientProtocol int32) {
return return
} }
switch p.ID { switch packetid.ClientboundPacketID(p.ID) {
case packetid.StatusResponse: // List case packetid.ClientboundStatusResponse: // List
var resp []byte var resp []byte
resp, err = s.listResp(clientProtocol) resp, err = s.listResp(clientProtocol)
if err != nil { if err != nil {
break break
} }
err = conn.WritePacket(pk.Marshal(0x00, pk.String(resp))) err = conn.WritePacket(pk.Marshal(0x00, pk.String(resp)))
case packetid.StatusPongResponse: // Ping case packetid.ClientboundStatusPongResponse: // Ping
err = conn.WritePacket(p) err = conn.WritePacket(p)
} }
if err != nil { if err != nil {

View File

@ -80,7 +80,7 @@ func (s *Server) AcceptConn(conn *net.Conn) {
var loginErr LoginFailErr var loginErr LoginFailErr
if errors.As(err, &loginErr) { if errors.As(err, &loginErr) {
_ = conn.WritePacket(pk.Marshal( _ = conn.WritePacket(pk.Marshal(
packetid.LoginDisconnect, packetid.ClientboundLoginDisconnect,
loginErr.reason, loginErr.reason,
)) ))
} }