Pixel sites real online

Real online, allows canvas online

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

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.

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

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

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!)

// ==UserScript==
// @name         Pixel sites real online
// @namespace    https://pixels.fun
// @version      1.8
// @license      MIT
// @description  Real online, allows canvas online
// @author       small bee
// @match        *://*.fun/*
// @grant        none
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    let lastSum = 0;
    let textNode = null;
    let observer = null;

    async function updateOnlineCount() {
        try {
            const res = await fetch('/api/shards', { credentials: 'same-origin' });
            if (!res.ok) return;
            const data = await res.json();
            let sum = 0;
            data.forEach(item => {
                if (Array.isArray(item) && item[1]) {
                    Object.entries(item[1]).forEach(([k, v]) => {
                        if (k !== 'total' && typeof v === 'number') sum += v;
                    });
                }
            });

            const span = document.querySelector('.onlinebox span[title="Total Online Users"]');
            if (!span) return;

            if (!textNode) {
                textNode = Array.from(span.childNodes).find(n => n.nodeType === Node.TEXT_NODE);
                if (!textNode) {
                    textNode = document.createTextNode('');
                    span.insertBefore(textNode, span.firstChild);
                }
            }

            lastSum = sum;
            textNode.textContent = lastSum;
        } catch (err) {
            console.error('updateOnlineCount error', err);
        }
    }

    function observeTextNode() {
        if (observer) observer.disconnect();
        if (!textNode) return;

        observer = new MutationObserver(mutations => {
            const span = textNode.parentNode;
            if (span.getAttribute('title') !== 'Total Online Users') return;

            for (const m of mutations) {
                if (m.type === 'characterData' && m.target === textNode) {
                    if (m.target.nodeValue !== String(lastSum)) {
                        console.warn('Blocked external change to online count:', m.target.nodeValue);
                        m.target.nodeValue = String(lastSum);
                    }
                }
            }
        });

        observer.observe(textNode, { characterData: true });
    }

    async function showCanvasOnline() {
        const div = document.querySelector('.onlinebox');
        if (!div) return;

        div.addEventListener('click', async () => {
            const span = div.querySelector('span[title="Total Online Users"]');
            if (!span || span.querySelectorAll('svg').length < 2) return;

            const hash = window.location.hash;
            const match = /^#([a-z0-9])/i.exec(hash);
            if (!match) return;
            const letter = match[1];

            try {
                const meRes = await fetch('/api/me', { credentials: 'same-origin' });
                if (!meRes.ok) return;
                const me = await meRes.json();
                const canvases = me.canvases || {};

                let canvasId = null;
                for (const [id, info] of Object.entries(canvases)) {
                    if (info.ident === letter) {
                        canvasId = id;
                        break;
                    }
                }
                if (canvasId === null) return;

                const shardsRes = await fetch('/api/shards', { credentials: 'same-origin' });
                if (!shardsRes.ok) return;
                const shards = await shardsRes.json();

                let onlineCount = null;
                for (const item of shards) {
                    if (Array.isArray(item) && item[1] && item[1][canvasId] != null) {
                        onlineCount = item[1][canvasId];
                        break;
                    }
                }
                if (onlineCount == null) return;

                span.setAttribute('title', 'Online Users on Canvas');

                if (!textNode) {
                    textNode = Array.from(span.childNodes).find(n => n.nodeType === Node.TEXT_NODE);
                    if (!textNode) {
                        textNode = document.createTextNode('');
                        span.insertBefore(textNode, span.firstChild);
                    }
                }

                lastSum = onlineCount;
                textNode.textContent = lastSum;

                observeTextNode();
            } catch (err) {
                console.error('showCanvasOnline error', err);
            }
        });
    }

    updateOnlineCount().then(observeTextNode);
    setInterval(updateOnlineCount, 15000);
    showCanvasOnline();

})();