ZeroAd: Unity WebGL

Production-grade Unity SDKManager callback interceptor - force rewards

Du musst eine Erweiterung wie Tampermonkey, Greasemonkey oder Violentmonkey installieren, um dieses Skript zu installieren.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

Sie müssten eine Skript Manager Erweiterung installieren damit sie dieses Skript installieren können

(Ich habe schon ein Skript Manager, Lass mich es installieren!)

Advertisement:

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

Advertisement:

// ==UserScript==
// @name         ZeroAd: Unity WebGL
// @namespace    https://greasyfork.org/users/zeroadv4
// @version      4.0.0
// @description  Production-grade Unity SDKManager callback interceptor - force rewards
// @author       ZeroAd Team
// @match        *://*/*
// @grant        none
// @run-at       document-start
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // ═══════════════════════════════════════════════════════════════
    // CONFIGURATION
    // ═══════════════════════════════════════════════════════════════
    const CONFIG = {
        DEBUG: true,
        SCAN_INTERVAL_MS: 2000,
        MAX_SCANS: 30 // 60 seconds total
    };

    const METRICS = {
        patchedInstances: 0,
        interceptedCalls: 0,
        forcedRewards: 0
    };

    // ═══════════════════════════════════════════════════════════════
    // UNITY SDK METHOD TARGETS
    // ═══════════════════════════════════════════════════════════════
    const SDK_METHODS = [
        'OnVideoAdEnded',
        'OnVideoAdError',
        'OnVideoAdSkipped',
        'OnRewardedAdComplete',
        'OnRewardedAdFailed',
        'OnAdComplete',
        'OnAdError',
        'OnAdSkipped',
        'AdComplete',
        'AdFailed',
        'AdSkipped',
        'RewardGranted',
        'RewardFailed'
    ];

    const SDK_OBJECTS = [
        'SDKManager',
        'AdsManager',
        'AdManager',
        'RewardManager',
        'VideoAdManager'
    ];

    // ═══════════════════════════════════════════════════════════════
    // UTILITY LAYER
    // ═══════════════════════════════════════════════════════════════
    const log = (...args) => CONFIG.DEBUG && console.log('[ZeroAd:Unity]', ...args);
    const warn = (...args) => CONFIG.DEBUG && console.warn('[ZeroAd:Unity]', ...args);
    const error = (...args) => console.error('[ZeroAd:Unity]', ...args);

    // ═══════════════════════════════════════════════════════════════
    // HOOK LAYER
    // ═══════════════════════════════════════════════════════════════

    /**
     * Patch Unity instance SendMessage to force reward callbacks
     */
    function patchUnityInstance(instance) {
        if (!instance || instance.__zaPatched || typeof instance.SendMessage !== 'function') {
            return false;
        }

        try {
            const originalSendMessage = instance.SendMessage;

            instance.SendMessage = function(objectName, methodName, param) {
                METRICS.interceptedCalls++;

                // Check if this is an SDK callback
                const isSDKObject = SDK_OBJECTS.includes(objectName);
                const isSDKMethod = SDK_METHODS.includes(methodName);

                if (isSDKObject && isSDKMethod) {
                    // Check if parameter indicates failure
                    const isFalse = param === 'false' ||
                                   param === false ||
                                   param === '0' ||
                                   param === 0;

                    if (isFalse) {
                        log(`${objectName}.${methodName}("${param}") → forcing "true"`);
                        param = 'true';
                        METRICS.forcedRewards++;
                    } else {
                        log(`${objectName}.${methodName}("${param}") → pass through`);
                    }
                }

                // Call original with potentially modified parameter
                return originalSendMessage.call(this, objectName, methodName, param);
            };

            instance.__zaPatched = true;
            METRICS.patchedInstances++;
            log('✓ Unity instance patched');
            return true;

        } catch (e) {
            error('Failed to patch Unity instance:', e);
            return false;
        }
    }

    /**
     * Scan for Unity instances across multiple possible locations
     */
    function scanAndPatchInstances() {
        let foundAny = false;

        // Check window.unityInstance (most common)
        if (window.unityInstance && !window.unityInstance.__zaPatched) {
            if (patchUnityInstance(window.unityInstance)) {
                foundAny = true;
            }
        }

        // Check window.gameInstance (alternative naming)
        if (window.gameInstance && !window.gameInstance.__zaPatched) {
            if (patchUnityInstance(window.gameInstance)) {
                foundAny = true;
            }
        }

        // Check window.unity (another variant)
        if (window.unity && typeof window.unity.SendMessage === 'function' && !window.unity.__zaPatched) {
            if (patchUnityInstance(window.unity)) {
                foundAny = true;
            }
        }

        // Check for instances in UnityLoader namespace
        if (window.UnityLoader?.instances) {
            Object.values(window.UnityLoader.instances).forEach(instance => {
                if (instance && !instance.__zaPatched) {
                    if (patchUnityInstance(instance)) {
                        foundAny = true;
                    }
                }
            });
        }

        return foundAny;
    }

    // ═══════════════════════════════════════════════════════════════
    // PROPERTY TRAPS
    // ═══════════════════════════════════════════════════════════════

    /**
     * Install property trap for window.unityInstance
     */
    function installUnityInstanceTrap() {
        let _unityInstance = window.unityInstance;

        Object.defineProperty(window, 'unityInstance', {
            configurable: true,
            enumerable: true,
            get() {
                return _unityInstance;
            },
            set(newValue) {
                if (newValue && !newValue.__zaPatched) {
                    log('unityInstance assigned, patching...');
                    patchUnityInstance(newValue);
                }
                _unityInstance = newValue;
            }
        });

        log('✓ unityInstance trap installed');
    }

    /**
     * Install property trap for window.gameInstance
     */
    function installGameInstanceTrap() {
        let _gameInstance = window.gameInstance;

        Object.defineProperty(window, 'gameInstance', {
            configurable: true,
            enumerable: true,
            get() {
                return _gameInstance;
            },
            set(newValue) {
                if (newValue && !newValue.__zaPatched) {
                    log('gameInstance assigned, patching...');
                    patchUnityInstance(newValue);
                }
                _gameInstance = newValue;
            }
        });

        log('✓ gameInstance trap installed');
    }

    // ═══════════════════════════════════════════════════════════════
    // ORCHESTRATION LAYER
    // ═══════════════════════════════════════════════════════════════
    let scanCount = 0;
    let scanInterval = null;

    /**
     * Periodic scan for Unity instances
     */
    function startPeriodicScan() {
        scanInterval = setInterval(() => {
            scanCount++;

            const foundNew = scanAndPatchInstances();

            if (foundNew) {
                log(`Found and patched instance(s) on scan ${scanCount}`);
            }

            // Stop scanning after max attempts
            if (scanCount >= CONFIG.MAX_SCANS) {
                log(`Stopping periodic scan after ${scanCount} attempts`);
                clearInterval(scanInterval);
            }

        }, CONFIG.SCAN_INTERVAL_MS);

        log('✓ Periodic scan started');
    }

    /**
     * Setup mutation observer to detect Unity loader scripts
     */
    function setupMutationObserver() {
        const observer = new MutationObserver((mutations) => {
            for (const mutation of mutations) {
                for (const node of mutation.addedNodes) {
                    if (node.tagName === 'SCRIPT') {
                        const src = node.src || '';
                        if (src.includes('unity') ||
                            src.includes('Build') ||
                            src.includes('loader.js')) {
                            log('Unity script detected:', src.substring(0, 80));

                            node.addEventListener('load', () => {
                                log('Unity script loaded, scanning for instances');
                                setTimeout(scanAndPatchInstances, 100);
                            });
                        }
                    }

                    // Check for canvas elements (Unity render target)
                    if (node.tagName === 'CANVAS') {
                        log('Canvas element added, scanning for Unity instances');
                        setTimeout(scanAndPatchInstances, 100);
                    }
                }
            }
        });

        if (document.documentElement) {
            observer.observe(document.documentElement, {
                childList: true,
                subtree: true
            });
        }

        log('✓ MutationObserver active');
    }

    // ═══════════════════════════════════════════════════════════════
    // INITIALIZATION
    // ═══════════════════════════════════════════════════════════════
    function initialize() {
        log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
        log('ZeroAd: Unity WebGL Reward Fix v4.0.0');
        log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');

        // Install property traps
        installUnityInstanceTrap();
        installGameInstanceTrap();

        // Immediate scan
        scanAndPatchInstances();

        // Start periodic scanning
        startPeriodicScan();

        // Setup mutation observer
        setupMutationObserver();

        // Expose metrics
        window._zaMetrics = window._zaMetrics || {};
        window._zaMetrics.unity = METRICS;

        // Status report
        setTimeout(() => {
            log('━━━ Status Report ━━━');
            log('Metrics:', METRICS);
            log('━━━━━━━━━━━━━━━━━━━━━');
        }, 10000);
    }

    // Run immediately
    initialize();

    // Defensive: re-run on DOM ready
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', scanAndPatchInstances);
    }

})();