Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.25.0

require (
github.com/coreos/go-oidc/v3 v3.17.0
github.com/df-mc/go-nethernet v1.0.20-0.20260818142457-6fc1eb6f907c
github.com/df-mc/go-nethernet v1.0.23
github.com/df-mc/go-playfab/v2 v2.0.2
github.com/df-mc/go-xsapi/v2 v2.0.3
github.com/df-mc/jsonc v1.0.5
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ github.com/df-mc/go-nethernet v1.0.20-0.20260818142457-6fc1eb6f907c h1:xQD4Be1l6
github.com/df-mc/go-nethernet v1.0.20-0.20260818142457-6fc1eb6f907c/go.mod h1:11MfQo5F7PDKfHg1H0zM5mMkrCv29jRE0lK5B4egW9w=
github.com/df-mc/go-nethernet v1.0.20 h1:H0MO0GkVlAypu9IDQhzg5Pph3mCD7r40+ExiImsXT2M=
github.com/df-mc/go-nethernet v1.0.20/go.mod h1:11MfQo5F7PDKfHg1H0zM5mMkrCv29jRE0lK5B4egW9w=
github.com/df-mc/go-nethernet v1.0.22 h1:3IkdhX504f4THbkl0avhxnvhR1t1CZjwF1jp+n+yWcw=
github.com/df-mc/go-nethernet v1.0.22/go.mod h1:11MfQo5F7PDKfHg1H0zM5mMkrCv29jRE0lK5B4egW9w=
github.com/df-mc/go-nethernet v1.0.23 h1:aFgwkwuhmfC12j0cKIfVV9wEy5ccezPVDAL+2GzMsrc=
github.com/df-mc/go-nethernet v1.0.23/go.mod h1:11MfQo5F7PDKfHg1H0zM5mMkrCv29jRE0lK5B4egW9w=
github.com/df-mc/go-playfab/v2 v2.0.2 h1:JqOsrpe0nt6Ni4V2JrdmOAhbBgTm7LBw1ppGXlmknhs=
github.com/df-mc/go-playfab/v2 v2.0.2/go.mod h1:CS3FV3tJlohvGQiX0IJiaYKOVNfy0p778B84oEVyxlw=
github.com/df-mc/go-xsapi/v2 v2.0.3 h1:fQLw4k15OinFhagWjbSj0BwQXeDUmY8a1J8yquEod3w=
Expand Down
86 changes: 78 additions & 8 deletions minecraft/nethernet.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@ import (
"net"

"github.com/df-mc/go-nethernet"
"github.com/df-mc/go-nethernet/endpoint"
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)

// NetherNet is an implementation of a NetherNet network, a WebRTC-based transport layer protocol.
// A valid Signaling implementation must be provided before use.
type NetherNet struct {
// Signaling is the interface used to exchange connection details with the remote peers.
// When set to nil, either [endpoint.Client] or [endpoint.Handler] will be created and used.
// If [endpoint.Client] is specified, the network cannot listen on an address.
// If [endpoint.Handler] is specified, the network cannot dial or ping a remote server.
Signaling nethernet.Signaling

// Dialer specifies options for establishing a connection with DialContext.
Expand All @@ -32,10 +36,10 @@ type NetherNet struct {
// as a net.Conn.
var _ packet.TransportCapabilities = (*nethernet.Conn)(nil)

// DialContext ...
// DialContext establishes a connection with the remote NetherNet network using the address.
func (n NetherNet) DialContext(ctx context.Context, address string) (net.Conn, error) {
if n.Signaling == nil {
return nil, errors.New("minecraft: NetherNet.DialContext: Signaling is nil")
n.Signaling = endpoint.NewClient()
}
if n.Dialer.Log == nil && n.Log != nil {
n.Dialer.Log = n.Log
Expand All @@ -62,18 +66,84 @@ func (n NetherNet) DialContextIdentity(ctx context.Context, address string, toke
return n.DialContext(ctx, address)
}

// PingContext ...
func (n NetherNet) PingContext(context.Context, string) ([]byte, error) {
// PingContext sends a ping to the specified address if the [NetherNet.Signaling]
// supports sending a ping request to the remote server. Otherwise, it returns an error.
// If [NetherNet.Signaling] is nil, [endpoint.Client] will be created and used instead.
// The address must be an HTTP/HTTPS URL with a port.
func (n NetherNet) PingContext(ctx context.Context, address string) ([]byte, error) {
if n.Signaling == nil {
n.Signaling = endpoint.NewClient()
}
if p, ok := n.Signaling.(pinger); ok {
return p.PingContext(ctx, address)
}
return nil, errors.New("minecraft: NetherNet.PingContext: not supported")
}

// Listen ...
func (n NetherNet) Listen(string) (NetworkListener, error) {
// Ensure that the default Signaling Client implementation used by NetherNet satisfies
// the pinger interface.
var _ pinger = (*endpoint.Client)(nil)

// pinger is implemented by [nethernet.Signaling] that support sending
// ping requests to remote servers.
type pinger interface {
PingContext(ctx context.Context, address string) ([]byte, error)
}

// Listen creates a NetherNet listener using the [NetherNet.Signaling] implementation.
// If [NetherNet.Signaling] is nil, an HTTP server will be started at the specified address.
// Unlike [PingContext] and [DialContext], the address should be formatted as 'ip:port'.
func (n NetherNet) Listen(address string) (_ NetworkListener, err error) {
var closeFunc func() error
if n.Signaling == nil {
return nil, errors.New("minecraft: NetherNet.Listen: Signaling is nil")
server, err := endpoint.Serve(address)
if err != nil {
return nil, fmt.Errorf("serve HTTP: %w", err)
}
n.Signaling = server
closeFunc = server.Close
}
if n.ListenConfig.Log == nil && n.Log != nil {
n.ListenConfig.Log = n.Log
}
return n.ListenConfig.Listen(n.Signaling)
l, err := n.ListenConfig.Listen(n.Signaling)
if err != nil {
if closeFunc != nil {
if err2 := closeFunc(); err2 != nil {
err = errors.Join(err, fmt.Errorf("close HTTP server: %w", err2))
}
}
return nil, err
}
return netherNetListener{NetworkListener: l, close: closeFunc}, nil
Comment thread
lactyy2 marked this conversation as resolved.
}

// netherNetListener implements [NetworkListener] with additional handling
// to close the HTTP server together with the underlying NetworkListener.
type netherNetListener struct {
NetworkListener
// close is a function for closing the HTTP server used to listen.
// It is only set if the caller did not specify [NetherNet.Signaling]
// when calling [NetherNet.Listen].
close func() error
}

// Close closes the underlying NetworkListener along with the HTTP server.
func (n netherNetListener) Close() (err error) {
if err2 := n.NetworkListener.Close(); err2 != nil {
err = errors.Join(err, err2)
}
if n.close != nil {
if err2 := n.close(); err2 != nil {
err = errors.Join(err, fmt.Errorf("close HTTP server: %w", err2))
}
}
return err
}

// init registers the NetherNet network.
func init() {
RegisterNetwork("nethernet", func(l *slog.Logger) Network {
return NetherNet{Log: l}
})
}
Loading