reaper-arcade-hub 2.9.0 → 2.9.2
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 +1 -1
- package/js/app.js +360 -115
- package/js/firebase-client.js +4 -4
- package/js/security-shield.js +2 -2
- package/js/visuals.js +1 -1
- package/main/index.html +1 -1
- package/main/js/app.js +360 -115
- package/main/js/firebase-client.js +4 -4
- package/main/js/security-shield.js +2 -2
- package/main/js/visuals.js +1 -1
- package/package.json +1 -1
- package/verify/index.html +4 -0
- package/verify/js/firebase-client.js +2 -2
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.
|
|
41
|
+
<span>V2.9.2 ZERO-LAG ENGINE</span> • 788 GAMES • LOCAL SAVE ENABLED
|
|
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.
|
|
2
|
-
window.REAPER_VERSION = "2.9.
|
|
3
|
-
window.REAPER_LATEST_URL = "https://unpkg.com/reaper-arcade-hub@2.9.
|
|
1
|
+
// REAPER HUB - Core Engine (v2.9.2 Persistent Local Save Engine & 60 FPS about:blank Isolation)
|
|
2
|
+
window.REAPER_VERSION = "2.9.2";
|
|
3
|
+
window.REAPER_LATEST_URL = "https://unpkg.com/reaper-arcade-hub@2.9.2/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 =
|
|
25
|
-
this.isChatLocked =
|
|
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.
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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) {
|
|
@@ -283,7 +283,19 @@ class ReaperHubApp {
|
|
|
283
283
|
}
|
|
284
284
|
}
|
|
285
285
|
|
|
286
|
-
// 11.
|
|
286
|
+
// 11. Realtime Persistent Game Save Synchronizer for about:blank tabs
|
|
287
|
+
window.addEventListener('message', (e) => {
|
|
288
|
+
if (e.data && e.data.type === 'REAPER_SAVE_SYNC' && e.data.gameId) {
|
|
289
|
+
try {
|
|
290
|
+
const key = 'reaper_save_' + e.data.gameId;
|
|
291
|
+
const current = JSON.parse(localStorage.getItem(key) || '{}');
|
|
292
|
+
Object.assign(current, e.data.data);
|
|
293
|
+
localStorage.setItem(key, JSON.stringify(current));
|
|
294
|
+
} catch(err) {}
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
// 12. Auto-show Update Logs on visit if new version
|
|
287
299
|
const lastSeenVersion = localStorage.getItem('reaper_last_seen_version');
|
|
288
300
|
if (lastSeenVersion !== '1.0.9') {
|
|
289
301
|
setTimeout(() => {
|
|
@@ -2515,31 +2527,8 @@ Query: **"${prompt}"**
|
|
|
2515
2527
|
}
|
|
2516
2528
|
|
|
2517
2529
|
attachCardTiltListeners() {
|
|
2518
|
-
|
|
2519
|
-
cards
|
|
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
|
-
});
|
|
2530
|
+
// Zero-lag optimization: Heavy per-pixel getBoundingClientRect() tilt calculations
|
|
2531
|
+
// across 788 cards are disabled in favor of hardware-accelerated CSS 3D transforms.
|
|
2543
2532
|
}
|
|
2544
2533
|
|
|
2545
2534
|
recordRecentlyPlayed(game) {
|
|
@@ -2611,7 +2600,7 @@ Query: **"${prompt}"**
|
|
|
2611
2600
|
}
|
|
2612
2601
|
}
|
|
2613
2602
|
|
|
2614
|
-
cleanAndOptimizeGameHTML(html) {
|
|
2603
|
+
cleanAndOptimizeGameHTML(html, gameId) {
|
|
2615
2604
|
if (!html || typeof html !== 'string') return '';
|
|
2616
2605
|
let cleaned = html;
|
|
2617
2606
|
|
|
@@ -2620,8 +2609,10 @@ Query: **"${prompt}"**
|
|
|
2620
2609
|
cleaned = cleaned.replace(/<div id="sidebarad2"[\s\S]*?<\/div>\s*<\/div>/gi, '');
|
|
2621
2610
|
cleaned = cleaned.replace(/<style>[\s\S]*?#sidebarad1[\s\S]*?<\/style>/gi, '');
|
|
2622
2611
|
|
|
2623
|
-
|
|
2624
|
-
|
|
2612
|
+
const safeGameId = String(gameId || (this.currentGame ? this.currentGame.id : 'default'));
|
|
2613
|
+
|
|
2614
|
+
// 2. Inject high-performance responsive scaling & Universal Persistent Local Save Engine
|
|
2615
|
+
const injectStylesAndSave = `
|
|
2625
2616
|
<style id="reaper-game-enhancer">
|
|
2626
2617
|
* { box-sizing: border-box; }
|
|
2627
2618
|
html, body {
|
|
@@ -2655,22 +2646,158 @@ Query: **"${prompt}"**
|
|
|
2655
2646
|
z-index: -9999 !important;
|
|
2656
2647
|
}
|
|
2657
2648
|
</style>
|
|
2658
|
-
<script>
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2649
|
+
<script id="reaper-save-engine">
|
|
2650
|
+
(function() {
|
|
2651
|
+
var GAME_ID = "` + safeGameId + `";
|
|
2652
|
+
var STORAGE_KEY = "reaper_save_" + GAME_ID;
|
|
2653
|
+
var memoryStore = {};
|
|
2654
|
+
|
|
2655
|
+
// Initial State Restoration from Parent Window or Opener
|
|
2656
|
+
try {
|
|
2657
|
+
if (window.parent && window.parent.REAPER_INITIAL_SAVE) {
|
|
2658
|
+
var p = window.parent.REAPER_INITIAL_SAVE;
|
|
2659
|
+
var parsed = typeof p === 'string' ? JSON.parse(p) : p;
|
|
2660
|
+
if (parsed && typeof parsed === 'object') Object.assign(memoryStore, parsed);
|
|
2661
|
+
}
|
|
2662
|
+
} catch(e) {}
|
|
2663
|
+
|
|
2664
|
+
if (Object.keys(memoryStore).length === 0) {
|
|
2665
|
+
try {
|
|
2666
|
+
if (window.parent && window.parent.localStorage) {
|
|
2667
|
+
var raw = window.parent.localStorage.getItem(STORAGE_KEY);
|
|
2668
|
+
if (raw) Object.assign(memoryStore, JSON.parse(raw));
|
|
2669
|
+
}
|
|
2670
|
+
} catch(e) {}
|
|
2671
|
+
}
|
|
2672
|
+
|
|
2673
|
+
if (Object.keys(memoryStore).length === 0) {
|
|
2674
|
+
try {
|
|
2675
|
+
if (window.opener && window.opener.localStorage) {
|
|
2676
|
+
var rawOpener = window.opener.localStorage.getItem(STORAGE_KEY);
|
|
2677
|
+
if (rawOpener) Object.assign(memoryStore, JSON.parse(rawOpener));
|
|
2678
|
+
}
|
|
2679
|
+
} catch(e) {}
|
|
2680
|
+
}
|
|
2681
|
+
|
|
2682
|
+
function persistSave() {
|
|
2683
|
+
var dataStr = JSON.stringify(memoryStore);
|
|
2684
|
+
try {
|
|
2685
|
+
if (window.parent && window.parent.localStorage) {
|
|
2686
|
+
window.parent.localStorage.setItem(STORAGE_KEY, dataStr);
|
|
2687
|
+
}
|
|
2688
|
+
} catch(e) {}
|
|
2689
|
+
try {
|
|
2690
|
+
if (window.opener && window.opener.localStorage) {
|
|
2691
|
+
window.opener.localStorage.setItem(STORAGE_KEY, dataStr);
|
|
2692
|
+
}
|
|
2693
|
+
} catch(e) {}
|
|
2694
|
+
try {
|
|
2695
|
+
var payload = { type: 'REAPER_SAVE_SYNC', gameId: GAME_ID, data: memoryStore };
|
|
2696
|
+
if (window.parent && window.parent !== window) {
|
|
2697
|
+
window.parent.postMessage(payload, '*');
|
|
2698
|
+
}
|
|
2699
|
+
if (window.opener) {
|
|
2700
|
+
window.opener.postMessage(payload, '*');
|
|
2701
|
+
}
|
|
2702
|
+
} catch(e) {}
|
|
2703
|
+
}
|
|
2704
|
+
|
|
2705
|
+
// Virtual Storage Implementation
|
|
2706
|
+
var virtualStorage = {
|
|
2707
|
+
getItem: function(k) {
|
|
2708
|
+
var key = String(k);
|
|
2709
|
+
return memoryStore.hasOwnProperty(key) ? memoryStore[key] : null;
|
|
2710
|
+
},
|
|
2711
|
+
setItem: function(k, v) {
|
|
2712
|
+
var key = String(k);
|
|
2713
|
+
var val = String(v);
|
|
2714
|
+
memoryStore[key] = val;
|
|
2715
|
+
persistSave();
|
|
2716
|
+
},
|
|
2717
|
+
removeItem: function(k) {
|
|
2718
|
+
var key = String(k);
|
|
2719
|
+
delete memoryStore[key];
|
|
2720
|
+
persistSave();
|
|
2721
|
+
},
|
|
2722
|
+
clear: function() {
|
|
2723
|
+
memoryStore = {};
|
|
2724
|
+
persistSave();
|
|
2725
|
+
},
|
|
2726
|
+
key: function(i) {
|
|
2727
|
+
var keys = Object.keys(memoryStore);
|
|
2728
|
+
return keys[i] !== undefined ? keys[i] : null;
|
|
2729
|
+
},
|
|
2730
|
+
get length() {
|
|
2731
|
+
return Object.keys(memoryStore).length;
|
|
2732
|
+
}
|
|
2733
|
+
};
|
|
2734
|
+
|
|
2735
|
+
// Proxy Handler for property access like storage.score = 50 or storage['save']
|
|
2736
|
+
var storageProxy = new Proxy(virtualStorage, {
|
|
2737
|
+
get: function(target, prop) {
|
|
2738
|
+
if (prop in target || typeof prop === 'symbol') {
|
|
2739
|
+
return target[prop];
|
|
2740
|
+
}
|
|
2741
|
+
return target.getItem(prop);
|
|
2742
|
+
},
|
|
2743
|
+
set: function(target, prop, val) {
|
|
2744
|
+
if (prop in target) {
|
|
2745
|
+
target[prop] = val;
|
|
2746
|
+
return true;
|
|
2747
|
+
}
|
|
2748
|
+
target.setItem(prop, val);
|
|
2749
|
+
return true;
|
|
2750
|
+
},
|
|
2751
|
+
deleteProperty: function(target, prop) {
|
|
2752
|
+
target.removeItem(prop);
|
|
2753
|
+
return true;
|
|
2754
|
+
}
|
|
2755
|
+
});
|
|
2756
|
+
|
|
2757
|
+
// Override localStorage & sessionStorage
|
|
2758
|
+
try {
|
|
2759
|
+
Object.defineProperty(window, 'localStorage', {
|
|
2760
|
+
value: storageProxy,
|
|
2761
|
+
configurable: true,
|
|
2762
|
+
enumerable: true,
|
|
2763
|
+
writable: true
|
|
2764
|
+
});
|
|
2765
|
+
} catch(e) {
|
|
2766
|
+
try { window.localStorage = storageProxy; } catch(e2) {}
|
|
2767
|
+
}
|
|
2768
|
+
|
|
2769
|
+
try {
|
|
2770
|
+
Object.defineProperty(window, 'sessionStorage', {
|
|
2771
|
+
value: storageProxy,
|
|
2772
|
+
configurable: true,
|
|
2773
|
+
enumerable: true,
|
|
2774
|
+
writable: true
|
|
2775
|
+
});
|
|
2776
|
+
} catch(e) {
|
|
2777
|
+
try { window.sessionStorage = storageProxy; } catch(e2) {}
|
|
2778
|
+
}
|
|
2779
|
+
|
|
2780
|
+
// Periodic & unload autosave sync
|
|
2781
|
+
setInterval(persistSave, 1500);
|
|
2782
|
+
window.addEventListener('beforeunload', persistSave);
|
|
2783
|
+
window.addEventListener('pagehide', persistSave);
|
|
2784
|
+
|
|
2785
|
+
// Focus game canvas on load & block intrusive popups
|
|
2786
|
+
window.open = function() { return null; };
|
|
2787
|
+
window.addEventListener('load', function() {
|
|
2788
|
+
var c = document.querySelector('canvas') || document.querySelector('#game');
|
|
2789
|
+
if (c && c.focus) c.focus();
|
|
2790
|
+
});
|
|
2791
|
+
})();
|
|
2665
2792
|
<\/script>
|
|
2666
2793
|
`;
|
|
2667
2794
|
|
|
2668
2795
|
if (cleaned.includes('<head>')) {
|
|
2669
|
-
cleaned = cleaned.replace('<head>', '<head>' +
|
|
2796
|
+
cleaned = cleaned.replace('<head>', '<head>' + injectStylesAndSave);
|
|
2670
2797
|
} else if (cleaned.includes('<html>')) {
|
|
2671
|
-
cleaned = cleaned.replace('<html>', '<html><head>' +
|
|
2798
|
+
cleaned = cleaned.replace('<html>', '<html><head>' + injectStylesAndSave + '</head>');
|
|
2672
2799
|
} else {
|
|
2673
|
-
cleaned =
|
|
2800
|
+
cleaned = injectStylesAndSave + cleaned;
|
|
2674
2801
|
}
|
|
2675
2802
|
|
|
2676
2803
|
return cleaned;
|
|
@@ -2701,6 +2828,11 @@ Query: **"${prompt}"**
|
|
|
2701
2828
|
window.reaperStarfield.pause();
|
|
2702
2829
|
}
|
|
2703
2830
|
|
|
2831
|
+
// Auto open in clean about:blank tab for 60 FPS zero-lag gameplay & school cloaking!
|
|
2832
|
+
this.openGameAboutBlank(game);
|
|
2833
|
+
}
|
|
2834
|
+
|
|
2835
|
+
openGameInModal(game) {
|
|
2704
2836
|
const modal = document.getElementById('game-player-modal');
|
|
2705
2837
|
const iframe = document.getElementById('player-iframe');
|
|
2706
2838
|
const nameEl = document.getElementById('player-game-name');
|
|
@@ -2721,37 +2853,31 @@ Query: **"${prompt}"**
|
|
|
2721
2853
|
const filename = game.url.split('/').pop();
|
|
2722
2854
|
iframe.removeAttribute('srcdoc');
|
|
2723
2855
|
|
|
2724
|
-
// 1. Instant Memory Cache Check (0.01s Load)
|
|
2725
2856
|
if (this.turboGameCache && this.turboGameCache[filename]) {
|
|
2726
|
-
iframe.srcdoc = this.cleanAndOptimizeGameHTML(this.turboGameCache[filename]);
|
|
2857
|
+
iframe.srcdoc = this.cleanAndOptimizeGameHTML(this.turboGameCache[filename], game.id);
|
|
2727
2858
|
if (statusBadge) statusBadge.textContent = '⚡ TURBO INSTANT (60 FPS)';
|
|
2728
2859
|
return;
|
|
2729
2860
|
}
|
|
2730
2861
|
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2862
|
+
fetch(`https://gn-math.github.io/html/${filename}`)
|
|
2863
|
+
.then(res => {
|
|
2864
|
+
if (res.ok) return res.text();
|
|
2865
|
+
throw new Error("CDN fallback");
|
|
2866
|
+
})
|
|
2867
|
+
.then(html => {
|
|
2736
2868
|
if (html && html.length > 50) {
|
|
2737
2869
|
this.turboGameCache[filename] = html;
|
|
2738
|
-
iframe.srcdoc = this.cleanAndOptimizeGameHTML(html);
|
|
2870
|
+
iframe.srcdoc = this.cleanAndOptimizeGameHTML(html, game.id);
|
|
2739
2871
|
if (statusBadge) statusBadge.textContent = 'ONLINE (60 FPS)';
|
|
2740
|
-
|
|
2872
|
+
} else {
|
|
2873
|
+
iframe.src = `https://gn-math.github.io/html/${filename}`;
|
|
2874
|
+
if (statusBadge) statusBadge.textContent = 'ONLINE';
|
|
2741
2875
|
}
|
|
2742
|
-
}
|
|
2743
|
-
|
|
2744
|
-
|
|
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
|
-
}
|
|
2876
|
+
})
|
|
2877
|
+
.catch(() => {
|
|
2878
|
+
iframe.src = `https://fastly.jsdelivr.net/gh/gn-math/html@main/${filename}`;
|
|
2879
|
+
if (statusBadge) statusBadge.textContent = 'ONLINE';
|
|
2880
|
+
});
|
|
2755
2881
|
}
|
|
2756
2882
|
}
|
|
2757
2883
|
|
|
@@ -2785,92 +2911,193 @@ Query: **"${prompt}"**
|
|
|
2785
2911
|
togglePlayerFullscreen() {
|
|
2786
2912
|
const frameWrapper = document.querySelector('.player-frame-wrapper');
|
|
2787
2913
|
if (!document.fullscreenElement) {
|
|
2788
|
-
frameWrapper.requestFullscreen().catch(e => console.error(e));
|
|
2914
|
+
if (frameWrapper && frameWrapper.requestFullscreen) frameWrapper.requestFullscreen().catch(e => console.error(e));
|
|
2789
2915
|
} else {
|
|
2790
2916
|
document.exitFullscreen();
|
|
2791
2917
|
}
|
|
2792
2918
|
}
|
|
2793
2919
|
|
|
2794
|
-
async openGameAboutBlank() {
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2920
|
+
async openGameAboutBlank(gameArg) {
|
|
2921
|
+
const game = gameArg || this.currentGame;
|
|
2922
|
+
if (!game) return;
|
|
2923
|
+
this.currentGame = game;
|
|
2924
|
+
|
|
2925
|
+
const filename = game.url.split('/').pop();
|
|
2926
|
+
const gameName = game.name;
|
|
2798
2927
|
const siteUrl = window.location.href;
|
|
2799
|
-
const
|
|
2800
|
-
|
|
2928
|
+
const directUrl = `https://gn-math.github.io/html/${filename}`;
|
|
2929
|
+
const fallbackUrl = `https://fastly.jsdelivr.net/gh/gn-math/html@main/${filename}`;
|
|
2930
|
+
|
|
2931
|
+
// Read stored game save from Reaper Hub local storage
|
|
2932
|
+
const initialSave = localStorage.getItem('reaper_save_' + game.id) || '{}';
|
|
2933
|
+
|
|
2934
|
+
// 1. Immediately open window in user click call stack to bypass browser popup blockers
|
|
2935
|
+
let win = null;
|
|
2936
|
+
try {
|
|
2937
|
+
win = window.open('about:blank', '_blank');
|
|
2938
|
+
} catch (e) {
|
|
2939
|
+
console.warn("Popup blocked:", e);
|
|
2940
|
+
}
|
|
2941
|
+
|
|
2942
|
+
if (!win) {
|
|
2943
|
+
// Browser popup blocker prevented window.open -> gracefully fallback to player modal
|
|
2944
|
+
this.showToast("Popup blocked by browser. Opening in player modal...");
|
|
2945
|
+
this.openGameInModal(game);
|
|
2946
|
+
return;
|
|
2947
|
+
}
|
|
2948
|
+
|
|
2949
|
+
this.isPlayingGame = true;
|
|
2950
|
+
this.lastActivityTime = Date.now();
|
|
2951
|
+
this.isPausedForInactivity = false;
|
|
2952
|
+
this.updatePlaytimeStatusUI();
|
|
2953
|
+
|
|
2954
|
+
// 2. Format about:blank with Google Classroom cloak, floating control bar, and full-screen isolation
|
|
2955
|
+
try {
|
|
2801
2956
|
const doc = win.document;
|
|
2802
2957
|
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
2958
|
|
|
2816
2959
|
doc.open();
|
|
2817
2960
|
doc.write(`
|
|
2818
2961
|
<!DOCTYPE html>
|
|
2819
|
-
<html>
|
|
2962
|
+
<html lang="en">
|
|
2820
2963
|
<head>
|
|
2964
|
+
<meta charset="UTF-8">
|
|
2965
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
2821
2966
|
<title>Home - Google Classroom</title>
|
|
2822
|
-
<link rel="icon" href="https://ssl.gstatic.com/classroom/favicon.png">
|
|
2967
|
+
<link rel="icon" type="image/png" href="https://ssl.gstatic.com/classroom/favicon.png">
|
|
2823
2968
|
<style>
|
|
2824
2969
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
2825
|
-
body {
|
|
2826
|
-
|
|
2970
|
+
html, body {
|
|
2971
|
+
width: 100vw; height: 100vh;
|
|
2972
|
+
overflow: hidden; background: #000;
|
|
2973
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
2974
|
+
}
|
|
2975
|
+
#game-frame {
|
|
2976
|
+
width: 100vw; height: 100vh;
|
|
2977
|
+
border: none; position: fixed; top: 0; left: 0; z-index: 1;
|
|
2978
|
+
display: block; background: #000;
|
|
2979
|
+
}
|
|
2827
2980
|
.reaper-bar {
|
|
2828
|
-
position: fixed; top:
|
|
2981
|
+
position: fixed; top: 10px; left: 12px; z-index: 99999;
|
|
2829
2982
|
display: flex; gap: 8px; align-items: center;
|
|
2830
|
-
background: rgba(3,
|
|
2831
|
-
border:
|
|
2832
|
-
box-shadow: 0 4px
|
|
2833
|
-
opacity: 0.
|
|
2983
|
+
background: rgba(3, 10, 24, 0.92); backdrop-filter: blur(10px);
|
|
2984
|
+
border: 1px solid rgba(0, 210, 255, 0.5); border-radius: 8px; padding: 6px 12px;
|
|
2985
|
+
box-shadow: 0 4px 25px rgba(0, 0, 0, 0.8), 0 0 15px rgba(0, 210, 255, 0.2);
|
|
2986
|
+
opacity: 0.9; transition: opacity 0.2s ease, border-color 0.2s ease;
|
|
2834
2987
|
}
|
|
2835
2988
|
.reaper-bar:hover { opacity: 1; border-color: #00d2ff; }
|
|
2989
|
+
.game-label {
|
|
2990
|
+
color: #fff; font-size: 11px; font-weight: 800; letter-spacing: 0.05em;
|
|
2991
|
+
margin-right: 6px; font-family: monospace;
|
|
2992
|
+
}
|
|
2836
2993
|
.btn-action {
|
|
2837
|
-
background: #
|
|
2838
|
-
padding: 5px
|
|
2994
|
+
background: #06162d; color: #00d2ff; border: 1px solid rgba(0, 210, 255, 0.4);
|
|
2995
|
+
padding: 5px 11px; border-radius: 6px; font-size: 11px; font-weight: 800;
|
|
2839
2996
|
cursor: pointer; text-decoration: none; display: inline-flex; align-items: center; gap: 4px;
|
|
2840
2997
|
transition: all 0.2s ease; font-family: inherit;
|
|
2841
2998
|
}
|
|
2842
|
-
.btn-action:hover {
|
|
2999
|
+
.btn-action:hover {
|
|
3000
|
+
background: #0088ff; color: #fff; box-shadow: 0 0 12px rgba(0, 136, 255, 0.7);
|
|
3001
|
+
}
|
|
2843
3002
|
.reaper-watermark {
|
|
2844
|
-
position: fixed; bottom:
|
|
2845
|
-
font-family: serif; font-weight: 900; font-size:
|
|
2846
|
-
color: rgba(255, 255, 255, 0.35); text-shadow: 0 0
|
|
3003
|
+
position: fixed; bottom: 8px; right: 12px; z-index: 99999;
|
|
3004
|
+
font-family: serif; font-weight: 900; font-size: 12px; letter-spacing: 0.15em;
|
|
3005
|
+
color: rgba(255, 255, 255, 0.35); text-shadow: 0 0 8px rgba(0, 210, 255, 0.5);
|
|
2847
3006
|
pointer-events: none; user-select: none;
|
|
2848
3007
|
}
|
|
3008
|
+
#loader-spinner {
|
|
3009
|
+
position: fixed; inset: 0; background: #020610; z-index: 10;
|
|
3010
|
+
display: flex; flex-direction: column; align-items: center; justify-content: center;
|
|
3011
|
+
color: #00d2ff; font-family: monospace; font-size: 14px; gap: 14px;
|
|
3012
|
+
}
|
|
3013
|
+
.spin {
|
|
3014
|
+
width: 42px; height: 42px; border: 3px solid rgba(0, 210, 255, 0.2);
|
|
3015
|
+
border-top: 3px solid #00d2ff; border-radius: 50%;
|
|
3016
|
+
animation: sp 0.8s linear infinite;
|
|
3017
|
+
}
|
|
3018
|
+
@keyframes sp { to { transform: rotate(360deg); } }
|
|
2849
3019
|
</style>
|
|
2850
|
-
<script>
|
|
2851
|
-
//
|
|
2852
|
-
window.
|
|
3020
|
+
<script id="reaper-parent-bridge">
|
|
3021
|
+
// Local Save Data Hub Bridge & Relay
|
|
3022
|
+
window.REAPER_GAME_ID = "` + game.id + `";
|
|
3023
|
+
window.REAPER_INITIAL_SAVE = ` + JSON.stringify(initialSave) + `;
|
|
3024
|
+
window.addEventListener('message', function(e) {
|
|
3025
|
+
if (e.data && e.data.type === 'REAPER_SAVE_SYNC') {
|
|
3026
|
+
try {
|
|
3027
|
+
localStorage.setItem('reaper_save_' + e.data.gameId, JSON.stringify(e.data.data));
|
|
3028
|
+
} catch(err) {}
|
|
3029
|
+
try {
|
|
3030
|
+
if (window.opener) window.opener.postMessage(e.data, '*');
|
|
3031
|
+
} catch(err) {}
|
|
3032
|
+
}
|
|
3033
|
+
});
|
|
3034
|
+
window.open = function() { return null; };
|
|
2853
3035
|
<\/script>
|
|
2854
3036
|
</head>
|
|
2855
3037
|
<body>
|
|
3038
|
+
<div id="loader-spinner">
|
|
3039
|
+
<div class="spin"></div>
|
|
3040
|
+
<div style="font-weight: 800; letter-spacing: 0.1em;">LAUNCHING ${this.escapeHtml(gameName)} (60 FPS TURBO)...</div>
|
|
3041
|
+
</div>
|
|
2856
3042
|
<div class="reaper-bar">
|
|
2857
|
-
<
|
|
3043
|
+
<span class="game-label">⚡ ${this.escapeHtml(gameName)}</span>
|
|
3044
|
+
<a href="${siteUrl}" class="btn-action" title="Back to Reaper Hub">🏠 HUB</a>
|
|
2858
3045
|
<button onclick="document.getElementById('game-frame').requestFullscreen()" class="btn-action">⛶ FULLSCREEN</button>
|
|
2859
3046
|
<button onclick="document.getElementById('game-frame').contentWindow.location.reload()" class="btn-action">🔄 RELOAD</button>
|
|
2860
3047
|
</div>
|
|
2861
|
-
<div class="reaper-watermark">REAPER HUB</div>
|
|
2862
|
-
<iframe id="game-frame" allow="autoplay; fullscreen; gamepad; focus-without-user-activation; pointer-lock"
|
|
3048
|
+
<div class="reaper-watermark">REAPER HUB // 60 FPS ZERO LAG</div>
|
|
3049
|
+
<iframe id="game-frame" allow="autoplay; fullscreen; gamepad; focus-without-user-activation; pointer-lock" style="opacity: 0; transition: opacity 0.3s ease;"></iframe>
|
|
2863
3050
|
</body>
|
|
2864
3051
|
</html>
|
|
2865
3052
|
`);
|
|
2866
3053
|
doc.close();
|
|
2867
3054
|
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
3055
|
+
const frame = doc.getElementById('game-frame');
|
|
3056
|
+
const spinner = doc.getElementById('loader-spinner');
|
|
3057
|
+
|
|
3058
|
+
const finalizeLoad = () => {
|
|
3059
|
+
if (frame) frame.style.opacity = '1';
|
|
3060
|
+
if (spinner) spinner.style.display = 'none';
|
|
3061
|
+
try {
|
|
3062
|
+
if (frame && frame.contentWindow) {
|
|
3063
|
+
frame.contentWindow.focus();
|
|
3064
|
+
}
|
|
3065
|
+
} catch(e) {}
|
|
3066
|
+
};
|
|
3067
|
+
|
|
3068
|
+
// 3. High-Speed Memory Pre-Cache Check
|
|
3069
|
+
if (this.turboGameCache && this.turboGameCache[filename]) {
|
|
3070
|
+
frame.srcdoc = this.cleanAndOptimizeGameHTML(this.turboGameCache[filename], game.id);
|
|
3071
|
+
frame.onload = finalizeLoad;
|
|
3072
|
+
setTimeout(finalizeLoad, 400);
|
|
3073
|
+
} else {
|
|
3074
|
+
// Direct fetch & clean injection for zero lag
|
|
3075
|
+
fetch(directUrl)
|
|
3076
|
+
.then(res => {
|
|
3077
|
+
if (res.ok) return res.text();
|
|
3078
|
+
throw new Error("Direct fetch fallback");
|
|
3079
|
+
})
|
|
3080
|
+
.then(html => {
|
|
3081
|
+
if (html && html.length > 50) {
|
|
3082
|
+
if (this.turboGameCache) this.turboGameCache[filename] = html;
|
|
3083
|
+
frame.srcdoc = this.cleanAndOptimizeGameHTML(html, game.id);
|
|
3084
|
+
frame.onload = finalizeLoad;
|
|
3085
|
+
setTimeout(finalizeLoad, 500);
|
|
3086
|
+
} else {
|
|
3087
|
+
frame.src = directUrl;
|
|
3088
|
+
frame.onload = finalizeLoad;
|
|
3089
|
+
}
|
|
3090
|
+
})
|
|
3091
|
+
.catch(() => {
|
|
3092
|
+
frame.src = fallbackUrl;
|
|
3093
|
+
frame.onload = finalizeLoad;
|
|
3094
|
+
});
|
|
2871
3095
|
}
|
|
2872
3096
|
|
|
2873
|
-
this.showToast("
|
|
3097
|
+
this.showToast(`Launched "${gameName}" in about:blank (60 FPS Turbo)`);
|
|
3098
|
+
} catch (err) {
|
|
3099
|
+
console.error("about:blank initialization error:", err);
|
|
3100
|
+
this.openGameInModal(game);
|
|
2874
3101
|
}
|
|
2875
3102
|
}
|
|
2876
3103
|
|
|
@@ -2906,6 +3133,24 @@ Query: **"${prompt}"**
|
|
|
2906
3133
|
}
|
|
2907
3134
|
}
|
|
2908
3135
|
|
|
3136
|
+
startFPSMonitor() {
|
|
3137
|
+
let lastTime = performance.now();
|
|
3138
|
+
let frames = 0;
|
|
3139
|
+
const updateFps = () => {
|
|
3140
|
+
frames++;
|
|
3141
|
+
const now = performance.now();
|
|
3142
|
+
if (now - lastTime >= 1000) {
|
|
3143
|
+
this.clientFps = Math.round((frames * 1000) / (now - lastTime));
|
|
3144
|
+
const fpsEl = document.getElementById('stat-fps-counter');
|
|
3145
|
+
if (fpsEl) fpsEl.textContent = `${this.clientFps} FPS`;
|
|
3146
|
+
frames = 0;
|
|
3147
|
+
lastTime = now;
|
|
3148
|
+
}
|
|
3149
|
+
requestAnimationFrame(updateFps);
|
|
3150
|
+
};
|
|
3151
|
+
requestAnimationFrame(updateFps);
|
|
3152
|
+
}
|
|
3153
|
+
|
|
2909
3154
|
handleOpiumSearch() {
|
|
2910
3155
|
const input = document.getElementById('opium-search-input');
|
|
2911
3156
|
if (!input || !input.value.trim()) return;
|
package/js/firebase-client.js
CHANGED
|
@@ -305,10 +305,10 @@ class FirebaseService {
|
|
|
305
305
|
callback(isLocked);
|
|
306
306
|
});
|
|
307
307
|
} catch (e) {
|
|
308
|
-
callback(
|
|
308
|
+
callback(false);
|
|
309
309
|
}
|
|
310
310
|
} else {
|
|
311
|
-
callback(
|
|
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(
|
|
334
|
+
callback(false);
|
|
335
335
|
}
|
|
336
336
|
} else {
|
|
337
|
-
callback(
|
|
337
|
+
callback(false);
|
|
338
338
|
}
|
|
339
339
|
}
|
|
340
340
|
|
package/js/security-shield.js
CHANGED
|
@@ -117,9 +117,9 @@
|
|
|
117
117
|
observer.observe(target, { childList: true, subtree: true });
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
// 4.
|
|
120
|
+
// 4. Lightweight Idle Purge Check (MutationObserver already handles live DOM with zero lag)
|
|
121
121
|
if (this.autoShield) {
|
|
122
|
-
setInterval(() => this.purgeSpyInjections(),
|
|
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')
|
|
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();
|