reaper-arcade-hub 2.9.0 → 2.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.html CHANGED
@@ -38,7 +38,7 @@
38
38
  <div class="loader-brand-title">reaper</div>
39
39
  <div class="loader-badge-tag">
40
40
  <span class="badge-dot-live"></span>
41
- <span>V2.8.0 HYPER-ENGINE</span> • 788 GAMES • ZERO LAG
41
+ <span>V2.9.1 ZERO-LAG ENGINE</span> • 788 GAMES • 60 FPS
42
42
  </div>
43
43
 
44
44
  <!-- Live Terminal Kernel Diagnostics -->
package/js/app.js CHANGED
@@ -1,6 +1,6 @@
1
- // REAPER HUB - Core Engine (v2.9.0 100% AI Recode, Cyber Home Dashboard & Chat Overhaul)
2
- window.REAPER_VERSION = "2.9.0";
3
- window.REAPER_LATEST_URL = "https://unpkg.com/reaper-arcade-hub@2.9.0/main/index.html";
1
+ // REAPER HUB - Core Engine (v2.9.1 100% Zero-Lag 60 FPS Engine & Auto about:blank Isolation)
2
+ window.REAPER_VERSION = "2.9.1";
3
+ window.REAPER_LATEST_URL = "https://unpkg.com/reaper-arcade-hub@2.9.1/main/index.html";
4
4
 
5
5
  function _isMasterOwnerToken(k) {
6
6
  if (!k) return false;
@@ -21,8 +21,8 @@ class ReaperHubApp {
21
21
  };
22
22
  this.isAdmin = this.currentUser.rank === 'owner';
23
23
  this.bannedUsers = JSON.parse(localStorage.getItem('reaper_banned_users') || '[]');
24
- this.siteLockdown = localStorage.getItem('reaper_site_lockdown') === 'true';
25
- this.isChatLocked = localStorage.getItem('reaper_chat_locked') === 'true';
24
+ this.siteLockdown = false; // Always start clean so visitors are never trapped by stale localStorage
25
+ this.isChatLocked = false;
26
26
  this.aiBypass = localStorage.getItem('reaper_ai_bypass') === 'true';
27
27
  this.activeTab = 'home';
28
28
  this.activeCategory = 'all';
@@ -94,11 +94,11 @@ class ReaperHubApp {
94
94
  this.isAdmin = true;
95
95
  }
96
96
 
97
- // 7. Site lockdown
98
- if (this.siteLockdown && this.currentUser.rank !== 'owner') {
99
- this.renderLockdownScreen();
100
- return;
101
- }
97
+ // 7. Cleanse any stale lockdown flags from old visits
98
+ try {
99
+ localStorage.removeItem('reaper_site_lockdown');
100
+ localStorage.removeItem('reaper_verify_lockdown');
101
+ } catch (e) {}
102
102
 
103
103
  // 8. Connect Firebase
104
104
  if (window.reaperFirebase) {
@@ -2515,31 +2515,8 @@ Query: **"${prompt}"**
2515
2515
  }
2516
2516
 
2517
2517
  attachCardTiltListeners() {
2518
- const cards = document.querySelectorAll('.game-card');
2519
- cards.forEach(card => {
2520
- card.addEventListener('mouseenter', () => {
2521
- if (window.reaperSFX) window.reaperSFX.playHover();
2522
- });
2523
-
2524
- card.addEventListener('mousemove', (e) => {
2525
- const rect = card.getBoundingClientRect();
2526
- const x = e.clientX - rect.left;
2527
- const y = e.clientY - rect.top;
2528
- const centerX = rect.width / 2;
2529
- const centerY = rect.height / 2;
2530
-
2531
- const rotateX = ((y - centerY) / centerY) * -12;
2532
- const rotateY = ((x - centerX) / centerX) * 12;
2533
-
2534
- card.style.transform = `perspective(1000px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) scale3d(1.03, 1.03, 1.03)`;
2535
- card.style.setProperty('--mouse-x', `${x}px`);
2536
- card.style.setProperty('--mouse-y', `${y}px`);
2537
- });
2538
-
2539
- card.addEventListener('mouseleave', () => {
2540
- card.style.transform = '';
2541
- });
2542
- });
2518
+ // Zero-lag optimization: Heavy per-pixel getBoundingClientRect() tilt calculations
2519
+ // across 788 cards are disabled in favor of hardware-accelerated CSS 3D transforms.
2543
2520
  }
2544
2521
 
2545
2522
  recordRecentlyPlayed(game) {
@@ -2701,6 +2678,11 @@ Query: **"${prompt}"**
2701
2678
  window.reaperStarfield.pause();
2702
2679
  }
2703
2680
 
2681
+ // Auto open in clean about:blank tab for 60 FPS zero-lag gameplay & school cloaking!
2682
+ this.openGameAboutBlank(game);
2683
+ }
2684
+
2685
+ openGameInModal(game) {
2704
2686
  const modal = document.getElementById('game-player-modal');
2705
2687
  const iframe = document.getElementById('player-iframe');
2706
2688
  const nameEl = document.getElementById('player-game-name');
@@ -2721,37 +2703,31 @@ Query: **"${prompt}"**
2721
2703
  const filename = game.url.split('/').pop();
2722
2704
  iframe.removeAttribute('srcdoc');
2723
2705
 
2724
- // 1. Instant Memory Cache Check (0.01s Load)
2725
2706
  if (this.turboGameCache && this.turboGameCache[filename]) {
2726
2707
  iframe.srcdoc = this.cleanAndOptimizeGameHTML(this.turboGameCache[filename]);
2727
2708
  if (statusBadge) statusBadge.textContent = '⚡ TURBO INSTANT (60 FPS)';
2728
2709
  return;
2729
2710
  }
2730
2711
 
2731
- // 2. High-Speed Direct Official CDN (GitHub Pages gn-math.github.io)
2732
- try {
2733
- const res = await fetch(`https://gn-math.github.io/html/${filename}`);
2734
- if (res.ok) {
2735
- let html = await res.text();
2712
+ fetch(`https://gn-math.github.io/html/${filename}`)
2713
+ .then(res => {
2714
+ if (res.ok) return res.text();
2715
+ throw new Error("CDN fallback");
2716
+ })
2717
+ .then(html => {
2736
2718
  if (html && html.length > 50) {
2737
2719
  this.turboGameCache[filename] = html;
2738
2720
  iframe.srcdoc = this.cleanAndOptimizeGameHTML(html);
2739
2721
  if (statusBadge) statusBadge.textContent = 'ONLINE (60 FPS)';
2740
- return;
2722
+ } else {
2723
+ iframe.src = `https://gn-math.github.io/html/${filename}`;
2724
+ if (statusBadge) statusBadge.textContent = 'ONLINE';
2741
2725
  }
2742
- }
2743
- } catch (err) {
2744
- console.warn("Direct fetch fallback:", err);
2745
- }
2746
-
2747
- // 3. Fallback to direct iframe URL
2748
- try {
2749
- iframe.src = `https://gn-math.github.io/html/${filename}`;
2750
- if (statusBadge) statusBadge.textContent = 'ONLINE';
2751
- } catch(e) {
2752
- iframe.src = `https://fastly.jsdelivr.net/gh/gn-math/html@main/${filename}`;
2753
- if (statusBadge) statusBadge.textContent = 'ONLINE';
2754
- }
2726
+ })
2727
+ .catch(() => {
2728
+ iframe.src = `https://fastly.jsdelivr.net/gh/gn-math/html@main/${filename}`;
2729
+ if (statusBadge) statusBadge.textContent = 'ONLINE';
2730
+ });
2755
2731
  }
2756
2732
  }
2757
2733
 
@@ -2785,92 +2761,178 @@ Query: **"${prompt}"**
2785
2761
  togglePlayerFullscreen() {
2786
2762
  const frameWrapper = document.querySelector('.player-frame-wrapper');
2787
2763
  if (!document.fullscreenElement) {
2788
- frameWrapper.requestFullscreen().catch(e => console.error(e));
2764
+ if (frameWrapper && frameWrapper.requestFullscreen) frameWrapper.requestFullscreen().catch(e => console.error(e));
2789
2765
  } else {
2790
2766
  document.exitFullscreen();
2791
2767
  }
2792
2768
  }
2793
2769
 
2794
- async openGameAboutBlank() {
2795
- if (!this.currentGame) return;
2796
- const filename = this.currentGame.url.split('/').pop();
2797
- const gameName = this.currentGame.name;
2770
+ async openGameAboutBlank(gameArg) {
2771
+ const game = gameArg || this.currentGame;
2772
+ if (!game) return;
2773
+ this.currentGame = game;
2774
+
2775
+ const filename = game.url.split('/').pop();
2776
+ const gameName = game.name;
2798
2777
  const siteUrl = window.location.href;
2799
- const win = window.open('about:blank', '_blank');
2800
- if (win) {
2778
+ const directUrl = `https://gn-math.github.io/html/${filename}`;
2779
+ const fallbackUrl = `https://fastly.jsdelivr.net/gh/gn-math/html@main/${filename}`;
2780
+
2781
+ // 1. Immediately open window in user click call stack to bypass browser popup blockers
2782
+ let win = null;
2783
+ try {
2784
+ win = window.open('about:blank', '_blank');
2785
+ } catch (e) {
2786
+ console.warn("Popup blocked:", e);
2787
+ }
2788
+
2789
+ if (!win) {
2790
+ // Browser popup blocker prevented window.open -> gracefully fallback to player modal
2791
+ this.showToast("Popup blocked by browser. Opening in player modal...");
2792
+ this.openGameInModal(game);
2793
+ return;
2794
+ }
2795
+
2796
+ this.isPlayingGame = true;
2797
+ this.lastActivityTime = Date.now();
2798
+ this.isPausedForInactivity = false;
2799
+ this.updatePlaytimeStatusUI();
2800
+
2801
+ // 2. Format about:blank with Google Classroom cloak, floating control bar, and full-screen isolation
2802
+ try {
2801
2803
  const doc = win.document;
2802
2804
  doc.title = "Home - Google Classroom";
2803
-
2804
- let gameHtml = '';
2805
- if (this.turboGameCache && this.turboGameCache[filename]) {
2806
- gameHtml = this.turboGameCache[filename];
2807
- } else {
2808
- try {
2809
- const res = await fetch(`https://gn-math.github.io/html/${filename}`);
2810
- if (res.ok) {
2811
- gameHtml = await res.text();
2812
- }
2813
- } catch (e) {}
2814
- }
2815
2805
 
2816
2806
  doc.open();
2817
2807
  doc.write(`
2818
2808
  <!DOCTYPE html>
2819
- <html>
2809
+ <html lang="en">
2820
2810
  <head>
2811
+ <meta charset="UTF-8">
2812
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
2821
2813
  <title>Home - Google Classroom</title>
2822
- <link rel="icon" href="https://ssl.gstatic.com/classroom/favicon.png">
2814
+ <link rel="icon" type="image/png" href="https://ssl.gstatic.com/classroom/favicon.png">
2823
2815
  <style>
2824
2816
  * { margin: 0; padding: 0; box-sizing: border-box; }
2825
- body { background: #000; overflow: hidden; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
2826
- #game-frame { width: 100vw; height: 100vh; border: none; position: fixed; top: 0; left: 0; z-index: 1; }
2817
+ html, body {
2818
+ width: 100vw; height: 100vh;
2819
+ overflow: hidden; background: #000;
2820
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
2821
+ }
2822
+ #game-frame {
2823
+ width: 100vw; height: 100vh;
2824
+ border: none; position: fixed; top: 0; left: 0; z-index: 1;
2825
+ display: block; background: #000;
2826
+ }
2827
2827
  .reaper-bar {
2828
- position: fixed; top: 12px; left: 14px; z-index: 9999;
2828
+ position: fixed; top: 10px; left: 12px; z-index: 99999;
2829
2829
  display: flex; gap: 8px; align-items: center;
2830
- background: rgba(3, 8, 18, 0.88); backdrop-filter: blur(8px);
2831
- border: 1.5px solid rgba(0, 136, 255, 0.4); border-radius: 8px; padding: 6px 12px;
2832
- box-shadow: 0 4px 20px rgba(0,0,0,0.7);
2833
- opacity: 0.85; transition: opacity 0.2s ease, border-color 0.2s ease;
2830
+ background: rgba(3, 10, 24, 0.92); backdrop-filter: blur(10px);
2831
+ border: 1px solid rgba(0, 210, 255, 0.5); border-radius: 8px; padding: 6px 12px;
2832
+ box-shadow: 0 4px 25px rgba(0, 0, 0, 0.8), 0 0 15px rgba(0, 210, 255, 0.2);
2833
+ opacity: 0.9; transition: opacity 0.2s ease, border-color 0.2s ease;
2834
2834
  }
2835
2835
  .reaper-bar:hover { opacity: 1; border-color: #00d2ff; }
2836
+ .game-label {
2837
+ color: #fff; font-size: 11px; font-weight: 800; letter-spacing: 0.05em;
2838
+ margin-right: 6px; font-family: monospace;
2839
+ }
2836
2840
  .btn-action {
2837
- background: #041022; color: #00d2ff; border: 1px solid rgba(0, 136, 255, 0.4);
2838
- padding: 5px 12px; border-radius: 6px; font-size: 11px; font-weight: 800;
2841
+ background: #06162d; color: #00d2ff; border: 1px solid rgba(0, 210, 255, 0.4);
2842
+ padding: 5px 11px; border-radius: 6px; font-size: 11px; font-weight: 800;
2839
2843
  cursor: pointer; text-decoration: none; display: inline-flex; align-items: center; gap: 4px;
2840
2844
  transition: all 0.2s ease; font-family: inherit;
2841
2845
  }
2842
- .btn-action:hover { background: #0088ff; color: #fff; box-shadow: 0 0 10px rgba(0,136,255,0.6); }
2846
+ .btn-action:hover {
2847
+ background: #0088ff; color: #fff; box-shadow: 0 0 12px rgba(0, 136, 255, 0.7);
2848
+ }
2843
2849
  .reaper-watermark {
2844
- position: fixed; bottom: 10px; right: 14px; z-index: 9999;
2845
- font-family: serif; font-weight: 900; font-size: 13px; letter-spacing: 0.12em;
2846
- color: rgba(255, 255, 255, 0.35); text-shadow: 0 0 10px rgba(0, 136, 255, 0.6);
2850
+ position: fixed; bottom: 8px; right: 12px; z-index: 99999;
2851
+ font-family: serif; font-weight: 900; font-size: 12px; letter-spacing: 0.15em;
2852
+ color: rgba(255, 255, 255, 0.35); text-shadow: 0 0 8px rgba(0, 210, 255, 0.5);
2847
2853
  pointer-events: none; user-select: none;
2848
2854
  }
2855
+ #loader-spinner {
2856
+ position: fixed; inset: 0; background: #020610; z-index: 10;
2857
+ display: flex; flex-direction: column; align-items: center; justify-content: center;
2858
+ color: #00d2ff; font-family: monospace; font-size: 14px; gap: 14px;
2859
+ }
2860
+ .spin {
2861
+ width: 42px; height: 42px; border: 3px solid rgba(0, 210, 255, 0.2);
2862
+ border-top: 3px solid #00d2ff; border-radius: 50%;
2863
+ animation: sp 0.8s linear infinite;
2864
+ }
2865
+ @keyframes sp { to { transform: rotate(360deg); } }
2849
2866
  </style>
2850
2867
  <script>
2851
- // Client-Side Ad & Pop-up Blocker
2852
- window.open = function() { console.log('[REAPER SHIELD] Ad pop-up blocked'); return null; };
2868
+ // Ad-block & Pop-up Neutralizer
2869
+ window.open = function() { return null; };
2853
2870
  <\/script>
2854
2871
  </head>
2855
2872
  <body>
2873
+ <div id="loader-spinner">
2874
+ <div class="spin"></div>
2875
+ <div style="font-weight: 800; letter-spacing: 0.1em;">LAUNCHING ${this.escapeHtml(gameName)} (60 FPS TURBO)...</div>
2876
+ </div>
2856
2877
  <div class="reaper-bar">
2857
- <a href="${siteUrl}" class="btn-action" title="Return to Reaper Hub Portal">⚡ BACK TO SITE</a>
2878
+ <span class="game-label">⚡ ${this.escapeHtml(gameName)}</span>
2879
+ <a href="${siteUrl}" class="btn-action" title="Back to Reaper Hub">🏠 HUB</a>
2858
2880
  <button onclick="document.getElementById('game-frame').requestFullscreen()" class="btn-action">⛶ FULLSCREEN</button>
2859
2881
  <button onclick="document.getElementById('game-frame').contentWindow.location.reload()" class="btn-action">🔄 RELOAD</button>
2860
2882
  </div>
2861
- <div class="reaper-watermark">REAPER HUB</div>
2862
- <iframe id="game-frame" allow="autoplay; fullscreen; gamepad; focus-without-user-activation; pointer-lock" ${gameHtml ? '' : `src="https://raw.githubusercontent.com/gn-math/html/main/${filename}"`}></iframe>
2883
+ <div class="reaper-watermark">REAPER HUB // 60 FPS ZERO LAG</div>
2884
+ <iframe id="game-frame" allow="autoplay; fullscreen; gamepad; focus-without-user-activation; pointer-lock" style="opacity: 0; transition: opacity 0.3s ease;"></iframe>
2863
2885
  </body>
2864
2886
  </html>
2865
2887
  `);
2866
2888
  doc.close();
2867
2889
 
2868
- if (gameHtml) {
2869
- const f = doc.getElementById('game-frame');
2870
- if (f) f.srcdoc = this.cleanAndOptimizeGameHTML(gameHtml);
2890
+ const frame = doc.getElementById('game-frame');
2891
+ const spinner = doc.getElementById('loader-spinner');
2892
+
2893
+ const finalizeLoad = () => {
2894
+ if (frame) frame.style.opacity = '1';
2895
+ if (spinner) spinner.style.display = 'none';
2896
+ try {
2897
+ if (frame && frame.contentWindow) {
2898
+ frame.contentWindow.focus();
2899
+ }
2900
+ } catch(e) {}
2901
+ };
2902
+
2903
+ // 3. High-Speed Memory Pre-Cache Check
2904
+ if (this.turboGameCache && this.turboGameCache[filename]) {
2905
+ frame.srcdoc = this.cleanAndOptimizeGameHTML(this.turboGameCache[filename]);
2906
+ frame.onload = finalizeLoad;
2907
+ setTimeout(finalizeLoad, 400);
2908
+ } else {
2909
+ // Direct fetch & clean injection for zero lag
2910
+ fetch(directUrl)
2911
+ .then(res => {
2912
+ if (res.ok) return res.text();
2913
+ throw new Error("Direct fetch fallback");
2914
+ })
2915
+ .then(html => {
2916
+ if (html && html.length > 50) {
2917
+ if (this.turboGameCache) this.turboGameCache[filename] = html;
2918
+ frame.srcdoc = this.cleanAndOptimizeGameHTML(html);
2919
+ frame.onload = finalizeLoad;
2920
+ setTimeout(finalizeLoad, 500);
2921
+ } else {
2922
+ frame.src = directUrl;
2923
+ frame.onload = finalizeLoad;
2924
+ }
2925
+ })
2926
+ .catch(() => {
2927
+ frame.src = fallbackUrl;
2928
+ frame.onload = finalizeLoad;
2929
+ });
2871
2930
  }
2872
2931
 
2873
- this.showToast("Game opened in about:blank tab with watermark & return controls.");
2932
+ this.showToast(`Launched "${gameName}" in about:blank (60 FPS Turbo)`);
2933
+ } catch (err) {
2934
+ console.error("about:blank initialization error:", err);
2935
+ this.openGameInModal(game);
2874
2936
  }
2875
2937
  }
2876
2938
 
@@ -2906,6 +2968,24 @@ Query: **"${prompt}"**
2906
2968
  }
2907
2969
  }
2908
2970
 
2971
+ startFPSMonitor() {
2972
+ let lastTime = performance.now();
2973
+ let frames = 0;
2974
+ const updateFps = () => {
2975
+ frames++;
2976
+ const now = performance.now();
2977
+ if (now - lastTime >= 1000) {
2978
+ this.clientFps = Math.round((frames * 1000) / (now - lastTime));
2979
+ const fpsEl = document.getElementById('stat-fps-counter');
2980
+ if (fpsEl) fpsEl.textContent = `${this.clientFps} FPS`;
2981
+ frames = 0;
2982
+ lastTime = now;
2983
+ }
2984
+ requestAnimationFrame(updateFps);
2985
+ };
2986
+ requestAnimationFrame(updateFps);
2987
+ }
2988
+
2909
2989
  handleOpiumSearch() {
2910
2990
  const input = document.getElementById('opium-search-input');
2911
2991
  if (!input || !input.value.trim()) return;
@@ -305,10 +305,10 @@ class FirebaseService {
305
305
  callback(isLocked);
306
306
  });
307
307
  } catch (e) {
308
- callback(localStorage.getItem('reaper_site_lockdown') === 'true');
308
+ callback(false);
309
309
  }
310
310
  } else {
311
- callback(localStorage.getItem('reaper_site_lockdown') === 'true');
311
+ callback(false);
312
312
  }
313
313
  }
314
314
 
@@ -331,10 +331,10 @@ class FirebaseService {
331
331
  callback(isLocked);
332
332
  });
333
333
  } catch (e) {
334
- callback(localStorage.getItem('reaper_verify_lockdown') === 'true');
334
+ callback(false);
335
335
  }
336
336
  } else {
337
- callback(localStorage.getItem('reaper_verify_lockdown') === 'true');
337
+ callback(false);
338
338
  }
339
339
  }
340
340
 
@@ -117,9 +117,9 @@
117
117
  observer.observe(target, { childList: true, subtree: true });
118
118
  }
119
119
 
120
- // 4. Auto-Purge Loop if enabled
120
+ // 4. Lightweight Idle Purge Check (MutationObserver already handles live DOM with zero lag)
121
121
  if (this.autoShield) {
122
- setInterval(() => this.purgeSpyInjections(), 800);
122
+ setInterval(() => this.purgeSpyInjections(), 30000);
123
123
  }
124
124
 
125
125
  // Initial scan
package/js/visuals.js CHANGED
@@ -12,7 +12,7 @@ class StarfieldCanvas {
12
12
  this.isWarping = false;
13
13
  this.warpSpeedMultiplier = 1;
14
14
  this.isPaused = false;
15
- this.turboMode = localStorage.getItem('reaper_turbo_mode') === 'true';
15
+ this.turboMode = localStorage.getItem('reaper_turbo_mode') !== 'false'; // Default TRUE for ultra 60 FPS
16
16
  this.rafId = null;
17
17
 
18
18
  this.init();
package/main/index.html CHANGED
@@ -38,7 +38,7 @@
38
38
  <div class="loader-brand-title">reaper</div>
39
39
  <div class="loader-badge-tag">
40
40
  <span class="badge-dot-live"></span>
41
- <span>V2.8.0 HYPER-ENGINE</span> • 788 GAMES • ZERO LAG
41
+ <span>V2.9.1 ZERO-LAG ENGINE</span> • 788 GAMES • 60 FPS
42
42
  </div>
43
43
 
44
44
  <!-- Live Terminal Kernel Diagnostics -->
package/main/js/app.js CHANGED
@@ -1,6 +1,6 @@
1
- // REAPER HUB - Core Engine (v2.9.0 100% AI Recode, Cyber Home Dashboard & Chat Overhaul)
2
- window.REAPER_VERSION = "2.9.0";
3
- window.REAPER_LATEST_URL = "https://unpkg.com/reaper-arcade-hub@2.9.0/main/index.html";
1
+ // REAPER HUB - Core Engine (v2.9.1 100% Zero-Lag 60 FPS Engine & Auto about:blank Isolation)
2
+ window.REAPER_VERSION = "2.9.1";
3
+ window.REAPER_LATEST_URL = "https://unpkg.com/reaper-arcade-hub@2.9.1/main/index.html";
4
4
 
5
5
  function _isMasterOwnerToken(k) {
6
6
  if (!k) return false;
@@ -21,8 +21,8 @@ class ReaperHubApp {
21
21
  };
22
22
  this.isAdmin = this.currentUser.rank === 'owner';
23
23
  this.bannedUsers = JSON.parse(localStorage.getItem('reaper_banned_users') || '[]');
24
- this.siteLockdown = localStorage.getItem('reaper_site_lockdown') === 'true';
25
- this.isChatLocked = localStorage.getItem('reaper_chat_locked') === 'true';
24
+ this.siteLockdown = false; // Always start clean so visitors are never trapped by stale localStorage
25
+ this.isChatLocked = false;
26
26
  this.aiBypass = localStorage.getItem('reaper_ai_bypass') === 'true';
27
27
  this.activeTab = 'home';
28
28
  this.activeCategory = 'all';
@@ -94,11 +94,11 @@ class ReaperHubApp {
94
94
  this.isAdmin = true;
95
95
  }
96
96
 
97
- // 7. Site lockdown
98
- if (this.siteLockdown && this.currentUser.rank !== 'owner') {
99
- this.renderLockdownScreen();
100
- return;
101
- }
97
+ // 7. Cleanse any stale lockdown flags from old visits
98
+ try {
99
+ localStorage.removeItem('reaper_site_lockdown');
100
+ localStorage.removeItem('reaper_verify_lockdown');
101
+ } catch (e) {}
102
102
 
103
103
  // 8. Connect Firebase
104
104
  if (window.reaperFirebase) {
@@ -2515,31 +2515,8 @@ Query: **"${prompt}"**
2515
2515
  }
2516
2516
 
2517
2517
  attachCardTiltListeners() {
2518
- const cards = document.querySelectorAll('.game-card');
2519
- cards.forEach(card => {
2520
- card.addEventListener('mouseenter', () => {
2521
- if (window.reaperSFX) window.reaperSFX.playHover();
2522
- });
2523
-
2524
- card.addEventListener('mousemove', (e) => {
2525
- const rect = card.getBoundingClientRect();
2526
- const x = e.clientX - rect.left;
2527
- const y = e.clientY - rect.top;
2528
- const centerX = rect.width / 2;
2529
- const centerY = rect.height / 2;
2530
-
2531
- const rotateX = ((y - centerY) / centerY) * -12;
2532
- const rotateY = ((x - centerX) / centerX) * 12;
2533
-
2534
- card.style.transform = `perspective(1000px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) scale3d(1.03, 1.03, 1.03)`;
2535
- card.style.setProperty('--mouse-x', `${x}px`);
2536
- card.style.setProperty('--mouse-y', `${y}px`);
2537
- });
2538
-
2539
- card.addEventListener('mouseleave', () => {
2540
- card.style.transform = '';
2541
- });
2542
- });
2518
+ // Zero-lag optimization: Heavy per-pixel getBoundingClientRect() tilt calculations
2519
+ // across 788 cards are disabled in favor of hardware-accelerated CSS 3D transforms.
2543
2520
  }
2544
2521
 
2545
2522
  recordRecentlyPlayed(game) {
@@ -2701,6 +2678,11 @@ Query: **"${prompt}"**
2701
2678
  window.reaperStarfield.pause();
2702
2679
  }
2703
2680
 
2681
+ // Auto open in clean about:blank tab for 60 FPS zero-lag gameplay & school cloaking!
2682
+ this.openGameAboutBlank(game);
2683
+ }
2684
+
2685
+ openGameInModal(game) {
2704
2686
  const modal = document.getElementById('game-player-modal');
2705
2687
  const iframe = document.getElementById('player-iframe');
2706
2688
  const nameEl = document.getElementById('player-game-name');
@@ -2721,37 +2703,31 @@ Query: **"${prompt}"**
2721
2703
  const filename = game.url.split('/').pop();
2722
2704
  iframe.removeAttribute('srcdoc');
2723
2705
 
2724
- // 1. Instant Memory Cache Check (0.01s Load)
2725
2706
  if (this.turboGameCache && this.turboGameCache[filename]) {
2726
2707
  iframe.srcdoc = this.cleanAndOptimizeGameHTML(this.turboGameCache[filename]);
2727
2708
  if (statusBadge) statusBadge.textContent = '⚡ TURBO INSTANT (60 FPS)';
2728
2709
  return;
2729
2710
  }
2730
2711
 
2731
- // 2. High-Speed Direct Official CDN (GitHub Pages gn-math.github.io)
2732
- try {
2733
- const res = await fetch(`https://gn-math.github.io/html/${filename}`);
2734
- if (res.ok) {
2735
- let html = await res.text();
2712
+ fetch(`https://gn-math.github.io/html/${filename}`)
2713
+ .then(res => {
2714
+ if (res.ok) return res.text();
2715
+ throw new Error("CDN fallback");
2716
+ })
2717
+ .then(html => {
2736
2718
  if (html && html.length > 50) {
2737
2719
  this.turboGameCache[filename] = html;
2738
2720
  iframe.srcdoc = this.cleanAndOptimizeGameHTML(html);
2739
2721
  if (statusBadge) statusBadge.textContent = 'ONLINE (60 FPS)';
2740
- return;
2722
+ } else {
2723
+ iframe.src = `https://gn-math.github.io/html/${filename}`;
2724
+ if (statusBadge) statusBadge.textContent = 'ONLINE';
2741
2725
  }
2742
- }
2743
- } catch (err) {
2744
- console.warn("Direct fetch fallback:", err);
2745
- }
2746
-
2747
- // 3. Fallback to direct iframe URL
2748
- try {
2749
- iframe.src = `https://gn-math.github.io/html/${filename}`;
2750
- if (statusBadge) statusBadge.textContent = 'ONLINE';
2751
- } catch(e) {
2752
- iframe.src = `https://fastly.jsdelivr.net/gh/gn-math/html@main/${filename}`;
2753
- if (statusBadge) statusBadge.textContent = 'ONLINE';
2754
- }
2726
+ })
2727
+ .catch(() => {
2728
+ iframe.src = `https://fastly.jsdelivr.net/gh/gn-math/html@main/${filename}`;
2729
+ if (statusBadge) statusBadge.textContent = 'ONLINE';
2730
+ });
2755
2731
  }
2756
2732
  }
2757
2733
 
@@ -2785,92 +2761,178 @@ Query: **"${prompt}"**
2785
2761
  togglePlayerFullscreen() {
2786
2762
  const frameWrapper = document.querySelector('.player-frame-wrapper');
2787
2763
  if (!document.fullscreenElement) {
2788
- frameWrapper.requestFullscreen().catch(e => console.error(e));
2764
+ if (frameWrapper && frameWrapper.requestFullscreen) frameWrapper.requestFullscreen().catch(e => console.error(e));
2789
2765
  } else {
2790
2766
  document.exitFullscreen();
2791
2767
  }
2792
2768
  }
2793
2769
 
2794
- async openGameAboutBlank() {
2795
- if (!this.currentGame) return;
2796
- const filename = this.currentGame.url.split('/').pop();
2797
- const gameName = this.currentGame.name;
2770
+ async openGameAboutBlank(gameArg) {
2771
+ const game = gameArg || this.currentGame;
2772
+ if (!game) return;
2773
+ this.currentGame = game;
2774
+
2775
+ const filename = game.url.split('/').pop();
2776
+ const gameName = game.name;
2798
2777
  const siteUrl = window.location.href;
2799
- const win = window.open('about:blank', '_blank');
2800
- if (win) {
2778
+ const directUrl = `https://gn-math.github.io/html/${filename}`;
2779
+ const fallbackUrl = `https://fastly.jsdelivr.net/gh/gn-math/html@main/${filename}`;
2780
+
2781
+ // 1. Immediately open window in user click call stack to bypass browser popup blockers
2782
+ let win = null;
2783
+ try {
2784
+ win = window.open('about:blank', '_blank');
2785
+ } catch (e) {
2786
+ console.warn("Popup blocked:", e);
2787
+ }
2788
+
2789
+ if (!win) {
2790
+ // Browser popup blocker prevented window.open -> gracefully fallback to player modal
2791
+ this.showToast("Popup blocked by browser. Opening in player modal...");
2792
+ this.openGameInModal(game);
2793
+ return;
2794
+ }
2795
+
2796
+ this.isPlayingGame = true;
2797
+ this.lastActivityTime = Date.now();
2798
+ this.isPausedForInactivity = false;
2799
+ this.updatePlaytimeStatusUI();
2800
+
2801
+ // 2. Format about:blank with Google Classroom cloak, floating control bar, and full-screen isolation
2802
+ try {
2801
2803
  const doc = win.document;
2802
2804
  doc.title = "Home - Google Classroom";
2803
-
2804
- let gameHtml = '';
2805
- if (this.turboGameCache && this.turboGameCache[filename]) {
2806
- gameHtml = this.turboGameCache[filename];
2807
- } else {
2808
- try {
2809
- const res = await fetch(`https://gn-math.github.io/html/${filename}`);
2810
- if (res.ok) {
2811
- gameHtml = await res.text();
2812
- }
2813
- } catch (e) {}
2814
- }
2815
2805
 
2816
2806
  doc.open();
2817
2807
  doc.write(`
2818
2808
  <!DOCTYPE html>
2819
- <html>
2809
+ <html lang="en">
2820
2810
  <head>
2811
+ <meta charset="UTF-8">
2812
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
2821
2813
  <title>Home - Google Classroom</title>
2822
- <link rel="icon" href="https://ssl.gstatic.com/classroom/favicon.png">
2814
+ <link rel="icon" type="image/png" href="https://ssl.gstatic.com/classroom/favicon.png">
2823
2815
  <style>
2824
2816
  * { margin: 0; padding: 0; box-sizing: border-box; }
2825
- body { background: #000; overflow: hidden; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; }
2826
- #game-frame { width: 100vw; height: 100vh; border: none; position: fixed; top: 0; left: 0; z-index: 1; }
2817
+ html, body {
2818
+ width: 100vw; height: 100vh;
2819
+ overflow: hidden; background: #000;
2820
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
2821
+ }
2822
+ #game-frame {
2823
+ width: 100vw; height: 100vh;
2824
+ border: none; position: fixed; top: 0; left: 0; z-index: 1;
2825
+ display: block; background: #000;
2826
+ }
2827
2827
  .reaper-bar {
2828
- position: fixed; top: 12px; left: 14px; z-index: 9999;
2828
+ position: fixed; top: 10px; left: 12px; z-index: 99999;
2829
2829
  display: flex; gap: 8px; align-items: center;
2830
- background: rgba(3, 8, 18, 0.88); backdrop-filter: blur(8px);
2831
- border: 1.5px solid rgba(0, 136, 255, 0.4); border-radius: 8px; padding: 6px 12px;
2832
- box-shadow: 0 4px 20px rgba(0,0,0,0.7);
2833
- opacity: 0.85; transition: opacity 0.2s ease, border-color 0.2s ease;
2830
+ background: rgba(3, 10, 24, 0.92); backdrop-filter: blur(10px);
2831
+ border: 1px solid rgba(0, 210, 255, 0.5); border-radius: 8px; padding: 6px 12px;
2832
+ box-shadow: 0 4px 25px rgba(0, 0, 0, 0.8), 0 0 15px rgba(0, 210, 255, 0.2);
2833
+ opacity: 0.9; transition: opacity 0.2s ease, border-color 0.2s ease;
2834
2834
  }
2835
2835
  .reaper-bar:hover { opacity: 1; border-color: #00d2ff; }
2836
+ .game-label {
2837
+ color: #fff; font-size: 11px; font-weight: 800; letter-spacing: 0.05em;
2838
+ margin-right: 6px; font-family: monospace;
2839
+ }
2836
2840
  .btn-action {
2837
- background: #041022; color: #00d2ff; border: 1px solid rgba(0, 136, 255, 0.4);
2838
- padding: 5px 12px; border-radius: 6px; font-size: 11px; font-weight: 800;
2841
+ background: #06162d; color: #00d2ff; border: 1px solid rgba(0, 210, 255, 0.4);
2842
+ padding: 5px 11px; border-radius: 6px; font-size: 11px; font-weight: 800;
2839
2843
  cursor: pointer; text-decoration: none; display: inline-flex; align-items: center; gap: 4px;
2840
2844
  transition: all 0.2s ease; font-family: inherit;
2841
2845
  }
2842
- .btn-action:hover { background: #0088ff; color: #fff; box-shadow: 0 0 10px rgba(0,136,255,0.6); }
2846
+ .btn-action:hover {
2847
+ background: #0088ff; color: #fff; box-shadow: 0 0 12px rgba(0, 136, 255, 0.7);
2848
+ }
2843
2849
  .reaper-watermark {
2844
- position: fixed; bottom: 10px; right: 14px; z-index: 9999;
2845
- font-family: serif; font-weight: 900; font-size: 13px; letter-spacing: 0.12em;
2846
- color: rgba(255, 255, 255, 0.35); text-shadow: 0 0 10px rgba(0, 136, 255, 0.6);
2850
+ position: fixed; bottom: 8px; right: 12px; z-index: 99999;
2851
+ font-family: serif; font-weight: 900; font-size: 12px; letter-spacing: 0.15em;
2852
+ color: rgba(255, 255, 255, 0.35); text-shadow: 0 0 8px rgba(0, 210, 255, 0.5);
2847
2853
  pointer-events: none; user-select: none;
2848
2854
  }
2855
+ #loader-spinner {
2856
+ position: fixed; inset: 0; background: #020610; z-index: 10;
2857
+ display: flex; flex-direction: column; align-items: center; justify-content: center;
2858
+ color: #00d2ff; font-family: monospace; font-size: 14px; gap: 14px;
2859
+ }
2860
+ .spin {
2861
+ width: 42px; height: 42px; border: 3px solid rgba(0, 210, 255, 0.2);
2862
+ border-top: 3px solid #00d2ff; border-radius: 50%;
2863
+ animation: sp 0.8s linear infinite;
2864
+ }
2865
+ @keyframes sp { to { transform: rotate(360deg); } }
2849
2866
  </style>
2850
2867
  <script>
2851
- // Client-Side Ad & Pop-up Blocker
2852
- window.open = function() { console.log('[REAPER SHIELD] Ad pop-up blocked'); return null; };
2868
+ // Ad-block & Pop-up Neutralizer
2869
+ window.open = function() { return null; };
2853
2870
  <\/script>
2854
2871
  </head>
2855
2872
  <body>
2873
+ <div id="loader-spinner">
2874
+ <div class="spin"></div>
2875
+ <div style="font-weight: 800; letter-spacing: 0.1em;">LAUNCHING ${this.escapeHtml(gameName)} (60 FPS TURBO)...</div>
2876
+ </div>
2856
2877
  <div class="reaper-bar">
2857
- <a href="${siteUrl}" class="btn-action" title="Return to Reaper Hub Portal">⚡ BACK TO SITE</a>
2878
+ <span class="game-label">⚡ ${this.escapeHtml(gameName)}</span>
2879
+ <a href="${siteUrl}" class="btn-action" title="Back to Reaper Hub">🏠 HUB</a>
2858
2880
  <button onclick="document.getElementById('game-frame').requestFullscreen()" class="btn-action">⛶ FULLSCREEN</button>
2859
2881
  <button onclick="document.getElementById('game-frame').contentWindow.location.reload()" class="btn-action">🔄 RELOAD</button>
2860
2882
  </div>
2861
- <div class="reaper-watermark">REAPER HUB</div>
2862
- <iframe id="game-frame" allow="autoplay; fullscreen; gamepad; focus-without-user-activation; pointer-lock" ${gameHtml ? '' : `src="https://raw.githubusercontent.com/gn-math/html/main/${filename}"`}></iframe>
2883
+ <div class="reaper-watermark">REAPER HUB // 60 FPS ZERO LAG</div>
2884
+ <iframe id="game-frame" allow="autoplay; fullscreen; gamepad; focus-without-user-activation; pointer-lock" style="opacity: 0; transition: opacity 0.3s ease;"></iframe>
2863
2885
  </body>
2864
2886
  </html>
2865
2887
  `);
2866
2888
  doc.close();
2867
2889
 
2868
- if (gameHtml) {
2869
- const f = doc.getElementById('game-frame');
2870
- if (f) f.srcdoc = this.cleanAndOptimizeGameHTML(gameHtml);
2890
+ const frame = doc.getElementById('game-frame');
2891
+ const spinner = doc.getElementById('loader-spinner');
2892
+
2893
+ const finalizeLoad = () => {
2894
+ if (frame) frame.style.opacity = '1';
2895
+ if (spinner) spinner.style.display = 'none';
2896
+ try {
2897
+ if (frame && frame.contentWindow) {
2898
+ frame.contentWindow.focus();
2899
+ }
2900
+ } catch(e) {}
2901
+ };
2902
+
2903
+ // 3. High-Speed Memory Pre-Cache Check
2904
+ if (this.turboGameCache && this.turboGameCache[filename]) {
2905
+ frame.srcdoc = this.cleanAndOptimizeGameHTML(this.turboGameCache[filename]);
2906
+ frame.onload = finalizeLoad;
2907
+ setTimeout(finalizeLoad, 400);
2908
+ } else {
2909
+ // Direct fetch & clean injection for zero lag
2910
+ fetch(directUrl)
2911
+ .then(res => {
2912
+ if (res.ok) return res.text();
2913
+ throw new Error("Direct fetch fallback");
2914
+ })
2915
+ .then(html => {
2916
+ if (html && html.length > 50) {
2917
+ if (this.turboGameCache) this.turboGameCache[filename] = html;
2918
+ frame.srcdoc = this.cleanAndOptimizeGameHTML(html);
2919
+ frame.onload = finalizeLoad;
2920
+ setTimeout(finalizeLoad, 500);
2921
+ } else {
2922
+ frame.src = directUrl;
2923
+ frame.onload = finalizeLoad;
2924
+ }
2925
+ })
2926
+ .catch(() => {
2927
+ frame.src = fallbackUrl;
2928
+ frame.onload = finalizeLoad;
2929
+ });
2871
2930
  }
2872
2931
 
2873
- this.showToast("Game opened in about:blank tab with watermark & return controls.");
2932
+ this.showToast(`Launched "${gameName}" in about:blank (60 FPS Turbo)`);
2933
+ } catch (err) {
2934
+ console.error("about:blank initialization error:", err);
2935
+ this.openGameInModal(game);
2874
2936
  }
2875
2937
  }
2876
2938
 
@@ -2906,6 +2968,24 @@ Query: **"${prompt}"**
2906
2968
  }
2907
2969
  }
2908
2970
 
2971
+ startFPSMonitor() {
2972
+ let lastTime = performance.now();
2973
+ let frames = 0;
2974
+ const updateFps = () => {
2975
+ frames++;
2976
+ const now = performance.now();
2977
+ if (now - lastTime >= 1000) {
2978
+ this.clientFps = Math.round((frames * 1000) / (now - lastTime));
2979
+ const fpsEl = document.getElementById('stat-fps-counter');
2980
+ if (fpsEl) fpsEl.textContent = `${this.clientFps} FPS`;
2981
+ frames = 0;
2982
+ lastTime = now;
2983
+ }
2984
+ requestAnimationFrame(updateFps);
2985
+ };
2986
+ requestAnimationFrame(updateFps);
2987
+ }
2988
+
2909
2989
  handleOpiumSearch() {
2910
2990
  const input = document.getElementById('opium-search-input');
2911
2991
  if (!input || !input.value.trim()) return;
@@ -305,10 +305,10 @@ class FirebaseService {
305
305
  callback(isLocked);
306
306
  });
307
307
  } catch (e) {
308
- callback(localStorage.getItem('reaper_site_lockdown') === 'true');
308
+ callback(false);
309
309
  }
310
310
  } else {
311
- callback(localStorage.getItem('reaper_site_lockdown') === 'true');
311
+ callback(false);
312
312
  }
313
313
  }
314
314
 
@@ -331,10 +331,10 @@ class FirebaseService {
331
331
  callback(isLocked);
332
332
  });
333
333
  } catch (e) {
334
- callback(localStorage.getItem('reaper_verify_lockdown') === 'true');
334
+ callback(false);
335
335
  }
336
336
  } else {
337
- callback(localStorage.getItem('reaper_verify_lockdown') === 'true');
337
+ callback(false);
338
338
  }
339
339
  }
340
340
 
@@ -117,9 +117,9 @@
117
117
  observer.observe(target, { childList: true, subtree: true });
118
118
  }
119
119
 
120
- // 4. Auto-Purge Loop if enabled
120
+ // 4. Lightweight Idle Purge Check (MutationObserver already handles live DOM with zero lag)
121
121
  if (this.autoShield) {
122
- setInterval(() => this.purgeSpyInjections(), 800);
122
+ setInterval(() => this.purgeSpyInjections(), 30000);
123
123
  }
124
124
 
125
125
  // Initial scan
@@ -12,7 +12,7 @@ class StarfieldCanvas {
12
12
  this.isWarping = false;
13
13
  this.warpSpeedMultiplier = 1;
14
14
  this.isPaused = false;
15
- this.turboMode = localStorage.getItem('reaper_turbo_mode') === 'true';
15
+ this.turboMode = localStorage.getItem('reaper_turbo_mode') !== 'false'; // Default TRUE for ultra 60 FPS
16
16
  this.rafId = null;
17
17
 
18
18
  this.init();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "reaper-arcade-hub",
3
- "version": "2.9.0",
3
+ "version": "2.9.1",
4
4
  "description": "Private Core Hub",
5
5
  "main": "main/index.html",
6
6
  "unpkg": "main/index.html",
package/verify/index.html CHANGED
@@ -185,6 +185,10 @@
185
185
  const DEFAULT_MASTER_KEYS = {};
186
186
 
187
187
  window.addEventListener('DOMContentLoaded', () => {
188
+ try {
189
+ localStorage.removeItem('reaper_verify_lockdown');
190
+ } catch(e) {}
191
+
188
192
  if (window.reaperFirebase) {
189
193
  window.reaperFirebase.init();
190
194
 
@@ -331,10 +331,10 @@ class FirebaseService {
331
331
  callback(isLocked);
332
332
  });
333
333
  } catch (e) {
334
- callback(localStorage.getItem('reaper_verify_lockdown') === 'true');
334
+ callback(false);
335
335
  }
336
336
  } else {
337
- callback(localStorage.getItem('reaper_verify_lockdown') === 'true');
337
+ callback(false);
338
338
  }
339
339
  }
340
340