Event‑driven loader for ZeroAd platform scripts (improved logging)
// ==UserScript==
// @name ZeroAd: Loader
// @namespace https://greasyfork.org/en/users/1483567-vujohn123
// @version 4.2.1
// @description Event‑driven loader for ZeroAd platform scripts (improved logging)
// @author ZeroAd Team
// @match *://*/*
// @grant none
// @run-at document-start
// @license MIT
// ==/UserScript==
(function() {
'use strict';
if (window.__zaLoaderLoaded) return;
window.__zaLoaderLoaded = true;
// ========== CONFIGURATION ==========
const CONFIG = {
DEBUG: true, // Set to false to disable verbose logs
RETRY_DELAY: 100, // Initial delay in ms
MAX_RETRIES: 5
};
// ========== LOGGER ==========
const PREFIX = '[ZeroAd:Loader]';
const log = {
info: (...args) => console.log(PREFIX, 'ℹ️', ...args),
debug: (...args) => CONFIG.DEBUG && console.log(PREFIX, '🐛', ...args),
warn: (...args) => console.warn(PREFIX, '⚠️', ...args),
error: (...args) => console.error(PREFIX, '❌', ...args),
success: (...args) => console.log(PREFIX, '✅', ...args),
};
log.info('Initializing...');
// ========== PLATFORM MAP ==========
const SCRIPT_MAP = {
'poki': 579407,
'crazygames': 579404,
'adinplay': 579403,
'gamevui': 579405,
'unity': 583938,
'gamedistribution': 583939,
'playgama': 592446,
'yandex': 592456
};
const FALLBACK_PLATFORM = 'unity';
const loaded = new Set();
const pending = new Set();
// ========== LOADER ENGINE ==========
function loadScriptWithRetry(platformKey, retries = CONFIG.MAX_RETRIES, delay = CONFIG.RETRY_DELAY) {
const platform = SCRIPT_MAP[platformKey];
if (!platform) {
log.warn(`Unknown platform key: "${platformKey}"`);
return;
}
if (loaded.has(platformKey)) {
log.debug(`Already loaded: ${platformKey}`);
return;
}
if (pending.has(platformKey)) {
log.debug(`Already pending: ${platformKey}`);
return;
}
pending.add(platformKey);
const url = `https://greasyfork.org/scripts/${platform}/code/${platform}.user.js`;
const script = document.createElement('script');
log.debug(`Loading "${platformKey}" from ${url}`);
const tryLoad = (attempt) => {
script.onload = () => {
loaded.add(platformKey);
pending.delete(platformKey);
log.success(`Loaded ${platformKey}`);
};
script.onerror = () => {
if (attempt < retries) {
const nextDelay = delay * Math.pow(2, attempt);
log.warn(`Retry ${platformKey} in ${nextDelay}ms (attempt ${attempt + 1}/${retries})`);
setTimeout(() => tryLoad(attempt + 1), nextDelay);
} else {
pending.delete(platformKey);
log.error(`Failed to load ${platformKey} after ${retries} attempts`);
}
};
script.src = url;
(document.head || document.documentElement).appendChild(script);
};
tryLoad(0);
}
// ========== DETECTION ==========
function detectPlatforms() {
const detected = new Set();
const host = window.location.hostname.toLowerCase();
// Host-based detection
if (host.includes('poki.com')) detected.add('poki');
if (host.includes('crazygames.com')) detected.add('crazygames');
if (host.includes('gamevui.vn')) detected.add('gamevui');
if (host.includes('gamedistribution.com')) detected.add('gamedistribution');
if (host.includes('playgama.com')) detected.add('playgama');
if (host.includes('yandex') || host.includes('games.yandex')) detected.add('yandex');
// Unity detection (global objects or canvas)
if (window.unityInstance ||
window.gameInstance ||
window.UnityLoader ||
document.querySelector('canvas.unity-canvas')) {
detected.add('unity');
}
// Fallback
if (detected.size === 0) {
log.debug('No platform detected, using fallback');
detected.add(FALLBACK_PLATFORM);
} else {
log.debug(`Detected: ${Array.from(detected).join(', ')}`);
}
return detected;
}
// ========== TRAP INSTALLATION ==========
function installGlobalTrap() {
const sdkNames = [
'PokiSDK', 'CrazyGames', 'CrazygamesAds', 'GVAdBreak',
'GDSdk', 'gdApi', 'unityInstance', 'gameInstance',
'PlayGama', 'playgama', 'pg', 'PG',
'ysdk', 'YaGames'
];
sdkNames.forEach(name => {
let _value = window[name];
Object.defineProperty(window, name, {
configurable: true,
enumerable: true,
get() {
return _value;
},
set(newVal) {
log.debug(`Trap triggered: window.${name} assigned`);
_value = newVal;
const detected = detectPlatforms();
detected.forEach(key => {
if (!loaded.has(key) && !pending.has(key)) {
loadScriptWithRetry(key);
}
});
}
});
});
log.debug('Global traps installed');
}
// ========== OBSERVER ==========
function setupObserver() {
const observer = new MutationObserver(mutations => {
let shouldCheck = false;
for (const m of mutations) {
for (const node of m.addedNodes) {
if (node.tagName === 'SCRIPT') {
shouldCheck = true;
break;
}
}
if (shouldCheck) break;
}
if (shouldCheck) {
log.debug('MutationObserver: script added, re-detecting...');
const detected = detectPlatforms();
detected.forEach(key => {
if (!loaded.has(key) && !pending.has(key)) {
loadScriptWithRetry(key);
}
});
}
});
observer.observe(document.documentElement, { childList: true, subtree: true });
log.debug('MutationObserver installed');
}
// ========== INIT ==========
function init() {
log.info('Starting...');
installGlobalTrap();
setupObserver();
const initial = detectPlatforms();
initial.forEach(key => {
if (!loaded.has(key) && !pending.has(key)) {
loadScriptWithRetry(key);
}
});
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
log.debug('DOMContentLoaded: re-detecting...');
const later = detectPlatforms();
later.forEach(key => loadScriptWithRetry(key));
});
}
log.info('Ready, waiting for events...');
}
// ========== EXECUTION ==========
if (document.head) {
init();
} else {
log.debug('Waiting for document.head...');
const obs = new MutationObserver(() => {
if (document.head) {
obs.disconnect();
init();
}
});
obs.observe(document.documentElement, { childList: true, subtree: true });
}
})();