# network

`import "github.com/matjam/bunyip/network"`

Package network moves typed messages between game instances over TCP and UDP. TCP is ordered and reliable, for turn-based games, lobbies and chat, and is encrypted with ListenTLS and DialTLS. UDP is faster. Send carries real-time state, and SendReliable delivers a message reliably and in order when it must arrive. A Registry names the message types both ends agree on. Messages are plain Go structs encoded as JSON unless they implement encoding.BinaryMarshaler.

Every connection delivers Events through a channel. Drain it once per frame with Poll, so game code needs no locking. A turn-based game can set OnActivity to Context.Wake to wake up when a message arrives. UDP peers get Connected and Disconnected events too, from a hello and keepalive exchange with a timeout.

For real-time state the helpers cover the usual techniques. Interpolator, Predictor, History and Clock handle smoothing, prediction, lag compensation and server time. EncodeDelta with SnapshotBuffer and SnapshotReceiver sends only what changed since the snapshot a client acknowledged, down to single array elements; the delta encoding may change before Bunyip 1.0, so both ends must run the same version. Interest chooses which entities a viewer needs at all. These stateful helpers have no internal synchronization; update them on the game loop goroutine or protect them externally. Snapshots of generic values are shallow copies, so treat referenced data as immutable.

## Constants

<a id="DefaultSendTimeout"></a>

```go
const DefaultSendTimeout = 5 * time.Second
```

DefaultSendTimeout bounds writer acquisition and network I/O for Send and the entire sequential Broadcast. Encoding runs synchronously; a custom marshaler that blocks cannot be interrupted by this timeout.

<a id="MaxDatagram"></a>

```go
const MaxDatagram = 1200
```

MaxDatagram is the largest packet a Peer sends, chosen to fit a typical path MTU without fragmentation. The packet header takes 17 bytes of it, plus 4 for a reliable message.

<a id="MaxMessage"></a>

```go
const MaxMessage = 16 << 20
```

MaxMessage caps a single message's encoded size.

## Variables

<a id="ErrCertificate"></a>

```go
var ErrCertificate = errors.New("network: server certificate does not match the pinned fingerprint")
```

ErrCertificate is returned when a server's certificate does not match the fingerprint a PinnedConfig expects.

<a id="ErrDeltaData"></a>

```go
var ErrDeltaData = errors.New("network: bad delta data")
```

ErrDeltaData is returned for delta bytes that do not fit the type.

<a id="ErrDeltaType"></a>

```go
var ErrDeltaType = errors.New("network: unsupported delta type")
```

ErrDeltaType is returned for a snapshot type a delta cannot describe.

<a id="ErrReset"></a>

```go
var ErrReset = errors.New("network: peer restarted")
```

ErrReset reports a UDP peer that restarted; reliable messages it had not acknowledged are lost.

<a id="ErrTimeout"></a>

```go
var ErrTimeout = errors.New("network: peer timed out")
```

ErrTimeout reports a UDP peer that went silent.

<a id="ErrUnknownMessage"></a>

```go
var ErrUnknownMessage = errors.New("network: message type not registered")
```

ErrUnknownMessage is returned for types outside the registry.

## Functions

<a id="AppendDelta"></a>

### AppendDelta

```go
func AppendDelta(dst []byte, baseline, current any) ([]byte, error)
```

AppendDelta appends the delta EncodeDelta would return to dst and returns the extended slice. To encode every tick without allocating, pass pointers to baseline and current and a buffer kept from the last call, truncated to zero length. Values passed by value are copied first, which allocates.

<a id="DecodeDelta"></a>

### DecodeDelta

```go
func DecodeDelta(baseline any, data []byte, into any) error
```

DecodeDelta applies delta bytes from EncodeDelta to a copy of baseline and stores the result in into, a pointer to the same struct type. baseline must be the same value the encoder used.

<a id="EncodeDelta"></a>

### EncodeDelta

```go
func EncodeDelta(baseline, current any) ([]byte, error)
```

EncodeDelta encodes the exported fields of current that differ from baseline, both structs (or pointers to structs) of the same type, as a change mask followed by only the changed values. An unchanged snapshot encodes to the mask alone, one byte per eight fields. To encode into a buffer the caller reuses, call AppendDelta.

Supported field types are bool, the sized and unsized integers, float32, float64, string, arrays of those, and nested structs, which are encoded as deltas of their own. An array is encoded as a change mask of its own, one bit per element, followed by only the changed elements, so a large array with a few changed entries stays small. Values are compared by their bits, so a float that changes from 0 to -0 is sent and an unchanged NaN is not. Unexported fields are skipped. Anything else (slices, maps, pointers, interfaces) is ErrDeltaType.

The encoding may change between Bunyip versions before 1.0; both ends must run the same version.

<a id="Fingerprint"></a>

### Fingerprint

```go
func Fingerprint(server *tls.Config) string
```

Fingerprint is the SHA-256 of a server config's first certificate as lower-case hex, or "" when it has none. A host shows it to players so they can join with PinnedConfig.

<a id="PinnedConfig"></a>

### PinnedConfig

```go
func PinnedConfig(fingerprint string) *tls.Config
```

PinnedConfig returns a client config for DialTLS that trusts exactly one server certificate, the one whose Fingerprint is given, whatever name or address the server is dialled at. Case and any ':' or ' ' separators in the fingerprint are ignored.

<a id="SelfSignedConfig"></a>

### SelfSignedConfig

```go
func SelfSignedConfig(hosts ...string) (server, client *tls.Config, err error)
```

SelfSignedConfig makes a fresh certificate for a LAN game and returns a server config that uses it and a client config that accepts only it. The certificate lists hosts (names or IP addresses) for the benefit of ordinary TLS clients; "localhost", 127.0.0.1 and ::1 when none are given. The client config pins the certificate by fingerprint instead, so it works whatever address the server is reached at. To let another machine join, show Fingerprint(server) to the host and have the joiner build its config with PinnedConfig.

## Types

<a id="Addr"></a>

### Addr

```go
type Addr = net.UDPAddr
```

Addr identifies a UDP peer.

<a id="Resolve"></a>

#### Resolve

```go
func Resolve(addr string) (*Addr, error)
```

Resolve parses "host:port" into an Addr for Send.

<a id="Client"></a>

<a id="Client.Conn"></a>

### Client

```go
type Client struct {
	*Conn
	// contains filtered or unexported fields
}
```

Client is the dialling side of a TCP connection.

<a id="Dial"></a>

#### Dial

```go
func Dial(addr string, reg *Registry, timeout time.Duration) (*Client, error)
```

Dial connects to a server. The client's first event is Connected. A nonpositive timeout defaults to ten seconds.

<a id="DialTLS"></a>

#### DialTLS

```go
func DialTLS(addr string, reg *Registry, cfg *tls.Config, timeout time.Duration) (*Client, error)
```

DialTLS connects to a ListenTLS server. The handshake completes before it returns, so a certificate the client does not trust is an error here rather than a later Disconnected event.

<a id="Client.Poll"></a>

#### Client.Poll

```go
func (cl *Client) Poll() []Event
```

Poll returns the events queued since the last call without blocking.

<a id="Client.SetOnActivity"></a>

#### Client.SetOnActivity

```go
func (cl *Client) SetOnActivity(fn func())
```

SetOnActivity sets the wake callback; point it at Context.Wake in a turn-based game. Pending events call fn before this returns; later events call it on a network goroutine. Callbacks run outside locks. It is safe to call concurrently, including from fn. A callback already captured by the reader may still run after SetOnActivity returns. A nil fn disables future callbacks. Keep fn short and drain pending events before registering again from a callback to avoid recursion.

<a id="Clock"></a>

### Clock

```go
type Clock struct {
	// contains filtered or unexported fields
}
```

Clock estimates the server's time from ping replies, so a client can timestamp what it sends and interpolate on the server's timeline. Feed it the local time a ping was sent, the server's time in the reply, and the local time the reply arrived.

<a id="Clock.RTT"></a>

#### Clock.RTT

```go
func (c *Clock) RTT() float64
```

RTT is the smoothed round-trip time.

<a id="Clock.Ready"></a>

#### Clock.Ready

```go
func (c *Clock) Ready() bool
```

Ready reports whether the clock has at least one sample.

<a id="Clock.Sample"></a>

#### Clock.Sample

```go
func (c *Clock) Sample(sent, serverTime, received float64)
```

Sample records one ping round trip; the offset is smoothed over the samples, weighting quick replies more since they are more accurate.

<a id="Clock.ServerTime"></a>

#### Clock.ServerTime

```go
func (c *Clock) ServerTime(local float64) float64
```

ServerTime converts a local time to the server's.

<a id="Conn"></a>

<a id="Conn.ID"></a>

<a id="Conn.Data"></a>

### Conn

```go
type Conn struct {
	ID   int // server-assigned, 1 upward; 0 on the client side
	Data any // for the game: player state, name, anything
	// contains filtered or unexported fields
}
```

Conn is one TCP peer. Messages sent on it arrive in order.

<a id="Conn.Addr"></a>

#### Conn.Addr

```go
func (c *Conn) Addr() string
```

Addr returns the peer's address.

<a id="Conn.Close"></a>

#### Conn.Close

```go
func (c *Conn) Close() error
```

Close ends the connection; the other side sees a Disconnected event. Locally queued events remain available to Poll. Pending events, including Disconnected, may be discarded if the local event queue is full.

<a id="Conn.Send"></a>

#### Conn.Send

```go
func (c *Conn) Send(msg any) error
```

Send encodes and writes one message with DefaultSendTimeout. It is safe from any goroutine. Use SendContext to choose a shorter or longer budget.

<a id="Conn.SendContext"></a>

#### Conn.SendContext

```go
func (c *Conn) SendContext(ctx context.Context, msg any) (err error)
```

SendContext encodes and writes one message. Cancellation covers waiting for another sender and transport writes, including TLS; encoding is synchronous and cannot interrupt a custom marshaler. A context without a deadline may wait indefinitely. Concurrent sends are serialized in acquisition order.

Cancellation before transport I/O leaves the connection usable. An interrupted TLS handshake or transport write failure closes it, since a partial frame or TLS write error can prevent further messages from being decoded. A successful write means the transport accepted the frame, not that the peer processed it. Cancellation racing after a complete frame does not turn that successful send into an error.

<a id="Event"></a>

<a id="Event.Kind"></a>

<a id="Event.Conn"></a>

<a id="Event.From"></a>

<a id="Event.Msg"></a>

<a id="Event.Err"></a>

### Event

```go
type Event struct {
	Kind EventKind // connection, disconnection or decoded message
	Conn *Conn     // TCP connection, when applicable
	From *Addr     // UDP address, when applicable
	Msg  any       // *T for a registered T
	Err  error     // disconnection cause; nil for a clean close
}
```

Event is something that happened on a connection.

<a id="EventKind"></a>

### EventKind

```go
type EventKind uint8
```

EventKind says what an Event reports.

<a id="Connected"></a>

<a id="Disconnected"></a>

<a id="Message"></a>

```go
const (
	Connected    EventKind = iota + 1 // a peer joined (server), the dial completed (client) or a UDP address sent its first packet
	Disconnected                      // the peer went away; Err says why, nil for a clean close or goodbye
	Message                           // Msg holds a pointer to a decoded message
)
```

<a id="History"></a>

<a id="History.Keep"></a>

### History

```go
type History[T any] struct {
	// Keep is how many ticks to keep; zero means 64.
	Keep int
	// contains filtered or unexported fields
}
```

History keeps past states by tick for lag compensation: when a client's shot arrives, the server rewinds the targets to where that client saw them (its interpolation time) before testing the hit.

<a id="History.At"></a>

#### History.At

```go
func (h *History[T]) At(time float64) (T, bool)
```

At returns the state recorded at or just before a time, false when nothing that old is kept.

<a id="History.Record"></a>

#### History.Record

```go
func (h *History[T]) Record(time float64, value T)
```

Record stores the state at a tick or time. Record times must be nondecreasing; At uses binary search over insertion order. Values are shallow copies, so do not mutate referenced data in a saved state.

<a id="Interest"></a>

<a id="Interest.Radius"></a>

<a id="Interest.Margin"></a>

### Interest

```go
type Interest[ID comparable] struct {
	// Radius is the distance within which an entity enters the set.
	Radius float32
	// Margin is how much further an entity may go before it leaves;
	// zero means a tenth of Radius.
	Margin float32
	// contains filtered or unexported fields
}
```

Interest decides which entities a viewer should hear about: those within Radius of the viewer, with hysteresis so an entity at the edge does not flicker in and out. An entity enters the set inside Radius and leaves only beyond Radius+Margin. Distances are in two dimensions; ID is whatever names an entity. Each frame, call Begin with the viewer's position, Visit for every candidate, then End. The zero value is ready to use once Radius is set.

<a id="Interest.Begin"></a>

#### Interest.Begin

```go
func (in *Interest[ID]) Begin(viewerX, viewerY float32)
```

Begin starts a frame at the viewer's position.

<a id="Interest.Contains"></a>

#### Interest.Contains

```go
func (in *Interest[ID]) Contains(id ID) bool
```

Contains reports whether an entity was in the set at the last End.

<a id="Interest.End"></a>

#### Interest.End

```go
func (in *Interest[ID]) End() (entered, left []ID)
```

End finishes the frame and reports which entities entered the set and which left it since the last End, in no particular order. An entity not visited this frame leaves. Both slices belong to the Interest and are refilled by the next End, so send from them during the frame and copy anything that has to outlive it.

<a id="Interest.Len"></a>

#### Interest.Len

```go
func (in *Interest[ID]) Len() int
```

Len is how many entities were in the set at the last End.

<a id="Interest.Visit"></a>

#### Interest.Visit

```go
func (in *Interest[ID]) Visit(id ID, x, y float32) bool
```

Visit considers an entity at a position and reports whether it is in the set this frame, so the caller can send it in the same pass.

<a id="Interpolator"></a>

<a id="Interpolator.Delay"></a>

<a id="Interpolator.Keep"></a>

### Interpolator

```go
type Interpolator[T any] struct {
	// Delay is subtracted from the time passed to At, in the snapshots'
	// time units. Two or three send intervals hide most jitter.
	// Nonpositive values mean one tenth of a unit.
	Delay float64
	// Keep is how many snapshots to keep; zero means 32.
	Keep int
	// contains filtered or unexported fields
}
```

Interpolator smooths remote state between the snapshots a server sends: it holds timestamped values and returns the value at a time a little in the past, blended between the two snapshots around it, so other players' ships move at the server's rate without stutter whatever the packet timing. T is whatever the game sends (a position, a whole entity state); the blend function says how to mix two of them.

<a id="Interpolator.Add"></a>

#### Interpolator.Add

```go
func (in *Interpolator[T]) Add(time float64, value T)
```

Add records a snapshot taken at a time. Snapshots may arrive out of order; they are kept sorted.

<a id="Interpolator.At"></a>

#### Interpolator.At

```go
func (in *Interpolator[T]) At(time float64, blend func(a, b T, k float32) T) (T, bool)
```

At returns the value at a time: the blend of the snapshots either side of time - Delay, the newest snapshot when time is past them all (no extrapolation), and false with the zero value before any snapshot. blend mixes two values, k from 0 (a) to 1 (b).

<a id="Interpolator.Latest"></a>

#### Interpolator.Latest

```go
func (in *Interpolator[T]) Latest() (T, bool)
```

Latest returns the newest snapshot, for things that should not be blended.

<a id="Peer"></a>

### Peer

```go
type Peer struct {
	// contains filtered or unexported fields
}
```

Peer sends and receives datagrams. Send fires a message that may be lost or arrive out of order (a stale packet that arrives after a newer one from the same sender is dropped), which is what real-time state updates want. SendReliable retransmits a message until the other side acknowledges it and delivers it in order with the other reliable messages from that sender, for anything that must arrive. Both kinds share one packet header, so every packet acknowledges what has been received so far.

A Peer also tracks who it is talking to. The first packet from an address is a Connected event, silence for the timeout is a Disconnected one (see SetTimeout), a peer that closes says goodbye and one that restarts shows up as Disconnected then Connected again. Peers lists the addresses in between; keepalives go out on idle links so a quiet game stays connected.

<a id="ListenUDP"></a>

#### ListenUDP

```go
func ListenUDP(addr string, reg *Registry) (*Peer, error)
```

ListenUDP binds a peer to addr (":0" for any free port).

<a id="Peer.Addr"></a>

#### Peer.Addr

```go
func (p *Peer) Addr() *Addr
```

Addr is the local address, useful after ":0".

<a id="Peer.Close"></a>

#### Peer.Close

```go
func (p *Peer) Close() error
```

Close says goodbye to every peer and stops receiving.

<a id="Peer.Connect"></a>

#### Peer.Connect

```go
func (p *Peer) Connect(to *Addr) error
```

Connect says hello to an address so both sides see Connected before any message is sent; Send does the same on first use.

<a id="Peer.Disconnect"></a>

#### Peer.Disconnect

```go
func (p *Peer) Disconnect(to *Addr)
```

Disconnect says goodbye to an address and forgets it; the other side sees Disconnected with no error. Unacknowledged reliable messages are dropped. Packets the other side sent before it read the goodbye are ignored, so the address does not come back as Connected; a new session from it, or a Send or Connect from this side, connects again.

<a id="Peer.Peers"></a>

#### Peer.Peers

```go
func (p *Peer) Peers() []*Addr
```

Peers lists the addresses that have sent something and not gone away.

<a id="Peer.Poll"></a>

#### Peer.Poll

```go
func (p *Peer) Poll() []Event
```

Poll returns the events received since the last call without blocking: Connected and Disconnected with From set, and Message. Unreliable messages are dropped when the 4096-entry queue is full; reliable messages and connection events wait for room or local close.

<a id="Peer.Send"></a>

#### Peer.Send

```go
func (p *Peer) Send(to *Addr, msg any) error
```

Send fires one message at to; it may be lost.

<a id="Peer.SendReliable"></a>

#### Peer.SendReliable

```go
func (p *Peer) SendReliable(to *Addr, msg any) error
```

SendReliable sends one message that is resent until acknowledged and delivered in order with the sender's other reliable messages. It returns once the message is queued; Stats reports what is pending. The initial datagram is written before return, so a write error can be returned while the message remains queued for retry. A reset, timeout, Disconnect or Close discards unacknowledged messages.

<a id="Peer.SetLoss"></a>

#### Peer.SetLoss

```go
func (p *Peer) SetLoss(rate float64)
```

SetLoss drops a fraction (0 to 1) of outgoing packets at random, for testing a game against a bad link. Zero, the default, sends everything.

<a id="Peer.SetOnActivity"></a>

#### Peer.SetOnActivity

```go
func (p *Peer) SetOnActivity(fn func())
```

SetOnActivity runs fn on a network goroutine when event activity is observed; point it at Context.Wake in a turn-based game. One callback may cover several events, including unreliable events dropped by a full queue. Keep fn short. A callback captured before replacement may still run afterward; nil disables future captures. Pending events call fn before SetOnActivity returns. Callbacks run outside locks and may close the peer or replace the hook. Drain pending events before registering again from a callback to avoid recursion.

<a id="Peer.SetTimeout"></a>

#### Peer.SetTimeout

```go
func (p *Peer) SetTimeout(d time.Duration)
```

SetTimeout sets how long an address may be silent before it is reported Disconnected; keepalives go out at a quarter of it on idle links. Zero restores the default of five seconds.

<a id="Peer.Stats"></a>

#### Peer.Stats

```go
func (p *Peer) Stats(addr *Addr) (Stats, bool)
```

Stats reports on the link to an address, false when there is none.

<a id="Predictor"></a>

<a id="Predictor.Step"></a>

### Predictor

```go
type Predictor[S, I any] struct {
	Step func(state S, in I) S // non-nil deterministic update, matching the server
	// contains filtered or unexported fields
}
```

Predictor runs the local player's inputs ahead of the server so controls feel instant, then reconciles when the server's state arrives: it rewinds to the server's state, replays the inputs the server has not seen yet, and the result is where the player should be. S is the player's state and I one update's input; Step applies an input to a state for one fixed step and must be the same function the server runs.

<a id="NewPredictor"></a>

#### NewPredictor

```go
func NewPredictor[S, I any](state S, step func(S, I) S) *Predictor[S, I]
```

NewPredictor starts prediction from a state with a step function.

<a id="Predictor.Apply"></a>

#### Predictor.Apply

```go
func (p *Predictor[S, I]) Apply(in I) (seq uint32)
```

Apply runs one input locally and returns its sequence number, which the game sends to the server with the input.

<a id="Predictor.Pending"></a>

#### Predictor.Pending

```go
func (p *Predictor[S, I]) Pending() int
```

Pending is how many inputs the server has not acknowledged, a measure of the round trip in steps.

<a id="Predictor.Reconcile"></a>

#### Predictor.Reconcile

```go
func (p *Predictor[S, I]) Reconcile(ack uint32, server S)
```

Reconcile takes the server's state after it applied the input with sequence ack, drops the inputs up to it, and replays the rest on top, so the local state agrees with the server plus what it has not yet seen.

<a id="Predictor.State"></a>

#### Predictor.State

```go
func (p *Predictor[S, I]) State() S
```

State is the predicted state right now.

<a id="Registry"></a>

### Registry

```go
type Registry struct {
	// contains filtered or unexported fields
}
```

Registry maps message types to the small numbers sent on the wire. Register the same types in the same order on both ends. Finish registration before sharing a registry with connections. Reads may run concurrently, but Register must not run alongside them. Use non-nil values and at most 65536 distinct types. A binary message must provide matching BinaryMarshaler and BinaryUnmarshaler implementations. Connections decode messages from read buffers they reuse, so UnmarshalBinary must copy the bytes it keeps, as the encoding.BinaryUnmarshaler contract requires.

<a id="NewRegistry"></a>

#### NewRegistry

```go
func NewRegistry() *Registry
```

NewRegistry makes an empty registry.

<a id="Registry.Register"></a>

#### Registry.Register

```go
func (r *Registry) Register(msgs ...any) *Registry
```

Register adds a message type, given as a zero value: r.Register(Move{}). Types are numbered in registration order.

<a id="Server"></a>

### Server

```go
type Server struct {
	// contains filtered or unexported fields
}
```

Server accepts TCP connections.

<a id="Listen"></a>

#### Listen

```go
func Listen(addr string, reg *Registry) (*Server, error)
```

Listen starts a server on addr (":7777" for every interface).

<a id="ListenTLS"></a>

#### ListenTLS

```go
func ListenTLS(addr string, reg *Registry, cfg *tls.Config) (*Server, error)
```

ListenTLS starts a server like Listen with every connection encrypted and authenticated by cfg, which needs a certificate: see SelfSignedConfig for one that takes no setup.

<a id="Server.Addr"></a>

#### Server.Addr

```go
func (s *Server) Addr() string
```

Addr is the address the server is listening on, useful after ":0".

<a id="Server.Broadcast"></a>

#### Server.Broadcast

```go
func (s *Server) Broadcast(msg any, except ...*Conn) map[*Conn]error
```

Broadcast sends to every connection except those in except. The message is encoded once, and the sends run sequentially with one shared DefaultSendTimeout budget. The returned map contains failed peers only; nil means every selected peer accepted its frame.

<a id="Server.BroadcastContext"></a>

#### Server.BroadcastContext

```go
func (s *Server) BroadcastContext(ctx context.Context, msg any, except ...*Conn) map[*Conn]error
```

BroadcastContext sends sequentially with one shared context budget. It returns failed peers only, including peers not reached before cancellation. Nil means every selected peer accepted its frame. The message is encoded once, before the first send, and a blocking custom marshaler cannot be interrupted; an encoding error is reported for every selected peer. The connection snapshot and iteration order are unspecified; sends may block the caller.

<a id="Server.Close"></a>

#### Server.Close

```go
func (s *Server) Close() error
```

Close stops accepting and closes every connection. Pending events may be discarded if the local event queue is full, as with Conn.Close. Already queued events remain available to Poll.

<a id="Server.Conns"></a>

#### Server.Conns

```go
func (s *Server) Conns() []*Conn
```

Conns lists the live connections.

<a id="Server.Poll"></a>

#### Server.Poll

```go
func (s *Server) Poll() []Event
```

Poll returns the events queued since the last call. Call it once per frame; it never blocks.

<a id="Server.SetOnActivity"></a>

#### Server.SetOnActivity

```go
func (s *Server) SetOnActivity(fn func())
```

SetOnActivity sets the wake callback for existing and future connections. If events are pending, it calls fn before returning; later calls run on network goroutines. Callbacks run outside locks and may replace the hook or close the server. Keep them short; drain pending events before registering again from a callback to avoid recursion. Nil disables future captures; an already captured callback may still run.

<a id="SnapshotBuffer"></a>

<a id="SnapshotBuffer.Keep"></a>

### SnapshotBuffer

```go
type SnapshotBuffer[K comparable, S any] struct {
	// Keep is how many sent snapshots to remember per client, so a late
	// acknowledgement still finds its baseline; zero means 32.
	Keep int
	// contains filtered or unexported fields
}
```

SnapshotBuffer is the server side of delta-compressed snapshots. Each client is sent its snapshot encoded against the last one it acknowledged, and the buffer remembers what was sent so the acknowledgement can be matched. K identifies a client (a Conn ID, an address string) and S is the snapshot struct, which must satisfy EncodeDelta. The zero value is ready to use; it is not safe for concurrent use.

<a id="SnapshotBuffer.Ack"></a>

#### SnapshotBuffer.Ack

```go
func (b *SnapshotBuffer[K, S]) Ack(client K, seq uint32)
```

Ack records that a client received the snapshot with sequence seq; later snapshots for it are encoded against that one. An unknown seq is ignored.

<a id="SnapshotBuffer.AppendEncode"></a>

#### SnapshotBuffer.AppendEncode

```go
func (b *SnapshotBuffer[K, S]) AppendEncode(dst []byte, client K, seq uint32, snap S) (base uint32, data []byte, err error)
```

AppendEncode encodes as Encode does and appends the delta to dst, returning the extended slice. To encode without allocating once the buffer has grown, pass the slice the last call returned, truncated to zero length, and send it before the next call.

<a id="SnapshotBuffer.Encode"></a>

#### SnapshotBuffer.Encode

```go
func (b *SnapshotBuffer[K, S]) Encode(client K, seq uint32, snap S) (base uint32, data []byte, err error)
```

Encode encodes a client's snapshot against the newest one it has acknowledged, or the zero S when it has acknowledged none the buffer still holds, and remembers it under seq (which must be nonzero and increase). It returns the baseline's sequence, 0 for the zero S, and the delta; send both with seq to the client for SnapshotReceiver. The delta is a fresh slice; to reuse one buffer for every client and tick, call AppendEncode.

<a id="SnapshotBuffer.Forget"></a>

#### SnapshotBuffer.Forget

```go
func (b *SnapshotBuffer[K, S]) Forget(client K)
```

Forget drops a client that has gone.

<a id="SnapshotReceiver"></a>

<a id="SnapshotReceiver.Keep"></a>

### SnapshotReceiver

```go
type SnapshotReceiver[S any] struct {
	// Keep is how many decoded snapshots to remember; zero means 32.
	Keep int
	// contains filtered or unexported fields
}
```

SnapshotReceiver is the client side of SnapshotBuffer: it decodes each snapshot against the earlier one the server named and keeps the result so it can be a baseline in turn. The zero value is ready to use.

<a id="SnapshotReceiver.Decode"></a>

#### SnapshotReceiver.Decode

```go
func (r *SnapshotReceiver[S]) Decode(base, seq uint32, data []byte) (S, error)
```

Decode applies a delta to the snapshot with sequence base (0 for the zero S), remembers the result under seq, and returns it. Acknowledge seq to the server afterwards. A baseline no longer held is ErrDeltaData; the fix is to acknowledge nothing until a snapshot against a baseline the receiver has arrives.

<a id="Stats"></a>

<a id="Stats.RTT"></a>

<a id="Stats.Loss"></a>

<a id="Stats.Pending"></a>

<a id="Stats.Connected"></a>

### Stats

```go
type Stats struct {
	RTT       time.Duration // smoothed round trip; zero before the first acknowledgement
	Loss      float32       // fraction of recent packets that went unacknowledged, 0 to 1
	Pending   int           // reliable messages sent but not yet acknowledged
	Connected bool          // whether a packet has arrived from the address
}
```

Stats describes one UDP link.

## Examples

Example:

```go
package main

import (
	"fmt"
	"time"

	"github.com/matjam/bunyip/network"
)

// Messages are plain structs; both ends register the same types in the
// same order.
type Chat struct{ From, Text string }

func main() {
	reg := network.NewRegistry().Register(Chat{})
	server, err := network.Listen("127.0.0.1:0", reg)
	if err != nil {
		panic(err)
	}
	defer server.Close()
	client, err := network.Dial(server.Addr(), reg, time.Second)
	if err != nil {
		panic(err)
	}
	defer client.Close()
	client.Send(Chat{From: "ann", Text: "hello"})

	// A game drains Poll once per frame; here we wait for the message.
	for deadline := time.Now().Add(5 * time.Second); time.Now().Before(deadline); {
		for _, ev := range server.Poll() {
			if m, ok := ev.Msg.(*Chat); ok {
				fmt.Println(m.From, "says", m.Text)
				return
			}
		}
		time.Sleep(time.Millisecond)
	}
}
```

Output:

```
ann says hello
```
