This commit is contained in:
crshqd/uiiuvdzfghvfd
2026-01-28 00:10:48 -05:00
commit e3f117a384
28 changed files with 2819 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
// Analytics helper functions for Unity WebGL
// Report scene playtime to GA4
// sceneName: string - The name of the scene (e.g., "map_city")
// gameMode: string - The game mode (e.g., "deathmatch")
// duration: number - Duration in seconds
window.reportScenePlaytime = function (sceneName, gameMode, duration) {
if (typeof gtag === 'function') {
gtag('event', 'scene_playtime', {
'scene_name': sceneName,
'game_mode': gameMode,
'duration': duration,
'value': duration // Useful for "average" metric calculations
});
// console.log(`[Analytics] Reported scene playtime: ${sceneName} (${gameMode}) - ${duration}s`);
} else {
console.warn("[Analytics] gtag not found, cannot report playtime.");
}
};
+213
View File
@@ -0,0 +1,213 @@
//v9
document.addEventListener('DOMContentLoaded', () => {
const canvas = document.getElementById('unity-canvas');
if (!canvas) {
console.error("Canvas element with id='unity-canvas' not found!");
return;
}
// Track lock state
window.isPointerLocked = false;
// Track if unadjustedMovement is supported (assume yes until proven otherwise)
let unadjustedMovementSupported = true;
// Utility: Check if pointer lock API is available
function hasPointerLockAPI() {
return !!(canvas.requestPointerLock ||
canvas.webkitRequestPointerLock ||
canvas.mozRequestPointerLock);
}
// Safari detection
function isSafari() {
// This UA check excludes Chrome on iOS
return /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
}
// Called when pointer lock state changes
function onPointerLockChange() {
const doc = document;
const locked = (
doc.pointerLockElement === canvas ||
doc.webkitPointerLockElement === canvas ||
doc.mozPointerLockElement === canvas
);
window.isPointerLocked = locked;
if (locked) {
console.log("----cursor locking----");
canvas.style.cursor = "none";
} else {
console.log("----cursor unlocking----");
canvas.style.cursor = "default";
}
sendLockStateToUnity(locked);
}
// Called when pointer lock errors out
function onPointerLockError(event) {
console.error("----pointer lock error----", event);
sendLockStateToUnity(false);
// For macOS Safari: add a one-time mousedown fallback if pointer isn't locked.
if (isSafari() && !window.isPointerLocked) {
console.log("----Safari fallback: adding one-time mousedown listener for pointer lock----");
addOneTimeMouseDownListener();
}
}
// Helper to add a one-time mousedown listener for fallback pointer lock
function addOneTimeMouseDownListener() {
// Use a named function so we can remove it if needed
function fallbackMouseDown(e) {
console.log("----Fallback mousedown triggered, retrying pointer lock----");
canvas.removeEventListener('mousedown', fallbackMouseDown);
lockPointerNow();
}
canvas.addEventListener('mousedown', fallbackMouseDown, false);
}
// Listen for pointer lock changes/errors (all vendor prefixes)
document.addEventListener('pointerlockchange', onPointerLockChange, false);
document.addEventListener('webkitpointerlockchange', onPointerLockChange, false);
document.addEventListener('mozpointerlockchange', onPointerLockChange, false);
document.addEventListener('pointerlockerror', onPointerLockError, false);
document.addEventListener('webkitpointerlockerror', onPointerLockError, false);
document.addEventListener('mozpointerlockerror', onPointerLockError, false);
// Send pointer lock state back to Unity
function sendLockStateToUnity(isLocked) {
if (typeof unityInstance !== 'undefined' && unityInstance && unityInstance.SendMessage) {
try {
unityInstance.SendMessage('CursorController', 'OnLockChange', isLocked ? 'true' : 'false');
} catch (e) {
console.warn("----Failed to send lock state to Unity----", e);
}
}
}
// Request pointer lock without options (basic fallback)
function requestPointerLockBasic() {
if (canvas.requestPointerLock) {
canvas.requestPointerLock();
} else if (canvas.webkitRequestPointerLock) {
canvas.webkitRequestPointerLock();
} else if (canvas.mozRequestPointerLock) {
canvas.mozRequestPointerLock();
}
}
// Request pointer lock with unadjustedMovement option
async function requestPointerLockWithOptions() {
if (canvas.requestPointerLock) {
return canvas.requestPointerLock({ unadjustedMovement: true });
} else if (canvas.webkitRequestPointerLock) {
return canvas.webkitRequestPointerLock({ unadjustedMovement: true });
} else if (canvas.mozRequestPointerLock) {
return canvas.mozRequestPointerLock({ unadjustedMovement: true });
}
}
// Attempt to lock pointer
async function lockPointerNow() {
console.log("----attempting pointer lock----");
if (!hasPointerLockAPI()) {
console.warn("----Pointer lock API not supported in this browser----");
sendLockStateToUnity(false);
return;
}
// If Safari, skip the options param entirely
if (isSafari()) {
console.log("----Safari detected; skipping unadjustedMovement param----");
try {
requestPointerLockBasic();
} catch (err) {
console.error("----Safari pointer lock request failed----", err);
sendLockStateToUnity(false);
}
return;
}
// Non-Safari browsers
// Check permissions if available (non-blocking, just for logging)
if (navigator.permissions && navigator.permissions.query) {
try {
const permStatus = await navigator.permissions.query({ name: 'pointer-lock' });
console.log("----pointer lock permission state: " + permStatus.state + "----");
} catch (permError) {
// Permissions API may not support pointer-lock query - that's fine
console.log("----Permissions API query not supported for pointer-lock----");
}
}
// Try with unadjustedMovement if supported, otherwise use basic request
if (unadjustedMovementSupported) {
try {
await requestPointerLockWithOptions();
console.log("----pointer lock request initiated with unadjustedMovement----");
} catch (err) {
// Check if error is due to unadjustedMovement not being supported
if (err && (err.name === 'NotSupportedError' || err.name === 'TypeError')) {
console.warn("----unadjustedMovement not supported, falling back to basic pointer lock----");
unadjustedMovementSupported = false;
// Retry immediately with basic request
try {
requestPointerLockBasic();
console.log("----pointer lock request initiated (basic fallback)----");
} catch (fallbackErr) {
console.error("----pointer lock request failed (basic fallback)----", fallbackErr);
sendLockStateToUnity(false);
}
} else {
console.error("----pointer lock request failed (non-Safari)----", err);
sendLockStateToUnity(false);
}
}
} else {
// We already know unadjustedMovement isn't supported, use basic request
try {
requestPointerLockBasic();
console.log("----pointer lock request initiated (basic, cached)----");
} catch (err) {
console.error("----pointer lock request failed (basic)----", err);
sendLockStateToUnity(false);
}
}
}
// Attempt to unlock pointer
function unlockPointerNow() {
console.log("----attempting pointer unlock----");
const doc = document;
if (doc.pointerLockElement === canvas ||
doc.webkitPointerLockElement === canvas ||
doc.mozpointerLockElement === canvas) {
if (doc.exitPointerLock) {
doc.exitPointerLock();
} else if (doc.webkitExitPointerLock) {
doc.webkitExitPointerLock();
} else if (doc.mozExitPointerLock) {
doc.mozExitPointerLock();
}
console.log("----pointer unlock request sent----");
} else {
console.warn("----pointer is not locked to the canvas----");
sendLockStateToUnity(false);
}
}
// Expose lock/unlock globally for Unity ExternalCall usage
window.lockPointerNow = lockPointerNow;
window.unlockPointerNow = unlockPointerNow;
// Expose a function to change cursor style
window.setCursor = function setCursor(cursorStyle) {
if (canvas) {
canvas.style.cursor = cursorStyle;
}
};
});
+139
View File
@@ -0,0 +1,139 @@
// Exit intent detection based on mouse movement patterns
let lastMousePosition = { x: 0, y: 0 };
let lastMouseMoveTime = 0;
let lastActivityTime = Date.now();
let exitIntentDetected = false;
let cornerDetectionTimeout = null;
let afkTimeout = null;
// Configuration
const CORNER_THRESHOLD = 50; // pixels from corner to consider it a corner
const MOVEMENT_THRESHOLD = 3; // minimum pixels to consider it a movement
const TIME_THRESHOLD = 3000; // 3 seconds to detect slow movement
const SLOW_MOVEMENT_THRESHOLD = 100; // pixels per second to consider it slow movement
const AFK_THRESHOLD = 240000; // 4 minutes in milliseconds
// Check if device is a touch-only mobile device
function isTouchOnlyDevice() {
// Check if device has touch capability
const hasTouch = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
// Check if device is a mobile device (excluding tablets and laptops with touch)
const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
// Check if device has a keyboard (laptops/desktops)
const hasKeyboard = navigator.keyboard !== undefined;
// Device is touch-only if it has touch, is mobile, and doesn't have a keyboard
return hasTouch && isMobile && !hasKeyboard;
}
function sendExitIntentToUnity() {
if (typeof unityInstance !== 'undefined') {
unityInstance.SendMessage('WebUtils', 'OnExitIntent');
}
}
function initExitIntentDetection() {
// Only initialize if not a touch-only device
if (isTouchOnlyDevice()) {
return;
}
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseleave', handleMouseLeave);
document.addEventListener('keydown', handleActivity);
document.addEventListener('click', handleActivity);
// Start AFK timer
resetAFKTimer();
}
function handleActivity() {
lastActivityTime = Date.now();
resetAFKTimer();
}
function resetAFKTimer() {
// Clear existing AFK timeout if any
if (afkTimeout) {
clearTimeout(afkTimeout);
}
// Set new AFK timeout
afkTimeout = setTimeout(() => {
if (!exitIntentDetected) {
exitIntentDetected = true;
sendExitIntentToUnity();
}
}, AFK_THRESHOLD);
}
function handleMouseMove(e) {
const currentTime = Date.now();
const timeDiff = currentTime - lastMouseMoveTime;
// Update last activity time
lastActivityTime = currentTime;
resetAFKTimer();
// Calculate movement speed
const distance = Math.sqrt(
Math.pow(e.clientX - lastMousePosition.x, 2) +
Math.pow(e.clientY - lastMousePosition.y, 2)
);
const speed = distance / (timeDiff / 1000); // pixels per second
// Check if mouse is in a corner and moving slowly
const isInCorner = isMouseInCorner(e.clientX, e.clientY);
const isMovingSlowly = speed < SLOW_MOVEMENT_THRESHOLD && distance > MOVEMENT_THRESHOLD;
if (isInCorner && isMovingSlowly && !exitIntentDetected) {
if (!cornerDetectionTimeout) {
cornerDetectionTimeout = setTimeout(() => {
if (isMouseInCorner(e.clientX, e.clientY)) {
exitIntentDetected = true;
sendExitIntentToUnity();
}
}, TIME_THRESHOLD);
}
} else if (!isInCorner || !isMovingSlowly) {
// Reset detection if mouse moves out of corner or moves too fast
if (cornerDetectionTimeout) {
clearTimeout(cornerDetectionTimeout);
cornerDetectionTimeout = null;
}
exitIntentDetected = false;
}
// Update last position and time
lastMousePosition = { x: e.clientX, y: e.clientY };
lastMouseMoveTime = currentTime;
}
function handleMouseLeave(e) {
// If mouse leaves through the top, consider it an exit intent
if (e.clientY <= 0) {
sendExitIntentToUnity();
}
}
function isMouseInCorner(x, y) {
const windowWidth = window.innerWidth;
const windowHeight = window.innerHeight;
// Check top-left corner
if (x <= CORNER_THRESHOLD && y <= CORNER_THRESHOLD) {
return true;
}
// Check top-right corner
if (x >= windowWidth - CORNER_THRESHOLD && y <= CORNER_THRESHOLD) {
return true;
}
return false;
}
// Initialize when the script loads
initExitIntentDetection();
+43
View File
@@ -0,0 +1,43 @@
const servers = [
'fra-1.2v2.io',
'usa-1.2v2.io'
];
function getRandomServer() {
return servers[Math.floor(Math.random() * servers.length)];
}
async function fetchBestRegion() {
const maxAttempts = 3;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
const randomServer = getRandomServer();
try {
const response = await fetch(`https://${randomServer}/api/init`);
if (response.ok) {
const data = await response.json();
const bestRegion = data.region; // use the "region" from the response JSON
console.log(`Attempt ${attempt}: Best region from server ${randomServer}: ${bestRegion}`);
return bestRegion;
} else {
console.error(`Attempt ${attempt}: Server ${randomServer} responded with status: ${response.status}`);
}
} catch (error) {
console.error(`Attempt ${attempt}: Error fetching best region from ${randomServer}:`, error);
}
}
console.error('All attempts failed. Falling back to default: USA');
return 'USA';
}
const cachedBestServer = localStorage.getItem('bestServer');
if (!cachedBestServer) {
fetchBestRegion().then(bestRegion => {
console.log('Best region to connect (Saving to localStorage):', bestRegion);
localStorage.setItem('bestServer', bestRegion);
}).catch(error => {
console.error('Error during region fetching:', error);
});
} else {
console.log('Using cached best server:', cachedBestServer);
}
+131
View File
@@ -0,0 +1,131 @@
window.deferredInstall = null;
window.addEventListener('beforeinstallprompt', (e) => {
window.deferredInstall = e;
console.log('PWA install prompt has been saved.');
if (typeof unityInstance !== 'undefined' && unityInstance != null) {
unityInstance.SendMessage('PWAManager', 'OnPwaPromptAvailabilityChanged', window.deferredInstall ? "1" : "0");
}
});
// Mobile fullscreen and viewport handling
(function () {
const ua = navigator.userAgent || '';
const isIOS = /iP(ad|hone|od)/.test(ua) || (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1);
const isAndroid = /Android/i.test(ua);
function enterFullscreen() {
const canvas = document.getElementById('unity-canvas') || document.documentElement;
// Prefer Unity's API when available
if (typeof window.unityInstance !== 'undefined' && window.unityInstance && typeof window.unityInstance.SetFullscreen === 'function') {
try { window.unityInstance.SetFullscreen(1); return; } catch (e) { /* fallback below */ }
}
const req = canvas.requestFullscreen || canvas.webkitRequestFullscreen || canvas.msRequestFullscreen || canvas.mozRequestFullScreen;
if (req) {
try { req.call(canvas); } catch (e) { console.warn('requestFullscreen failed:', e); }
}
}
function setupAndroidAutoFullscreenOnGesture() {
if (!isAndroid) return;
// Create a transparent full-screen overlay button to guarantee a user gesture
const overlayId = 'android-fs-overlay';
if (document.getElementById(overlayId)) return;
const overlay = document.createElement('button');
overlay.id = overlayId;
overlay.type = 'button';
overlay.setAttribute('aria-label', 'Enter Fullscreen');
overlay.style.cssText = 'position:fixed;inset:0;z-index:2147483647;background:transparent;border:0;margin:0;padding:0;width:100%;height:100%;opacity:0;touch-action:manipulation;';
const onTap = () => {
enterFullscreen();
if (screen.orientation && screen.orientation.lock) {
try { screen.orientation.lock('landscape'); } catch (_) {}
}
// Remove overlay after attempting fullscreen
requestAnimationFrame(() => {
overlay.remove();
});
};
overlay.addEventListener('pointerdown', onTap, { once: true });
overlay.addEventListener('click', onTap, { once: true });
overlay.addEventListener('touchend', onTap, { once: true });
document.addEventListener('DOMContentLoaded', () => {
document.body.appendChild(overlay);
});
}
function setupIOSDynamicViewportAndBarCollapse() {
if (!isIOS) return;
// Allow root scroll so Safari can collapse the URL bar.
// Do not block touchmove anywhere.
document.documentElement.style.overflowY = 'scroll';
document.documentElement.style.webkitOverflowScrolling = 'touch';
document.body.style.overflowY = 'visible';
// Dynamic 1% viewport unit to handle iOS chrome changes
const setVh = () => {
const vh = window.innerHeight * 0.01;
document.documentElement.style.setProperty('--vh', vh + 'px');
};
setVh();
window.addEventListener('resize', setVh);
if (window.visualViewport && typeof window.visualViewport.addEventListener === 'function') {
window.visualViewport.addEventListener('resize', setVh);
}
document.addEventListener('visibilitychange', setVh);
// Ensure extra scroll area exists at the bottom
const ensureFiller = () => {
let filler = document.getElementById('ios-filler');
if (!filler) {
filler = document.createElement('div');
filler.id = 'ios-filler';
filler.style.cssText = 'height:140px;width:1px;opacity:0;pointer-events:none;';
document.body.appendChild(filler);
}
};
ensureFiller();
// Nudge scroll on load and retries to encourage bar collapse
let nudgeCount = 0;
const tryNudge = () => {
ensureFiller();
try { window.scrollTo(0, 1); } catch (e) {}
nudgeCount++;
if (nudgeCount < 5) setTimeout(tryNudge, 300);
};
window.addEventListener('load', () => setTimeout(tryNudge, 400));
window.addEventListener('orientationchange', () => setTimeout(tryNudge, 400));
window.addEventListener('resize', () => setTimeout(tryNudge, 400));
}
function applySizing() {
const container = document.getElementById('unity-container');
const canvas = document.getElementById('unity-canvas');
if (!container || !canvas) return;
// Prefer visualViewport to avoid initial 50% height bug on iOS
if (window.visualViewport && typeof window.visualViewport.height === 'number') {
container.style.height = window.visualViewport.height + 'px';
canvas.style.height = '100%';
} else {
const vhVar = getComputedStyle(document.documentElement).getPropertyValue('--vh');
if (vhVar) {
const h = parseFloat(vhVar) * 100;
container.style.height = h + 'px';
canvas.style.height = '100%';
}
}
container.style.width = '100%';
}
window.addEventListener('resize', applySizing);
if (window.visualViewport && typeof window.visualViewport.addEventListener === 'function') {
window.visualViewport.addEventListener('resize', applySizing);
}
document.addEventListener('DOMContentLoaded', applySizing);
setupAndroidAutoFullscreenOnGesture();
setupIOSDynamicViewportAndBarCollapse();
})();
+57
View File
@@ -0,0 +1,57 @@
// sw.js
// Import Workbox from the CDN
importScripts('https://storage.googleapis.com/workbox-cdn/releases/6.5.4/workbox-sw.js');
// Turn off debug logging for production
workbox.setConfig({ debug: false });
// Versioning your cache
const CACHE_VERSION = 'v1';
const RUNTIME_CACHE = `runtime-cache-${CACHE_VERSION}`;
// Cache Unity build files with StaleWhileRevalidate strategy
workbox.routing.registerRoute(
({ url }) => url.href.includes('Build'),
new workbox.strategies.StaleWhileRevalidate({
cacheName: RUNTIME_CACHE,
plugins: [
new workbox.expiration.ExpirationPlugin({
maxEntries: 50, // Adjust as needed
purgeOnQuotaError: true,
}),
],
})
);
// Cache other assets (images, CSS, JS) with StaleWhileRevalidate
workbox.routing.registerRoute(
({ request }) =>
request.destination === 'style' ||
request.destination === 'script' ||
request.destination === 'image',
new workbox.strategies.StaleWhileRevalidate({
cacheName: 'assets-cache',
plugins: [
new workbox.expiration.ExpirationPlugin({
maxEntries: 100, // Adjust as needed
purgeOnQuotaError: true,
}),
],
})
);
// Use NetworkFirst for HTML files to ensure you get the latest version
workbox.routing.registerRoute(
({ request }) => request.mode === 'navigate',
new workbox.strategies.NetworkFirst({
cacheName: 'pages-cache',
plugins: [
new workbox.expiration.ExpirationPlugin({
maxEntries: 20, // Adjust as needed
purgeOnQuotaError: true,
}),
],
})
);
+228
View File
@@ -0,0 +1,228 @@
//fingerprint
/*
window.deviceId = localStorage.getItem("device_id");
if (!window.deviceId) {
const script = document.createElement('script');
script.src = "https://openfpcdn.io/fingerprintjs/v3";
script.async = true;
script.onload = () => {
FingerprintJS.load()
.then(fp => fp.get())
.then(result => {
window.deviceId = result.visitorId;
localStorage.setItem("device_id", window.deviceId);
})
.catch(() => {
window.deviceId = crypto.randomUUID();
localStorage.setItem("device_id", window.deviceId);
});
};
script.onerror = () => {
window.deviceId = crypto.randomUUID();
localStorage.setItem("device_id", window.deviceId);
};
document.head.appendChild(script);
}
*/
//fingerprint-end
//iframe-detection
try {
window.iframed = window.self !== window.top;
} catch (e) {
window.iframed = true;
}
//iframe-detection-end
const useKeyboardApiOnFullscreen = true;
function setURLHash(param) {
var hash = String(param);
if (typeof history.replaceState === 'function') {
if (hash) {
history.replaceState(null, null, '#' + encodeURIComponent(hash));
} else {
history.replaceState(null, null, window.location.pathname + window.location.search);
}
} else {
if (hash) {
window.location.hash = encodeURIComponent(hash);
} else {
window.location.hash = '';
}
}
}
function getURLHash() {
var hash = window.location.hash ? window.location.hash.substring(1) : null;
return hash ? decodeURIComponent(hash) : null;
}
window.alert = function() {
console.log('Blocked alert');
};
window.confirm = function() {
console.log('Blocked confirm');
return false;
};
window.prompt = function() {
console.log('Blocked prompt');
return null;
};
window.toggleFullscreen = toggleFullscreen;
function toggleFullscreen() {
if (!document.fullscreenElement && // Standard browsers
!document.mozFullScreenElement && // Firefox
!document.webkitFullscreenElement && // Chrome, Safari, Opera
!document.msFullscreenElement) { // IE/Edge
// Enter fullscreen mode
enterFullScreenMode();
} else {
// Exit fullscreen mode
exitFullscreenMode();
}
}
window.enterFullScreenMode = enterFullScreenMode;
function enterFullScreenMode(){
var elem = document.documentElement;
if (elem.requestFullscreen) {
elem.requestFullscreen();
} else if (elem.mozRequestFullScreen) {
elem.mozRequestFullScreen();
} else if (elem.webkitRequestFullscreen) {
elem.webkitRequestFullscreen();
} else if (elem.msRequestFullscreen) {
elem.msRequestFullscreen();
}
// Lock screen orientation
if (screen.orientation && screen.orientation.lock) {
screen.orientation.lock('landscape').catch(() => { /* Ignore errors */ });
}
// Use Keyboard Lock API if enabled and supported
if (useKeyboardApiOnFullscreen && 'keyboard' in navigator && 'lock' in navigator.keyboard) {
// Lock the ESC key immediately after entering fullscreen
navigator.keyboard.lock(['Escape']).then(() => {
console.log('ESC key locked.');
}).catch(err => {
console.error('Failed to lock keyboard:', err);
});
}
}
function exitFullscreenMode(){
if (document.exitFullscreen) {
document.exitFullscreen();
} else if (document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if (document.webkitExitFullscreen) {
document.webkitExitFullscreen();
} else if (document.msExitFullscreen) {
document.msExitFullscreen();
}
// Unlock the keyboard
if (useKeyboardApiOnFullscreen && 'keyboard' in navigator && 'unlock' in navigator.keyboard) {
navigator.keyboard.unlock();
console.log('Keyboard unlocked.');
}
}
document.addEventListener('keydown', function(event) {
if (event.key === 'Escape') {
if (document.pointerLockElement) {
document.exitPointerLock();
console.log('Pointer unlocked due to ESC key press.');
}
}
});
window.addEventListener('beforeunload', function (e) {
//if (!window.isPointerLocked)
// return;
e.preventDefault();
e.returnValue = '';
return '';
});
function goToWindow(windowName) {
const currentPath = window.location.pathname;
const newPath = `/${windowName}`;
if (currentPath === newPath) {
return;
}
history.pushState({}, '', newPath);
}
window.addEventListener('popstate', function(event) {
sendPopState();
});
function sendPopState() {
unityInstance.SendMessage('BrowserHistoryManager', 'PopState', window.location.pathname);
}
async function setClipboard(stringToSetAsClipboard) {
try {
await navigator.clipboard.writeText(stringToSetAsClipboard);
console.log('Text copied to clipboard successfully!');
} catch (err) {
console.error('Failed to copy text: ', err);
}
}
function getClipboard() {
navigator.clipboard.readText()
.then((clipboardContent) => {
unityInstanceWrapper.sendMessage('MainManager', 'OnGotClipboard', clipboardContent);
})
.catch((error) => {
console.error('Clipboard access error:', error);
});
}
// Prevent default scrolling behavior
window.addEventListener("wheel", (event) => event.preventDefault(), {
passive: false,
});
// Prevent default actions for specific keys
window.addEventListener("keydown", (event) => {
const key = event.key;
const ctrlPressed = event.ctrlKey || event.metaKey; // For Mac compatibility
// Prevent default actions for ArrowUp, ArrowDown, Space, Escape, and Ctrl+W
if (
["ArrowUp", "ArrowDown"].includes(key) ||
key === "Escape" ||
(ctrlPressed && key.toLowerCase() === "w")
) {
event.preventDefault();
}
});
// Rare situation where the GPU resets
window.addEventListener("webglcontextlost", function (event) {
event.preventDefault();
console.warn("WebGL context lost. Reloading...");
// Prevent unload blocker
window.onbeforeunload = null;
setTimeout(() => location.reload(), 100);
});