| @@ -75,8 +75,21 @@ | ||
| 75 | 75 | * underneath (`wordsUrl` — see |
| 76 | 76 | * includes/games/config.php); the |
| 77 | 77 | * game's keys win on collision. |
| 78 | 78 | * @type string[] $capabilities Gate: ALL caps must match. |
| 79 | + * @type array $window The game window's size, as any | |
| 80 | + * subset of `{ width, height, | |
| 81 | + * minWidth, minHeight }` in pixels. | |
| 82 | + * Declare it here as well as in the | |
| 83 | + * JS def: a game's bundle is fetched | |
| 84 | + * on first play, so the shell opens | |
| 85 | + * the window — and paints its loading | |
| 86 | + * spinner — before it has seen the | |
| 87 | + * def. Without this the first window | |
| 88 | + * of a session opens at the framework | |
| 89 | + * default. The def still wins once it | |
| 90 | + * arrives, so declaring only in JS | |
| 91 | + * keeps working. | |
| 79 | 92 | * } |
| 80 | 93 | * @return true|WP_Error `true` on success; `WP_Error` otherwise. |
| 81 | 94 | */ |
| 82 | 95 | function openstation_register_game( $id, $args = array() ) { |
| @@ -96,8 +109,9 @@ | ||
| 96 | 109 | 'script' => '', |
| 97 | 110 | 'score_columns' => array(), |
| 98 | 111 | 'config' => array(), |
| 99 | 112 | 'capabilities' => array(), |
| 113 | + 'window' => array(), | |
| 100 | 114 | ); |
| 101 | 115 | $args = wp_parse_args( $args, $defaults ); |
| 102 | 116 | |
| 103 | 117 | $svg = trim( (string) $args['icon_svg'] ); |
| @@ -161,8 +175,9 @@ | ||
| 161 | 175 | 'icon' => openstation_sanitize_dock_icon( (string) $args['icon'] ), |
| 162 | 176 | 'script' => (string) $args['script'], |
| 163 | 177 | 'score_columns' => openstation_games_sanitize_score_columns( $args['score_columns'] ), |
| 164 | 178 | 'config' => is_array( $args['config'] ) ? $args['config'] : array(), |
| 179 | + 'window' => openstation_games_sanitize_window( $args['window'] ), | |
| 165 | 180 | ); |
| 166 | 181 | openstation_games_registry( $id, $entry ); |
| 167 | 182 | |
| 168 | 183 | /** |
| @@ -179,8 +194,56 @@ | ||
| 179 | 194 | return true; |
| 180 | 195 | } |
| 181 | 196 | |
| 182 | 197 | /** |
| 198 | + * Normalize the `window` declaration — the game window's size, known | |
| 199 | + * before its bundle is. | |
| 200 | + * | |
| 201 | + * **Why this is registered server-side at all**, when the JS def | |
| 202 | + * already carries a `window` block: a game's bundle is heavyweight | |
| 203 | + * (the game, its engine, sometimes a dictionary asset), so it is | |
| 204 | + * fetched on first play rather than at boot. The shell therefore has | |
| 205 | + * to open the window — and paint its loading spinner — *before* it has | |
| 206 | + * ever seen the def. Without a size here that first window opens at | |
| 207 | + * the framework default and would have to jump to the real size once | |
| 208 | + * the def landed. With it, the size is right from the first frame. | |
| 209 | + * | |
| 210 | + * The JS def still wins once it arrives, so a game that declares only | |
| 211 | + * in JS keeps working exactly as before; it simply gets the default | |
| 212 | + * size on the first open of a session. | |
| 213 | + * | |
| 214 | + * Values are clamped rather than rejected: a nonsensical size is a | |
| 215 | + * plugin bug that should not stop the game opening, and an unopenable | |
| 216 | + * window is a worse answer than an oddly-sized one. | |
| 217 | + * | |
| 218 | + * @internal | |
| 219 | + * | |
| 220 | + * @param mixed $window Raw caller input. | |
| 221 | + * @return array Sanitized subset of `{ width, height, minWidth, minHeight }`. | |
| 222 | + */ | |
| 223 | +function openstation_games_sanitize_window( $window ) { | |
| 224 | + if ( ! is_array( $window ) ) { | |
| 225 | + return array(); | |
| 226 | + } | |
| 227 | + $out = array(); | |
| 228 | + foreach ( array( 'width', 'height', 'minWidth', 'minHeight' ) as $key ) { | |
| 229 | + if ( ! isset( $window[ $key ] ) || ! is_numeric( $window[ $key ] ) ) { | |
| 230 | + continue; | |
| 231 | + } | |
| 232 | + $value = (int) $window[ $key ]; | |
| 233 | + if ( $value <= 0 ) { | |
| 234 | + continue; | |
| 235 | + } | |
| 236 | + // The ceiling is generous on purpose — it exists to catch a | |
| 237 | + // typo'd pixel value, not to have an opinion about how big a | |
| 238 | + // game may be. The window manager clamps to the viewport anyway. | |
| 239 | + $out[ $key ] = min( $value, 10000 ); | |
| 240 | + } | |
| 241 | + | |
| 242 | + return $out; | |
| 243 | +} | |
| 244 | + | |
| 245 | +/** | |
| 183 | 246 | * Normalize the `score_columns` declaration: drop rows without a |
| 184 | 247 | * valid key, default labels to the key, and clamp `type` to the |
| 185 | 248 | * supported set. |
| 186 | 249 | * |
| @@ -348,8 +411,16 @@ | ||
| 348 | 411 | 'config' => array_merge( |
| 349 | 412 | openstation_games_framework_config(), |
| 350 | 413 | isset( $entry['config'] ) && is_array( $entry['config'] ) ? $entry['config'] : array() |
| 351 | 414 | ), |
| 415 | + // The window's size, known before its bundle is — so the | |
| 416 | + // shell can open the window (and start its loading spinner) | |
| 417 | + // on the click rather than after the download. Omitted | |
| 418 | + // entirely when the game declared none, which reads as | |
| 419 | + // "use the framework defaults" on the JS side. | |
| 420 | + 'window' => isset( $entry['window'] ) && is_array( $entry['window'] ) | |
| 421 | + ? $entry['window'] | |
| 422 | + : array(), | |
| 352 | 423 | 'scriptUrl' => $payload['url'], |
| 353 | 424 | 'scriptHandle' => $handle, |
| 354 | 425 | 'scriptBefore' => $payload['before'], |
| 355 | 426 | 'scriptAfter' => $payload['after'], |
| @@ -354,8 +425,11 @@ | ||
| 354 | 425 | 'scriptBefore' => $payload['before'], |
| 355 | 426 | 'scriptAfter' => $payload['after'], |
| 356 | 427 | 'scriptL10n' => $payload['l10n'], |
| 357 | 428 | 'scriptTranslations' => $payload['translations'], |
| 429 | + // The handle's dependency closure, replayed before the bundle | |
| 430 | + // on its lazy load — see `openstation_resolve_script_dependencies()`. | |
| 431 | + 'scriptDeps' => openstation_resolve_script_dependencies( $handle ), | |
| 358 | 432 | ); |
| 359 | 433 | } |
| 360 | 434 | return $out; |
| 361 | 435 | } |