PluginProbe
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin / 1.1.10
OpenStation: Desktop Windows, Dock & Virtual Desktops for WP Admin v1.1.10
1.1.10 1.1.9 1.1.8 1.1.7 1.1.6 1.1.5 1.1.4 1.1.3 1.1.2 1.1.1 1.1.0 1.0.1 1.0.0 0.9.8 0.9.7 0.9.6 0.9.4 0.9.5 0.9.3 0.9.2 0.9.1 0.9.0 0.8.9 0.8.8 0.8.7 All 34 releases
← All changes | includes/games/store.php +55 -88 0.9.71.1.10 View file →
@@ -1,13 +1,13 @@
1 1 <?php
2 2 /**
3 - * Desktop Mode — Games store.
3 + * OpenStation — Games store.
4 4 *
5 5 * CRUD for the two games tables. Scores are client-asserted (arcade
6 6 * trust model): the server clamps and sanitizes what it can — the
7 7 * game must be server-registered, the score is a non-negative int,
8 8 * the meta blob is a bounded flat scalar map — and exposes the
9 - * `desktop_mode_game_score_pre_save` filter for plugins that want
9 + * `openstation_game_score_pre_save` filter for plugins that want
10 10 * stricter validation.
11 11 *
12 12 * The challenge state machine is enforced HERE, not in REST:
13 13 * `pending → accepted | declined`, `accepted → completed`. Every
@@ -12,10 +12,9 @@
12 12 * The challenge state machine is enforced HERE, not in REST:
13 13 * `pending → accepted | declined`, `accepted → completed`. Every
14 14 * mutation bumps `updated_at_ms`, the Heartbeat high-water mark.
15 15 *
16 - * @package WPDesktopMode
17 - * @since 0.9.6
16 + * @package OpenStation
18 17 */
19 18
20 19 defined( 'ABSPATH' ) || exit;
21 20
@@ -23,14 +22,12 @@
23 22 * Bound and sanitize a score meta blob: flat map, slug keys, scalar
24 23 * values only. Strings are text-sanitized and truncated; the map is
25 24 * capped at 20 keys so a hostile client can't fatten the table.
26 25 *
27 - * @since 0.9.6
28 - *
29 26 * @param mixed $meta Raw caller input.
30 27 * @return array Sanitized flat map.
31 28 */
32 -function desktop_mode_games_sanitize_score_meta( $meta ) {
29 +function openstation_games_sanitize_score_meta( $meta ) {
33 30 if ( ! is_array( $meta ) ) {
34 31 return array();
35 32 }
36 33 $out = array();
@@ -56,10 +53,8 @@
56 53
57 54 /**
58 55 * Persist a finished game run.
59 56 *
60 - * @since 0.9.6
61 - *
62 57 * @param string $game Registered game id.
63 58 * @param int $user_id Player.
64 59 * @param int $score Primary sort value. Clamped to >= 0.
65 60 * @param array $meta Flexible per-game fields (see the game's
@@ -65,19 +60,19 @@
65 60 * @param array $meta Flexible per-game fields (see the game's
66 61 * `score_columns`).
67 62 * @return int|WP_Error Row id on success.
68 63 */
69 -function desktop_mode_games_save_score( $game, $user_id, $score, $meta = array() ) {
64 +function openstation_games_save_score( $game, $user_id, $score, $meta = array() ) {
70 65 global $wpdb;
71 66
72 67 $game = sanitize_key( (string) $game );
73 68 $user_id = (int) $user_id;
74 69 $score = max( 0, (int) $score );
75 - $meta = desktop_mode_games_sanitize_score_meta( $meta );
70 + $meta = openstation_games_sanitize_score_meta( $meta );
76 71
77 - if ( ! desktop_mode_games_is_registered( $game ) ) {
72 + if ( ! openstation_games_is_registered( $game ) ) {
78 73 return new WP_Error(
79 - 'desktop_mode_unknown_game',
74 + 'openstation_unknown_game',
80 75 __( 'Unknown game.', 'desktop-mode' ),
81 76 array( 'status' => 404 )
82 77 );
83 78 }
@@ -82,9 +77,9 @@
82 77 );
83 78 }
84 79 if ( $user_id <= 0 ) {
85 80 return new WP_Error(
86 - 'desktop_mode_invalid_user',
81 + 'openstation_invalid_user',
87 82 __( 'A valid user is required to save a score.', 'desktop-mode' ),
88 83 array( 'status' => 400 )
89 84 );
90 85 }
@@ -94,10 +89,8 @@
94 89 * `WP_Error` to reject the save (surfaced to the client), or
95 90 * `null` to proceed. The extension point for anti-cheat
96 91 * plugins (rate limits, plausibility checks).
97 92 *
98 - * @since 0.9.6
99 - *
100 93 * @param null|WP_Error $pre Null to proceed.
101 94 * @param string $game Game id.
102 95 * @param int $user_id Player.
103 96 * @param int $score Clamped score.
@@ -102,14 +95,14 @@
102 95 * @param int $user_id Player.
103 96 * @param int $score Clamped score.
104 97 * @param array $meta Sanitized meta map.
105 98 */
106 - $pre = apply_filters( 'desktop_mode_game_score_pre_save', null, $game, $user_id, $score, $meta );
99 + $pre = apply_filters( 'openstation_game_score_pre_save', null, $game, $user_id, $score, $meta );
107 100 if ( is_wp_error( $pre ) ) {
108 101 return $pre;
109 102 }
110 103
111 - $tables = desktop_mode_games_table_names();
104 + $tables = openstation_games_table_names();
112 105 $ok = $wpdb->insert(
113 106 $tables['scores'],
114 107 array(
115 108 'game' => $game,
@@ -115,15 +108,15 @@
115 108 'game' => $game,
116 109 'user_id' => $user_id,
117 110 'score' => $score,
118 111 'meta' => wp_json_encode( $meta ),
119 - 'created_at_ms' => desktop_mode_games_now_ms(),
112 + 'created_at_ms' => openstation_games_now_ms(),
120 113 ),
121 114 array( '%s', '%d', '%d', '%s', '%d' )
122 115 );
123 116 if ( false === $ok ) {
124 117 return new WP_Error(
125 - 'desktop_mode_score_save_failed',
118 + 'openstation_score_save_failed',
126 119 __( 'Could not save the score.', 'desktop-mode' ),
127 120 array( 'status' => 500 )
128 121 );
129 122 }
@@ -131,10 +124,8 @@
131 124
132 125 /**
133 126 * Fires after a game score is saved.
134 127 *
135 - * @since 0.9.6
136 - *
137 128 * @param int $id Score row id.
138 129 * @param string $game Game id.
139 130 * @param int $user_id Player.
140 131 * @param int $score Saved score.
@@ -139,9 +130,9 @@
139 130 * @param int $user_id Player.
140 131 * @param int $score Saved score.
141 132 * @param array $meta Saved meta map.
142 133 */
143 - do_action( 'desktop_mode_game_score_saved', $id, $game, $user_id, $score, $meta );
134 + do_action( 'openstation_game_score_saved', $id, $game, $user_id, $score, $meta );
144 135
145 136 return $id;
146 137 }
147 138
@@ -147,10 +138,8 @@
147 138
148 139 /**
149 140 * Leaderboard query.
150 141 *
151 - * @since 0.9.6
152 - *
153 142 * @param string $game Registered game id.
154 143 * @param array $args {
155 144 * @type int $page 1-based page. Default 1.
156 145 * @type int $per_page Rows per page, 1–100. Default 25.
@@ -159,9 +148,9 @@
159 148 * @type int $user_id Restrict to one player. Default 0 (all).
160 149 * }
161 150 * @return array{ rows: array[], total: int }
162 151 */
163 -function desktop_mode_games_get_scores( $game, $args = array() ) {
152 +function openstation_games_get_scores( $game, $args = array() ) {
164 153 global $wpdb;
165 154
166 155 $game = sanitize_key( (string) $game );
167 156 $page = max( 1, (int) ( $args['page'] ?? 1 ) );
@@ -169,9 +158,9 @@
169 158 $orderby = ( 'created' === ( $args['orderby'] ?? '' ) ) ? 'created_at_ms' : 'score';
170 159 $order = ( 'asc' === strtolower( (string) ( $args['order'] ?? 'desc' ) ) ) ? 'ASC' : 'DESC';
171 160 $user_id = (int) ( $args['user_id'] ?? 0 );
172 161
173 - $tables = desktop_mode_games_table_names();
162 + $tables = openstation_games_table_names();
174 163 $where = 'game = %s';
175 164 $params = array( $game );
176 165 if ( $user_id > 0 ) {
177 166 $where .= ' AND user_id = %d';
@@ -199,9 +188,9 @@
199 188 ARRAY_A
200 189 );
201 190
202 191 return array(
203 - 'rows' => array_map( 'desktop_mode_games_shape_score', (array) $rows ),
192 + 'rows' => array_map( 'openstation_games_shape_score', (array) $rows ),
204 193 'total' => $total,
205 194 );
206 195 }
207 196
@@ -208,14 +197,12 @@
208 197 /**
209 198 * Shape a scores row for the wire: camelCase keys + player display
210 199 * name and avatar.
211 200 *
212 - * @since 0.9.6
213 - *
214 201 * @param array $row Raw table row.
215 202 * @return array
216 203 */
217 -function desktop_mode_games_shape_score( $row ) {
204 +function openstation_games_shape_score( $row ) {
218 205 $user_id = (int) $row['user_id'];
219 206 $user = get_userdata( $user_id );
220 207 $meta = json_decode( (string) ( $row['meta'] ?? '' ), true );
221 208 return array(
@@ -232,10 +219,8 @@
232 219
233 220 /**
234 221 * Create a score-to-beat challenge.
235 222 *
236 - * @since 0.9.6
237 - *
238 223 * @param string $game Registered game id.
239 224 * @param int $challenger_id Sender.
240 225 * @param int $recipient_id Receiver.
241 226 * @param int $score_to_beat The challenger's score.
@@ -241,9 +226,9 @@
241 226 * @param int $score_to_beat The challenger's score.
242 227 * @param array $score_meta The challenger's score meta map.
243 228 * @return int|WP_Error Challenge id on success.
244 229 */
245 -function desktop_mode_games_create_challenge( $game, $challenger_id, $recipient_id, $score_to_beat, $score_meta = array() ) {
230 +function openstation_games_create_challenge( $game, $challenger_id, $recipient_id, $score_to_beat, $score_meta = array() ) {
246 231 global $wpdb;
247 232
248 233 $game = sanitize_key( (string) $game );
249 234 $challenger_id = (int) $challenger_id;
@@ -248,11 +233,11 @@
248 233 $game = sanitize_key( (string) $game );
249 234 $challenger_id = (int) $challenger_id;
250 235 $recipient_id = (int) $recipient_id;
251 236
252 - if ( ! desktop_mode_games_is_registered( $game ) ) {
237 + if ( ! openstation_games_is_registered( $game ) ) {
253 238 return new WP_Error(
254 - 'desktop_mode_unknown_game',
239 + 'openstation_unknown_game',
255 240 __( 'Unknown game.', 'desktop-mode' ),
256 241 array( 'status' => 404 )
257 242 );
258 243 }
@@ -257,9 +242,9 @@
257 242 );
258 243 }
259 244 if ( $recipient_id <= 0 || ! get_userdata( $recipient_id ) ) {
260 245 return new WP_Error(
261 - 'desktop_mode_invalid_recipient',
246 + 'openstation_invalid_recipient',
262 247 __( 'The challenged user does not exist.', 'desktop-mode' ),
263 248 array( 'status' => 400 )
264 249 );
265 250 }
@@ -264,16 +249,16 @@
264 249 );
265 250 }
266 251 if ( $recipient_id === $challenger_id ) {
267 252 return new WP_Error(
268 - 'desktop_mode_self_challenge',
253 + 'openstation_self_challenge',
269 254 __( 'You cannot challenge yourself.', 'desktop-mode' ),
270 255 array( 'status' => 400 )
271 256 );
272 257 }
273 258
274 - $now = desktop_mode_games_now_ms();
275 - $tables = desktop_mode_games_table_names();
259 + $now = openstation_games_now_ms();
260 + $tables = openstation_games_table_names();
276 261 $ok = $wpdb->insert(
277 262 $tables['challenges'],
278 263 array(
279 264 'game' => $game,
@@ -279,9 +264,9 @@
279 264 'game' => $game,
280 265 'challenger_id' => $challenger_id,
281 266 'recipient_id' => $recipient_id,
282 267 'score_to_beat' => max( 0, (int) $score_to_beat ),
283 - 'score_meta' => wp_json_encode( desktop_mode_games_sanitize_score_meta( $score_meta ) ),
268 + 'score_meta' => wp_json_encode( openstation_games_sanitize_score_meta( $score_meta ) ),
284 269 'state' => 'pending',
285 270 'created_at_ms' => $now,
286 271 'updated_at_ms' => $now,
287 272 ),
@@ -288,9 +273,9 @@
288 273 array( '%s', '%d', '%d', '%d', '%s', '%s', '%d', '%d' )
289 274 );
290 275 if ( false === $ok ) {
291 276 return new WP_Error(
292 - 'desktop_mode_challenge_create_failed',
277 + 'openstation_challenge_create_failed',
293 278 __( 'Could not create the challenge.', 'desktop-mode' ),
294 279 array( 'status' => 500 )
295 280 );
296 281 }
@@ -298,14 +283,12 @@
298 283
299 284 /**
300 285 * Fires after a game challenge is created.
301 286 *
302 - * @since 0.9.6
303 - *
304 287 * @param int $id Challenge id.
305 288 * @param array $row The challenge row.
306 289 */
307 - do_action( 'desktop_mode_game_challenge_created', $id, desktop_mode_games_get_challenge( $id ) );
290 + do_action( 'openstation_game_challenge_created', $id, openstation_games_get_challenge( $id ) );
308 291
309 292 return $id;
310 293 }
311 294
@@ -311,16 +294,14 @@
311 294
312 295 /**
313 296 * Fetch one challenge row.
314 297 *
315 - * @since 0.9.6
316 - *
317 298 * @param int $id Challenge id.
318 299 * @return array|null Raw table row.
319 300 */
320 -function desktop_mode_games_get_challenge( $id ) {
301 +function openstation_games_get_challenge( $id ) {
321 302 global $wpdb;
322 - $tables = desktop_mode_games_table_names();
303 + $tables = openstation_games_table_names();
323 304 $row = $wpdb->get_row(
324 305 $wpdb->prepare( "SELECT * FROM {$tables['challenges']} WHERE id = %d", (int) $id ),
325 306 ARRAY_A
326 307 );
@@ -330,28 +311,26 @@
330 311 /**
331 312 * Transition a challenge to `accepted` or `declined`. Only valid
332 313 * from `pending`.
333 314 *
334 - * @since 0.9.6
335 - *
336 315 * @param int $id Challenge id.
337 316 * @param string $state 'accepted' | 'declined'.
338 317 * @return true|WP_Error
339 318 */
340 -function desktop_mode_games_set_challenge_state( $id, $state ) {
319 +function openstation_games_set_challenge_state( $id, $state ) {
341 320 global $wpdb;
342 321
343 322 if ( ! in_array( $state, array( 'accepted', 'declined' ), true ) ) {
344 323 return new WP_Error(
345 - 'desktop_mode_invalid_challenge_state',
324 + 'openstation_invalid_challenge_state',
346 325 __( 'Invalid challenge state.', 'desktop-mode' ),
347 326 array( 'status' => 400 )
348 327 );
349 328 }
350 - $row = desktop_mode_games_get_challenge( $id );
329 + $row = openstation_games_get_challenge( $id );
351 330 if ( ! $row ) {
352 331 return new WP_Error(
353 - 'desktop_mode_challenge_not_found',
332 + 'openstation_challenge_not_found',
354 333 __( 'Challenge not found.', 'desktop-mode' ),
355 334 array( 'status' => 404 )
356 335 );
357 336 }
@@ -356,9 +335,9 @@
356 335 );
357 336 }
358 337 if ( 'pending' !== $row['state'] ) {
359 338 return new WP_Error(
360 - 'desktop_mode_challenge_state_conflict',
339 + 'openstation_challenge_state_conflict',
361 340 __( 'This challenge has already been decided.', 'desktop-mode' ),
362 341 array( 'status' => 409 )
363 342 );
364 343 }
@@ -365,10 +344,10 @@
365 344
366 345 // Monotonic bump: a transition landing in the same millisecond as
367 346 // the previous write must still move `updated_at_ms` forward, or
368 347 // version-gated Heartbeat clients would never see the change.
369 - $now = max( desktop_mode_games_now_ms(), (int) $row['updated_at_ms'] + 1 );
370 - $tables = desktop_mode_games_table_names();
348 + $now = max( openstation_games_now_ms(), (int) $row['updated_at_ms'] + 1 );
349 + $tables = openstation_games_table_names();
371 350 $wpdb->update(
372 351 $tables['challenges'],
373 352 array(
374 353 'state' => $state,
@@ -383,24 +362,20 @@
383 362 if ( 'accepted' === $state ) {
384 363 /**
385 364 * Fires after a challenge is accepted by its recipient.
386 365 *
387 - * @since 0.9.6
388 - *
389 366 * @param int $id Challenge id.
390 367 * @param array $row The (pre-transition) challenge row.
391 368 */
392 - do_action( 'desktop_mode_game_challenge_accepted', (int) $id, $row );
369 + do_action( 'openstation_game_challenge_accepted', (int) $id, $row );
393 370 } else {
394 371 /**
395 372 * Fires after a challenge is declined by its recipient.
396 373 *
397 - * @since 0.9.6
398 - *
399 374 * @param int $id Challenge id.
400 375 * @param array $row The (pre-transition) challenge row.
401 376 */
402 - do_action( 'desktop_mode_game_challenge_declined', (int) $id, $row );
377 + do_action( 'openstation_game_challenge_declined', (int) $id, $row );
403 378 }
404 379
405 380 return true;
406 381 }
@@ -408,22 +383,20 @@
408 383 /**
409 384 * Record the recipient's run against an accepted challenge. Also
410 385 * persists the run as a normal leaderboard score row.
411 386 *
412 - * @since 0.9.6
413 - *
414 387 * @param int $id Challenge id.
415 388 * @param int $score The recipient's score.
416 389 * @param array $meta The recipient's score meta map.
417 390 * @return array|WP_Error The updated challenge row.
418 391 */
419 -function desktop_mode_games_complete_challenge( $id, $score, $meta = array() ) {
392 +function openstation_games_complete_challenge( $id, $score, $meta = array() ) {
420 393 global $wpdb;
421 394
422 - $row = desktop_mode_games_get_challenge( $id );
395 + $row = openstation_games_get_challenge( $id );
423 396 if ( ! $row ) {
424 397 return new WP_Error(
425 - 'desktop_mode_challenge_not_found',
398 + 'openstation_challenge_not_found',
426 399 __( 'Challenge not found.', 'desktop-mode' ),
427 400 array( 'status' => 404 )
428 401 );
429 402 }
@@ -428,9 +401,9 @@
428 401 );
429 402 }
430 403 if ( 'accepted' !== $row['state'] ) {
431 404 return new WP_Error(
432 - 'desktop_mode_challenge_state_conflict',
405 + 'openstation_challenge_state_conflict',
433 406 __( 'Only an accepted challenge can be completed.', 'desktop-mode' ),
434 407 array( 'status' => 409 )
435 408 );
436 409 }
@@ -435,22 +408,22 @@
435 408 );
436 409 }
437 410
438 411 $score = max( 0, (int) $score );
439 - $meta = desktop_mode_games_sanitize_score_meta( $meta );
412 + $meta = openstation_games_sanitize_score_meta( $meta );
440 413 $result = $score > (int) $row['score_to_beat'] ? 'beaten' : 'not_beaten';
441 414
442 415 // The run also lands on the leaderboard — a challenge game is a
443 416 // real game. A veto from the pre-save filter aborts the whole
444 417 // completion so the two writes can't diverge.
445 - $score_id = desktop_mode_games_save_score( $row['game'], (int) $row['recipient_id'], $score, $meta );
418 + $score_id = openstation_games_save_score( $row['game'], (int) $row['recipient_id'], $score, $meta );
446 419 if ( is_wp_error( $score_id ) ) {
447 420 return $score_id;
448 421 }
449 422
450 423 // Same monotonic-bump rule as `set_challenge_state()` — see there.
451 - $now = max( desktop_mode_games_now_ms(), (int) $row['updated_at_ms'] + 1 );
452 - $tables = desktop_mode_games_table_names();
424 + $now = max( openstation_games_now_ms(), (int) $row['updated_at_ms'] + 1 );
425 + $tables = openstation_games_table_names();
453 426 $wpdb->update(
454 427 $tables['challenges'],
455 428 array(
456 429 'state' => 'completed',
@@ -464,20 +437,18 @@
464 437 array( '%s', '%s', '%d', '%s', '%d', '%d' ),
465 438 array( '%d' )
466 439 );
467 440
468 - $updated = desktop_mode_games_get_challenge( $id );
441 + $updated = openstation_games_get_challenge( $id );
469 442
470 443 /**
471 444 * Fires after a challenge run is completed.
472 445 *
473 - * @since 0.9.6
474 - *
475 446 * @param int $id Challenge id.
476 447 * @param string $result 'beaten' | 'not_beaten'.
477 448 * @param array $row The updated challenge row.
478 449 */
479 - do_action( 'desktop_mode_game_challenge_completed', (int) $id, $result, $updated );
450 + do_action( 'openstation_game_challenge_completed', (int) $id, $result, $updated );
480 451
481 452 return $updated;
482 453 }
483 454
@@ -485,18 +456,16 @@
485 456 * Challenges involving a user (as challenger or recipient) whose
486 457 * `updated_at_ms` exceeds the given high-water mark. The Heartbeat
487 458 * delta query.
488 459 *
489 - * @since 0.9.6
490 - *
491 460 * @param int $user_id Viewer.
492 461 * @param int $since_ms Last-seen `updated_at_ms`. 0 = everything.
493 462 * @param int $cap Row cap.
494 463 * @return array[] Raw rows, oldest change first.
495 464 */
496 -function desktop_mode_games_get_challenges_for_user( $user_id, $since_ms = 0, $cap = 50 ) {
465 +function openstation_games_get_challenges_for_user( $user_id, $since_ms = 0, $cap = 50 ) {
497 466 global $wpdb;
498 - $tables = desktop_mode_games_table_names();
467 + $tables = openstation_games_table_names();
499 468 $rows = $wpdb->get_results(
500 469 $wpdb->prepare(
501 470 "SELECT * FROM {$tables['challenges']}
502 471 WHERE ( challenger_id = %d OR recipient_id = %d )
@@ -516,17 +485,15 @@
516 485 /**
517 486 * Shape a challenge row for the wire: camelCase keys plus display
518 487 * name + avatar for both parties.
519 488 *
520 - * @since 0.9.6
521 - *
522 489 * @param array $row Raw table row.
523 490 * @return array
524 491 */
525 -function desktop_mode_games_shape_challenge( $row ) {
526 - $challenger = get_userdata( (int) $row['challenger_id'] );
527 - $recipient = get_userdata( (int) $row['recipient_id'] );
528 - $score_meta = json_decode( (string) ( $row['score_meta'] ?? '' ), true );
492 +function openstation_games_shape_challenge( $row ) {
493 + $challenger = get_userdata( (int) $row['challenger_id'] );
494 + $recipient = get_userdata( (int) $row['recipient_id'] );
495 + $score_meta = json_decode( (string) ( $row['score_meta'] ?? '' ), true );
529 496 $result_meta = json_decode( (string) ( $row['result_meta'] ?? '' ), true );
530 497 return array(
531 498 'id' => (int) $row['id'],
532 499 'game' => (string) $row['game'],