From d094865eee77b8aae9456bb4b34e829b90c1f781 Mon Sep 17 00:00:00 2001 From: Sandertv Date: Tue, 15 Oct 2024 09:32:01 +0200 Subject: [PATCH 1/9] go.mod: Updated dependencies. --- go.mod | 14 +++++++------- go.sum | 30 ++++++++++++++---------------- 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/go.mod b/go.mod index fe3ce02f..97bc9447 100644 --- a/go.mod +++ b/go.mod @@ -9,17 +9,17 @@ require ( github.com/go-jose/go-jose/v3 v3.0.3 github.com/golang/snappy v0.0.4 github.com/google/uuid v1.6.0 - github.com/klauspost/compress v1.17.9 + github.com/klauspost/compress v1.17.11 github.com/muhammadmuzzammil1998/jsonc v1.0.0 github.com/pelletier/go-toml v1.9.5 - github.com/sandertv/go-raknet v1.14.1 - golang.org/x/net v0.26.0 - golang.org/x/oauth2 v0.21.0 - golang.org/x/text v0.16.0 + github.com/sandertv/go-raknet v1.14.2 + golang.org/x/net v0.30.0 + golang.org/x/oauth2 v0.23.0 + golang.org/x/text v0.19.0 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect - golang.org/x/crypto v0.24.0 // indirect - golang.org/x/image v0.17.0 // indirect + golang.org/x/crypto v0.28.0 // indirect + golang.org/x/image v0.21.0 // indirect ) diff --git a/go.sum b/go.sum index 359f81c3..567ed9dd 100644 --- a/go.sum +++ b/go.sum @@ -11,18 +11,16 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= -github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/muhammadmuzzammil1998/jsonc v1.0.0 h1:8o5gBQn4ZA3NBA9DlTujCj2a4w0tqWrPVjDwhzkgTIs= github.com/muhammadmuzzammil1998/jsonc v1.0.0/go.mod h1:saF2fIVw4banK0H4+/EuqfFLpRnoy5S+ECwTOCcRcSU= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/sandertv/go-raknet v1.14.0 h1:2vtO1m1DFLFszeCcV7mVZfVgkDcAbSxcjM2BlrVrEGs= -github.com/sandertv/go-raknet v1.14.0/go.mod h1:/yysjwfCXm2+2OY8mBazLzcxJ3irnylKCyG3FLgUPVU= -github.com/sandertv/go-raknet v1.14.1 h1:V2Gslo+0x4jfj+p0PM48mWxmMbYkxSlgeKy//y3ZrzI= -github.com/sandertv/go-raknet v1.14.1/go.mod h1:/yysjwfCXm2+2OY8mBazLzcxJ3irnylKCyG3FLgUPVU= +github.com/sandertv/go-raknet v1.14.2 h1:UZLyHn5yQU2Dq2GVq/LlxwAUikaq4q4AA1rl/Pf3AXQ= +github.com/sandertv/go-raknet v1.14.2/go.mod h1:/yysjwfCXm2+2OY8mBazLzcxJ3irnylKCyG3FLgUPVU= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -30,11 +28,11 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5t golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/image v0.0.0-20190321063152-3fc05d484e9f/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.17.0 h1:nTRVVdajgB8zCMZVsViyzhnMKPwYeroEERRC64JuLco= -golang.org/x/image v0.17.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E= +golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s= +golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -42,10 +40,10 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= -golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= -golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= +golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -68,8 +66,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= From a8e2f99224df2554eeb536a556911b6aa4cb19aa Mon Sep 17 00:00:00 2001 From: Sandertv Date: Tue, 15 Oct 2024 10:30:43 +0200 Subject: [PATCH 2/9] dial.go/listener.go: Start using log/slog. Also improved errors returned during dialing. Closes #267. --- minecraft/conn.go | 30 ++++++++-------- minecraft/dial.go | 72 +++++++++++++++++++++++---------------- minecraft/err.go | 7 +--- minecraft/internal/log.go | 15 ++++++++ minecraft/listener.go | 38 ++++++++++----------- minecraft/network.go | 12 ++++--- minecraft/raknet.go | 13 ++++--- 7 files changed, 108 insertions(+), 79 deletions(-) create mode 100644 minecraft/internal/log.go diff --git a/minecraft/conn.go b/minecraft/conn.go index 0e2ef8db..a8d35d18 100644 --- a/minecraft/conn.go +++ b/minecraft/conn.go @@ -20,7 +20,7 @@ import ( "github.com/sandertv/gophertunnel/minecraft/resource" "github.com/sandertv/gophertunnel/minecraft/text" "io" - "log" + "log/slog" "net" "strings" "sync" @@ -54,7 +54,7 @@ type Conn struct { close chan struct{} conn net.Conn - log *log.Logger + log *slog.Logger authEnabled bool proto Protocol @@ -148,7 +148,7 @@ type Conn struct { // Minecraft packets to that net.Conn. // newConn accepts a private key which will be used to identify the connection. If a nil key is passed, the // key is generated. -func newConn(netConn net.Conn, key *ecdsa.PrivateKey, log *log.Logger, proto Protocol, flushRate time.Duration, limits bool) *Conn { +func newConn(netConn net.Conn, key *ecdsa.PrivateKey, log *slog.Logger, proto Protocol, flushRate time.Duration, limits bool) *Conn { conn := &Conn{ enc: packet.NewEncoder(netConn), dec: packet.NewDecoder(netConn), @@ -159,7 +159,7 @@ func newConn(netConn net.Conn, key *ecdsa.PrivateKey, log *log.Logger, proto Pro spawn: make(chan struct{}), conn: netConn, privateKey: key, - log: log, + log: log.With("raddr", netConn.RemoteAddr().String()), hdr: &packet.Header{}, proto: proto, readerLimits: limits, @@ -360,7 +360,7 @@ func (conn *Conn) ReadPacket() (pk packet.Packet, err error) { if data, ok := conn.takeDeferredPacket(); ok { pk, err := data.decode(conn) if err != nil { - conn.log.Println(err) + conn.log.Error("read packet: " + err.Error()) return conn.ReadPacket() } if len(pk) == 0 { @@ -380,7 +380,7 @@ func (conn *Conn) ReadPacket() (pk packet.Packet, err error) { case data := <-conn.packets: pk, err := data.decode(conn) if err != nil { - conn.log.Println(err) + conn.log.Error("read packet: " + err.Error()) return conn.ReadPacket() } if len(pk) == 0 { @@ -870,7 +870,7 @@ func (conn *Conn) handleResourcePacksInfo(pk *packet.ResourcePacksInfo) error { for index, pack := range pk.TexturePacks { if _, ok := conn.packQueue.downloadingPacks[pack.UUID]; ok { - conn.log.Printf("handle ResourcePacksInfo: duplicate texture pack (UUID=%v)\n", pack.UUID) + conn.log.Warn("handle ResourcePacksInfo: duplicate texture pack", "UUID", pack.UUID) conn.packQueue.packAmount-- continue } @@ -893,7 +893,7 @@ func (conn *Conn) handleResourcePacksInfo(pk *packet.ResourcePacksInfo) error { } for index, pack := range pk.BehaviourPacks { if _, ok := conn.packQueue.downloadingPacks[pack.UUID]; ok { - conn.log.Printf("handle ResourcePacksInfo: duplicate behaviour pack (UUID=%v)\n", pack.UUID) + conn.log.Warn("handle ResourcePacksInfo: duplicate behaviour pack", "UUID", pack.UUID) conn.packQueue.packAmount-- continue } @@ -937,9 +937,9 @@ func (conn *Conn) handleResourcePackStack(pk *packet.ResourcePackStack) error { for _, pack := range pk.TexturePacks { for i, behaviourPack := range pk.BehaviourPacks { if pack.UUID == behaviourPack.UUID { - // We had a behaviour pack with the same UUID as the texture pack, so we drop the texture + // We had a behaviour pack with the same UUID as the texture pack, so we drop the behaviour // pack and log it. - conn.log.Printf("handle ResourcePackStack: dropping behaviour pack (UUID=%v) due to a texture pack with the same UUID\n", pack.UUID) + conn.log.Warn("handle ResourcePackStack: dropping behaviour pack due to a texture pack with the same UUID", "UUID", pack.UUID) pk.BehaviourPacks = append(pk.BehaviourPacks[:i], pk.BehaviourPacks[i+1:]...) } } @@ -1106,12 +1106,12 @@ func (conn *Conn) handleResourcePackDataInfo(pk *packet.ResourcePackDataInfo) er if !ok { // We either already downloaded the pack or we got sent an invalid UUID, that did not match any pack // sent in the ResourcePacksInfo packet. - return fmt.Errorf("unknown pack (UUID=%v)", id) + return fmt.Errorf("handle ResourcePackDataInfo: unknown pack (UUID=%v)", id) } if pack.size != pk.Size { // Size mismatch: The ResourcePacksInfo packet had a size for the pack that did not match with the // size sent here. - conn.log.Printf("pack (UUID=%v) had a different size in ResourcePacksInfo than in ResourcePackDataInfo\n", id) + conn.log.Warn("handle ResourcePackDataInfo: pack had a different size in ResourcePacksInfo than in ResourcePackDataInfo", "UUID", id) pack.size = pk.Size } @@ -1147,13 +1147,13 @@ func (conn *Conn) handleResourcePackDataInfo(pk *packet.ResourcePackDataInfo) er defer conn.packMu.Unlock() if pack.buf.Len() != int(pack.size) { - conn.log.Printf("incorrect resource pack size (UUID=%v): expected %v, got %v\n", id, pack.size, pack.buf.Len()) + conn.log.Error(fmt.Sprintf("download resource pack: incorrect resource pack size: expected %v, got %v", pack.size, pack.buf.Len()), "UUID", id) return } // First parse the resource pack from the total byte buffer we obtained. newPack, err := resource.Read(pack.buf) if err != nil { - conn.log.Printf("invalid full resource pack data (UUID=%v): %v\n", id, err) + conn.log.Error("download resource pack: invalid full resource pack data: "+err.Error(), "UUID", id) return } conn.packQueue.packAmount-- @@ -1442,5 +1442,5 @@ func (conn *Conn) closeErr(op string) error { if msg := *conn.disconnectMessage.Load(); msg != "" { return conn.wrap(DisconnectError(msg), op) } - return conn.wrap(errClosed, op) + return conn.wrap(net.ErrClosed, op) } diff --git a/minecraft/dial.go b/minecraft/dial.go index 854c7aff..0dbc46ae 100644 --- a/minecraft/dial.go +++ b/minecraft/dial.go @@ -15,14 +15,14 @@ import ( "github.com/go-jose/go-jose/v3/jwt" "github.com/google/uuid" "github.com/sandertv/gophertunnel/minecraft/auth" + "github.com/sandertv/gophertunnel/minecraft/internal" "github.com/sandertv/gophertunnel/minecraft/protocol" "github.com/sandertv/gophertunnel/minecraft/protocol/login" "github.com/sandertv/gophertunnel/minecraft/protocol/packet" "golang.org/x/oauth2" - "log" + "log/slog" "math/rand" "net" - "os" "strconv" "strings" "time" @@ -33,7 +33,7 @@ import ( type Dialer struct { // ErrorLog is a log.Logger that errors that occur during packet handling of servers are written to. By // default, ErrorLog is set to one equal to the global logger. - ErrorLog *log.Logger + ErrorLog *slog.Logger // ClientData is the client data used to login to the server with. It includes fields such as the skin, // locale and UUIDs unique to the client. If empty, a default is sent produced using defaultClientData(). @@ -148,8 +148,18 @@ func (d Dialer) DialTimeout(network, address string, timeout time.Duration) (*Co // typically "raknet". A Conn is returned which may be used to receive packets from and send packets to. // If a connection is not established before the context passed is cancelled, DialContext returns an error. func (d Dialer) DialContext(ctx context.Context, network, address string) (conn *Conn, err error) { - key, _ := ecdsa.GenerateKey(elliptic.P384(), cryptorand.Reader) + if d.ErrorLog == nil { + d.ErrorLog = slog.New(internal.DiscardHandler{}) + } + d.ErrorLog = d.ErrorLog.With("src", "dialer") + if d.Protocol == nil { + d.Protocol = DefaultProtocol + } + if d.FlushRate == 0 { + d.FlushRate = time.Second / 20 + } + key, _ := ecdsa.GenerateKey(elliptic.P384(), cryptorand.Reader) var chainData string if d.TokenSource != nil { chainData, err = authChain(ctx, d.TokenSource, key) @@ -158,19 +168,10 @@ func (d Dialer) DialContext(ctx context.Context, network, address string) (conn } d.IdentityData = readChainIdentityData([]byte(chainData)) } - if d.ErrorLog == nil { - d.ErrorLog = log.New(os.Stderr, "", log.LstdFlags) - } - if d.Protocol == nil { - d.Protocol = DefaultProtocol - } - if d.FlushRate == 0 { - d.FlushRate = time.Second / 20 - } - n, ok := networkByID(network) + n, ok := networkByID(network, d.ErrorLog) if !ok { - return nil, fmt.Errorf("dial: no network under id %v", network) + return nil, &net.OpError{Op: "dial", Net: "minecraft", Err: fmt.Errorf("dial: no network under id %v", network)} } var pong []byte @@ -218,34 +219,35 @@ func (d Dialer) DialContext(ctx context.Context, network, address string) (conn conn.identityData = identityData } - l, c := make(chan struct{}), make(chan struct{}) - go listenConn(conn, d.ErrorLog, l, c) + readyForLogin, connected := make(chan struct{}), make(chan struct{}) + ctx, cancel := context.WithCancelCause(ctx) + go listenConn(conn, readyForLogin, connected, cancel) conn.expect(packet.IDNetworkSettings, packet.IDPlayStatus) if err := conn.WritePacket(&packet.RequestNetworkSettings{ClientProtocol: d.Protocol.ID()}); err != nil { - return nil, err + return nil, conn.wrap(fmt.Errorf("send request network settings: %w", err), "dial") } _ = conn.Flush() select { + case <-ctx.Done(): + return nil, conn.wrap(context.Cause(ctx), "dial") case <-conn.close: return nil, conn.closeErr("dial") - case <-ctx.Done(): - return nil, conn.wrap(ctx.Err(), "dial") - case <-l: + case <-readyForLogin: // We've received our network settings, so we can now send our login request. conn.expect(packet.IDServerToClientHandshake, packet.IDPlayStatus) if err := conn.WritePacket(&packet.Login{ConnectionRequest: request, ClientProtocol: d.Protocol.ID()}); err != nil { - return nil, err + return nil, conn.wrap(fmt.Errorf("send login: %w", err), "dial") } _ = conn.Flush() select { + case <-ctx.Done(): + return nil, conn.wrap(context.Cause(ctx), "dial") case <-conn.close: return nil, conn.closeErr("dial") - case <-ctx.Done(): - return nil, conn.wrap(ctx.Err(), "dial") - case <-c: + case <-connected: // We've connected successfully. We return the connection and no error. return conn, nil } @@ -278,35 +280,45 @@ func readChainIdentityData(chainData []byte) login.IdentityData { // listenConn listens on the connection until it is closed on another goroutine. The channel passed will // receive a value once the connection is logged in. -func listenConn(conn *Conn, logger *log.Logger, l, c chan struct{}) { +func listenConn(conn *Conn, readyForLogin, connected chan struct{}, cancel context.CancelCauseFunc) { defer func() { _ = conn.Close() }() + cancelContext := true for { // We finally arrived at the packet decoding loop. We constantly decode packets that arrive // and push them to the Conn so that they may be processed. packets, err := conn.dec.Decode() if err != nil { if !errors.Is(err, net.ErrClosed) { - logger.Printf("dialer conn: %v\n", err) + if cancelContext { + cancel(err) + } else { + conn.log.Error(err.Error()) + } } return } for _, data := range packets { loggedInBefore, readyToLoginBefore := conn.loggedIn, conn.readyToLogin if err := conn.receive(data); err != nil { - logger.Printf("dialer conn: %v", err) + if cancelContext { + cancel(err) + } else { + conn.log.Error(err.Error()) + } return } if !readyToLoginBefore && conn.readyToLogin { // This is the signal that the connection is ready to login, so we put a value in the channel so that // it may be detected. - l <- struct{}{} + readyForLogin <- struct{}{} } if !loggedInBefore && conn.loggedIn { // This is the signal that the connection was considered logged in, so we put a value in the channel so // that it may be detected. - c <- struct{}{} + cancelContext = false + connected <- struct{}{} } } } diff --git a/minecraft/err.go b/minecraft/err.go index 04eb2ae1..9ab2fd7d 100644 --- a/minecraft/err.go +++ b/minecraft/err.go @@ -5,12 +5,7 @@ import ( "net" ) -var ( - // TODO: Change this to net.ErrClosed in 1.16. - errClosed = errors.New("use of closed network connection") - errBufferTooSmall = errors.New("a message sent was larger than the buffer used to receive the message into") - errListenerClosed = errors.New("use of closed listener") -) +var errBufferTooSmall = errors.New("a message sent was larger than the buffer used to receive the message into") // wrap wraps the error passed into a net.OpError with the op as operation and returns it, or nil if the error // passed is nil. diff --git a/minecraft/internal/log.go b/minecraft/internal/log.go new file mode 100644 index 00000000..ce9b1213 --- /dev/null +++ b/minecraft/internal/log.go @@ -0,0 +1,15 @@ +package internal + +import ( + "context" + "log/slog" +) + +// DiscardHandler implements a slog.Handler that is always disabled. Each of its +// methods return immediately without any code running. +type DiscardHandler struct{} + +func (d DiscardHandler) Enabled(context.Context, slog.Level) bool { return false } +func (d DiscardHandler) Handle(context.Context, slog.Record) error { return nil } +func (d DiscardHandler) WithAttrs([]slog.Attr) slog.Handler { return d } +func (d DiscardHandler) WithGroup(string) slog.Handler { return d } diff --git a/minecraft/listener.go b/minecraft/listener.go index 001860bb..6284e701 100644 --- a/minecraft/listener.go +++ b/minecraft/listener.go @@ -6,12 +6,12 @@ import ( "crypto/rand" "errors" "fmt" + "github.com/sandertv/gophertunnel/minecraft/internal" "github.com/sandertv/gophertunnel/minecraft/protocol" "github.com/sandertv/gophertunnel/minecraft/protocol/packet" "github.com/sandertv/gophertunnel/minecraft/resource" - "log" + "log/slog" "net" - "os" "slices" "sync" "sync/atomic" @@ -22,7 +22,7 @@ import ( type ListenConfig struct { // ErrorLog is a log.Logger that errors that occur during packet handling of clients are written to. By // default, ErrorLog is set to one equal to the global logger. - ErrorLog *log.Logger + ErrorLog *slog.Logger // AuthenticationDisabled specifies if authentication of players that join is disabled. If set to true, no // verification will be done to ensure that the player connecting is authenticated using their XBOX Live @@ -107,19 +107,10 @@ type Listener struct { // If the host in the address parameter is empty or a literal unspecified IP address, Listen listens on all // available unicast and anycast IP addresses of the local system. func (cfg ListenConfig) Listen(network string, address string) (*Listener, error) { - n, ok := networkByID(network) - if !ok { - return nil, fmt.Errorf("listen: no network under id %v", network) - } - - netListener, err := n.Listen(address) - if err != nil { - return nil, err - } - if cfg.ErrorLog == nil { - cfg.ErrorLog = log.New(os.Stderr, "", log.LstdFlags) + cfg.ErrorLog = slog.New(internal.DiscardHandler{}) } + cfg.ErrorLog = cfg.ErrorLog.With("src", "listener") if cfg.StatusProvider == nil { cfg.StatusProvider = NewStatusProvider("Minecraft Server", "Gophertunnel") } @@ -129,6 +120,16 @@ func (cfg ListenConfig) Listen(network string, address string) (*Listener, error if cfg.FlushRate == 0 { cfg.FlushRate = time.Second / 20 } + + n, ok := networkByID(network, cfg.ErrorLog) + if !ok { + return nil, fmt.Errorf("listen: no network under id %v", network) + } + + netListener, err := n.Listen(address) + if err != nil { + return nil, err + } key, _ := ecdsa.GenerateKey(elliptic.P384(), rand.Reader) listener := &Listener{ cfg: cfg, @@ -162,7 +163,7 @@ func Listen(network, address string) (*Listener, error) { func (listener *Listener) Accept() (net.Conn, error) { conn, ok := <-listener.incoming if !ok { - return nil, &net.OpError{Op: "accept", Net: "minecraft", Addr: listener.Addr(), Err: errListenerClosed} + return nil, &net.OpError{Op: "accept", Net: "minecraft", Addr: listener.Addr(), Err: net.ErrClosed} } return conn, nil } @@ -212,8 +213,7 @@ func (listener *Listener) updatePongData() { s := listener.status() listener.listener.PongData([]byte(fmt.Sprintf("MCPE;%v;%v;%v;%v;%v;%v;%v;%v;%v;%v;%v;%v;", s.ServerName, protocol.CurrentProtocol, protocol.CurrentVersion, s.PlayerCount, s.MaxPlayers, - listener.listener.ID(), s.ServerSubName, "Creative", 1, listener.Addr().(*net.UDPAddr).Port, listener.Addr().(*net.UDPAddr).Port, - 0, + listener.listener.ID(), s.ServerSubName, "Creative", 1, listener.Addr().(*net.UDPAddr).Port, listener.Addr().(*net.UDPAddr).Port, 0, ))) } @@ -305,14 +305,14 @@ func (listener *Listener) handleConn(conn *Conn) { packets, err := conn.dec.Decode() if err != nil { if !errors.Is(err, net.ErrClosed) { - conn.log.Printf("listener conn: %v\n", err) + conn.log.Error(err.Error()) } return } for _, data := range packets { loggedInBefore := conn.loggedIn if err := conn.receive(data); err != nil { - conn.log.Printf("listener conn: %v", err) + conn.log.Error(err.Error()) return } if !loggedInBefore && conn.loggedIn { diff --git a/minecraft/network.go b/minecraft/network.go index e0c8f91c..06e8ca13 100644 --- a/minecraft/network.go +++ b/minecraft/network.go @@ -2,6 +2,7 @@ package minecraft import ( "context" + "log/slog" "net" ) @@ -41,15 +42,18 @@ type NetworkListener interface { // networks holds a map of id => Network to be used for looking up the network by an ID. It is registered to when calling // RegisterNetwork. -var networks = map[string]Network{} +var networks = map[string]func(l *slog.Logger) Network{} // RegisterNetwork registers a network so that it can be used for Gophertunnel. -func RegisterNetwork(id string, n Network) { +func RegisterNetwork(id string, n func(l *slog.Logger) Network) { networks[id] = n } // networkByID returns the network with the ID passed. If no network is found, the second return value will be false. -func networkByID(id string) (Network, bool) { +func networkByID(id string, l *slog.Logger) (Network, bool) { n, ok := networks[id] - return n, ok + if ok { + return n(l), true + } + return nil, false } diff --git a/minecraft/raknet.go b/minecraft/raknet.go index 2412846a..81f3fdc9 100644 --- a/minecraft/raknet.go +++ b/minecraft/raknet.go @@ -3,28 +3,31 @@ package minecraft import ( "context" "github.com/sandertv/go-raknet" + "log/slog" "net" ) // RakNet is an implementation of a RakNet v10 Network. -type RakNet struct{} +type RakNet struct { + l *slog.Logger +} // DialContext ... func (r RakNet) DialContext(ctx context.Context, address string) (net.Conn, error) { - return raknet.DialContext(ctx, address) + return raknet.Dialer{ErrorLog: r.l.With("net origin", "raknet")}.DialContext(ctx, address) } // PingContext ... func (r RakNet) PingContext(ctx context.Context, address string) (response []byte, err error) { - return raknet.PingContext(ctx, address) + return raknet.Dialer{ErrorLog: r.l.With("net origin", "raknet")}.PingContext(ctx, address) } // Listen ... func (r RakNet) Listen(address string) (NetworkListener, error) { - return raknet.Listen(address) + return raknet.ListenConfig{ErrorLog: r.l.With("net origin", "raknet")}.Listen(address) } // init registers the RakNet network. func init() { - RegisterNetwork("raknet", RakNet{}) + RegisterNetwork("raknet", func(l *slog.Logger) Network { return RakNet{} }) } From 9cbfc71a0bd051e343fd4e3f5cbe98dcfde9dcd4 Mon Sep 17 00:00:00 2001 From: Sandertv Date: Tue, 15 Oct 2024 10:31:39 +0200 Subject: [PATCH 3/9] minecraft/raknet.go: Pass logger to RakNet Network. --- minecraft/raknet.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/minecraft/raknet.go b/minecraft/raknet.go index 81f3fdc9..a6aabf6a 100644 --- a/minecraft/raknet.go +++ b/minecraft/raknet.go @@ -29,5 +29,5 @@ func (r RakNet) Listen(address string) (NetworkListener, error) { // init registers the RakNet network. func init() { - RegisterNetwork("raknet", func(l *slog.Logger) Network { return RakNet{} }) + RegisterNetwork("raknet", func(l *slog.Logger) Network { return RakNet{l: l} }) } From 1f2f7c7d1746cdb9508e615f2336429f56e56ddf Mon Sep 17 00:00:00 2001 From: Sandertv Date: Tue, 15 Oct 2024 10:49:02 +0200 Subject: [PATCH 4/9] dial.go/listener.go: Updated ErrorLog documentation. --- minecraft/dial.go | 4 ++-- minecraft/listener.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/minecraft/dial.go b/minecraft/dial.go index 0dbc46ae..f9d58702 100644 --- a/minecraft/dial.go +++ b/minecraft/dial.go @@ -31,8 +31,8 @@ import ( // Dialer allows specifying specific settings for connection to a Minecraft server. // The zero value of Dialer is used for the package level Dial function. type Dialer struct { - // ErrorLog is a log.Logger that errors that occur during packet handling of servers are written to. By - // default, ErrorLog is set to one equal to the global logger. + // ErrorLog is a log.Logger that errors that occur during packet handling of + // servers are written to. By default, errors are not logged. ErrorLog *slog.Logger // ClientData is the client data used to login to the server with. It includes fields such as the skin, diff --git a/minecraft/listener.go b/minecraft/listener.go index 6284e701..2a72ca78 100644 --- a/minecraft/listener.go +++ b/minecraft/listener.go @@ -20,8 +20,8 @@ import ( // ListenConfig holds settings that may be edited to change behaviour of a Listener. type ListenConfig struct { - // ErrorLog is a log.Logger that errors that occur during packet handling of clients are written to. By - // default, ErrorLog is set to one equal to the global logger. + // ErrorLog is a log.Logger that errors that occur during packet handling of + // clients are written to. By default, errors are not logged. ErrorLog *slog.Logger // AuthenticationDisabled specifies if authentication of players that join is disabled. If set to true, no From eed76a658af11eee7ae14960b5edc65f655cff25 Mon Sep 17 00:00:00 2001 From: TwistedAsylumMC Date: Wed, 23 Oct 2024 15:20:16 +0100 Subject: [PATCH 5/9] minecraft/protocol: Support 1.21.40 --- minecraft/conn.go | 11 +++--- minecraft/protocol/camera.go | 9 +++++ minecraft/protocol/info.go | 4 +-- minecraft/protocol/login/data.go | 14 ++++++++ minecraft/protocol/packet/id.go | 2 ++ .../protocol/packet/inventory_content.go | 8 +++-- minecraft/protocol/packet/inventory_slot.go | 8 +++-- minecraft/protocol/packet/mob_effect.go | 2 +- minecraft/protocol/packet/movement_effect.go | 36 +++++++++++++++++++ .../protocol/packet/player_auth_input.go | 18 ++++++---- minecraft/protocol/packet/pool.go | 2 ++ .../protocol/packet/resource_packs_info.go | 4 --- .../protocol/packet/set_movement_authority.go | 24 +++++++++++++ .../protocol/packet/set_player_game_type.go | 4 +-- minecraft/protocol/player.go | 1 + minecraft/protocol/resource_pack.go | 4 +++ 16 files changed, 124 insertions(+), 27 deletions(-) create mode 100644 minecraft/protocol/packet/movement_effect.go create mode 100644 minecraft/protocol/packet/set_movement_authority.go diff --git a/minecraft/conn.go b/minecraft/conn.go index 78071a70..ba25a90d 100644 --- a/minecraft/conn.go +++ b/minecraft/conn.go @@ -768,13 +768,12 @@ func (conn *Conn) handleClientToServerHandshake() error { } pk := &packet.ResourcePacksInfo{TexturePackRequired: conn.texturePacksRequired} for _, pack := range conn.resourcePacks { - if pack.DownloadURL() != "" { - pk.PackURLs = append(pk.PackURLs, protocol.PackURL{ - UUIDVersion: fmt.Sprintf("%s_%s", pack.UUID(), pack.Version()), - URL: pack.DownloadURL(), - }) + texturePack := protocol.TexturePackInfo{ + UUID: pack.UUID(), + Version: pack.Version(), + Size: uint64(pack.Len()), + DownloadURL: pack.DownloadURL(), } - texturePack := protocol.TexturePackInfo{UUID: pack.UUID(), Version: pack.Version(), Size: uint64(pack.Len())} if pack.Encrypted() { texturePack.ContentKey = pack.ContentKey() texturePack.ContentIdentity = pack.Manifest().Header.UUID diff --git a/minecraft/protocol/camera.go b/minecraft/protocol/camera.go index 19233144..2dfe1a71 100644 --- a/minecraft/protocol/camera.go +++ b/minecraft/protocol/camera.go @@ -156,6 +156,12 @@ type CameraPreset struct { RotationSpeed Optional[float32] // SnapToTarget determines whether the camera should snap to the target entity or not. SnapToTarget Optional[bool] + // HorizontalRotationLimit is the horizontal rotation limit of the camera. + HorizontalRotationLimit Optional[mgl32.Vec2] + // VerticalRotationLimit is the vertical rotation limit of the camera. + VerticalRotationLimit Optional[mgl32.Vec2] + // ContinueTargeting determines whether the camera should continue targeting the entity or not. + ContinueTargeting Optional[bool] // ViewOffset is only used in a follow_orbit camera and controls an offset based on a pivot point to the // player, causing it to be shifted in a certain direction. ViewOffset Optional[mgl32.Vec2] @@ -169,6 +175,9 @@ type CameraPreset struct { AudioListener Optional[byte] // PlayerEffects is currently unknown. PlayerEffects Optional[bool] + // AlignTargetAndCameraForward determines whether the camera should align the target and the camera forward + // or not. + AlignTargetAndCameraForward Optional[bool] } // Marshal encodes/decodes a CameraPreset. diff --git a/minecraft/protocol/info.go b/minecraft/protocol/info.go index e1f3fc47..265103dc 100644 --- a/minecraft/protocol/info.go +++ b/minecraft/protocol/info.go @@ -2,7 +2,7 @@ package protocol const ( // CurrentProtocol is the current protocol version for the version below. - CurrentProtocol = 729 + CurrentProtocol = 748 // CurrentVersion is the current version of Minecraft as supported by the `packet` package. - CurrentVersion = "1.21.30" + CurrentVersion = "1.21.40" ) diff --git a/minecraft/protocol/login/data.go b/minecraft/protocol/login/data.go index 0a391274..b7570745 100644 --- a/minecraft/protocol/login/data.go +++ b/minecraft/protocol/login/data.go @@ -193,6 +193,20 @@ type ClientData struct { // CompatibleWithClientSideChunkGen is a boolean indicating if the client's hardware is capable of using the client // side chunk generation system. CompatibleWithClientSideChunkGen bool + // MaxViewDistance is the highest render distance that the client's hardware can handle. + MaxViewDistance int + // MemoryTier is the tier of memory that the client's hardware has. This is a number between 0 and 5. The + // full calculation of this tier is currently unknown but the following is a rough estimate from a + // developer at Mojang: + // 0 - Undetermined + // 1 - Super Low, less than ~1.5GB of memory + // 2 - Low, less than ~2GB of memory + // 3 - Mid, less than ~4GB of memory + // 4 - High, less than ~8GB of memory + // 5 - Super High, more than ~8GB of memory + MemoryTier int + // PlatformType is the type of platform the client is running. + PlatformType int } // PersonaPiece represents a piece of a persona skin. All pieces are sent separately. diff --git a/minecraft/protocol/packet/id.go b/minecraft/protocol/packet/id.go index 92f98689..c036fa9f 100644 --- a/minecraft/protocol/packet/id.go +++ b/minecraft/protocol/packet/id.go @@ -218,4 +218,6 @@ const ( IDServerBoundDiagnostics IDCameraAimAssist IDContainerRegistryCleanup + IDMovementEffect + IDSetMovementAuthority ) diff --git a/minecraft/protocol/packet/inventory_content.go b/minecraft/protocol/packet/inventory_content.go index 09bef7a2..1a44bedd 100644 --- a/minecraft/protocol/packet/inventory_content.go +++ b/minecraft/protocol/packet/inventory_content.go @@ -16,8 +16,10 @@ type InventoryContent struct { Content []protocol.ItemInstance // Container is the protocol.FullContainerName that describes the container that the content is for. Container protocol.FullContainerName - // DynamicContainerSize is the size of the container, if the container is dynamic. - DynamicContainerSize uint32 + // StorageItem is the item that is acting as the storage container for the inventory. If the inventory is + // not a dynamic container then this field should be left empty. When set, only the item type is used by + // the client and none of the other stack info. + StorageItem protocol.ItemInstance } // ID ... @@ -29,5 +31,5 @@ func (pk *InventoryContent) Marshal(io protocol.IO) { io.Varuint32(&pk.WindowID) protocol.FuncSlice(io, &pk.Content, io.ItemInstance) protocol.Single(io, &pk.Container) - io.Varuint32(&pk.DynamicContainerSize) + io.ItemInstance(&pk.StorageItem) } diff --git a/minecraft/protocol/packet/inventory_slot.go b/minecraft/protocol/packet/inventory_slot.go index 421c86ae..5529fab6 100644 --- a/minecraft/protocol/packet/inventory_slot.go +++ b/minecraft/protocol/packet/inventory_slot.go @@ -16,8 +16,10 @@ type InventorySlot struct { Slot uint32 // Container is the protocol.FullContainerName that describes the container that the content is for. Container protocol.FullContainerName - // DynamicContainerSize is the size of the container, if the container is dynamic. - DynamicContainerSize uint32 + // StorageItem is the item that is acting as the storage container for the inventory. If the inventory is + // not a dynamic container then this field should be left empty. When set, only the item type is used by + // the client and none of the other stack info. + StorageItem protocol.ItemInstance // NewItem is the item to be put in the slot at Slot. It will overwrite any item that may currently // be present in that slot. NewItem protocol.ItemInstance @@ -32,6 +34,6 @@ func (pk *InventorySlot) Marshal(io protocol.IO) { io.Varuint32(&pk.WindowID) io.Varuint32(&pk.Slot) protocol.Single(io, &pk.Container) - io.Varuint32(&pk.DynamicContainerSize) + io.ItemInstance(&pk.StorageItem) io.ItemInstance(&pk.NewItem) } diff --git a/minecraft/protocol/packet/mob_effect.go b/minecraft/protocol/packet/mob_effect.go index b476c9e4..b52148ed 100644 --- a/minecraft/protocol/packet/mob_effect.go +++ b/minecraft/protocol/packet/mob_effect.go @@ -78,5 +78,5 @@ func (pk *MobEffect) Marshal(io protocol.IO) { io.Varint32(&pk.Amplifier) io.Bool(&pk.Particles) io.Varint32(&pk.Duration) - io.Uint64(&pk.Tick) + io.Varuint64(&pk.Tick) } diff --git a/minecraft/protocol/packet/movement_effect.go b/minecraft/protocol/packet/movement_effect.go new file mode 100644 index 00000000..17cb5583 --- /dev/null +++ b/minecraft/protocol/packet/movement_effect.go @@ -0,0 +1,36 @@ +package packet + +import ( + "github.com/sandertv/gophertunnel/minecraft/protocol" +) + +const ( + MovementEffectTypeGlideBoost = iota +) + +// MovementEffect is sent by the server to the client to update specific movement effects to allow the client +// to predict its movement. For example, fireworks used during gliding will send this packet to tell the +// client the exact duration of the boost. +type MovementEffect struct { + // EntityRuntimeID is the runtime ID of the entity. The runtime ID is unique for each world session, and + // entities are generally identified in packets using this runtime ID. + EntityRuntimeID uint64 + // Type is the type of movement effect being updated. It is one of the constants found above. + Type int32 + // Duration is the duration of the effect, measured in ticks. + Duration int32 + // Tick is the server tick at which the packet was sent. It is used in relation to CorrectPlayerMovePrediction. + Tick uint64 +} + +// ID ... +func (*MovementEffect) ID() uint32 { + return IDMovementEffect +} + +func (pk *MovementEffect) Marshal(io protocol.IO) { + io.Varuint64(&pk.EntityRuntimeID) + io.Varint32(&pk.Type) + io.Varint32(&pk.Duration) + io.Varuint64(&pk.Tick) +} diff --git a/minecraft/protocol/packet/player_auth_input.go b/minecraft/protocol/packet/player_auth_input.go index c4470fe5..79a6cb06 100644 --- a/minecraft/protocol/packet/player_auth_input.go +++ b/minecraft/protocol/packet/player_auth_input.go @@ -59,6 +59,10 @@ const ( InputFlagVerticalCollision InputFlagDownLeft InputFlagDownRight + InputFlagCameraRelativeMovementEnabled + InputFlagRotControlledByMoveDirection + InputFlagStartSpinAttack + InputFlagStopSpinAttack ) const ( @@ -113,9 +117,10 @@ type PlayerAuthInput struct { // InteractionModel is a constant representing the interaction model the player is using. It is one of the // constants that may be found above. InteractionModel uint32 - // GazeDirection is the direction in which the player is gazing, when the PlayMode is PlayModeReality: In - // other words, when the player is playing in virtual reality. - GazeDirection mgl32.Vec3 + // InteractPitch and interactYaw is the rotation the player is looking that they intend to use for + // interactions. This is only different to Pitch and Yaw in cases such as VR or when custom cameras + // being used. + InteractPitch, InteractYaw float32 // Tick is the server tick at which the packet was sent. It is used in relation to // CorrectPlayerMovePrediction. Tick uint64 @@ -135,6 +140,7 @@ type PlayerAuthInput struct { // AnalogueMoveVector is a Vec2 that specifies the direction in which the player moved, as a combination // of X/Z values which are created using an analogue input. AnalogueMoveVector mgl32.Vec2 + CameraOrientation mgl32.Vec3 } // ID ... @@ -152,9 +158,8 @@ func (pk *PlayerAuthInput) Marshal(io protocol.IO) { io.Varuint32(&pk.InputMode) io.Varuint32(&pk.PlayMode) io.Varuint32(&pk.InteractionModel) - if pk.PlayMode == PlayModeReality { - io.Vec3(&pk.GazeDirection) - } + io.Float32(&pk.InteractPitch) + io.Float32(&pk.InteractYaw) io.Varuint64(&pk.Tick) io.Vec3(&pk.Delta) @@ -176,4 +181,5 @@ func (pk *PlayerAuthInput) Marshal(io protocol.IO) { } io.Vec2(&pk.AnalogueMoveVector) + io.Vec3(&pk.CameraOrientation) } diff --git a/minecraft/protocol/packet/pool.go b/minecraft/protocol/packet/pool.go index 78620a2f..4efab6cd 100644 --- a/minecraft/protocol/packet/pool.go +++ b/minecraft/protocol/packet/pool.go @@ -258,6 +258,8 @@ func init() { IDServerBoundDiagnostics: func() Packet { return &ServerBoundDiagnostics{} }, IDCameraAimAssist: func() Packet { return &CameraAimAssist{} }, IDContainerRegistryCleanup: func() Packet { return &ContainerRegistryCleanup{} }, + IDMovementEffect: func() Packet { return &MovementEffect{} }, + IDSetMovementAuthority: func() Packet { return &SetMovementAuthority{} }, } for id, pk := range serverOriginating { RegisterPacketFromServer(id, pk) diff --git a/minecraft/protocol/packet/resource_packs_info.go b/minecraft/protocol/packet/resource_packs_info.go index effc41b2..531f8acf 100644 --- a/minecraft/protocol/packet/resource_packs_info.go +++ b/minecraft/protocol/packet/resource_packs_info.go @@ -21,9 +21,6 @@ type ResourcePacksInfo struct { // The order of these texture packs is not relevant in this packet. It is however important in the // ResourcePackStack packet. TexturePacks []protocol.TexturePackInfo - // PackURLs is a list of URLs that the client can use to download a resource pack instead of downloading - // it the usual way. - PackURLs []protocol.PackURL } // ID ... @@ -36,5 +33,4 @@ func (pk *ResourcePacksInfo) Marshal(io protocol.IO) { io.Bool(&pk.HasAddons) io.Bool(&pk.HasScripts) protocol.SliceUint16Length(io, &pk.TexturePacks) - protocol.Slice(io, &pk.PackURLs) } diff --git a/minecraft/protocol/packet/set_movement_authority.go b/minecraft/protocol/packet/set_movement_authority.go new file mode 100644 index 00000000..98c69fca --- /dev/null +++ b/minecraft/protocol/packet/set_movement_authority.go @@ -0,0 +1,24 @@ +package packet + +import ( + "github.com/sandertv/gophertunnel/minecraft/protocol" +) + +// SetMovementAuthority is sent by the server to the client to change its movement mode. +type SetMovementAuthority struct { + // MovementType specifies the way the server handles player movement. Available options are + // protocol.PlayerMovementModeClient, protocol.PlayerMovementModeServer and + // protocol.PlayerMovementModeServerWithRewind, where the server authoritative types result + // in the client sending PlayerAuthInput packets instead of MovePlayer packets and the rewind mode + // requires sending the tick of movement and several actions. + MovementType byte +} + +// ID ... +func (*SetMovementAuthority) ID() uint32 { + return IDSetMovementAuthority +} + +func (pk *SetMovementAuthority) Marshal(io protocol.IO) { + io.Uint8(&pk.MovementType) +} diff --git a/minecraft/protocol/packet/set_player_game_type.go b/minecraft/protocol/packet/set_player_game_type.go index e8eda3f0..f7dc74e6 100644 --- a/minecraft/protocol/packet/set_player_game_type.go +++ b/minecraft/protocol/packet/set_player_game_type.go @@ -20,7 +20,7 @@ type SetPlayerGameType struct { // GameType is the new game type of the player. It is one of the constants that can be found above. Some // of these game types require additional flags to be set in an AdventureSettings packet for the game mode // to obtain its full functionality. - GameType int32 + GameType int64 } // ID ... @@ -29,5 +29,5 @@ func (*SetPlayerGameType) ID() uint32 { } func (pk *SetPlayerGameType) Marshal(io protocol.IO) { - io.Varint32(&pk.GameType) + io.Varint64(&pk.GameType) } diff --git a/minecraft/protocol/player.go b/minecraft/protocol/player.go index 69633e7b..063977f8 100644 --- a/minecraft/protocol/player.go +++ b/minecraft/protocol/player.go @@ -42,6 +42,7 @@ const ( PlayerActionStartFlying PlayerActionStopFlying PlayerActionClientAckServerData + PlayerActionStartUsingItem ) const ( diff --git a/minecraft/protocol/resource_pack.go b/minecraft/protocol/resource_pack.go index 24581faa..0c6fdc34 100644 --- a/minecraft/protocol/resource_pack.go +++ b/minecraft/protocol/resource_pack.go @@ -28,6 +28,9 @@ type TexturePackInfo struct { AddonPack bool // RTXEnabled specifies if the texture pack uses the raytracing technology introduced in 1.16.200. RTXEnabled bool + // DownloadURL is a URL that the client can use to download the pack instead of the server sending it in + // chunks, which it will continue to do if this field is left empty. + DownloadURL string } // Marshal encodes/decodes a TexturePackInfo. @@ -41,6 +44,7 @@ func (x *TexturePackInfo) Marshal(r IO) { r.Bool(&x.HasScripts) r.Bool(&x.AddonPack) r.Bool(&x.RTXEnabled) + r.String(&x.DownloadURL) } // StackResourcePack represents a resource pack sent on the stack of the client. When sent, the client will From fcec2b1eaf360cdf4f18b4aa714daef97fde0eb3 Mon Sep 17 00:00:00 2001 From: TwistedAsylumMC Date: Wed, 23 Oct 2024 15:34:09 +0100 Subject: [PATCH 6/9] protocol/camera: Marshal new fields --- minecraft/protocol/camera.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/minecraft/protocol/camera.go b/minecraft/protocol/camera.go index 2dfe1a71..98b3d4d2 100644 --- a/minecraft/protocol/camera.go +++ b/minecraft/protocol/camera.go @@ -191,9 +191,13 @@ func (x *CameraPreset) Marshal(r IO) { OptionalFunc(r, &x.RotY, r.Float32) OptionalFunc(r, &x.RotationSpeed, r.Float32) OptionalFunc(r, &x.SnapToTarget, r.Bool) + OptionalFunc(r, &x.HorizontalRotationLimit, r.Vec2) + OptionalFunc(r, &x.VerticalRotationLimit, r.Vec2) + OptionalFunc(r, &x.ContinueTargeting, r.Bool) OptionalFunc(r, &x.ViewOffset, r.Vec2) OptionalFunc(r, &x.EntityOffset, r.Vec3) OptionalFunc(r, &x.Radius, r.Float32) OptionalFunc(r, &x.AudioListener, r.Uint8) OptionalFunc(r, &x.PlayerEffects, r.Bool) + OptionalFunc(r, &x.AlignTargetAndCameraForward, r.Bool) } From 69356c8ede349f337530af3f3d38a130ca8c8414 Mon Sep 17 00:00:00 2001 From: TwistedAsylumMC Date: Wed, 23 Oct 2024 21:59:37 +0100 Subject: [PATCH 7/9] packet/set_player_game_type.go: Original type was correct --- minecraft/protocol/packet/set_player_game_type.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/minecraft/protocol/packet/set_player_game_type.go b/minecraft/protocol/packet/set_player_game_type.go index f7dc74e6..e8eda3f0 100644 --- a/minecraft/protocol/packet/set_player_game_type.go +++ b/minecraft/protocol/packet/set_player_game_type.go @@ -20,7 +20,7 @@ type SetPlayerGameType struct { // GameType is the new game type of the player. It is one of the constants that can be found above. Some // of these game types require additional flags to be set in an AdventureSettings packet for the game mode // to obtain its full functionality. - GameType int64 + GameType int32 } // ID ... @@ -29,5 +29,5 @@ func (*SetPlayerGameType) ID() uint32 { } func (pk *SetPlayerGameType) Marshal(io protocol.IO) { - io.Varint64(&pk.GameType) + io.Varint32(&pk.GameType) } From 8883ef90ab211028f9c553114112b56101047417 Mon Sep 17 00:00:00 2001 From: TwistedAsylumMC Date: Sun, 27 Oct 2024 08:33:26 +0000 Subject: [PATCH 8/9] protocol/camera.go: Add missing EntityOffset field to CameraInstructionSet --- minecraft/protocol/camera.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/minecraft/protocol/camera.go b/minecraft/protocol/camera.go index 98b3d4d2..5c6838a8 100644 --- a/minecraft/protocol/camera.go +++ b/minecraft/protocol/camera.go @@ -75,6 +75,8 @@ type CameraInstructionSet struct { // ViewOffset is an offset based on a pivot point to the player, causing the camera to be shifted in a // certain direction. ViewOffset Optional[mgl32.Vec2] + // EntityOffset is an offset from the entity that the camera should be rendered at. + EntityOffset Optional[mgl32.Vec3] // Default determines whether the camera is a default camera or not. Default Optional[bool] } @@ -87,6 +89,7 @@ func (x *CameraInstructionSet) Marshal(r IO) { OptionalFunc(r, &x.Rotation, r.Vec2) OptionalFunc(r, &x.Facing, r.Vec3) OptionalFunc(r, &x.ViewOffset, r.Vec2) + OptionalFunc(r, &x.EntityOffset, r.Vec3) OptionalFunc(r, &x.Default, r.Bool) } From 00e14fde7f8538f9bee344e1eec94cda792d29cf Mon Sep 17 00:00:00 2001 From: didntpot Date: Mon, 28 Oct 2024 18:49:45 +0000 Subject: [PATCH 9/9] tedac-gophertunnel: Fix bad merge. --- go.mod | 8 ++++---- go.sum | 8 ++------ minecraft/err.go | 5 +---- minecraft/raknet.go | 8 ++++---- 4 files changed, 11 insertions(+), 18 deletions(-) diff --git a/go.mod b/go.mod index f6e933d2..55d44380 100644 --- a/go.mod +++ b/go.mod @@ -19,10 +19,10 @@ require ( ) require ( - github.com/df-mc/atomic v1.10.0 // indirect - golang.org/x/crypto v0.24.0 // indirect - golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 // indirect - golang.org/x/image v0.17.0 // indirect + github.com/df-mc/atomic v1.10.0 // indirect + golang.org/x/crypto v0.28.0 // indirect + golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 // indirect + golang.org/x/image v0.17.0 // indirect ) replace github.com/sandertv/go-raknet => github.com/tedacmc/tedac-raknet v0.0.4 diff --git a/go.sum b/go.sum index 59312e17..286c1c2b 100644 --- a/go.sum +++ b/go.sum @@ -21,8 +21,6 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/sandertv/go-raknet v1.14.2 h1:UZLyHn5yQU2Dq2GVq/LlxwAUikaq4q4AA1rl/Pf3AXQ= -github.com/sandertv/go-raknet v1.14.2/go.mod h1:/yysjwfCXm2+2OY8mBazLzcxJ3irnylKCyG3FLgUPVU= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -36,13 +34,11 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E= golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= golang.org/x/image v0.0.0-20190321063152-3fc05d484e9f/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s= -golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78= +golang.org/x/image v0.17.0 h1:nTRVVdajgB8zCMZVsViyzhnMKPwYeroEERRC64JuLco= +golang.org/x/image v0.17.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= diff --git a/minecraft/err.go b/minecraft/err.go index acb0b977..9ab2fd7d 100644 --- a/minecraft/err.go +++ b/minecraft/err.go @@ -5,10 +5,7 @@ import ( "net" ) -var ( - errBufferTooSmall = errors.New("a message sent was larger than the buffer used to receive the message into") - errListenerClosed = errors.New("use of closed listener") -) +var errBufferTooSmall = errors.New("a message sent was larger than the buffer used to receive the message into") // wrap wraps the error passed into a net.OpError with the op as operation and returns it, or nil if the error // passed is nil. diff --git a/minecraft/raknet.go b/minecraft/raknet.go index 9064abee..4dc5c08f 100644 --- a/minecraft/raknet.go +++ b/minecraft/raknet.go @@ -3,13 +3,13 @@ package minecraft import ( "context" "github.com/sandertv/go-raknet" - "log/slog" "github.com/sandertv/gophertunnel/minecraft/protocol/packet" + "log/slog" "net" ) -// RakNet is an implementation of a RakNet v10 Network. -type RakNet struct{ +// RakNet is an implementation of a RakNet v11 Network. +type RakNet struct { l *slog.Logger } @@ -32,4 +32,4 @@ func (RakNet) Compression(net.Conn) packet.Compression { return packet.FlateComp // init registers the RakNet network. func init() { RegisterNetwork("raknet", func(l *slog.Logger) Network { return RakNet{l: l} }) -} \ No newline at end of file +}