@utsp/types
Version:
Type definitions and interfaces for UTSP (Universal Text Stream Protocol)
1,489 lines (1,464 loc) • 44.4 kB
TypeScript
/**
* Input device types enumeration
* Each device type has its own input enum
*/
declare enum InputDeviceType {
Keyboard = 0,
Mouse = 1,
Gamepad = 2,
Touch = 3,
TVRemote = 4,
TextInput = 5
}
/**
* Keyboard input enumeration
* Maps keyboard keys to numeric values for efficient storage and comparison
*
* Based on KeyboardEvent.code standard
*/
declare enum KeyboardInput {
KeyA = 0,
KeyB = 1,
KeyC = 2,
KeyD = 3,
KeyE = 4,
KeyF = 5,
KeyG = 6,
KeyH = 7,
KeyI = 8,
KeyJ = 9,
KeyK = 10,
KeyL = 11,
KeyM = 12,
KeyN = 13,
KeyO = 14,
KeyP = 15,
KeyQ = 16,
KeyR = 17,
KeyS = 18,
KeyT = 19,
KeyU = 20,
KeyV = 21,
KeyW = 22,
KeyX = 23,
KeyY = 24,
KeyZ = 25,
Digit0 = 26,
Digit1 = 27,
Digit2 = 28,
Digit3 = 29,
Digit4 = 30,
Digit5 = 31,
Digit6 = 32,
Digit7 = 33,
Digit8 = 34,
Digit9 = 35,
ArrowUp = 36,
ArrowDown = 37,
ArrowLeft = 38,
ArrowRight = 39,
Space = 40,
Enter = 41,
Escape = 42,
Backspace = 43,
Tab = 44,
ShiftLeft = 45,
ShiftRight = 46,
ControlLeft = 47,
ControlRight = 48,
AltLeft = 49,
AltRight = 50,
MetaLeft = 51,
MetaRight = 52,
CapsLock = 53,
Delete = 54,
Insert = 55,
Home = 56,
End = 57,
PageUp = 58,
PageDown = 59,
F1 = 60,
F2 = 61,
F3 = 62,
F4 = 63,
F5 = 64,
F6 = 65,
F7 = 66,
F8 = 67,
F9 = 68,
F10 = 69,
F11 = 70,
F12 = 71,
Minus = 72,
Equal = 73,
BracketLeft = 74,
BracketRight = 75,
Backslash = 76,
Semicolon = 77,
Quote = 78,
Comma = 79,
Period = 80,
Slash = 81,
Backquote = 82,
Numpad0 = 91,
Numpad1 = 92,
Numpad2 = 93,
Numpad3 = 94,
Numpad4 = 95,
Numpad5 = 96,
Numpad6 = 97,
Numpad7 = 98,
Numpad8 = 99,
Numpad9 = 100,
NumpadAdd = 101,
NumpadSubtract = 102,
NumpadMultiply = 103,
NumpadDivide = 104,
NumpadDecimal = 105,
NumpadEnter = 106,
NumLock = 107,
PrintScreen = 111,
ScrollLock = 112,
Pause = 113,
ContextMenu = 114
}
/**
* Helper to convert KeyboardEvent.code to KeyboardInput enum
*/
declare function keyCodeToKeyboardInput(code: string): KeyboardInput | null;
/**
* Helper to convert KeyboardInput enum to KeyboardEvent.code
*/
declare function keyboardInputToKeyCode(input: KeyboardInput): string;
/**
* Mouse input enumeration
* Covers buttons and axes (position, delta, wheel)
*/
declare enum MouseInput {
LeftButton = 0,
MiddleButton = 1,
RightButton = 2,
Button4 = 3,// Back button
Button5 = 4,// Forward button
PositionX = 100,
PositionY = 101,
DeltaX = 102,
DeltaY = 103,
WheelDeltaY = 104,
WheelDeltaX = 105
}
/**
* Check if the input is a button
*/
declare function isMouseButton(input: MouseInput): boolean;
/**
* Check if the input is an axis
*/
declare function isMouseAxis(input: MouseInput): boolean;
/**
* Gamepad input enumeration
* Based on the Standard Gamepad mapping (W3C specification)
*
* Buttons: 0-99
* Axes: 100-199
*/
declare enum GamepadInput {
ButtonA = 0,// South (X on PlayStation, A on Xbox)
ButtonB = 1,// East (Circle on PlayStation, B on Xbox)
ButtonX = 2,// West (Square on PlayStation, X on Xbox)
ButtonY = 3,// North (Triangle on PlayStation, Y on Xbox)
LeftShoulder = 4,// L1 / LB
RightShoulder = 5,// R1 / RB
LeftTrigger = 6,// L2 / LT
RightTrigger = 7,// R2 / RT
Select = 8,// Select / Back / Share
Start = 9,// Start / Menu / Options
LeftStick = 10,// L3
RightStick = 11,// R3
DPadUp = 12,
DPadDown = 13,
DPadLeft = 14,
DPadRight = 15,
Home = 16,// PS button / Xbox button / Home
Touchpad = 17,// PlayStation touchpad button
LeftStickX = 100,
LeftStickY = 101,
RightStickX = 102,
RightStickY = 103,
LeftTriggerAxis = 104,// Alternative analog representation
RightTriggerAxis = 105
}
/**
* Check if the input is a button
*/
declare function isGamepadButton(input: GamepadInput): boolean;
/**
* Check if the input is an axis
*/
declare function isGamepadAxis(input: GamepadInput): boolean;
/**
* Map from standard gamepad button index to GamepadInput enum
*/
declare function gamepadButtonIndexToInput(index: number): GamepadInput | null;
/**
* Map from standard gamepad axis index to GamepadInput enum
*/
declare function gamepadAxisIndexToInput(index: number): GamepadInput | null;
/**
* Physical source for an axis
*
* Different types of possible sources:
* - keyboard: Two keys (negative/positive)
* - gamepad: Analog axis (stick/trigger)
* - mouse: Mouse movement (delta X/Y or wheel)
* - gyroscope: Device orientation (pitch/roll/yaw)
* - touch: Virtual touch joystick
*/
interface AxisSource {
/** Unique source ID (0-255, for compression) */
sourceId: number;
/** Type of input source */
type: InputDeviceType;
/** Key for -1 value (e.g., KeyboardInput.ArrowLeft) */
negativeKey?: KeyboardInput;
/** Key for +1 value (e.g., KeyboardInput.ArrowRight) */
positiveKey?: KeyboardInput;
/** Gamepad index (0-3) */
gamepadIndex?: number;
/** Axis enum (e.g., GamepadInput.LeftStickX) */
axis?: GamepadInput;
/** Mouse axis enum (e.g., MouseInput.DeltaX, MouseInput.Wheel) */
mouseAxis?: MouseInput;
/** Rotation axis */
gyroAxis?: 'pitch' | 'roll' | 'yaw';
/** Virtual touch joystick ID (0-3) */
touchId?: number;
/** Joystick axis */
touchAxis?: 'x' | 'y';
/** Dead zone (default: 0.0) */
deadzone?: number;
/** Multiplier (default: 1.0) */
scale?: number;
/** Invert axis (default: false) */
invert?: boolean;
/** Sensitivity (for mouse/gyro, default: 1.0) */
sensitivity?: number;
}
/**
* Physical source for a button
*
* Different types of possible sources:
* - keyboard: A key
* - gamepad: A button
* - mouse: Mouse button
* - touch: Touch button
*/
interface ButtonSource {
/** Unique source ID (0-255, for compression) */
sourceId: number;
/** Type of input source */
type: InputDeviceType;
/** Key enum (e.g., KeyboardInput.Space) */
key?: KeyboardInput;
/** Gamepad index (0-3) */
gamepadIndex?: number;
/** Button enum (e.g., GamepadInput.A) */
button?: GamepadInput;
/** Mouse button enum (e.g., MouseInput.LeftButton) */
mouseButton?: MouseInput;
/** Touch button ID (0-9) */
touchButton?: number;
}
/**
* Axis binding definition
*
* Defines how an abstract axis (e.g., "MoveHorizontal") maps to
* physical input sources (keyboard, gamepad, etc.)
*/
interface AxisBinding {
/** Unique binding ID (0-255) */
bindingId: number;
/** Axis name (e.g., "MoveHorizontal", "CameraX") */
name: string;
/** Minimum value (e.g., -1.0) */
min: number;
/** Maximum value (e.g., +1.0) */
max: number;
/** Default value (e.g., 0.0) */
defaultValue: number;
/** Physical sources (values are added together) */
sources: AxisSource[];
}
/**
* Button binding definition
*
* Defines how an abstract button (e.g., "Jump") maps to
* physical input sources (keyboard, gamepad, etc.)
*/
interface ButtonBinding {
/** Unique binding ID (0-255) */
bindingId: number;
/** Button name (e.g., "Jump", "Attack") */
name: string;
/** Default value (e.g., false) */
defaultValue: boolean;
/** Physical sources (combined with OR logic) */
sources: ButtonSource[];
}
/**
* Load packet for input bindings
*
* This JSON packet is sent from server to client via the Load channel
* to configure which inputs the client should capture and send back.
*
* Architecture flow:
* 1. Server defines bindings (bindingId ↔ name + sources)
* 2. Server generates this LoadPacket as JSON
* 3. Client receives the packet and configures input mappings
* 4. Client captures physical inputs and sends compressed values
*/
interface InputBindingLoadPacket {
/** Packet type identifier */
type: 'input-binding';
/** Binding version (for future evolution) */
version: number;
/** Axis bindings configuration */
axes: AxisBinding[];
/** Button bindings configuration */
buttons: ButtonBinding[];
}
/**
* Touch input enumeration
* Supports multi-touch with up to 10 simultaneous touches
*
* Each touch has an identifier (button) and position axes (X, Y)
*/
declare enum TouchInput {
Touch0 = 0,
Touch1 = 1,
Touch2 = 2,
Touch3 = 3,
Touch4 = 4,
Touch5 = 5,
Touch6 = 6,
Touch7 = 7,
Touch8 = 8,
Touch9 = 9,
Touch0X = 100,
Touch0Y = 101,
Touch1X = 102,
Touch1Y = 103,
Touch2X = 104,
Touch2Y = 105,
Touch3X = 106,
Touch3Y = 107,
Touch4X = 108,
Touch4Y = 109,
Touch5X = 110,
Touch5Y = 111,
Touch6X = 112,
Touch6Y = 113,
Touch7X = 114,
Touch7Y = 115,
Touch8X = 116,
Touch8Y = 117,
Touch9X = 118,
Touch9Y = 119,
PinchScale = 200,// Pinch-to-zoom scale
RotationAngle = 201,// Two-finger rotation
SwipeVelocityX = 202,// Swipe velocity
SwipeVelocityY = 203
}
/**
* Check if the input is a touch identifier (button)
*/
declare function isTouchButton(input: TouchInput): boolean;
/**
* Check if the input is a position axis
*/
declare function isTouchPosition(input: TouchInput): boolean;
/**
* Check if the input is a gesture value
*/
declare function isTouchGesture(input: TouchInput): boolean;
/**
* Get the X position axis for a touch index
*/
declare function getTouchXAxis(touchIndex: number): TouchInput | null;
/**
* Get the Y position axis for a touch index
*/
declare function getTouchYAxis(touchIndex: number): TouchInput | null;
/**
* TV Remote input enumeration
* Common buttons found on TV remotes and set-top box controllers
*/
declare enum TVRemoteInput {
DPadUp = 0,
DPadDown = 1,
DPadLeft = 2,
DPadRight = 3,
DPadCenter = 4,// OK/Select button
Play = 5,
Pause = 6,
PlayPause = 7,// Combined play/pause toggle
Stop = 8,
Rewind = 9,
FastForward = 10,
Previous = 11,// Previous track/channel
Next = 12,// Next track/channel
Record = 13,
VolumeUp = 15,
VolumeDown = 16,
Mute = 17,
ChannelUp = 18,
ChannelDown = 19,
Back = 20,
Home = 21,
Menu = 22,
Info = 23,
Guide = 24,
Exit = 25,
Red = 30,
Green = 31,
Yellow = 32,
Blue = 33,
Digit0 = 40,
Digit1 = 41,
Digit2 = 42,
Digit3 = 43,
Digit4 = 44,
Digit5 = 45,
Digit6 = 46,
Digit7 = 47,
Digit8 = 48,
Digit9 = 49,
Input = 50,// Switch input source
Settings = 51,
Subtitle = 52,
Audio = 53,// Audio track selection
Power = 54,
PointerX = 100,// For remotes with motion control
PointerY = 101,
GyroX = 102,// Gyroscope data
GyroY = 103,
GyroZ = 104
}
/**
* Check if the input is a button
*/
declare function isTVRemoteButton(input: TVRemoteInput): boolean;
/**
* Check if the input is an axis (motion/pointer)
*/
declare function isTVRemoteAxis(input: TVRemoteInput): boolean;
/**
* Universal Input System Types
*
* Provides type-safe input handling across all device types
* using numeric enums for performance and compact serialization.
*/
/**
* Union type of all possible input enums
*/
type InputEnum = KeyboardInput | MouseInput | GamepadInput | TouchInput | TVRemoteInput;
/**
* Generic input descriptor
* Identifies any input across all device types
*/
interface InputDescriptor {
device: InputDeviceType;
input: InputEnum;
}
/**
* Type-safe input descriptors for each device
*/
interface KeyboardInputDescriptor {
device: InputDeviceType.Keyboard;
input: KeyboardInput;
}
interface MouseInputDescriptor {
device: InputDeviceType.Mouse;
input: MouseInput;
}
interface GamepadInputDescriptor {
device: InputDeviceType.Gamepad;
input: GamepadInput;
}
interface TouchInputDescriptor {
device: InputDeviceType.Touch;
input: TouchInput;
}
interface TVRemoteInputDescriptor {
device: InputDeviceType.TVRemote;
input: TVRemoteInput;
}
/**
* Discriminated union of all input descriptors
*/
type TypedInputDescriptor = KeyboardInputDescriptor | MouseInputDescriptor | GamepadInputDescriptor | TouchInputDescriptor | TVRemoteInputDescriptor;
/**
* Helper to create a typed input descriptor
*/
declare function createInputDescriptor<T extends InputDeviceType>(device: T, input: T extends InputDeviceType.Keyboard ? KeyboardInput : T extends InputDeviceType.Mouse ? MouseInput : T extends InputDeviceType.Gamepad ? GamepadInput : T extends InputDeviceType.Touch ? TouchInput : T extends InputDeviceType.TVRemote ? TVRemoteInput : never): InputDescriptor;
/**
* Check if an input is a button (vs axis)
*/
declare function isButton(descriptor: InputDescriptor): boolean;
/**
* Check if an input is an axis
*/
declare function isAxis(descriptor: InputDescriptor): boolean;
/**
* Serialize input descriptor to a compact binary format
* Format: [device: u8][input: u16] = 3 bytes total
*/
declare function serializeInputDescriptor(descriptor: InputDescriptor): Uint8Array;
/**
* Deserialize input descriptor from binary format
*/
declare function deserializeInputDescriptor(buffer: Uint8Array, offset?: number): InputDescriptor;
/**
* Convert input descriptor to human-readable string
* Format: "DeviceType:InputName"
*/
declare function inputDescriptorToString(descriptor: InputDescriptor): string;
/**
* Parse string format back to input descriptor
* Format: "DeviceType:InputName" or "devicetype:inputname"
*/
declare function parseInputDescriptor(str: string): InputDescriptor | null;
/**
* Input system interface for querying input state
*/
interface IInputSystem {
/** Query button state (returns boolean) */
getButton(device: InputDeviceType, input: number): boolean;
/** Query if button was just pressed this frame (transition false→true) */
getButtonJustPressed(device: InputDeviceType, input: number): boolean;
/** Query if button was just released this frame (transition true→false) */
getButtonJustReleased(device: InputDeviceType, input: number): boolean;
/** Query axis value (returns number, typically -1 to 1) */
getAxis(device: InputDeviceType, input: number): number;
/** Start listening to events */
start(): void;
/** Stop listening to events */
stop(): void;
/** Reset all states */
reset(): void;
/** Cleanup */
destroy(): void;
/** Check if listening (optional) */
isListening?(): boolean;
/** Poll transient states - called once per frame */
poll?(): void;
}
/**
* 2D vector with immutable and in-place operations
*/
declare class Vector2 {
x: number;
y: number;
constructor(x?: number, y?: number);
static zero(): Vector2;
static one(): Vector2;
static up(): Vector2;
static down(): Vector2;
static left(): Vector2;
static right(): Vector2;
add(v: Vector2): Vector2;
subtract(v: Vector2): Vector2;
multiply(scalar: number): Vector2;
divide(scalar: number): Vector2;
length(): number;
lengthSquared(): number;
normalize(): Vector2;
distance(v: Vector2): number;
distanceSquared(v: Vector2): number;
dot(v: Vector2): number;
cross(v: Vector2): number;
angle(): number;
angleTo(v: Vector2): number;
rotate(angle: number): Vector2;
lerp(v: Vector2, t: number): Vector2;
clone(): Vector2;
equals(v: Vector2): boolean;
toString(): string;
toArray(): [number, number];
set(x: number, y: number): this;
copy(v: Vector2): this;
addInPlace(v: Vector2): this;
subtractInPlace(v: Vector2): this;
multiplyInPlace(scalar: number): this;
divideInPlace(scalar: number): this;
normalizeInPlace(): this;
}
/**
* 3D vector with immutable and in-place operations
*/
declare class Vector3 {
x: number;
y: number;
z: number;
constructor(x?: number, y?: number, z?: number);
static zero(): Vector3;
static one(): Vector3;
static up(): Vector3;
static down(): Vector3;
static left(): Vector3;
static right(): Vector3;
static forward(): Vector3;
static back(): Vector3;
add(v: Vector3): Vector3;
subtract(v: Vector3): Vector3;
multiply(scalar: number): Vector3;
divide(scalar: number): Vector3;
length(): number;
lengthSquared(): number;
normalize(): Vector3;
distance(v: Vector3): number;
distanceSquared(v: Vector3): number;
dot(v: Vector3): number;
cross(v: Vector3): Vector3;
lerp(v: Vector3, t: number): Vector3;
clone(): Vector3;
equals(v: Vector3): boolean;
toString(): string;
toArray(): [number, number, number];
set(x: number, y: number, z: number): this;
copy(v: Vector3): this;
addInPlace(v: Vector3): this;
subtractInPlace(v: Vector3): this;
multiplyInPlace(scalar: number): this;
divideInPlace(scalar: number): this;
normalizeInPlace(): this;
}
/**
* RGBA color (0-255 per channel)
*/
interface RGBColor$1 {
/** Red (0-255) */
r: number;
/** Green (0-255) */
g: number;
/** Blue (0-255) */
b: number;
/** Alpha (0-255, 0=transparent, 255=opaque) */
a: number;
}
/**
* Rendered cell with palette indices for optimal caching
*/
interface RenderedCell {
/** Character */
char: string;
/** Foreground color index (0-255) */
fgColorIndex: number;
/** Background color index (0-255) */
bgColorIndex: number;
/** Foreground emission (0-255, 0=none) */
fgEmission: number;
/** Background emission (0-255, 0=none) */
bgEmission: number;
}
/**
* Display render state with cells and color palette
*/
interface RenderState {
/** Width in cells */
width: number;
/** Height in cells */
height: number;
/** Cell array (length = width × height) */
cells: RenderedCell[];
/** Color palette (256 colors) */
palette: RGBColor$1[];
}
/**
* Complete user render state
*/
interface UserRenderState {
/** User ID */
userId: string;
/** User name */
userName: string;
/** Current tick number */
tick: number;
/** Render states for each display */
displays: RenderState[];
}
/**
* RGB color for palette configuration
*/
interface RGBColor {
r: number;
g: number;
b: number;
a: number;
}
/**
* Renderer interface - Contract for all rendering implementations
*
* This is a universal interface that works in any environment (browser, Node.js, native).
* Canvas element is typed as `unknown` to avoid DOM dependencies in server code.
* Client code should cast to `HTMLCanvasElement` when needed.
*
* @example
* ```ts
* class TerminalGL implements IRenderer {
* renderDisplayData(data: RenderState): void { }
* resize(cols: number, rows: number): void { }
* destroy(): void { }
* getCols(): number { return this.cols; }
* getRows(): number { return this.rows; }
* isReady(): boolean { return this.ready; }
* getCanvas(): unknown { return this.canvas; }
* setBitmapFont(font, cw, ch, cellW, cellH): void { }
* setPalette(palette): void { }
* }
* ```
*/
interface IRenderer {
/**
* Render display data to screen (called every frame)
* @param data - Cell data with palette indices
*/
renderDisplayData(data: RenderState): void;
/**
* Resize renderer dimensions
* @param cols - Width in columns
* @param rows - Height in rows
*/
resize(cols: number, rows: number): void;
/** Clear display (optional) */
clear?(): void;
/** Cleanup resources */
destroy(): void;
/** Get width in columns */
getCols(): number;
/** Get height in rows */
getRows(): number;
/** Check if ready to render */
isReady(): boolean;
/**
* Get rendering offsets (for renderers that center content within canvas)
* Optional method - only implement if renderer centers content
* @returns Offset in pixels from canvas origin to rendered content
*/
getOffsets?(): {
offsetX: number;
offsetY: number;
} | undefined;
/**
* Get canvas element (typed as unknown to avoid DOM dependencies)
* Cast to HTMLCanvasElement in browser environments
* @returns Canvas element or null if not available
*/
getCanvas(): unknown;
/**
* Set bitmap font for character rendering
* @param font - Map of character codes to bitmap data (Uint8Array per character)
* @param charWidth - Character width in pixels
* @param charHeight - Character height in pixels
* @param cellWidth - Cell width in pixels (includes spacing)
* @param cellHeight - Cell height in pixels (includes spacing)
*/
setBitmapFont(font: Map<number, Uint8Array>, charWidth: number, charHeight: number, cellWidth: number, cellHeight: number): void;
/**
* Set color palette
* @param palette - Array of 256 RGB colors
*/
setPalette(palette: RGBColor[]): void;
}
/**
* Color palette interface
*
* @example
* ```ts
* const palette = new VGAPalette();
* const white = palette.getColor(15);
* ```
*/
interface IColorPalette {
/** Get color by index (returns CSS color string) */
getColor(index: number): string;
/** Set color at index */
setColor(index: number, color: string): void;
/** Get palette size */
getSize(): number;
/** Reset to default colors (optional) */
reset?(): void;
}
/**
* Network client interface for client-to-server communication
* Implementations: Socket.IO, WebRTC, WebSocket, etc.
*/
/** Event handler callback */
type NetworkEventHandler<T = any> = (data: T) => void;
/** Connection state */
declare enum NetworkState {
Disconnected = 0,
Connecting = 1,
Connected = 2,
Reconnecting = 3,
Error = 4
}
/** Client connection options */
interface NetworkClientOptions {
/** Server URL */
url: string;
/** Auto-reconnect on disconnect */
autoReconnect?: boolean;
/** Reconnect delay (ms) */
reconnectDelay?: number;
/** Max reconnect attempts (0=infinite) */
maxReconnectAttempts?: number;
/** Connection timeout (ms) */
timeout?: number;
/** Auth data */
auth?: Record<string, any>;
/** Debug logging */
debug?: boolean;
}
/** Network client interface */
interface INetworkClient {
/** Current state */
readonly state: NetworkState;
/** Check if connected */
isConnected(): boolean;
/** Connect to server */
connect(): Promise<void>;
/** Disconnect from server */
disconnect(): void;
/** Send event to server */
send(event: string, data: any): void;
/** Listen for server events */
on<T = any>(event: string, handler: NetworkEventHandler<T>): void;
/** Remove event listener */
off<T = any>(event: string, handler: NetworkEventHandler<T>): void;
/** Remove all listeners for event */
removeAllListeners(event?: string): void;
/** Send and wait for response */
request<T = any>(event: string, data: any, timeout?: number): Promise<T>;
/** Get ping to server (ms) */
getPing(): number;
/** Cleanup */
destroy(): void;
}
/**
* Network server interface for server-side communication
* Implementations: Socket.IO, WebRTC, WebSocket, etc.
*/
/** Server event handler */
type ServerEventHandler<T = any> = (clientId: string, data: T) => void;
/** Connection handler */
type ConnectionHandler = (clientId: string) => void;
/** Disconnection handler */
type DisconnectionHandler = (clientId: string, reason: string) => void;
/** Server options */
interface NetworkServerOptions {
/** Port */
port: number;
/** Host (default: "0.0.0.0") */
host?: string;
/** CORS config */
cors?: {
origin: string | string[];
credentials?: boolean;
};
/** Max connections */
maxConnections?: number;
/** Ping interval (ms) */
pingInterval?: number;
/** Ping timeout (ms) */
pingTimeout?: number;
/** Debug logging */
debug?: boolean;
}
/** Client info */
interface ClientInfo {
/** Client ID */
id: string;
/** Connection timestamp */
connectedAt: number;
/** IP address */
address: string;
/** Custom data */
data: Record<string, any>;
}
/** Network server interface */
interface INetworkServer {
/** Check if running */
isRunning(): boolean;
/** Start server */
start(): Promise<void>;
/** Stop server */
stop(): Promise<void>;
/** Get connected client IDs */
getClients(): string[];
/** Get client info */
getClientInfo(clientId: string): ClientInfo | null;
/** Send to specific client */
sendToClient(clientId: string, event: string, data: any): void;
/** Send volatile (can be dropped if congested) */
sendToClientVolatile?(clientId: string, event: string, data: any): void;
/** Broadcast to all clients */
broadcast(event: string, data: any): void;
/** Broadcast volatile (can be dropped) */
broadcastVolatile?(event: string, data: any): void;
/** Broadcast except one client */
broadcastExcept(excludeClientId: string, event: string, data: any): void;
/** Send to room */
sendToRoom(room: string, event: string, data: any): void;
/** Add client to room */
joinRoom(clientId: string, room: string): void;
/** Remove client from room */
leaveRoom(clientId: string, room: string): void;
/** Get room clients */
getRoomClients(room: string): string[];
/** Disconnect client */
disconnectClient(clientId: string, reason?: string): void;
/** Listen for events */
on<T = any>(event: string, handler: ServerEventHandler<T>): void;
/** Listen for connections */
onConnect(handler: ConnectionHandler): void;
/** Listen for disconnections */
onDisconnect(handler: DisconnectionHandler): void;
/** Remove listener */
off<T = any>(event: string, handler: ServerEventHandler<T>): void;
/** Set client custom data */
setClientData(clientId: string, key: string, value: any): void;
/** Get client custom data */
getClientData(clientId: string, key: string): any;
/** Get server stats */
getStats(): {
connectedClients: number;
totalConnections: number;
uptime: number;
};
/** Cleanup */
destroy(): Promise<void>;
}
/**
* Network message types for UTSP protocol
*/
/** Base message */
interface NetworkMessage {
/** Message type */
type: string;
/** Timestamp (client send time) */
timestamp: number;
/** Sequence number (optional) */
seq?: number;
}
/** Client input (client→server) */
interface InputMessage extends NetworkMessage {
type: 'input';
data: Uint8Array;
}
/** Server update (server→client) */
interface UpdateMessage extends NetworkMessage {
type: 'update';
data: Uint8Array;
tick: number;
}
/** Asset load (server→client) */
interface LoadMessage extends NetworkMessage {
type: 'load';
data: Uint8Array;
}
/** Join game (client→server) */
interface JoinMessage extends NetworkMessage {
type: 'join';
username: string;
roomId?: string;
token?: string;
}
/** Join response (server→client) */
interface JoinResponseMessage extends NetworkMessage {
type: 'join_response';
success: boolean;
userId?: string;
roomId?: string;
error?: string;
}
/** Leave game (client→server) */
interface LeaveMessage extends NetworkMessage {
type: 'leave';
}
/** Ping (latency measurement) */
interface PingMessage extends NetworkMessage {
type: 'ping';
}
/** Pong (ping response) */
interface PongMessage extends NetworkMessage {
type: 'pong';
pingTime: number;
}
/** Error message */
interface ErrorMessage extends NetworkMessage {
type: 'error';
code: string;
message: string;
}
/** Chat message (example custom type) */
interface ChatMessage extends NetworkMessage {
type: 'chat';
text: string;
userId: string;
username: string;
}
/** Union of all message types */
type AnyNetworkMessage = InputMessage | UpdateMessage | LoadMessage | JoinMessage | JoinResponseMessage | LeaveMessage | PingMessage | PongMessage | ErrorMessage | ChatMessage;
/** Message type literals */
type MessageType = AnyNetworkMessage['type'];
/**
* Application lifecycle interface for games, dashboards, signage, etc.
* Supports single-user (local) and multi-user (networked) scenarios.
*
* @template TCore - Core type (typically Core)
* @template TUser - User type (typically User)
* @template TRuntime - Runtime type (optional)
*/
interface IApplication<TCore = any, TUser = any, TRuntime = unknown> {
/**
* Initialize application (called once at startup, before users)
* Can be async if you need to load resources.
* @param core - Core instance
* @param runtime - Runtime instance (optional)
*/
init(core: TCore, runtime?: TRuntime): void | Promise<void>;
/**
* Update global logic (called every frame, runs once)
* @param core - Core instance
* @param deltaTime - Time since last update (seconds)
*/
update(core: TCore, deltaTime: number): void;
/**
* Initialize new user (called when user connects/joins)
* @param core - Core instance
* @param user - User instance
* @param metadata - Optional user metadata (username, preferences, etc.)
*/
initUser(core: TCore, user: TUser, metadata?: UserMetadata): void;
/**
* Update user-specific logic (called every frame per user)
* @param core - Core instance
* @param user - User instance
* @param deltaTime - Time since last update (seconds)
*/
updateUser(core: TCore, user: TUser, deltaTime: number): void;
/**
* Cleanup when user disconnects (optional)
* @param core - Core instance
* @param user - User instance
* @param reason - Disconnect reason (optional)
*/
destroyUser?(core: TCore, user: TUser, reason?: string): void;
/**
* Cleanup when application shuts down (optional)
*/
destroy?(): void;
/**
* Commit changes after all updates (optional)
* Called after update() and all updateUser() to trigger render/broadcast.
* @param core - Core instance
* @param deltaTime - Time since last update (seconds)
*/
onCommit?(core: TCore, deltaTime: number): void;
/**
* Handle errors during execution (optional)
* @param core - Core instance
* @param error - The error that occurred
* @param context - Error context (phase, user, timestamp)
* @returns true to continue, false to stop runtime
*/
onError?(core: TCore, error: Error, context: {
phase: 'init' | 'update' | 'initUser' | 'updateUser' | 'destroyUser' | 'destroy';
user?: TUser;
timestamp: number;
}): boolean;
}
/**
* User metadata for initUser
*/
interface UserMetadata {
/** Display name */
username?: string;
/** Avatar/skin ID */
avatar?: string | number;
/** Team/faction */
team?: string | number;
/** Color preference */
color?: number;
/** Theme preference */
theme?: string;
/** Auth token */
token?: string;
/** Custom data */
[key: string]: unknown;
}
/**
* Audio types for UTSP
* @packageDocumentation
*/
/**
* Supported audio file formats
*/
type SoundFormat = 'mp3' | 'wav' | 'ogg' | 'webm' | 'aac';
/**
* Sound loading type
*/
type SoundLoadType = 'file' | 'external';
/**
* A single sound to load via File mode (embedded data)
*/
interface SoundFileEntry {
/** Unique sound identifier (0-255) */
soundId: number;
/** Human-readable name for playback (e.g., 'coin', 'explosion') */
name: string;
/** Audio format */
format: SoundFormat;
/** Raw audio data */
data: Uint8Array;
}
/**
* A single sound to load via External mode (URL reference)
*/
interface SoundExternalEntry {
/** Unique sound identifier (0-255) */
soundId: number;
/** Human-readable name for playback */
name: string;
/** Audio format */
format: SoundFormat;
/** URL to fetch the audio from (CDN, external server, etc.) */
url: string;
/** Expected file size in bytes (optional, for progress tracking) */
size?: number;
/** Checksum for validation (optional) */
checksum?: string;
}
/**
* Sound Load packet - File mode
* Audio data is embedded in the packet and sent via UTSP
*/
interface SoundLoadPacket {
type: 'sound';
mode: 'file';
sounds: SoundFileEntry[];
}
/**
* Sound External Load packet - External mode
* Only URLs are sent, client downloads from external sources
*/
interface SoundExternalLoadPacket {
type: 'sound';
mode: 'external';
sounds: SoundExternalEntry[];
}
/**
* Union of all sound load packet types
*/
type AnySoundLoadPacket = SoundLoadPacket | SoundExternalLoadPacket;
/**
* Client acknowledgment that an external sound was loaded
* Sent after successfully downloading and decoding an external sound
*/
interface SoundLoadedAck {
/** Sound ID that was loaded */
soundId: number;
/** Loading status */
status: 'loaded' | 'error';
/** Error message if status is 'error' */
error?: string;
}
/**
* Unique identifier for a playing sound instance
* Allows multiple instances of the same sound to play simultaneously
* and be controlled independently
*/
type SoundInstanceId = number;
/**
* Command to play a sound on the client
* Sent from server to trigger sound playback
*/
interface PlaySoundCommand {
/** Sound ID or name to play */
sound: number | string;
/** Unique instance ID for this playback (allows stopping specific instances) */
instanceId?: SoundInstanceId;
/** Volume multiplier (0.0 to 1.0) */
volume?: number;
/** Pitch/playback rate (0.5 = octave down, 2.0 = octave up) */
pitch?: number;
/** Loop the sound */
loop?: boolean;
/** Fade in duration in seconds (0 = no fade, default: 0) */
fadeIn?: number;
/** Spatial position (if set, enables 2D positional audio) */
position?: {
/** X position (0-65535, 16-bit) */
x: number;
/** Y position (0-65535, 16-bit) */
y: number;
};
}
/**
* Command to stop a sound on the client
*/
interface StopSoundCommand {
/**
* What to stop:
* - SoundInstanceId (number > 0): Stop specific instance
* - Sound name (string): Stop all instances of that sound
* - 'all': Stop all sounds
*/
sound: SoundInstanceId | string | 'all';
}
/**
* Command to fade out a sound on the client
*/
interface FadeOutSoundCommand {
/** Marker to identify this as a fade-out command */
fadeOut: true;
/**
* What to fade out:
* - SoundInstanceId (number > 0): Fade out specific instance
* - Sound name (string): Fade out all instances of that sound
* - 'all': Fade out all sounds
*/
sound: SoundInstanceId | string | 'all';
/** Fade out duration in seconds */
duration: number;
}
/**
* Command to pause a sound on the client
*/
interface PauseSoundCommand {
/** Marker to identify this as a pause command */
pause: true;
/**
* What to pause:
* - SoundInstanceId (number > 0): Pause specific instance
* - Sound name (string): Pause all instances of that sound
* - 'all': Pause all sounds
*/
sound: SoundInstanceId | string | 'all';
}
/**
* Command to resume a paused sound on the client
*/
interface ResumeSoundCommand {
/** Marker to identify this as a resume command */
resume: true;
/**
* What to resume:
* - SoundInstanceId (number > 0): Resume specific instance
* - Sound name (string): Resume all instances of that sound
* - 'all': Resume all paused sounds
*/
sound: SoundInstanceId | string | 'all';
}
/**
* Command to set the listener position for spatial audio
*/
interface SetListenerPositionCommand {
type: 'set-listener-position';
/** X position (0-65535) */
x: number;
/** Y position (0-65535) */
y: number;
}
/**
* Command to configure spatial audio parameters
*/
interface ConfigureSpatialCommand {
type: 'configure-spatial';
/** Maximum audible distance */
maxDistance?: number;
/** Reference distance (full volume) */
referenceDistance?: number;
/** Rolloff factor */
rolloffFactor?: number;
/** Pan spread (0-1) */
panSpread?: number;
}
/**
* Union of all audio configuration commands
*/
type AudioConfigCommand = SetListenerPositionCommand | ConfigureSpatialCommand;
/**
* Configuration for 2D spatial audio (listener position)
*/
interface SpatialAudioConfig {
/** Listener X position (0-65535) - where the "ear" is */
listenerX: number;
/** Listener Y position (0-65535) - where the "ear" is */
listenerY: number;
/** Maximum audible distance (beyond this, volume = 0) */
maxDistance: number;
/** Reference distance (volume = 1.0 at this distance) */
referenceDistance: number;
/** Rolloff factor for distance attenuation (higher = faster falloff) */
rolloffFactor: number;
/** Pan spread factor (how much X offset affects stereo pan, 0-1) */
panSpread: number;
}
/**
* Options for playing a sound via IAudioProcessor
*/
interface AudioPlayOptions {
/** Volume multiplier (0.0 to 1.0, default: 1.0) */
volume?: number;
/** Playback rate / pitch (0.5 = octave down, 2.0 = octave up, default: 1.0) */
pitch?: number;
/** Loop the sound (default: false) */
loop?: boolean;
/** Fade in duration in seconds (0 = no fade, default: 0) */
fadeIn?: number;
/** Spatial position for 2D positional audio */
position?: {
x: number;
y: number;
};
/** Instance ID (provided by server in connected mode, auto-generated otherwise) */
instanceId?: number;
}
/**
* Result of playing a sound
*/
interface AudioPlayResult {
/** Unique instance ID for this playback */
instanceId: number;
}
/**
* Spatial audio configuration options
*/
interface AudioSpatialOptions {
/** Maximum audible distance */
maxDistance?: number;
/** Reference distance (full volume) */
referenceDistance?: number;
/** Rolloff factor */
rolloffFactor?: number;
/** Pan spread (0-1) */
panSpread?: number;
}
/**
* Interface for audio playback processing
*
* Implemented by AudioManager in @utsp/audio.
* Used by User in @utsp/core to play sounds without direct dependency on AudioManager.
*
* This abstraction allows:
* - Standalone mode: ClientRuntime injects AudioManager
* - Connected mode: NetworkSync uses the same interface via User
* - No circular dependency: Core → Types ← Audio
*
* @example
* ```typescript
* // In ClientRuntime
* user.setAudioProcessor(audioManager);
*
* // In User.applyAudioCommands()
* this.audioProcessor?.play('coin', { volume: 0.8 });
* ```
*/
interface IAudioProcessor {
/**
* Play a sound by name or ID
* @param sound - Sound name (string) or ID (number)
* @param options - Playback options
* @returns Play result with instanceId, or null if sound not found
*/
play(sound: string | number, options?: AudioPlayOptions): AudioPlayResult | null;
/**
* Stop sound(s) immediately
* @param target - Instance ID (number) to stop specific instance,
* or sound name (string) to stop all instances of that sound
* @returns Number of instances stopped
*/
stop(target: number | string): number;
/**
* Fade out and stop sound(s)
* @param target - Instance ID (number) to fade specific instance,
* or sound name (string) to fade all instances of that sound,
* or 'all' to fade all sounds
* @param duration - Fade duration in seconds
* @returns Number of instances being faded
*/
fadeOut(target: number | string | 'all', duration: number): number;
/**
* Stop all playing sounds immediately
* @returns Number of instances stopped
*/
stopAll(): number;
/**
* Pause sound(s)
* @param target - Instance ID (number) to pause specific instance,
* or sound name (string) to pause all instances of that sound,
* or 'all' to pause all sounds
* @returns Number of instances paused
*/
pause(target: number | string | 'all'): number;
/**
* Resume paused sound(s)
* @param target - Instance ID (number) to resume specific instance,
* or sound name (string) to resume all instances of that sound,
* or 'all' to resume all paused sounds
* @returns Number of instances resumed
*/
resume(target: number | string | 'all'): number;
/**
* Set the listener position for spatial audio
* @param x - X position (0-65535)
* @param y - Y position (0-65535)
*/
setListenerPosition(x: number, y: number): void;
/**
* Configure spatial audio parameters
* @param config - Spatial audio options
*/
configureSpatial(config: AudioSpatialOptions): void;
}
/**
* Interface for loading sounds into the audio system
*
* Implemented by SoundBank in @utsp/audio.
* Abstracts the difference between Browser (Web Audio API) and Node.js environments.
*
* @example
* ```typescript
* // File mode - embedded data
* await soundLoader.loadFromData(0, 'coin', audioData);
*
* // External mode - URL
* await soundLoader.loadFromUrl(1, 'music', 'https://cdn.example.com/music.mp3');
* ```
*/
interface ISoundLoader {
/**
* Load a sound from binary data (File mode)
* @param soundId - Unique sound identifier (0-255)
* @param name - Human-readable name for playback
* @param data - Raw audio data (mp3, wav, ogg, etc.)
*/
loadFromData(soundId: number, name: string, data: Uint8Array): Promise<void>;
/**
* Load a sound from a URL (External mode)
* @param soundId - Unique sound identifier (0-255)
* @param name - Human-readable name for playback
* @param url - URL to fetch the audio from
*/
loadFromUrl(soundId: number, name: string, url: string): Promise<void>;
/**
* Check if a sound is loaded
* @param nameOrId - Sound name or ID
* @returns true if sound is loaded and ready to play
*/
has(nameOrId: string | number): boolean;
}
export { GamepadInput, InputDeviceType, KeyboardInput, MouseInput, NetworkState, TVRemoteInput, TouchInput, Vector2, Vector3, createInputDescriptor, deserializeInputDescriptor, gamepadAxisIndexToInput, gamepadButtonIndexToInput, getTouchXAxis, getTouchYAxis, inputDescriptorToString, isAxis, isButton, isGamepadAxis, isGamepadButton, isMouseAxis, isMouseButton, isTVRemoteAxis, isTVRemoteButton, isTouchButton, isTouchGesture, isTouchPosition, keyCodeToKeyboardInput, keyboardInputToKeyCode, parseInputDescriptor, serializeInputDescriptor };
export type { AnyNetworkMessage, AnySoundLoadPacket, AudioConfigCommand, AudioPlayOptions, AudioPlayResult, AudioSpatialOptions, AxisBinding, AxisSource, ButtonBinding, ButtonSource, ChatMessage, ClientInfo, ConfigureSpatialCommand, ConnectionHandler, DisconnectionHandler, ErrorMessage, FadeOutSoundCommand, GamepadInputDescriptor, IApplication, IAudioProcessor, IColorPalette, IInputSystem, INetworkClient, INetworkServer, IRenderer, ISoundLoader, InputBindingLoadPacket, InputDescriptor, InputEnum, InputMessage, JoinMessage, JoinResponseMessage, KeyboardInputDescriptor, LeaveMessage, LoadMessage, MessageType, MouseInputDescriptor, NetworkClientOptions, NetworkEventHandler, NetworkMessage, NetworkServerOptions, PauseSoundCommand, PingMessage, PlaySoundCommand, PongMessage, RGBColor, RenderState, RenderedCell, ResumeSoundCommand, ServerEventHandler, SetListenerPositionCommand, SoundExternalEntry, SoundExternalLoadPacket, SoundFileEntry, SoundFormat, SoundInstanceId, SoundLoadPacket, SoundLoadType, SoundLoadedAck, SpatialAudioConfig, StopSoundCommand, TVRemoteInputDescriptor, TouchInputDescriptor, TypedInputDescriptor, UpdateMessage, UserMetadata, UserRenderState };