Greasy Fork is available in English.

Enchanted Agar.io

Optimize Agar.io performance and blocking ads.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey, το Greasemonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

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

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Userscripts για να εγκαταστήσετε αυτόν τον κώδικα.

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

Θα χρειαστεί να εγκαταστήσετε μια επέκταση διαχείρισης κώδικα χρήστη για να εγκαταστήσετε αυτόν τον κώδικα.

(Έχω ήδη έναν διαχειριστή κώδικα χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

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.

(Έχω ήδη έναν διαχειριστή στυλ χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

// ==UserScript==
// @name         Enchanted Agar.io
// @icon         https://i.imgur.com/sjYtMDN.png
// @namespace    https://greatest.deepsurf.us/users/1372128
// @version      1.4
// @description  Optimize Agar.io performance and blocking ads.
// @author       Dragon9135
// @match        *://agar.io/*
// @run-at       document-start
// @grant        none
// @license      Custom License
// ==/UserScript==

// Copyright (C) 2026 Dragon9135
// This software cannot be modified, copied, or redistributed in any form.

(function() {
    'use strict';

    // ─── Constants ────────────────────────────────────────────────────────────────
    const SPLIT_DELAY = 25;
    const AD_SELECTORS = [
        'iframe[src*="ads"]', 'iframe[src*="adserver"]',
        'iframe[src*="doubleclick"]', 'iframe[src*="googlesyndication"]',
        '[id*="adBanner"]', '[id*="adContainer"]', '[id*="ad-container"]',
        '[class*="adBox"]', '[class*="ad-container"]',
        '[id*="google_ads"]', '[class*="google_ads"]',
        '[id*="agar-io_300x250"]', '[class*="agar-io_300x250"]',
        '[id*="agar-io_160x600"]', '[class*="agar-io_160x600"]',
        '[id*="google_ads_iframe"]', '[class*="google_ads_iframe"]',
        '[id*="agar-io_160x600_2"]', '[class*="agar-io_160x600_2"]',
        '[id*="agar-io_970x90"]', '[class*="agar-io_970x90"]',
        '#preroll', '.preroll', '#adsBottom', '.adsBottom',
        '[id^="google_ads"]',
        '[id*="divFullscreenLoading"]', '[class*="divFullscreenLoading"]'
    ].join(',');

    // ─── WASM Binary Patch ───────────────────────────────────────────────────────

    function findPattern(buffer, pattern) {
        for (let i = 0; i <= buffer.length - pattern.length; i++) {
            let match = true;
            for (let j = 0; j < pattern.length; j++) {
                if (buffer[i + j] !== pattern[j]) {
                    match = false;
                    break;
                }
            }
            if (match) return i;
        }
        return -1;
    }

    function concatUint8Arrays(arrays) {
        const total = arrays.reduce((s, a) => s + a.length, 0);
        const result = new Uint8Array(total);
        let offset = 0;
        for (const a of arrays) {
            result.set(a, offset);
            offset += a.length;
        }
        return result;
    }

    function readULEB(buffer, offset) {
        let result = 0,
            shift = 0,
            pos = offset;
        while (pos < buffer.length) {
            const byte = buffer[pos++];
            result |= (byte & 0x7f) << shift >>> 0;
            if ((byte & 0x80) === 0) break;
            shift += 7;
        }
        return {
            value: result >>> 0,
            length: pos - offset
        };
    }

    function writeULEB(value) {
        const out = [];
        let v = value >>> 0;
        do {
            let byte = v & 0x7f;
            v >>>= 7;
            if (v !== 0) byte |= 0x80;
            out.push(byte);
        } while (v !== 0);
        return new Uint8Array(out);
    }

    function fixSectionSizeByDelta(u8, sectionId, delta) {
        if (delta === 0) return u8;
        if (u8[0] !== 0x00 || u8[1] !== 0x61 || u8[2] !== 0x73 || u8[3] !== 0x6d) return u8;
        let i = 8;
        while (i < u8.length) {
            const sid = u8[i++];
            const sizeInfo = readULEB(u8, i);
            const sizeStart = i,
                sizeLen = sizeInfo.length;
            const payloadSize = sizeInfo.value >>> 0;
            if (sid === sectionId) {
                const newSize = payloadSize + delta;
                if (newSize < 0) return u8;
                const newSizeBytes = writeULEB(newSize);
                return concatUint8Arrays([u8.slice(0, sizeStart), newSizeBytes, u8.slice(sizeStart + sizeLen)]);
            }
            i += sizeLen + payloadSize;
        }
        return u8;
    }

    function applyPatch(u8, operations) {
        let result = u8;
        const initialLength = u8.length;
        let anyFail = false;
        for (const {
                pattern,
                payload,
                type
            }
            of operations) {
            const index = findPattern(result, pattern);
            if (index === -1) {
                console.warn('[Enchanted] WASM pattern not found:', pattern.map(b => b.toString(16)).join(' '));
                anyFail = true;
                continue;
            }
            const pi = index + pattern.length;
            if (type === 'insertAfter') {
                result = concatUint8Arrays([result.slice(0, pi), new Uint8Array(payload), result.slice(pi)]);
            } else if (type === 'replaceAfter') {
                result = concatUint8Arrays([result.slice(0, pi), new Uint8Array(payload), result.slice(pi + payload.length)]);
            } else if (type === 'replaceUlebAfter') {
                const {
                    value: oldVal,
                    length: oldLen
                } = readULEB(result, pi);
                const newBytes = writeULEB(oldVal + (result.length - initialLength));
                result = concatUint8Arrays([result.slice(0, pi), newBytes, result.slice(pi + oldLen)]);
            }
        }
        return anyFail ? u8 : result;
    }

    function patchWasm(buffer) {
        const bytes = hex => hex.split(' ').map(b => parseInt(b, 16));
        const original = new Uint8Array(buffer);
        const patched = applyPatch(original, [{
                pattern: bytes('D4 01 2D 00 00 45 0D 00 20 02 10 0F 20 01 20 02 10 1E 21 01'),
                payload: bytes('20 00 28 02 1C 45 04 40 0F 0B'),
                type: 'insertAfter'
            },
            {
                pattern: bytes('00 0B 37 03 00 20 00 20 04 37 03 08 20 03 41 10 6A 24 00 0B'),
                payload: bytes('8A'),
                type: 'replaceAfter'
            },
            {
                pattern: bytes('41 1B 6C 41 01 6A 73 3A 00 07 20 1F BF 44 00 00 00 00 00 00'),
                payload: bytes('00 00'),
                type: 'replaceAfter'
            }
        ]);
        const delta = patched.length - original.length;
        return (delta === 0 ? patched : fixSectionSizeByDelta(patched, 0x0a, delta)).buffer;
    }

    // Intercept agario.core.js before it's added to DOM, apply WASM patch
    function initCoreInterceptor() {
        const observer = new MutationObserver((mutations) => {
            for (const mutation of mutations) {
                for (const node of mutation.addedNodes) {
                    if (node.nodeName !== 'SCRIPT') continue;
                    const src = node.src || '';
                    if (!src.match(/agario\.core\.js/i)) continue;

                    observer.disconnect();
                    node.remove();

                    const xhr = new XMLHttpRequest();
                    xhr.open('GET', src, true);
                    xhr.responseType = 'text';
                    xhr.onload = () => {
                        const origInstantiate = WebAssembly.instantiate;
                        WebAssembly.instantiate = function(bufferOrModule, importObject) {
                            if (bufferOrModule instanceof ArrayBuffer) {
                                bufferOrModule = patchWasm(bufferOrModule);
                            }
                            return origInstantiate.call(this, bufferOrModule, importObject);
                        };
                        const blob = new Blob([xhr.responseText], {
                            type: 'text/javascript'
                        });
                        const blobURL = URL.createObjectURL(blob);
                        const script = document.createElement('script');
                        script.src = blobURL;
                        script.onload = () => URL.revokeObjectURL(blobURL);
                        // document.body might not exist yet at document-start
                        (document.body || document.documentElement).appendChild(script);
                        console.log('[Enchanted] agario.core.js intercepted, WASM patch applied.');
                    };
                    xhr.onerror = () => {
                        (document.body || document.documentElement).appendChild(node);
                        console.warn('[Enchanted] Core intercept failed, loading original...');
                    };
                    xhr.send();
                    return;
                }
            }
        });
        observer.observe(document, {
            childList: true,
            subtree: true
        });
    }

    // ─── FPS Limit Removal ─────────────────────────────────────────────────────
    // Caught when core is set, setFpsCap is always called with -1
    function initFpsCap() {
        let _core;
        Object.defineProperty(window, 'core', {
            get() {
                return _core;
            },
            set(value) {
                _core = value;
                setTimeout(() => {
                    if (typeof _core?.setFpsCap !== 'function') return;
                    const orig = _core.setFpsCap;
                    _core.setFpsCap = () => orig(-1);
                    orig(-1);
                    console.log('[Enchanted] FPS limit removed.');
                    // Enable the Minimap
                    enableMinimap(_core);
                }, 0);
            },
            configurable: true,
        });
    }

    // Run immediately at document-start
    initCoreInterceptor();
    initFpsCap();

    // ─── Minimap ─────────────────────────────────────────────────────────────────
    function enableMinimap(coreObj) {
        if (typeof coreObj !== 'undefined') {
            if (typeof coreObj.setMinimap === 'function') {
                coreObj.setMinimap(1);
                console.log('[Enchanted] Minimap: It worked successfully.');
            } else {
                console.warn('[Enchanted] core.setMinimap function could not be found.');
            }

            if (typeof coreObj.playersMinimap === 'function') {
                coreObj.playersMinimap(1);
            }
        }
    }

    // ─── Ad Removal (DOM) ────────────────────────────────────────────────────────
    let adCheckScheduled = false;

    function scheduleAdCheck() {
        if (adCheckScheduled) return;
        adCheckScheduled = true;
        requestIdleCallback(() => {
            removeAds();
            adCheckScheduled = false;
        }, {
            timeout: 500
        });
    }

    function removeAds() {
        document.querySelectorAll(AD_SELECTORS).forEach(el => el.remove());
        document.documentElement.style.setProperty('--bottom-banner-height', '0px');
    }

    function adjustBannerHeight() {
        document.documentElement.style.setProperty('--bottom-banner-height', '0px');
    }

    // ─── Advanced Ad Blocking ────────────────────────────────────────────────────

    // 1) Disable ads by setting isPayingUser=true
    function patchPayingUser() {
        document.addEventListener('update_user_info', function(e) {
            if (e.detail?.isPayingUser) return;
            const detail = Object.assign({}, e.detail, {
                isPayingUser: true
            });
            e.stopPropagation();
            document.dispatchEvent(new CustomEvent('update_user_info', {
                detail
            }));
        }, true);
    }

    // 2) Make agarApp.ads API no-op
    function patchAgarAppAds() {
        const noop = () => {};
        const adsPatch = {
            requestAds: noop,
            requestAd: noop,
            refreshAd: noop,
            destroyAd: noop,
            adSlots: noop,
            enableTargetedAds: noop,
            disableTargetedAds: noop,
            isTargeted: noop,
            supersonicAds: {
                BrandConnectReadyEvent: noop,
                BrandConnectDoneEvent: noop,
                BrandConnectOpenEvent: noop,
                BrandConnectCloseEvent: noop,
                BrandConnectCompletedEvent: noop,
                hasEngagement: () => false,
            }
        };

        function applyAdsPatch() {
            if (!window['agarApp']) return;
            window['agarApp'].ads = Object.assign(window['agarApp'].ads || {}, adsPatch);
        }
        let _agarApp = window['agarApp'];
        Object.defineProperty(window, 'agarApp', {
            get() {
                return _agarApp;
            },
            set(value) {
                _agarApp = value;
                applyAdsPatch();
            },
            configurable: true,
        });
        applyAdsPatch();
    }

    // 3) toggleTargetedAds — crashes when this._ads is undefined, wrap with try/catch
    function patchToggleTargetedAds() {
        const waitForCore = setInterval(() => {
            if (typeof window['core'] === 'undefined') return;
            clearInterval(waitForCore);
            try {
                const proto = Object.getPrototypeOf(window['core']);
                if (proto && typeof proto.toggleTargetedAds === 'function') {
                    const orig = proto.toggleTargetedAds;
                    proto.toggleTargetedAds = function() {
                        try {
                            return orig.apply(this, arguments);
                        } catch (e) {}
                    };
                }
            } catch (e) {}
        }, 200);
    }

    // 4) Patch Vue components upon loading
    function findVueNodes(root, cond) {
        const results = [];

        function walk(node) {
            if (!node) return;
            if (cond(node)) results.push(node);
            (node.$children || []).forEach(walk);
            (node.children || []).forEach(walk);
        }
        walk(root);
        return results;
    }

    function patchVueAdNodes() {
        const waitForApp = setInterval(() => {
            if (!window['agarApp']?.home) return;
            clearInterval(waitForApp);
            setTimeout(() => {
                const app = window['agarApp'];
                findVueNodes(app.home, n => {
                    const tag = (n.$vnode?.tag) || '';
                    return tag.includes('-ads') || tag.includes('-promo');
                }).forEach(n => {
                    try {
                        n.$destroy();
                    } catch (e) {}
                });

                findVueNodes(app.home, n => n.elm?.id === 'socialButtons')
                    .forEach(n => {
                        try {
                            n.elm.parentElement.removeChild(n.elm);
                        } catch (e) {}
                    });

                const adNode = findVueNodes(app.home, n =>
                    Object.getPrototypeOf(n).hasOwnProperty('hasBottomAd')
                )[0];
                if (adNode) {
                    ['hasBottomAd', 'hasSideAds'].forEach(prop => {
                        if (adNode._computedWatchers?.[prop])
                            adNode._computedWatchers[prop].getter = () => false;
                    });
                    Object.defineProperties(adNode, {
                        hasBottomAd: {
                            get: () => false,
                            configurable: true
                        },
                        hasSideAds: {
                            get: () => false,
                            configurable: true
                        },
                        fastEntry: {
                            get: () => true,
                            configurable: true
                        },
                    });
                }
                adjustBannerHeight();
            }, 500);
        }, 500);
    }

    // ─── Auto Collect Coins ───────────────────────────────────────────────────────
    let isLoggedIn = false;
    let coinCollectPending = false;

    function collectCoins() {
        if (!isLoggedIn || coinCollectPending) return;
        coinCollectPending = true;
        try {
            if (window['agarApp']?.API) {
                window['agarApp'].API.getFreeCoins();
                setTimeout(() => {
                    try {
                        window['agarApp'].API.closeTopView();
                    } catch (e) {}
                    coinCollectPending = false;
                }, 300);
                console.log('[Enchanted] Auto Collect Coins: It worked successfully.');
            } else {
                coinCollectPending = false;
            }
        } catch (e) {
            console.warn('[Enchanted] Auto Collect Coins error:', e);
            coinCollectPending = false;
        }
    }

    function initAutoCollectCoins() {
        window.addEventListener('login', () => {
            if (isLoggedIn) return;
            isLoggedIn = true;
            coinCollectPending = false;
            setTimeout(collectCoins, 500);
        });
        window.addEventListener('logout', () => {
            isLoggedIn = false;
            coinCollectPending = false;
        });
        window.addEventListener('free_coins_timer', () => {
            coinCollectPending = false;
            collectCoins();
        });
    }

    // ─── UI Customization ────────────────────────────────────────────────────────
    function addInstructions() {
        const instructions = document.getElementById('instructions');
        if (instructions) {
            instructions.innerHTML += `
                <center><span class='text-muted'>
                    <span>Press <b>D</b> or <b>2</b> to doublesplit</span><br>
                    <span>Press <b>3</b> to triplesplit</span><br>
                    <span>Press <b>T</b> or <b>4</b> to tricksplit</span><br>
                    <span>Press <b>E</b> to eject macro mass</span>
                </span></center>
            `;
        }
    }

    function customizeUI() {
        const mainUI = document.getElementById('mainui-play');
        if (mainUI) mainUI.style.height = '385px';
        const nickInput = document.getElementById('nick');
        if (nickInput) nickInput.maxLength = 99;
    }

    // ─── Key Controls ────────────────────────────────────────────────────────────
    let isFeedActive = false;

    function dispatchKey(keyCode, type) {
        window.dispatchEvent(new KeyboardEvent(type, {
            keyCode,
            bubbles: true
        }));
    }

    function triggerFeed() {
        dispatchKey(87, 'keydown');
        dispatchKey(87, 'keyup');
    }

    function triggerSplit() {
        dispatchKey(32, 'keydown');
        dispatchKey(32, 'keyup');
    }

    function splitMultiple(times) {
        triggerSplit();
        for (let i = 1; i < times; i++) setTimeout(triggerSplit, SPLIT_DELAY * i);
    }

    function macroFeed() {
        if (!isFeedActive) return;
        triggerFeed();
        setTimeout(macroFeed, SPLIT_DELAY);
    }

    function keydown(event) {
        switch (event.keyCode) {
            case 69:
                if (!isFeedActive) {
                    isFeedActive = true;
                    macroFeed();
                }
                break;
            case 84:
            case 52:
                splitMultiple(4);
                break;
            case 51:
                splitMultiple(3);
                break;
            case 68:
            case 50:
                splitMultiple(2);
                break;
            case 49:
                triggerSplit();
                break;
        }
    }

    function keyup(event) {
        if (event.keyCode === 69) isFeedActive = false;
    }
    window.addEventListener('keydown', keydown);
    window.addEventListener('keyup', keyup);

    // ─── MutationObserver ─────────────────────────────────────────────────────────
    const domObserver = new MutationObserver((mutations) => {
        let hasNewNodes = false;
        let overlaysChanged = false;
        for (const mutation of mutations) {
            if (mutation.addedNodes.length > 0) hasNewNodes = true;
            for (const node of [...mutation.addedNodes, ...mutation.removedNodes]) {
                if (node.id === 'overlays') overlaysChanged = true;
            }
        }
        if (hasNewNodes) scheduleAdCheck();
    });

    // ─── Initialization ──────────────────────────────────────────────────────────
    function init() {
        addInstructions();
        customizeUI();
        removeAds();
        patchPayingUser();
        patchAgarAppAds();
        patchToggleTargetedAds();
        patchVueAdNodes();
        initAutoCollectCoins();

        domObserver.observe(document.body, {
            childList: true,
            subtree: true
        });
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }

})();