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/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"
"strings"
"github.com/google/uuid"
"github.com/Tnze/go-mc/chat"
"github.com/Tnze/go-mc/data/packetid"
"github.com/Tnze/go-mc/net"
"github.com/Tnze/go-mc/net/CFB8"
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
type Auth struct {
Name string
@ -196,7 +290,7 @@ func genEncryptionKeyResponse(shareSecret, publicKey, verifyToken []byte) (erp p
return erp, err
}
return pk.Marshal(
packetid.LoginEncryptionResponse,
packetid.ServerboundLoginEncryptionResponse,
pk.ByteArray(cryptPK),
pk.ByteArray(verifyT),
), nil

View File

@ -10,10 +10,7 @@ import (
"net"
"strconv"
"github.com/google/uuid"
"github.com/Tnze/go-mc/chat"
"github.com/Tnze/go-mc/data/packetid"
mcnet "github.com/Tnze/go-mc/net"
pk "github.com/Tnze/go-mc/net/packet"
"github.com/Tnze/go-mc/net/queue"
@ -22,7 +19,7 @@ import (
// ProtocolVersion is the protocol version number of minecraft net protocol
const (
ProtocolVersion = 763
ProtocolVersion = 764
DefaultPort = mcnet.DefaultPort
)
@ -110,90 +107,18 @@ func (c *Client) join(addr string, options JoinOptions) error {
if err != nil {
return LoginErr{"handshake", err}
}
// Login Start
c.UUID, err = uuid.Parse(c.Auth.UUID)
PlayerUUID := pk.Option[pk.UUID, *pk.UUID]{
Has: err == nil,
Val: pk.UUID(c.UUID),
if err := c.joinLogin(conn); err != nil {
return err
}
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
switch p.ID {
case packetid.LoginDisconnect: // LoginDisconnect
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)
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}
}
}
// Configuration
if err := c.joinConfiguration(conn); err != nil {
return err
}
c.Conn = warpConn(conn, options.QueueRead, options.QueueWrite)
return nil
}
type LoginErr struct {

View File

@ -94,7 +94,7 @@ func pingAndList(ctx context.Context, addr string, conn *mcnet.Conn) (data []byt
// LIST
// 请求服务器状态
err = conn.WritePacket(pk.Marshal(
packetid.StatusRequest,
packetid.ServerboundStatusRequest,
))
if err != nil {
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
startTime := time.Now()
err = conn.WritePacket(pk.Marshal(
packetid.StatusPingRequest,
packetid.ServerboundStatusPingRequest,
pk.Long(startTime.Unix()),
))
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 {
return conn.WritePacket(pk.Marshal(
packetid.LoginEncryptionRequest,
packetid.ClientboundLoginEncryptionRequest,
pk.String(""),
pk.ByteArray(publicKey),
pk.ByteArray(verifyToken),
@ -92,7 +92,7 @@ func encryptionResponse(conn *net.Conn, serverKey *rsa.PrivateKey, verifyToken [
if err != nil {
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)
}

View File

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

View File

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

View File

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