From 006f958f4369a1a07718c5f83047865a9b4b767e Mon Sep 17 00:00:00 2001 From: Tnze Date: Sun, 19 Nov 2023 02:10:15 +0800 Subject: [PATCH] configuration protocol support draft --- README.md | 8 ---- bot/configuration.go | 67 +++++++++++++++++++++++++++++++ bot/login.go | 96 +++++++++++++++++++++++++++++++++++++++++++- bot/mcbot.go | 95 +++++-------------------------------------- bot/pinglist.go | 4 +- server/auth/auth.go | 4 +- server/login.go | 8 ++-- server/ping.go | 6 +-- server/server.go | 2 +- 9 files changed, 184 insertions(+), 106 deletions(-) create mode 100644 bot/configuration.go diff --git a/README.md b/README.md index 739545d..853518b 100644 --- a/README.md +++ b/README.md @@ -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包本身是跨版本的。 diff --git a/bot/configuration.go b/bot/configuration.go new file mode 100644 index 0000000..3c9bbdb --- /dev/null +++ b/bot/configuration.go @@ -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(®istryCodec)) + if err != nil { + return LoginErr{"registry data", err} + } + // TODO: Handle registries + + case packetid.ClientboundConfigResourcePack: + case packetid.ClientboundConfigUpdateEnabledFeatures: + case packetid.ClientboundConfigUpdateTags: + } + } +} diff --git a/bot/login.go b/bot/login.go index 51f536e..199d909 100644 --- a/bot/login.go +++ b/bot/login.go @@ -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 diff --git a/bot/mcbot.go b/bot/mcbot.go index 873dc9b..063d4b1 100644 --- a/bot/mcbot.go +++ b/bot/mcbot.go @@ -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 { diff --git a/bot/pinglist.go b/bot/pinglist.go index 3d87376..26d2eaf 100644 --- a/bot/pinglist.go +++ b/bot/pinglist.go @@ -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 { diff --git a/server/auth/auth.go b/server/auth/auth.go index 74d89c8..3f95b44 100644 --- a/server/auth/auth.go +++ b/server/auth/auth.go @@ -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) } diff --git a/server/login.go b/server/login.go index d8eb601..c86c1d9 100644 --- a/server/login.go +++ b/server/login.go @@ -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), diff --git a/server/ping.go b/server/ping.go index 6b43733..e3d5d4a 100644 --- a/server/ping.go +++ b/server/ping.go @@ -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 { diff --git a/server/server.go b/server/server.go index 9520bd5..7c484b2 100644 --- a/server/server.go +++ b/server/server.go @@ -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, )) }