Skip to content

Latest commit

 

History

History
139 lines (106 loc) · 11.1 KB

File metadata and controls

139 lines (106 loc) · 11.1 KB

How to Customize the Simulation Engine

The engine allows several ways to customize the behavior and controls of the simulated device, below all the ways a host application can set it up, during initialization and execution of BrightScript apps.

Device Information

As described on the engine API documentation, the asynchronous method initialize() accepts, as first parameter, an object containing custom device configurations. Below is an example of the object with all its default values, defined internally on the library. Most of these parameters are accessible by the BrightScript app via roDeviceInfo component. It's not necessary to send all parameters, only the ones to be changed.

const deviceInfo = {
    developerId: "34c6fceca75e456f25e7e99531e2425c6c1de443", // As Roku, this ID segregates Registry data (can't be empty or have a dot)
    friendlyName: "BrightScript Engine Library",
    deviceModel: "8000X", // Roku TV (Midland)
    clientId: "6c5bf3a5-b2a5-4918-824d-7691d5c85364",
    RIDA: "f51ac698-bc60-4409-aae3-8fc3abc025c4", // Unique identifier for advertisement tracking
    countryCode: "US", // App Store Country
    timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
    locale: "en_US", // Valid locales: en_US, es_MX, pt_BR, fr_CA, de_DE
    captionLanguage: "en", // Preferred caption language
    clockFormat: "12h",
    displayMode: "720p", // Supported modes: 480p (SD), 720p (HD) and 1080p (FHD)
    maxSimulStreams: 2, // Max number of `roAudioResource` streams (1 or 2)
    customFeatures: [], // String array with custom features (see below)
    localIps: ["eth1,127.0.0.1"], // In a Browser isn't possible to get a real IP, populate it on NodeJS or Electron
    audioVolume: 50, // Defines the default volume level for system sounds - valid: (0-100)
    audioLanguage: "en", // Preferred audio track language
    autoPlayEnabled: true, // Autoplay device setting, returned by `roDeviceInfo.IsAutoplayEnabled()` (default: enabled)
    minVideoBufferMs: 700, // Simulated minimum buffering floor (ms) before video playback starts
    maxFps: 60, // Maximum frames per second for rendering
    tmpVolSize: 32 * 1024 * 1024, // Allocated size for `tmp:/` volume (32 MB)
    cacheFSVolSize: 32 * 1024 * 1024, // Allocated size for `cachefs:/` volume (32 MB)
    logLevel: LogLevel.Warning, // Log level for the engine (Debug, Warning, Error)
    corsProxy: "https://your-cors-proxy-instance.yourdomain.com/", // (optional) Add your CORS-Anywhere URL here
};

CORS Proxy Configuration

Simulated Device Features

In BrightScript, various device features can be checked using the roDeviceInfo method hasFeature(). The engine leverages this method to provide developers with a way to verify specific simulator features.

Below is a table with the extended set of features, internally created by the engine, that can be used in BrightScript code. This allows apps to behave differently when running on a Roku device or under the simulation engine.

Feature Name Description
simulation_engine Always true when running under brs-engine
platform_cli Returns true when running in the terminal, see the CLI doc for more details
platform_browser Returns true when running under a Browser
platform_chromium Returns true when running under Chromium
platform_firefox Returns true when running under Firefox
platform_safari Returns true when running under Safari
platform_electron Returns true when running under Electron
platform_linux Returns true when running under Linux
platform_macos Returns true when running under MacOS
platform_windows Returns true when running under Windows
platform_chromeos Returns true when running under ChromeOS
platform_ios Returns true when running under iOS
platform_android Returns true when running under Android

Custom Features

In the Device Information section above, the deviceInfo object has an Array parameter named customFeatures that can be used to pass specific features implemented by the host application to the BrightScript apps.

For example, if you want to define that your application is running on a device that supports touch features, emulating the remote control, you can add customFeatures: ["touch_controls"]. Inside the app, you can check this feature as follows:

  di = CreateObject("roDeviceInfo")
  if di.hasFeature("touch_controls") 'This will always return `false` in a Roku device
    showTouchInstructions()
  else
    showRemoteInstructions()
  end if

App Manifest

There is also a way BrightScript apps can change the behavior of the simulation engine, by using special manifest entries. The valid options are:

  • multi_key_events=1: If this flag is defined, will inform the simulator to handle multiple key events in parallel, instead of the default Roku behavior, that is handling one key at a time.
  • cors_proxy=0: If this flag is defined with zero, the engine will disable the corsProxy URL for the app, if configured in the DeviceInfo object.
  • multi_controllers=1 (experimental, simulator only): If this flag is defined, enables full support for multiple simultaneous game controllers, an expanded button map (X/Y, L1/R1/L2/R2, independent right stick), and the GetValue() analog extension on roUniversalControlEvent. This is a brs-engine-only capability with no real-Roku equivalent, and its API may change in future engine versions. See Multiple Controllers Support below.

Note: these special manifest entries are ignored by Roku Devices.

Control Mapping

It is also possible to customize the Remote Control mapping for the Keyboard and Game Pad, either by sending the custom mapping in the Options parameter when running initialize() method, or by using setCustomKeys() and setCustomPadButtons() later on. Check the details in the engine API documentation. To learn about the default mapping check the Remote Control Simulation page.

Multiple Key Events Support

By default, the engine simulates the Roku behavior of handling one key event at a time. This means that if you press and hold a key, it will generate a keyDown event, and if another key is pressed while the first one is still held down, it will be generate a keyUp event for the first key, followed by a keyDown event for the second key. This prevents the app from receiving multiple key events simultaneously, and sometimes in games this is not the desired behavior. So if you want to enable multiple key events, you can do it by adding the multi_key_events=1 entry in your app manifest file allowing the app to receive multiple key events at the same time.

You can find an example of how to take advantage of this behavior in packages/browser/index.js file:

const customKeys = new Map();
customKeys.set("NumpadMultiply", "info"); // Keep consistency with older versions
customKeys.set("ShiftLeft", "playonly"); // Support for Prince of Persia
customKeys.set("Shift+ArrowRight", "right"); // Support for Prince of Persia
customKeys.set("Shift+ArrowLeft", "left"); // Support for Prince of Persia
customKeys.set("Shift+ArrowUp", "up"); // Support for Prince of Persia
customKeys.set("Shift+ArrowDown", "down"); // Support for Prince of Persia

// ...

await brs.initialize(customDeviceInfo, {
    debugToConsole: true,
    customKeys: customKeys,
    showStats: true,
});

This example shows how to map the Shift key in combination with the arrow keys to simulate the player walking in all directions in the "Prince of Persia" game. This way, the app can receive multiple key events when the Shift key is held down while pressing the arrow keys, and with the manifest entry multi_key_events=1, the app will receive all key events without generating keyUp events for the previously pressed keys, so the game can differentiate between walking and running.

Notice that I used the ShiftLeft code to map the playonly key, as playonly is not mapped by default and could be used as an additional button in games. When ShiftLeft is pressed alone, the app can detect and handle it, as in case of the "Prince of Persia" game, it makes the character to hang from a ledge or pick an item. To see how this is implemented in the game check the source code in the repository: https://github.com/lvcabral/Prince-of-Persia-Roku.

Multiple Controllers Support

Warning

Experimental — simulator only. This feature (and its API: the multi_controllers=1 manifest entry, the expanded button map, setCustomExtendedPadButtons(), and GetValue() on roUniversalControlEvent) exists only in brs-engine's simulated remote control and has no equivalent on real Roku hardware. It may change in future engine versions without following normal deprecation timelines.

By default, all connected game pads share a single 5-slot key buffer and a single "one key at a time" debounce state, matching a single-remote Roku device — this can cause simultaneous input from more than one controller to clobber or delay each other's events, and the digital button map only covers a Roku remote's vocabulary (no X/Y face buttons, no L1/L2/R1/R2, and the right stick aliases the same D-pad keys as the left stick). Adding the multi_controllers=1 entry to your app manifest file enables:

  • Correct per-controller event delivery — a press on one game pad no longer forces a synthetic release of another controller's held key, and events from different controllers are no longer dropped as false duplicates.
  • An expanded button map: A/B/X/Y, L1/R1/L2/R2, D-pad, Back/Home/Select/Info, in addition to the sticks.
  • The right analog stick becomes analog-only (see below) instead of aliasing the left stick's D-pad keys.
  • The GetValue() analog extension on roUniversalControlEvent — see Remote Control Simulation for details.

This flag only affects game pad/gamepad-style remotes; keyboard and other remote input are unaffected.