]`.
*
* This deliberate laziness is the one way the games registry differs
* from the wallpaper registry it is otherwise modeled on: wallpaper
* scripts are enqueued eagerly because the active wallpaper must
* paint at boot; game code is only needed when someone plays.
*
* @package OpenStation
*/
defined( 'ABSPATH' ) || exit;
/**
* Register a server-side desktop game.
*
* Example:
*
* ```php
* openstation_register_game( 'inkfall', array(
* 'title' => __( 'Inkfall', 'desktop-mode' ),
* 'description' => __( 'Type the falling words.', 'desktop-mode' ),
* 'icon_svg' => '',
* 'script' => 'os-game-inkfall',
* 'score_columns' => array(
* array( 'key' => 'score', 'label' => __( 'Score', 'desktop-mode' ), 'type' => 'number' ),
* array( 'key' => 'time', 'label' => __( 'Time', 'desktop-mode' ), 'type' => 'time' ),
* ),
* 'config' => array( 'pace' => 'brisk' ),
* ) );
* ```
*
* ```js
* // Inside os-game-inkfall.js
* window.openStationGames = window.openStationGames || {};
* window.openStationGames.inkfall = {
* id: 'inkfall',
* title: 'Inkfall',
* icon: 'data:image/svg+xml;base64,…',
* scoreColumns: [ … ],
* render: function ( ctx ) { return function () {}; },
* };
* ```
*
* @param string $id Game id (slug). Must match the
* `window.openStationGames[]` key the game's
* JS publishes.
* @param array $args {
* @type string $title Launcher label. Required.
* @type string $description Plain-text description shown on the
* launcher tile. Optional.
* @type string $icon Dashicon class, http(s) URL, or
* `data:image/svg+xml` URI.
* @type string $icon_svg Raw SVG markup shorthand — converted
* to a base64 data URI. Wins over
* `icon`.
* @type string $script Registered script handle whose file
* publishes the game def. Required.
* @type array[] $score_columns Scoreboard column declarations:
* `{ key, label, type }` with type one
* of `number` | `time` | `text`.
* @type array $config Arbitrary blob shipped to the game's
* launch context (asset URLs, tuning).
* The framework merges its own keys in
* underneath (`wordsUrl` — see
* includes/games/config.php); the
* game's keys win on collision.
* @type string[] $capabilities Gate: ALL caps must match.
* @type array $window The game window's size, as any
* subset of `{ width, height,
* minWidth, minHeight }` in pixels.
* Declare it here as well as in the
* JS def: a game's bundle is fetched
* on first play, so the shell opens
* the window — and paints its loading
* spinner — before it has seen the
* def. Without this the first window
* of a session opens at the framework
* default. The def still wins once it
* arrives, so declaring only in JS
* keeps working.
* }
* @return true|WP_Error `true` on success; `WP_Error` otherwise.
*/
function openstation_register_game( $id, $args = array() ) {
$id = sanitize_key( (string) $id );
if ( '' === $id ) {
return openstation_registration_error(
'openstation_missing_id',
__( 'Game id is required and must be a valid slug.', 'desktop-mode' )
);
}
$defaults = array(
'title' => '',
'description' => '',
'icon' => 'dashicons-admin-generic',
'icon_svg' => '',
'script' => '',
'score_columns' => array(),
'config' => array(),
'capabilities' => array(),
'window' => array(),
);
$args = wp_parse_args( $args, $defaults );
$svg = trim( (string) $args['icon_svg'] );
if ( '' !== $svg ) {
// Same defence-in-depth as desktop icons: the data URI is
// consumed via `` (which sandboxes SVG scripts),
// but reject script tags outright anyway.
if ( false !== stripos( $svg, '