Event‑driven loader for ZeroAd platform scripts (now with Yandex Games)
Pada tanggal
// ==UserScript==
// @name ZeroAd: Loader
// @namespace https://greasyfork.org/en/users/1483567-vujohn123
// @version 4.2.0
// @description Event‑driven loader for ZeroAd platform scripts (now with Yandex Games)
// @author ZeroAd Team
// @match *://*/*
// @grant none
// @run-at document-start
// @license MIT
// ==/UserScript==
(function() {
'use strict';
if (window.__zaLoaderLoaded) return;
window.__zaLoaderLoaded = true;
const SCRIPT_MAP = {
'poki': 579407,
'crazygames': 579404,
'adinplay': 579403,
'gamevui': 579405,
'unity': 583938,
'gamedistribution': 583939,
'playgama': 592446,
'yandex': 592456 // ← ZeroAd: Yandex Games
};
const FALLBACK_PLATFORM = 'adinplay';
const loaded = new Set();
const pending = new Set();
function loadScriptWithRetry(platformKey, retries = 5, delay = 100) {
const platform = SCRIPT_MAP[platformKey];
if (!platform || loaded.has(platformKey) || pending.has(platformKey)) return;
pending.add(platformKey);
const url = `https://greasyfork.org/scripts/${platform}/code/${platform}.user.js`;
const script = document.createElement('script');
const tryLoad = (attempt) => {
script.onload = () => {
loaded.add(platformKey);
pending.delete(platformKey);
console.log(`[ZeroAd] ✅ Loaded ${platformKey}`);
};
script.onerror = () => {
if (attempt < retries) {
const nextDelay = delay * Math.pow(2, attempt);
console.warn(`[ZeroAd] ⚠️ Retry ${platformKey} in ${nextDelay}ms (attempt ${attempt+1}/${retries})`);
setTimeout(() => tryLoad(attempt + 1), nextDelay);
} else {
pending.delete(platformKey);
console.error(`[ZeroAd] ❌ Failed to load ${platformKey} after ${retries} attempts`);
}
};
script.src = url;
(document.head || document.documentElement).appendChild(script);
};
tryLoad(0);
}
function detectPlatforms() {
const detected = new Set();
const host = window.location.hostname.toLowerCase();
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');
if (window.unityInstance || window.gameInstance || window.UnityLoader || document.querySelector('canvas.unity-canvas')) detected.add('unity');
if (detected.size === 0) detected.add(FALLBACK_PLATFORM);
return detected;
}
function installGlobalTrap() {
const sdkNames = [
'PokiSDK', 'CrazyGames', 'CrazygamesAds', 'GVAdBreak',
'GDSdk', 'gdApi', 'unityInstance', 'gameInstance',
'PlayGama', 'playgama', 'pg', 'PG',
'ysdk', 'YaGames' // ← Yandex Games SDK
];
sdkNames.forEach(name => {
let _value = window[name];
Object.defineProperty(window, name, {
configurable: true, enumerable: true,
get() { return _value; },
set(newVal) {
_value = newVal;
const detected = detectPlatforms();
detected.forEach(key => {
if (!loaded.has(key) && !pending.has(key)) loadScriptWithRetry(key);
});
}
});
});
}
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) {
const detected = detectPlatforms();
detected.forEach(key => {
if (!loaded.has(key) && !pending.has(key)) loadScriptWithRetry(key);
});
}
});
observer.observe(document.documentElement, { childList: true, subtree: true });
}
function init() {
installGlobalTrap();
setupObserver();
const initial = detectPlatforms();
initial.forEach(key => {
if (!loaded.has(key) && !pending.has(key)) loadScriptWithRetry(key);
});
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
const later = detectPlatforms();
later.forEach(key => loadScriptWithRetry(key));
});
}
}
if (document.head) init();
else {
const obs = new MutationObserver(() => {
if (document.head) { obs.disconnect(); init(); }
});
obs.observe(document.documentElement, { childList: true, subtree: true });
}
})();