Skip to content

Commit

Permalink
Fetch from upstream. (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
didntpot authored Nov 2, 2024
2 parents 60dba9f + 00e14fd commit 7e689ca
Show file tree
Hide file tree
Showing 23 changed files with 259 additions and 118 deletions.
12 changes: 6 additions & 6 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,18 @@ 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/df-mc/atomic v1.10.0 // indirect
golang.org/x/crypto v0.24.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
)
Expand Down
20 changes: 10 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ 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=
Expand All @@ -32,8 +32,8 @@ go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
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/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=
Expand All @@ -46,10 +46,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=
Expand All @@ -72,8 +72,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=
Expand Down
37 changes: 18 additions & 19 deletions minecraft/conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (
"github.com/sandertv/gophertunnel/minecraft/resource"
"github.com/sandertv/gophertunnel/minecraft/text"
"io"
"log"
"log/slog"
"net"
"strings"
"sync"
Expand Down Expand Up @@ -54,7 +54,7 @@ type Conn struct {
close chan struct{}

conn net.Conn
log *log.Logger
log *slog.Logger
authEnabled bool

proto Protocol
Expand Down Expand Up @@ -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),
Expand All @@ -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,
Expand Down Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down Expand Up @@ -802,13 +802,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
Expand Down Expand Up @@ -891,7 +890,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
}
Expand Down Expand Up @@ -935,9 +934,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:]...)
}
}
Expand Down Expand Up @@ -1104,12 +1103,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
}

Expand Down Expand Up @@ -1145,13 +1144,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--
Expand Down
76 changes: 44 additions & 32 deletions minecraft/dial.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -31,9 +31,9 @@ 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 *log.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,
// locale and UUIDs unique to the client. If empty, a default is sent produced using defaultClientData().
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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{}{}
}
}
}
Expand Down
5 changes: 1 addition & 4 deletions minecraft/err.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
15 changes: 15 additions & 0 deletions minecraft/internal/log.go
Original file line number Diff line number Diff line change
@@ -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 }
Loading

0 comments on commit 7e689ca

Please sign in to comment.