reaper-arcade-hub 2.9.1 → 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 +188 -23
- package/main/index.html +1 -1
- package/main/js/app.js +188 -23
- package/package.json +1 -1
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.9.
|
|
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;
|
|
@@ -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(() => {
|
|
@@ -2588,7 +2600,7 @@ Query: **"${prompt}"**
|
|
|
2588
2600
|
}
|
|
2589
2601
|
}
|
|
2590
2602
|
|
|
2591
|
-
cleanAndOptimizeGameHTML(html) {
|
|
2603
|
+
cleanAndOptimizeGameHTML(html, gameId) {
|
|
2592
2604
|
if (!html || typeof html !== 'string') return '';
|
|
2593
2605
|
let cleaned = html;
|
|
2594
2606
|
|
|
@@ -2597,8 +2609,10 @@ Query: **"${prompt}"**
|
|
|
2597
2609
|
cleaned = cleaned.replace(/<div id="sidebarad2"[\s\S]*?<\/div>\s*<\/div>/gi, '');
|
|
2598
2610
|
cleaned = cleaned.replace(/<style>[\s\S]*?#sidebarad1[\s\S]*?<\/style>/gi, '');
|
|
2599
2611
|
|
|
2600
|
-
|
|
2601
|
-
|
|
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 = `
|
|
2602
2616
|
<style id="reaper-game-enhancer">
|
|
2603
2617
|
* { box-sizing: border-box; }
|
|
2604
2618
|
html, body {
|
|
@@ -2632,22 +2646,158 @@ Query: **"${prompt}"**
|
|
|
2632
2646
|
z-index: -9999 !important;
|
|
2633
2647
|
}
|
|
2634
2648
|
</style>
|
|
2635
|
-
<script>
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
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
|
+
})();
|
|
2642
2792
|
<\/script>
|
|
2643
2793
|
`;
|
|
2644
2794
|
|
|
2645
2795
|
if (cleaned.includes('<head>')) {
|
|
2646
|
-
cleaned = cleaned.replace('<head>', '<head>' +
|
|
2796
|
+
cleaned = cleaned.replace('<head>', '<head>' + injectStylesAndSave);
|
|
2647
2797
|
} else if (cleaned.includes('<html>')) {
|
|
2648
|
-
cleaned = cleaned.replace('<html>', '<html><head>' +
|
|
2798
|
+
cleaned = cleaned.replace('<html>', '<html><head>' + injectStylesAndSave + '</head>');
|
|
2649
2799
|
} else {
|
|
2650
|
-
cleaned =
|
|
2800
|
+
cleaned = injectStylesAndSave + cleaned;
|
|
2651
2801
|
}
|
|
2652
2802
|
|
|
2653
2803
|
return cleaned;
|
|
@@ -2704,7 +2854,7 @@ Query: **"${prompt}"**
|
|
|
2704
2854
|
iframe.removeAttribute('srcdoc');
|
|
2705
2855
|
|
|
2706
2856
|
if (this.turboGameCache && this.turboGameCache[filename]) {
|
|
2707
|
-
iframe.srcdoc = this.cleanAndOptimizeGameHTML(this.turboGameCache[filename]);
|
|
2857
|
+
iframe.srcdoc = this.cleanAndOptimizeGameHTML(this.turboGameCache[filename], game.id);
|
|
2708
2858
|
if (statusBadge) statusBadge.textContent = '⚡ TURBO INSTANT (60 FPS)';
|
|
2709
2859
|
return;
|
|
2710
2860
|
}
|
|
@@ -2717,7 +2867,7 @@ Query: **"${prompt}"**
|
|
|
2717
2867
|
.then(html => {
|
|
2718
2868
|
if (html && html.length > 50) {
|
|
2719
2869
|
this.turboGameCache[filename] = html;
|
|
2720
|
-
iframe.srcdoc = this.cleanAndOptimizeGameHTML(html);
|
|
2870
|
+
iframe.srcdoc = this.cleanAndOptimizeGameHTML(html, game.id);
|
|
2721
2871
|
if (statusBadge) statusBadge.textContent = 'ONLINE (60 FPS)';
|
|
2722
2872
|
} else {
|
|
2723
2873
|
iframe.src = `https://gn-math.github.io/html/${filename}`;
|
|
@@ -2778,6 +2928,9 @@ Query: **"${prompt}"**
|
|
|
2778
2928
|
const directUrl = `https://gn-math.github.io/html/${filename}`;
|
|
2779
2929
|
const fallbackUrl = `https://fastly.jsdelivr.net/gh/gn-math/html@main/${filename}`;
|
|
2780
2930
|
|
|
2931
|
+
// Read stored game save from Reaper Hub local storage
|
|
2932
|
+
const initialSave = localStorage.getItem('reaper_save_' + game.id) || '{}';
|
|
2933
|
+
|
|
2781
2934
|
// 1. Immediately open window in user click call stack to bypass browser popup blockers
|
|
2782
2935
|
let win = null;
|
|
2783
2936
|
try {
|
|
@@ -2864,8 +3017,20 @@ Query: **"${prompt}"**
|
|
|
2864
3017
|
}
|
|
2865
3018
|
@keyframes sp { to { transform: rotate(360deg); } }
|
|
2866
3019
|
</style>
|
|
2867
|
-
<script>
|
|
2868
|
-
//
|
|
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
|
+
});
|
|
2869
3034
|
window.open = function() { return null; };
|
|
2870
3035
|
<\/script>
|
|
2871
3036
|
</head>
|
|
@@ -2902,7 +3067,7 @@ Query: **"${prompt}"**
|
|
|
2902
3067
|
|
|
2903
3068
|
// 3. High-Speed Memory Pre-Cache Check
|
|
2904
3069
|
if (this.turboGameCache && this.turboGameCache[filename]) {
|
|
2905
|
-
frame.srcdoc = this.cleanAndOptimizeGameHTML(this.turboGameCache[filename]);
|
|
3070
|
+
frame.srcdoc = this.cleanAndOptimizeGameHTML(this.turboGameCache[filename], game.id);
|
|
2906
3071
|
frame.onload = finalizeLoad;
|
|
2907
3072
|
setTimeout(finalizeLoad, 400);
|
|
2908
3073
|
} else {
|
|
@@ -2915,7 +3080,7 @@ Query: **"${prompt}"**
|
|
|
2915
3080
|
.then(html => {
|
|
2916
3081
|
if (html && html.length > 50) {
|
|
2917
3082
|
if (this.turboGameCache) this.turboGameCache[filename] = html;
|
|
2918
|
-
frame.srcdoc = this.cleanAndOptimizeGameHTML(html);
|
|
3083
|
+
frame.srcdoc = this.cleanAndOptimizeGameHTML(html, game.id);
|
|
2919
3084
|
frame.onload = finalizeLoad;
|
|
2920
3085
|
setTimeout(finalizeLoad, 500);
|
|
2921
3086
|
} else {
|
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.9.
|
|
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/main/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;
|
|
@@ -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(() => {
|
|
@@ -2588,7 +2600,7 @@ Query: **"${prompt}"**
|
|
|
2588
2600
|
}
|
|
2589
2601
|
}
|
|
2590
2602
|
|
|
2591
|
-
cleanAndOptimizeGameHTML(html) {
|
|
2603
|
+
cleanAndOptimizeGameHTML(html, gameId) {
|
|
2592
2604
|
if (!html || typeof html !== 'string') return '';
|
|
2593
2605
|
let cleaned = html;
|
|
2594
2606
|
|
|
@@ -2597,8 +2609,10 @@ Query: **"${prompt}"**
|
|
|
2597
2609
|
cleaned = cleaned.replace(/<div id="sidebarad2"[\s\S]*?<\/div>\s*<\/div>/gi, '');
|
|
2598
2610
|
cleaned = cleaned.replace(/<style>[\s\S]*?#sidebarad1[\s\S]*?<\/style>/gi, '');
|
|
2599
2611
|
|
|
2600
|
-
|
|
2601
|
-
|
|
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 = `
|
|
2602
2616
|
<style id="reaper-game-enhancer">
|
|
2603
2617
|
* { box-sizing: border-box; }
|
|
2604
2618
|
html, body {
|
|
@@ -2632,22 +2646,158 @@ Query: **"${prompt}"**
|
|
|
2632
2646
|
z-index: -9999 !important;
|
|
2633
2647
|
}
|
|
2634
2648
|
</style>
|
|
2635
|
-
<script>
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
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
|
+
})();
|
|
2642
2792
|
<\/script>
|
|
2643
2793
|
`;
|
|
2644
2794
|
|
|
2645
2795
|
if (cleaned.includes('<head>')) {
|
|
2646
|
-
cleaned = cleaned.replace('<head>', '<head>' +
|
|
2796
|
+
cleaned = cleaned.replace('<head>', '<head>' + injectStylesAndSave);
|
|
2647
2797
|
} else if (cleaned.includes('<html>')) {
|
|
2648
|
-
cleaned = cleaned.replace('<html>', '<html><head>' +
|
|
2798
|
+
cleaned = cleaned.replace('<html>', '<html><head>' + injectStylesAndSave + '</head>');
|
|
2649
2799
|
} else {
|
|
2650
|
-
cleaned =
|
|
2800
|
+
cleaned = injectStylesAndSave + cleaned;
|
|
2651
2801
|
}
|
|
2652
2802
|
|
|
2653
2803
|
return cleaned;
|
|
@@ -2704,7 +2854,7 @@ Query: **"${prompt}"**
|
|
|
2704
2854
|
iframe.removeAttribute('srcdoc');
|
|
2705
2855
|
|
|
2706
2856
|
if (this.turboGameCache && this.turboGameCache[filename]) {
|
|
2707
|
-
iframe.srcdoc = this.cleanAndOptimizeGameHTML(this.turboGameCache[filename]);
|
|
2857
|
+
iframe.srcdoc = this.cleanAndOptimizeGameHTML(this.turboGameCache[filename], game.id);
|
|
2708
2858
|
if (statusBadge) statusBadge.textContent = '⚡ TURBO INSTANT (60 FPS)';
|
|
2709
2859
|
return;
|
|
2710
2860
|
}
|
|
@@ -2717,7 +2867,7 @@ Query: **"${prompt}"**
|
|
|
2717
2867
|
.then(html => {
|
|
2718
2868
|
if (html && html.length > 50) {
|
|
2719
2869
|
this.turboGameCache[filename] = html;
|
|
2720
|
-
iframe.srcdoc = this.cleanAndOptimizeGameHTML(html);
|
|
2870
|
+
iframe.srcdoc = this.cleanAndOptimizeGameHTML(html, game.id);
|
|
2721
2871
|
if (statusBadge) statusBadge.textContent = 'ONLINE (60 FPS)';
|
|
2722
2872
|
} else {
|
|
2723
2873
|
iframe.src = `https://gn-math.github.io/html/${filename}`;
|
|
@@ -2778,6 +2928,9 @@ Query: **"${prompt}"**
|
|
|
2778
2928
|
const directUrl = `https://gn-math.github.io/html/${filename}`;
|
|
2779
2929
|
const fallbackUrl = `https://fastly.jsdelivr.net/gh/gn-math/html@main/${filename}`;
|
|
2780
2930
|
|
|
2931
|
+
// Read stored game save from Reaper Hub local storage
|
|
2932
|
+
const initialSave = localStorage.getItem('reaper_save_' + game.id) || '{}';
|
|
2933
|
+
|
|
2781
2934
|
// 1. Immediately open window in user click call stack to bypass browser popup blockers
|
|
2782
2935
|
let win = null;
|
|
2783
2936
|
try {
|
|
@@ -2864,8 +3017,20 @@ Query: **"${prompt}"**
|
|
|
2864
3017
|
}
|
|
2865
3018
|
@keyframes sp { to { transform: rotate(360deg); } }
|
|
2866
3019
|
</style>
|
|
2867
|
-
<script>
|
|
2868
|
-
//
|
|
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
|
+
});
|
|
2869
3034
|
window.open = function() { return null; };
|
|
2870
3035
|
<\/script>
|
|
2871
3036
|
</head>
|
|
@@ -2902,7 +3067,7 @@ Query: **"${prompt}"**
|
|
|
2902
3067
|
|
|
2903
3068
|
// 3. High-Speed Memory Pre-Cache Check
|
|
2904
3069
|
if (this.turboGameCache && this.turboGameCache[filename]) {
|
|
2905
|
-
frame.srcdoc = this.cleanAndOptimizeGameHTML(this.turboGameCache[filename]);
|
|
3070
|
+
frame.srcdoc = this.cleanAndOptimizeGameHTML(this.turboGameCache[filename], game.id);
|
|
2906
3071
|
frame.onload = finalizeLoad;
|
|
2907
3072
|
setTimeout(finalizeLoad, 400);
|
|
2908
3073
|
} else {
|
|
@@ -2915,7 +3080,7 @@ Query: **"${prompt}"**
|
|
|
2915
3080
|
.then(html => {
|
|
2916
3081
|
if (html && html.length > 50) {
|
|
2917
3082
|
if (this.turboGameCache) this.turboGameCache[filename] = html;
|
|
2918
|
-
frame.srcdoc = this.cleanAndOptimizeGameHTML(html);
|
|
3083
|
+
frame.srcdoc = this.cleanAndOptimizeGameHTML(html, game.id);
|
|
2919
3084
|
frame.onload = finalizeLoad;
|
|
2920
3085
|
setTimeout(finalizeLoad, 500);
|
|
2921
3086
|
} else {
|