h
This commit is contained in:
@@ -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
@@ -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;
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
})();
|
||||
@@ -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
@@ -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);
|
||||
});
|
||||
Reference in New Issue
Block a user