BGE/Controller

Alias: BGE.Controller


Enums

BGE.Controller.ConnectionMode

enumstaticreadonly

Properties

  • http (default: "http")
  • websocket (default: "websocket")

BGE.Controller.ControllerServerEventType

enumstaticreadonly

Properties

  • onOpen (default: "onOpen")
  • onMessage (default: "onMessage")
  • onClose (default: "onClose")

Other

ActionBinding

static

One bindAction() binding - internal bookkeeping, not part of the public API.

Properties

  • remoteButton (dynamic) — string, or invalid if this action has no remote binding
  • controllerButton (dynamic) — string, or invalid if this action has no remote binding
  • playerIndex (integer) — string, or invalid if this action has no controller binding
  • label (dynamic) — string, or invalid if none given

AxisBinding

static

One bindAxis() binding - internal bookkeeping, not part of the public API.

Properties

  • stick (string)
  • playerIndex (integer)
  • label (dynamic) — string, or invalid if none given

ActionState

static

One action's live press/held/release/duration state - internal bookkeeping, not part of the public API (isActionPressed()/isActionHeld()/isActionReleased()/getActionHeldTimeMs() are how a game reads this).

Properties

  • press (boolean)
  • held (boolean)
  • release (boolean)
  • heldTimeMs (integer)

ControlMap

static

Unified mapping from remote-button and controller input to named actions/axes - the one class a game needs to learn to support both input sources. Exposed as Game.controls; see Game.enableControllerInput().

Parameters

  • registry (BGE.Controller.ControllerRegistry)

Returns

  • BGE.Controller.ControlMap

ControllerConnectionState

static

Internal per-connection controller state (buttons/sticks/held-timers/custom payload) plus the remote's own d-pad fallback state. Not part of the public API - a game uses BGE.Controller.ControlMap (game.controls) instead.

Properties

  • buttons (dynamic) — name -> boolean
  • heldTimers (dynamic) — name -> boolean
  • sticks (dynamic) — name -> roTimespan, present only while held
  • custom (dynamic) — name -> BGE.Math.Vector

ControllerRegistry

static

CONTROLLER_HTTP_BUFFER_SIZE

staticreadonly

Default: 10240

CONTROLLER_WS_READ_SIZE

staticreadonly

Default: 10240

CONTROLLER_WS_BUFFER_MAX_SIZE

staticreadonly

Cap on connection.wsBuffer's accumulated size, mirroring CONTROLLER_HTTP_BUFFER_SIZE's guard on httpBuffer - without this, a client declaring a huge 64-bit extended WebSocket frame payload length and then trickling/withholding bytes could grow wsBuffer unbounded while decodeFrames() keeps waiting for the rest of the frame to arrive.

Default: 65536

ControllerConnection

static

Properties

  • socket (roStreamSocket)
  • mode (string)
  • wsBuffer (roByteArray)
  • httpBuffer (roByteArray) — Accumulates raw bytes across possibly-multiple recv()/poll() calls ' until a full HTTP request (terminated by a blank line) has arrived - ' see onHttpReadable(). A single recv() is not guaranteed to return an ' entire request in one call, especially a real browser's larger ' WebSocket-upgrade request (many headers) split across TCP segments.
  • staleReadPolls (integer) — Consecutive poll() cycles where isReadable() reported true but ' receive() returned a negative "not ready yet" result with no bytes ' actually delivered - see onHttpReadable()/onWebSocketReadable(). Reset ' to 0 on every poll that actually receives bytes. Guards against a dead ' connection (peer vanished without a clean FIN) spinning forever now ' that a negative receive() no longer closes the connection immediately. ' ' IMPORTANT, confirmed on a real 2024 Roku Ultra with an on-device debug ' capture: isReadable() is NOT a reliable "there might be data" signal ' for a websocket socket on this hardware - once a connection has ever ' been read from, isReadable() reports true on essentially every single ' subsequent poll() even while the peer is sitting perfectly idle and ' sends nothing, and receive() returns -1 every one of those times, with ' no gaps and no distinguishing pattern versus an actually-dead peer. ' Concretely: a real browser client left untouched (no stick/button ' movement) hit exactly this and got closed after ~300 consecutive ' negative-receive polls (~5s) - not a hypothetical, this is what a ' silently-idle player looks like on this hardware. There is no signal ' available here to tell "healthy but idle" apart from "actually dead" - ' both look identical. The real fix is the browser client ' (controller-web/index.html) resending its current state once a ' second even when nothing changed, so a healthy connection always has ' real bytes arriving well inside this threshold; staleReadPolls stays ' as a backstop that still reaps a connection whose heartbeats have ' genuinely stopped (tab closed, network gone), just no longer trusted ' to distinguish idle-vs-dead on its own.
  • pendingSend (roByteArray) — Bytes still waiting to be flushed to the socket - see ' ControllerServer.queueSend()/flushPendingSend(). invalid when ' nothing is queued. roStreamSocket.send() is non-blocking and can ' return 0 ("would block, try again") under ordinary backpressure - ' looping on that in place would busy-spin the whole render loop ' (Roku's socket has no blocking wait), so instead any unsent ' remainder is parked here and retried once per poll() cycle until it ' fully flushes (or a real send error occurs).
  • pendingSendOffset (integer) — How far into pendingSend the previous partial send(s) already got.
  • pendingSendAction (string) — What to do once pendingSend fully flushes - "" (nothing further), ' "close" (close the connection - e.g. after an HTTP response), or ' "upgrade" (flip into websocket mode and emit onOpen - deferred until ' the handshake response has actually reached the client, so a flush ' failure never leaves a "ghost" upgraded connection).

CONTROLLER_MAX_STALE_READ_POLLS

staticreadonly

Consecutive negative-receive polls before a connection is assumed dead and closed - see ControllerConnection.staleReadPolls. At ~60 polls/sec this is a multi-second grace period. The browser client now resends its state once a second (see controller-web/index.html), so this threshold only needs to comfortably clear that 1s heartbeat cadence (it does, by ~5x) - it no longer needs to double as "long enough for the flakiest possible isReadable() behavior on idle, untouched input," since that case no longer exists once the client is always sending.

Default: 300

ControllerServerEvent

static

Properties

  • type (string) — BGE.Controller.ControllerServerEventType
  • connectionId (string) — BGE.Controller.ControllerServerEventType
  • message (string) — only set for onMessage

HttpRequest

static

A parseHttpRequest() result - the request line plus headers, both used verbatim by onHttpReadable()'s routing. headers stays a plain dictionary (its keys are whatever header names the client happened to send, not a fixed set).

Properties

  • command (string)
  • path (string)
  • protocol (string)
  • headers (object)

ControllerServer

static

Minimal HTTP + WebSocket (RFC 6455) server for streaming controller input from a browser page, without SceneGraph. Adapted and trimmed from https://github.com/markwpearce/roku-gamepad - serves GET only (static files under pkg:/source/controller-web), no upload/directory listing.

Parameters

  • port (integer)

Properties

  • port (integer)

Returns

  • BGE.Controller.ControllerServer