nethernet

package module
v0.0.0-...-da40ae9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 26, 2025 License: MIT Imports: 16 Imported by: 0

README

go-nethernet

go-nethernet is a library that implements a basic version of the NetherNet protocol, which is currently used for Minecraft: Bedrock Edition connections over LAN and Xbox Live.

[!WARNING] This library is still in development and is not yet feature-complete.

Documentation

PkgGoDev

Contact

Discord Banner 2

Documentation

Index

Constants

View Source
const (
	// SignalTypeOffer is signaled by a client to request a connection to the remote host.
	// Signals of SignalTypeOffer typically contain a data for a local description of the connection.
	SignalTypeOffer = "CONNECTREQUEST"

	// SignalTypeAnswer is signaled by a server in response to a SignalTypeOffer.
	// Signals with SignalTypeAnswer typically contain a data for a local description of the host.
	SignalTypeAnswer = "CONNECTRESPONSE"

	// SignalTypeCandidate is signaled by both server and client to notify an ICE candidate to the remote
	// connection. It is typically sent after a SignalTypeOffer or SignalTypeAnswer. Signals with SignalTypeCandidate
	// typically contain a data for the ICE candidate formatted with the standard format used by the C++
	// implementation of WebRTC, otherwise it may be ignored.
	SignalTypeCandidate = "CANDIDATEADD"

	// SignalTypeError is signaled by both server and client to report an error that occurred during the connection.
	// Signals with SignalTypeError typically contain a data of the error code, which is one of the constants
	// defined below.
	SignalTypeError = "CONNECTERROR"
)
View Source
const (
	ErrorCodeNone = iota
	ErrorCodeDestinationNotLoggedIn
	ErrorCodeNegotiationTimeout
	ErrorCodeWrongTransportVersion
	ErrorCodeFailedToCreatePeerConnection
	ErrorCodeICE
	ErrorCodeConnectRequest
	ErrorCodeConnectResponse
	ErrorCodeCandidateAdd
	ErrorCodeInactivityTimeout
	ErrorCodeFailedToCreateOffer
	ErrorCodeFailedToCreateAnswer
	ErrorCodeFailedToSetLocalDescription
	ErrorCodeFailedToSetRemoteDescription
	ErrorCodeNegotiationTimeoutWaitingForResponse
	ErrorCodeNegotiationTimeoutWaitingForAccept
	ErrorCodeIncomingConnectionIgnored
	ErrorCodeSignalingParsingFailure
	ErrorCodeSignalingUnknownError
	ErrorCodeSignalingUnicastMessageDeliveryFailed
	ErrorCodeSignalingBroadcastDeliveryFailed
	ErrorCodeSignalingMessageDeliveryFailed
	ErrorCodeSignalingTurnAuthFailed
	ErrorCodeSignalingFallbackToBestEffortDelivery
	ErrorCodeNoSignalingChannel
	ErrorCodeNotLoggedIn
	ErrorCodeSignalingFailedToSend
)

Variables

View Source
var ErrSignalingStopped = errors.New("nethernet: Notifier stopped")

ErrSignalingStopped is notified to Notifier by Signaling through [Notifier.NotifyError] when the function returned by [Signaling.Notify] has been called to stop receiving notifications. Once ErrSignalingStopped is notified, the Notifier will no longer receive notifications, and the underlying Listener or Dialer should be closed or returned.

View Source
var ErrUnsupported = errors.New("nethernet: unsupported")

Functions

This section is empty.

Types

type Addr

type Addr struct {
	// ConnectionID is a unique ID assigned to a connection. It is generated by the client and
	// used in Signals signaled between clients and servers to uniquely reference a specific connection.
	ConnectionID uint64

	// NetworkID is a unique ID for the NetherNet network.
	NetworkID uint64

	// Candidates contains a list of ICE candidates. These candidates are either gathered locally or
	// signaled from a remote connection. ICE candidates are used to determine the UDP/TCP addresses
	// for establishing ICE transport and can be used to determine the network address of the connection.
	Candidates []webrtc.ICECandidate

	// SelectedCandidate is the candidate selected to connect with the ICE transport within a Conn.
	// An ICE candidate may be used to determine the UDP/TCP address of the connection. It may be nil
	// if the Conn has been closed, or if the Conn has encountered an error when obtaining the selected
	// ICE candidate pair.
	SelectedCandidate *webrtc.ICECandidate
}

Addr represents a network address that encapsulates both local and remote connection IDs and implements net.Addr.

The Addr provides details for the unique IDs of Conn and ICE Candidates used for establishing network connectivity.

func (*Addr) Network

func (addr *Addr) Network() string

Network returns the network type for the Addr, which is always 'nethernet'.

func (*Addr) String

func (addr *Addr) String() string

String formats the Addr as a string.

type Conn

type Conn struct {
	// contains filtered or unexported fields
}

Conn is an implementation of net.Conn for a peer connection between a specific remote NetherNet network/connection. Conn is safe for concurrent use by multiple goroutines except Read and ReadPacket.

A Conn represents a WebRTC peer connection using ICE, DTLS, and SCTP transports and encapsulates two data channels labeled 'ReliableDataChannel' and 'UnreliableDataChannel'. Most methods within Conn utilize the 'ReliableDataChannel', as the functionality of the 'ReliableDataChannel' is less defined.

A Conn may be established by dialing a remote network ID using Dialer.DialContext (and other aliases), or by accepting connections from a Listener that listens on a local network.

The Conn do not utilize webrtc.PeerConnection as it does not allow creating a sdp.SessionDescription with custom.

Once established and negotiated through either Dialer or Listener, Conn handles messages sent over the 'ReliableDataChannel' (which may be read using Read or ReadPacket), and ensures closure of its WebRTC transports to confirm that all transports within Conn are closed.

func (*Conn) Close

func (c *Conn) Close() (err error)

Close closes the 'ReliableDataChannel' and 'UnreliableDataChannel', then closes the SCTP, DTLS, and ICE transports of the Conn. An error may be returned using errors.Join, which contains non-nil errors encountered during closure.

func (*Conn) LocalAddr

func (c *Conn) LocalAddr() net.Addr

LocalAddr returns an Addr that includes the local network ID of the Conn with locally-gathered ICE candidates.

func (*Conn) PacketHeader

func (c *Conn) PacketHeader() (byte, bool)

PacketHeader always returns 0 and false as no header is prefixed before packets.

func (*Conn) Read

func (c *Conn) Read(b []byte) (n int, err error)

Read receives a message from the 'ReliableDataChannel'. The bytes of the message data are copied to the given data. An error may be returned if the Conn has been closed by Conn.Close.

func (*Conn) ReadPacket

func (c *Conn) ReadPacket() ([]byte, error)

ReadPacket receives a message from the 'ReliableDataChannel' and returns the bytes. It is implemented for Minecraft read operations to avoid some bugs related to the Read method in their decoder.

func (*Conn) RemoteAddr

func (c *Conn) RemoteAddr() net.Addr

RemoteAddr returns an Addr that includes the remote network ID of the Conn with remotely-signaled ICE candidates. Candidates are atomically added when a Signal of type SignalTypeCandidate has been handled.

func (*Conn) SetDeadline

func (*Conn) SetDeadline(time.Time) error

SetDeadline is a no-op implementation of net.Conn.SetDeadline and returns ErrUnsupported.

func (*Conn) SetReadDeadline

func (*Conn) SetReadDeadline(time.Time) error

SetReadDeadline is a no-op implementation of net.Conn.SetReadDeadline and returns ErrUnsupported.

func (*Conn) SetWriteDeadline

func (*Conn) SetWriteDeadline(time.Time) error

SetWriteDeadline is a no-op implementation of net.Conn.SetWriteDeadline and returns ErrUnsupported.

func (*Conn) Write

func (c *Conn) Write(b []byte) (n int, err error)

Write writes the data into the 'ReliableDataChannel'. If the data exceeds 10000 bytes, it is split into multiple segments. An error may be returned while writing a segment or if the Conn has been closed by Conn.Close.

type Credentials

type Credentials struct {
	ExpirationInSeconds int         `json:"ExpirationInSeconds"`
	ICEServers          []ICEServer `json:"TurnAuthServers"`
}

Credentials holds the configuration for ICE servers used for gathering local ICE candidates.

type Dialer

type Dialer struct {
	// ConnectionID is the unique ID of a Conn being established. If zero, a random value will be automatically
	// set from [rand.Uint64].
	ConnectionID uint64

	// Log is used for logging messages at various log levels. If nil, the default [slog.Logger] will be automatically
	// set from [slog.Default]. Log will be extended when a Conn is being established by [Dialer] with additional attributes
	// such as the connection ID and network ID, and will have a 'src' attribute set to 'listener' to mark that the Conn
	// has been negotiated by Dialer.
	Log *slog.Logger

	// API specifies custom configuration for WebRTC transports and data channels. If nil, a new [webrtc.API] will be
	// set from [webrtc.NewAPI]. The [webrtc.SettingEngine] of the API should not allow detaching data channels, as it requires
	// additional steps on the Conn (which cannot be determined by the Conn).
	API *webrtc.API
}

Dialer encapsulates options for establishing a connection with a NetherNet network through Dialer.DialContext and other aliases. It allows customizing logging, WebRTC API settings, and contexts for a negotiation.

func (Dialer) DialContext

func (d Dialer) DialContext(ctx context.Context, networkID uint64, signaling Signaling) (*Conn, error)

DialContext establishes a Conn with a remote network referenced by the ID. The Signaling implementation may be used to signal an offer and local ICE candidates, and to notify Signals of SignalTypeAnswer and SignalTypeCandidate signaled from the remote connection. The context.Context may be used to cancel the connection as soon as possible. If the context.Context is done, and context.Context.Err returns context.DeadlineExceeded, it signals back a Signal of SignalTypeError with ErrorCodeInactivityTimeout or ErrorCodeNegotiationTimeoutWaitingForAccept based on the progress. A Conn may be returned, that is ready to receive and send packets.

type ICEServer

type ICEServer struct {
	Username string   `json:"Username"`
	Password string   `json:"Password"`
	URLs     []string `json:"Urls"`
}

ICEServer represents a single ICE server configuration, including its authentication details and connection URLs. Each server requires a username and password for authentication.

type ListenConfig

type ListenConfig struct {
	// Log is used for logging messages at various levels. If nil, the default [slog.Logger] will be set from
	// [slog.Default]. Log will be extended when a Conn is being accepted by [Listener.Accept] with additional
	// attributes such as the connection ID and network ID, and will have a 'src' attribute set to 'listener'
	// to mark that the Conn has been negotiated by Listener.
	Log *slog.Logger

	// API specifies custom configuration for WebRTC transports and data channels. If nil, a new [webrtc.API] will
	// be set from [webrtc.NewAPI]. The [webrtc.SettingEngine] of the API should not allow detaching data channels,
	// as it requires additional steps on the Conn (which cannot be determined by the Conn).
	API *webrtc.API

	// ConnContext provides a [context.Context] for starting the ICE, DTLS, and SCTP transports of the Conn. If nil,
	// a default [context.Context] with 5 seconds timeout will be used. The parent [context.Context] may be used to
	// create a [context.Context] to be returned (likely using [context.WithCancel] or [context.WithTimeout]).
	ConnContext func(parent context.Context, conn *Conn) context.Context

	// NegotiationContext provides a [context.Context] for the negotiation. If nil, a default [context.Context]
	// with 5 seconds timeout will be used. The parent [context.Context] may be used to create a [context.Context]
	// to be returned (likely using [context.WithCancel] or [context.WithTimeout]). If the deadline of the context
	// is exceeded, a Signal of SignalTypeError with ErrorCodeNegotiationTimeoutWaitingForAccept will be signaled back.
	NegotiationContext func(parent context.Context) context.Context
}

ListenConfig encapsulates options for creating a new Listener through ListenConfig.Listen. It allows customizing logging, WebRTC API settings, and contexts for negotiations.

func (ListenConfig) Listen

func (conf ListenConfig) Listen(signaling Signaling) (*Listener, error)

Listen listens on the local network ID specified by the Signaling implementation. It returns a Listener that may be used to accept established connections from Listener.Accept. Signaling will be used to notify incoming Signals from remote connections.

type Listener

type Listener struct {
	// contains filtered or unexported fields
}

Listener implements a NetherNet connection listener.

func (*Listener) Accept

func (l *Listener) Accept() (net.Conn, error)

Accept waits for and returns the next Conn to the Listener. An error may be returned, if the Listener has been closed.

func (*Listener) Addr

func (l *Listener) Addr() net.Addr

Addr returns an Addr that represents the local network ID of the Listener.

func (*Listener) Close

func (l *Listener) Close() error

Close closes the Listener, ensuring that any blocking methods will return net.ErrClosed as an error.

func (*Listener) ID

func (l *Listener) ID() int64

ID returns the network ID of Listener.

func (*Listener) PongData

func (l *Listener) PongData(b []byte)

PongData is a stub.

type Notifier

type Notifier interface {
	// NotifySignal notifies the Signal to the Notifier. It is called by Signaling when a Signal
	// has been signaled from the remote network denoted in [Signal.NetworkID].
	NotifySignal(signal *Signal)

	// NotifyError notifies the error to the Notifier. If the error is ErrSignalingStopped, the
	// Dialer will return immediately, and the Listener will close itself.
	NotifyError(err error)
}

Notifier receives notifications from Signaling.

type Signal

type Signal struct {
	// Type indicates the type of Signal. It is one of constants defined above.
	Type string

	// ConnectionID is the unique ID of the connection that has sent the Signal.
	// It is used by both server and client to uniquely reference the connection.
	ConnectionID uint64

	// Data is the actual data of the Signal, represented as a string.
	Data string

	// NetworkID is the internal ID used by Signaling to reference a remote network and not
	// included to the String representation to be signaled to the remote network. If sent by
	// a server, it represents the sender ID. If sent by a client, it represents the recipient ID.
	NetworkID uint64
}

Signal represents a signal sent or received to negotiate a connection in NetherNet network.

func (*Signal) MarshalText

func (s *Signal) MarshalText() ([]byte, error)

MarshalText returns the bytes of a string representation returned from Signal.String.

func (*Signal) String

func (s *Signal) String() string

String returns a string representation of the Signal in the format '[Signal.Type] [Signal.ConnectionID] [Signal.Data]'.

func (*Signal) UnmarshalText

func (s *Signal) UnmarshalText(b []byte) (err error)

UnmarshalText decodes the text into the Signal. An error may be returned, if the text is invalid or does not follow the format '[Signal.Type] [Signal.ConnectionID] [Signal.Data]'.

type Signaling

type Signaling interface {
	// Signal sends a Signal to a remote network referenced by [Signal.NetworkID].
	Signal(signal *Signal) error

	// Notify registers a Notifier to receive notifications for signals and errors. It returns
	// a function to stop receiving notifications on Notifier. Once the stopping function is called,
	// ErrSignalingStopped will be notified to the Notifier, and the underlying negotiator should
	// handle the error by closing or returning.
	Notify(n Notifier) (stop func())

	// Credentials blocks until Credentials are received by Signaling, and returns them. If Signaling
	// does not support returning Credentials, it will return nil. Credentials are typically received
	// from a WebSocket connection. The [context.Context] may be used to cancel the blocking.
	Credentials(ctx context.Context) (*Credentials, error)

	// NetworkID returns the local network ID of Signaling. It is used by Listener to obtain its local
	// network ID.
	NetworkID() uint64
	// PongData sets the server data in the format of a RakNet pong response. It is used by the Listener
	// to respond to a ping request in the correct format.
	PongData(b []byte)
}

Signaling implements an interface for sending and receiving Signals over a network.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL