Documentation
¶
Overview ¶
Package session defines the transport-agnostic Session boundary. Lobby and game code read keystrokes and write frames through a Session and never branch on which transport produced it.
Index ¶
- func RunHeartbeat(ctx context.Context, interval, timeout time.Duration, maxMiss int, ...)
- type Caps
- type ColorDepth
- type ControlItem
- type ControlsNotifier
- type InputContextNotifier
- type MemSession
- func (s *MemSession) Capabilities() Caps
- func (s *MemSession) Close() error
- func (s *MemSession) CloseInputWriter() error
- func (s *MemSession) Feed(b []byte) (int, error)
- func (s *MemSession) Identity() sdk.Player
- func (s *MemSession) Output() string
- func (s *MemSession) Read(p []byte) (int, error)
- func (s *MemSession) RemoteIP() string
- func (s *MemSession) Resize(cols, rows int)
- func (s *MemSession) Window() (int, int)
- func (s *MemSession) WindowChanges() <-chan Size
- func (s *MemSession) Write(p []byte) (int, error)
- type PasskeyAuthenticator
- type PasskeyRegistrar
- type Session
- type SignOuter
- type Size
- type StartupIntent
- type StartupIntentKind
- type StartupRouter
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func RunHeartbeat ¶
func RunHeartbeat(ctx context.Context, interval, timeout time.Duration, maxMiss int, ping func(context.Context) error, onDead func())
RunHeartbeat probes a peer to detect a vanished connection that never sent a clean close. On each interval it calls ping with a context bounded by timeout; after maxMiss consecutive failures it invokes onDead (e.g. close the session) and returns. A successful ping resets the miss counter. It returns when ctx is cancelled. The shared logic backs both the WebSocket ping and the SSH keepalive front doors. A nil ping, non-positive interval, or non-positive maxMiss makes it a no-op.
Types ¶
type Caps ¶
type Caps struct {
ColorDepth ColorDepth
UTF8 bool
Mouse bool
}
Caps are per-session capabilities derived from the SSH environment.
type ColorDepth ¶
type ColorDepth uint8
ColorDepth is the session's color capability.
const ( ColorNone ColorDepth = iota Color16 Color256 ColorTrue )
func (ColorDepth) String ¶
func (c ColorDepth) String() string
type ControlItem ¶
ControlItem is one tappable control surfaced on a client-side deck: a short display label and the literal bytes a tap sends on the terminal stream — indistinguishable from the corresponding keypress.
type ControlsNotifier ¶
type ControlsNotifier interface {
NotifyControls(items []ControlItem)
}
ControlsNotifier is an OPTIONAL capability, sibling to InputContextNotifier, for transports whose control surface can render per-game tappable controls (the web touch deck's chips). The SSH transport does not implement it. The lobby calls NotifyControls with the active game's declared controls on game enter and with nil on return to the lobby; the transport forwards the set out-of-band, invisible to hub.Serve and the games.
type InputContextNotifier ¶
type InputContextNotifier interface {
NotifyInputContext(ctx string)
}
InputContextNotifier is an OPTIONAL capability a Session may implement when its transport renders a client-side control surface that adapts to the game's published input context (the web front door's touch deck). The SSH transport does not implement it. The lobby type-asserts for it and calls NotifyInputContext on game enter/leave and whenever the active game's context changes; the transport forwards it out-of-band (a JSON control frame), invisible to hub.Serve and the games. Contexts are the wire strings "nav", "command", and "text".
type MemSession ¶
type MemSession struct {
// contains filtered or unexported fields
}
MemSession is the test-only in-memory Session double: scripted keystroke input and captured frame output, with no socket. It is the forcing function that keeps lobby and game code honest about the Session boundary.
func NewMemSession ¶
func NewMemSession(id sdk.Player, caps Caps, cols, rows int) *MemSession
NewMemSession builds an in-memory session.
func (*MemSession) Capabilities ¶
func (s *MemSession) Capabilities() Caps
func (*MemSession) Close ¶
func (s *MemSession) Close() error
func (*MemSession) CloseInputWriter ¶
func (s *MemSession) CloseInputWriter() error
CloseInputWriter half-closes the session: only the input pipe's write end, so the program's reader sees a clean io.EOF while the transport (output, window channel) stays up — the state a disconnect-detection test needs to stage before the full Close.
func (*MemSession) Feed ¶
func (s *MemSession) Feed(b []byte) (int, error)
Feed writes scripted bytes as keystrokes (blocks until consumed).
func (*MemSession) Identity ¶
func (s *MemSession) Identity() sdk.Player
func (*MemSession) Output ¶
func (s *MemSession) Output() string
Output returns the bytes written by the renderer so far.
func (*MemSession) RemoteIP ¶
func (s *MemSession) RemoteIP() string
func (*MemSession) Resize ¶
func (s *MemSession) Resize(cols, rows int)
Resize updates the window size and emits a window-change event.
func (*MemSession) Window ¶
func (s *MemSession) Window() (int, int)
func (*MemSession) WindowChanges ¶
func (s *MemSession) WindowChanges() <-chan Size
type PasskeyAuthenticator ¶
type PasskeyAuthenticator interface {
// CanLoginPasskey reports whether a login ceremony is available (the transport
// is web AND WebAuthn is configured).
CanLoginPasskey() bool
// LoginPasskey runs the ceremony and, on success, returns the resolved account
// player; the session's own Identity() also reflects it.
LoginPasskey() (sdk.Player, error)
}
PasskeyAuthenticator is an OPTIONAL capability, sibling to PasskeyRegistrar, for transports that can run a discoverable-credential WebAuthn *login* ceremony out-of-band (the web / WebSocket front door). The SSH transport does not implement it. The lobby type-asserts for it to offer the "Log in with a passkey" item and invokes LoginPasskey to resolve a returning account and swap the live session's identity to it in place — no page reload.
type PasskeyRegistrar ¶
type PasskeyRegistrar interface {
// CanRegisterPasskey reports whether a passkey ceremony is actually available
// (the transport is web AND WebAuthn is configured).
CanRegisterPasskey() bool
// RegisterPasskey runs the ceremony and, on success, returns the promoted
// (member) player; the session's own Identity() also reflects it.
RegisterPasskey() (sdk.Player, error)
}
PasskeyRegistrar is an OPTIONAL capability a Session may implement when its transport can run a WebAuthn registration ceremony out-of-band (the web / WebSocket front door). The SSH transport does not implement it. The lobby type-asserts for it to decide whether to offer the "Create Passkey" item, and invokes RegisterPasskey to run the ceremony — keeping all WebAuthn protocol confined to the transport, invisible to hub.Serve and the games.
type Session ¶
type Session interface {
io.ReadWriter
Identity() sdk.Player
Window() (cols, rows int)
WindowChanges() <-chan Size
Capabilities() Caps
// RemoteIP is the client's IP (host portion only), used for per-login audit
// records. "?" when unknown.
RemoteIP() string
Close() error
}
Session is the unified transport boundary. The default window is 80x24 when unknown.
type SignOuter ¶
type SignOuter interface {
// CanSignOut reports whether a sign-out is available (the transport is web with
// a durable cookie configured).
CanSignOut() bool
// SignOut reverts the live session to a brand-new guest account, clears the
// durable cookie (browser-side, via the same one-time-token mechanism passkey
// login uses to SET it), and returns the new guest player; the session's own
// Identity() also reflects it.
SignOut() (sdk.Player, error)
}
SignOuter is an OPTIONAL capability a Session may implement when its transport holds a durable, revocable identity the user can drop in place — the web / WebSocket front door, whose identity is a signed guest cookie. The SSH transport does NOT implement it (its identity is the connection's key, nothing to sign out of). The lobby type-asserts for it to offer the "Sign out" item and invokes SignOut to revert the live session to a fresh guest and clear the cookie.
type StartupIntent ¶
type StartupIntent struct {
Kind StartupIntentKind
Slug string // StartupQuickMatch: the exact author/name game slug.
Code string // StartupJoin: the invite code.
Reason string // StartupInvalid: why the request could not be parsed.
}
StartupIntent is a transport-neutral routing hint a Session may carry when its transport let the client name a destination at connect time (the SSH command string). It is a hint only: the front door does not resolve the slug/code or contact the matchmaker — the lobby validates and routes it. A session with no intent (the common case, and every /ws session) behaves exactly as before.
type StartupIntentKind ¶
type StartupIntentKind uint8
StartupIntentKind is the routing a session requested at connect time.
const ( // StartupQuickMatch routes straight into a quick match for Slug. StartupQuickMatch StartupIntentKind = iota // StartupJoin routes straight into the lobby named by Code. StartupJoin // StartupInvalid carries a human-readable Reason for an unparseable request; // the lobby shows it and falls through to the root menu. StartupInvalid )
type StartupRouter ¶
type StartupRouter interface {
// StartupIntent reports the parsed connect-time intent. ok is false when the
// session carried no command (normal lobby).
StartupIntent() (StartupIntent, bool)
}
StartupRouter is an OPTIONAL capability a Session implements when its transport carries a connect-time command (the SSH front door parses one from sess.Command()). The lobby type-asserts for it once at startup; a transport that never carries a command (the /ws mobile/web front door) does not implement it, so the lobby sees no intent and behaves as it does today.