array( 'name' => 'Ocean', 'bg1' => '#cce9fb', 'bg2' => '#a8d8f0', 'acc' => '#f57c00', 'da' => '#e65100', 'text' => '#0d2a4a', 'card' => 'rgba(255,255,255,0.45)', 'dm' => false, ), 'midnight' => array( 'name' => 'Midnight', 'bg1' => '#0f172a', 'bg2' => '#1e293b', 'acc' => '#60a5fa', 'da' => '#3b82f6', 'text' => '#e2e8f0', 'card' => 'rgba(15,23,42,0.65)', 'dm' => true, ), 'forest' => array( 'name' => 'Forest', 'bg1' => '#d1fae5', 'bg2' => '#a7f3d0', 'acc' => '#059669', 'da' => '#047857', 'text' => '#064e3b', 'card' => 'rgba(255,255,255,0.45)', 'dm' => false, ), 'sunset' => array( 'name' => 'Sunset', 'bg1' => '#fff1e6', 'bg2' => '#fde68a', 'acc' => '#ea580c', 'da' => '#c2410c', 'text' => '#7c2d12', 'card' => 'rgba(255,255,255,0.45)', 'dm' => false, ), 'slate' => array( 'name' => 'Slate', 'bg1' => '#e2e8f0', 'bg2' => '#cbd5e1', 'acc' => '#7c3aed', 'da' => '#6d28d9', 'text' => '#1e293b', 'card' => 'rgba(255,255,255,0.45)', 'dm' => false, ), 'rose' => array( 'name' => 'Rose', 'bg1' => '#fff1f2', 'bg2' => '#fecdd3', 'acc' => '#e11d48', 'da' => '#be123c', 'text' => '#881337', 'card' => 'rgba(255,255,255,0.45)', 'dm' => false, ), 'emerald' => array( 'name' => 'Emerald', 'bg1' => '#ecfdf5', 'bg2' => '#d1fae5', 'acc' => '#d97706', 'da' => '#b45309', 'text' => '#064e3b', 'card' => 'rgba(255,255,255,0.45)', 'dm' => false, ), 'violet' => array( 'name' => 'Violet', 'bg1' => '#1e1b4b', 'bg2' => '#312e81', 'acc' => '#a78bfa', 'da' => '#7c3aed', 'text' => '#ede9fe', 'card' => 'rgba(49,46,129,0.5)', 'dm' => true, ), 'charcoal' => array( 'name' => 'Charcoal', 'bg1' => '#1c1c1e', 'bg2' => '#2c2c2e', 'acc' => '#f57c00', 'da' => '#e65100', 'text' => '#e5e5ea', 'card' => 'rgba(44,44,46,0.6)', 'dm' => true, ), 'arctic' => array( 'name' => 'Arctic', 'bg1' => '#f0fdfa', 'bg2' => '#ccfbf1', 'acc' => '#0d9488', 'da' => '#0f766e', 'text' => '#134e4a', 'card' => 'rgba(255,255,255,0.45)', 'dm' => false, ), 'copper' => array( 'name' => 'Copper', 'bg1' => '#fdf6ec', 'bg2' => '#fde8c8', 'acc' => '#b45309', 'da' => '#92400e', 'text' => '#451a03', 'card' => 'rgba(255,255,255,0.45)', 'dm' => false, ), 'cosmic' => array( 'name' => 'Cosmic', 'bg1' => '#0a0015', 'bg2' => '#1a0033', 'acc' => '#e879f9', 'da' => '#d946ef', 'text' => '#fae8ff', 'card' => 'rgba(26,0,51,0.5)', 'dm' => true, ), ); } /** Builds inline CSS overrides for the chosen colour scheme (empty string for default). */ public static function get_404_scheme_css( string $key ): string { $schemes = self::get_404_schemes(); if ( ! isset( $schemes[ $key ] ) || 'ocean' === $key ) { return ''; } $s = $schemes[ $key ]; $bg1 = esc_attr( $s['bg1'] ); $bg2 = esc_attr( $s['bg2'] ); $acc = esc_attr( $s['acc'] ); $da = esc_attr( $s['da'] ); $text = esc_attr( $s['text'] ); $card = esc_attr( $s['card'] ); $css = "body{background:linear-gradient(160deg,{$bg1} 0%,{$bg2} 100%);color:{$text};}"; $css .= ".cs404-heading{background:linear-gradient(135deg,{$acc},{$da});-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;}"; $css .= ".cs404-btn,.cs404-home-btn{background:linear-gradient(135deg,{$acc},{$da});box-shadow:0 4px 24px {$acc}44;}"; $css .= ".cs404-btn:hover,.cs404-home-btn:hover{box-shadow:0 6px 28px {$acc}66;}"; $css .= ".cs404-tab.active{background:linear-gradient(135deg,{$acc},{$da});box-shadow:0 2px 12px {$acc}44;}"; $css .= "#cs404-game{background:{$card};border-color:rgba(128,128,128,0.2);}"; $css .= ".cs404-lb-score{color:{$acc};}"; $css .= ".cs404-lb-row-gold{background:{$acc}18;}"; if ( $s['dm'] ) { $css .= ".cs404-desc,.cs404-site-name,.cs404-tagline{color:{$text};}"; $css .= ".cs404-tab{background:rgba(255,255,255,0.08);color:{$text};border-color:rgba(255,255,255,0.1);}"; $css .= '.cs404-tab:hover{background:rgba(255,255,255,0.14);}'; $css .= ".cs404-miner-btn{background:rgba(255,255,255,0.1);color:{$text};border-color:rgba(255,255,255,0.15);}"; $css .= '#cs404-lb-panel{background:rgba(255,255,255,0.05);border-color:rgba(255,255,255,0.1);}'; $css .= ".cs404-lb-header{background:rgba(255,255,255,0.07);color:{$text};}"; $css .= ".cs404-lb-name{color:{$text};}.cs404-lb-empty{color:{$text};}"; $css .= '.cs404-lb-row{border-bottom-color:rgba(255,255,255,0.07);}'; } return $css; } /** * The colour scheme an administrator is previewing, or '' for an ordinary visitor. * * The preview link on the Custom 404 settings card carries a nonce created beside it * (`csdtDevtools404.previewNonce`, action `csdt_404_preview`). Without that nonce, or * without the capability, the query parameter is ignored and the saved scheme is used: * a visitor cannot pick the page's palette by editing the URL. * * @return string Scheme key, or '' when not a valid preview request. */ private static function preview_scheme_key(): string { if ( ! isset( $_GET['csdt_devtools_preview_scheme'], $_GET['_wpnonce'] ) ) { return ''; } if ( ! current_user_can( 'manage_options' ) ) { return ''; } if ( ! wp_verify_nonce( sanitize_key( wp_unslash( $_GET['_wpnonce'] ) ), 'csdt_404_preview' ) ) { return ''; } return sanitize_key( wp_unslash( $_GET['csdt_devtools_preview_scheme'] ) ); } /** * The plugin's credit on the 404 page. * * Empty unless the administrator ticked "Show Powered by CloudScale" (option * csdt_show_powered_by, default off), the one opt-in that governs every credit the * plugin can show a visitor. WordPress.org guideline 10: attribution on a public * page only when the site owner asked for it. * * @return string HTML, or '' when not opted in. */ private static function attribution_badge(): string { // The credit and its opt-in share one statement: the administrator's "Show Powered by // CloudScale credit" checkbox (option csdt_show_powered_by, default off) decides it. return CSDT_Login::credit_opted_in() ? '⚡Powered by CloudScale Devtools' : ''; } /** * Intercepts WordPress 404 responses and outputs the custom games page. * * Hooked on `template_redirect` at priority 1. */ public static function maybe_custom_404(): void { if ( ! is_404() ) { return; } $preview_key = self::preview_scheme_key(); $is_preview = '' !== $preview_key; if ( ! $is_preview && ! get_option( CSDT_DevTools::CUSTOM_404_OPTION, 0 ) ) { return; } status_header( 404 ); nocache_headers(); header( 'Content-Type: text/html; charset=utf-8' ); $site_name = get_bloginfo( 'name' ); $site_tagline = get_bloginfo( 'description' ); $home_url = home_url( '/' ); $logo_html = ''; if ( has_custom_logo() ) { $logo_html = get_custom_logo(); } elseif ( $icon_url = get_site_icon_url( 64 ) ) { $logo_html = ''; } $root_path = plugin_dir_path( __DIR__ ); $root_url = plugin_dir_url( __DIR__ ); $css_path = $root_path . 'assets/cs-custom-404.css'; $js_path = $root_path . 'assets/cs-custom-404.js'; $js_url = $root_url . 'assets/cs-custom-404.js?ver=' . CSDT_DevTools::VERSION . '.' . filemtime( $js_path ); $all_schemes = self::get_404_schemes(); $active_scheme = ( $preview_key && isset( $all_schemes[ $preview_key ] ) ) ? $preview_key : get_option( CSDT_DevTools::SCHEME_404_OPTION, 'ocean' ); $scheme_css = self::get_404_scheme_css( $active_scheme ); ?> <?php echo esc_html__( 'Page Not Found', 'cloudscale-cyber-devtools' ); ?>, <?php echo esc_html( $site_name ); ?>

404

← Home
🏆 Runner, Top 10

No scores yet, be the first!

rest_url( CSDT_DevTools::HISCORE_NS ), 'nonce' => wp_create_nonce( CSDT_DevTools::SCORE_NONCE_ACTION ), ) ) . ';', 'before' ); wp_print_scripts( array( 'csdt-404' ) ); ?> runner|jetpack|racer|miner|asteroids|snake|spaceinvaders|mrdo)', array( array( 'methods' => 'GET', 'callback' => array( __CLASS__, 'rest_get_hiscore' ), // Public leaderboard read, no authentication required. 'permission_callback' => '__return_true', 'args' => array( 'game' => array( 'required' => true, 'type' => 'string', ), ), ), array( 'methods' => 'POST', 'callback' => array( __CLASS__, 'rest_set_hiscore' ), // Open to guests by design (the 404 mini-games), so the gate is the // score nonce the 404 page prints, checked before the callback runs. 'permission_callback' => array( __CLASS__, 'hiscore_write_allowed' ), 'args' => array( 'game' => array( 'required' => true, 'type' => 'string', ), 'score' => array( 'required' => true, 'type' => 'integer', 'minimum' => 1, 'maximum' => 999999, ), 'name' => array( 'required' => true, 'type' => 'string', 'maxLength' => 30, ), ), ), ) ); } /** Returns the top-10 leaderboard for one game. */ public static function rest_get_hiscore( WP_REST_Request $request ): WP_REST_Response { $game = sanitize_key( $request->get_param( 'game' ) ); $raw = get_option( 'csdt_devtools_leaderboard_' . $game, '' ); $lb = $raw ? json_decode( $raw, true ) : array(); if ( ! is_array( $lb ) ) { $lb = array(); } return rest_ensure_response( array( 'leaderboard' => $lb ) ); } /** * Permission check for a score submission: the request must carry the score nonce. * * @param WP_REST_Request $request The request. * @return true|WP_Error */ public static function hiscore_write_allowed( WP_REST_Request $request ) { $nonce = $request->get_header( 'x_wp_score_nonce' ); if ( ! $nonce || ! wp_verify_nonce( sanitize_text_field( $nonce ), CSDT_DevTools::SCORE_NONCE_ACTION ) ) { return new WP_Error( 'forbidden', __( 'Invalid nonce.', 'cloudscale-cyber-devtools' ), array( 'status' => 403 ) ); } return true; } /** Inserts a score into the top-10 leaderboard for one game. The nonce is checked by hiscore_write_allowed(). */ public static function rest_set_hiscore( WP_REST_Request $request ) { $game = sanitize_key( $request->get_param( 'game' ) ); $score = (int) $request->get_param( 'score' ); $name = sanitize_text_field( $request->get_param( 'name' ) ); $score_caps = array( 'runner' => 999999, 'jetpack' => 999999, 'racer' => 999999, 'miner' => 2000, 'asteroids' => 999999, 'snake' => 9990, 'spaceinvaders' => 99990, 'mrdo' => 99990, ); if ( isset( $score_caps[ $game ] ) && $score > $score_caps[ $game ] ) { return new WP_Error( 'score_invalid', __( 'Score exceeds maximum for this game.', 'cloudscale-cyber-devtools' ), array( 'status' => 422 ) ); } // Rate limit: max 5 submissions per IP per game per 10 minutes. Use the // CF-validated client IP (not raw REMOTE_ADDR, which is the edge IP behind // Cloudflare and would collapse all visitors into one bucket). $ip = CSDT_DevTools::get_client_ip(); $ip_key = 'csdt_devtools_rl_' . md5( $ip . $game ); $count = (int) get_transient( $ip_key ); if ( $count >= 5 ) { return new WP_Error( 'rate_limited', __( 'Too many score submissions. Try again later.', 'cloudscale-cyber-devtools' ), array( 'status' => 429 ) ); } set_transient( $ip_key, $count + 1, 600 ); $raw = get_option( 'csdt_devtools_leaderboard_' . $game, '' ); $lb = $raw ? json_decode( $raw, true ) : array(); if ( ! is_array( $lb ) ) { $lb = array(); } foreach ( $lb as $entry ) { if ( (int) $entry['score'] === $score && $entry['name'] === $name ) { return rest_ensure_response( array( 'ok' => false, 'leaderboard' => $lb, ) ); } } $lowest = isset( $lb[9] ) ? (int) $lb[9]['score'] : 0; if ( count( $lb ) >= 10 && $score <= $lowest ) { return rest_ensure_response( array( 'ok' => false, 'leaderboard' => $lb, ) ); } $lb[] = array( 'score' => $score, 'name' => $name, ); usort( $lb, fn( $a, $b ) => (int) $b['score'] - (int) $a['score'] ); $lb = array_slice( $lb, 0, 10 ); update_option( 'csdt_devtools_leaderboard_' . $game, wp_json_encode( $lb ), false ); return rest_ensure_response( array( 'ok' => true, 'leaderboard' => $lb, ) ); } /** AJAX handler: saves the 404 enable toggle and colour scheme. */ public static function ajax_save_404_settings(): void { check_ajax_referer( 'csdt_devtools_404_settings', 'nonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'Forbidden.', 'cloudscale-cyber-devtools' ) ); } $custom_404 = isset( $_POST['custom_404'] ) ? ( absint( wp_unslash( $_POST['custom_404'] ) ) ? 1 : 0 ) : 0; update_option( CSDT_DevTools::CUSTOM_404_OPTION, $custom_404 ); if ( isset( $_POST['scheme'] ) ) { $schemes = self::get_404_schemes(); $scheme_key = sanitize_key( wp_unslash( $_POST['scheme'] ) ); if ( isset( $schemes[ $scheme_key ] ) ) { update_option( CSDT_DevTools::SCHEME_404_OPTION, $scheme_key ); } } wp_send_json_success( array( 'custom_404' => $custom_404, 'scheme' => get_option( CSDT_DevTools::SCHEME_404_OPTION, 'ocean' ), ) ); } /** Renders the 404 Games settings panel. */ public static function render_404_panel(): void { $current_scheme = get_option( CSDT_DevTools::SCHEME_404_OPTION, 'ocean' ); $enabled = (bool) get_option( CSDT_DevTools::CUSTOM_404_OPTION, 0 ); ?>
🎮 404 GAMES PAGE 'Enable', 'rec' => 'Toggle', 'desc' => 'When enabled, replaces the default WordPress 404 page with a fun interactive page featuring 8 mini-games: Runner, Jetpack, Racer, Miner, Asteroids, Snake, Digger and Alien Defence. No theme dependency, works even if the active theme is broken.', ), array( 'name' => 'Colour Scheme', 'rec' => 'Optional', 'desc' => 'Choose from 12 built-in colour palettes. Use Preview 404 to see the result, then click Save Scheme to apply it to your live 404 page, clicking a swatch alone does not save it.', ), ) ); ?>
$s ) : ?>
→