Sigmally Fixes

Many necessary improvements for Sigmally and SigMod

Verze ze dne 04. 01. 2024. Zobrazit nejnovější verzi.

K instalaci tototo skriptu si budete muset nainstalovat rozšíření jako Tampermonkey, Greasemonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Violentmonkey.

K instalaci tohoto skriptu si budete muset nainstalovat rozšíření jako Tampermonkey nebo Userscripts.

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

K instalaci tohoto skriptu si budete muset nainstalovat manažer uživatelských skriptů.

(Už mám manažer uživatelských skriptů, nechte mě ho nainstalovat!)

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.

(Už mám manažer uživatelských stylů, nechte mě ho nainstalovat!)

// ==UserScript==
// @name         Sigmally Fixes
// @namespace    https://8y8x.dev/sigmally-fixes
// @version      2024-01-03
// @description  Many necessary improvements for Sigmally and SigMod
// @author       8y8x
// @match        https://sigmally.com/
// @icon         https://8y8x.dev/favicon.ico
// @license      MIT
// @grant        none
// ==/UserScript==

'use strict';

async function wait(ms) {
	return new Promise(r => setTimeout(r, ms));
}

// Cell improvements
(async function() {
    const { options, settings } = await import('./assets/mjs/settings.mjs');

	// search for Cell prototype
	let CellProto;
	do {
		const allCells = options.cells.list;
		if (allCells.length === 0) {
			await wait(50);
		} else {
			CellProto = Object.getPrototypeOf(allCells[0]);
		}
	} while (!CellProto);

	// prevent setting transparency while drawing cells
	// gets rid of a lot of lag when big
	let drawingCell = false;
	const globalAlphaDescriptor = Object.getOwnPropertyDescriptor(CanvasRenderingContext2D.prototype, 'globalAlpha');
	Object.defineProperty(CanvasRenderingContext2D.prototype, 'globalAlpha', {
		set: function(x) {
			if (drawingCell) return;
			globalAlphaDescriptor.set.call(this, x);
		}
	})

	// don't call ctx.save() and ctx.restore(), saves a bit per frame
	// may cause problems in the future if rendering changes, but unlikely
	CellProto.draw = function(ctx) {
		ctx.globalAlpha = 1;
		drawingCell = true;
		try {
			this.drawShape(ctx);
			this.drawText(ctx);
		} finally {
			drawingCell = false;
		}
	}

    // outline cell if it can't split
	const oldDrawShape = CellProto.drawShape;
	CellProto.drawShape = function(ctx) {
		const { byId, mine } = options.cells;
		const idx = mine.indexOf(this.id);
		if (idx === -1)
			return oldDrawShape.call(this, ctx);

		if (this.ns >= 128) {
			// mass >= 163
			let nextId = options.cells.mine.length;
			for (let i = 0; i < idx; ++i) {
				const cell = byId[mine[i]];
				if (cell.ns >= 128) // mass >= 163
					++nextId;
			}

			if (nextId < 16)
				return oldDrawShape.call(this, ctx);
		}

		const realSColor = this.sColor;
		this.sColor = settings.gameSettings.darkTheme ? '#fff' : '#000';
		oldDrawShape.call(this, ctx);
		this.sColor = realSColor;
	}

	// sidestep text caching altogether - which significantly drags game performance -
	// and just draw the damn text
	// this mostly gets rid of lag spikes when switching tabs, especially when multiboxing
	let subFill;
	function drawText(ctx, x, y, size, text, isSub) {
		ctx.save();
		ctx.font = size + 'px Ubuntu';
		ctx.textBaseline = 'middle';
		ctx.textAlign = 'center';
		if (isSub) {
			if (!subFill) {
				subFill = ctx.createLinearGradient(0, 50, 200, 50);
				subFill.addColorStop(0, '#eb9500');
				subFill.addColorStop(0.53, '#f9bf0d');
				subFill.addColorStop(1, '#e4b110');
			}

			ctx.fillStyle = subFill;
			ctx.strokeStyle = '#40200a';
		} else {
			ctx.fillStyle = '#FFF';
			ctx.strokeStyle = '#000';
		}

		// note: game uses floor, i use ceil and +1
		ctx.lineWidth = Math.ceil(size / 10) + 1;
		if (ctx.lineWidth * options.camera.scale > 1)
			ctx.strokeText(text, x, y);
		ctx.fillText(text, x, y);
		ctx.restore();
	}

	// always draw mass text, avoid text caching
	CellProto.drawText = function(ctx) {
		if (this.isPellet || this.isJagged) return;
		let y = this.y;
		if (this.name && settings.gameSettings.showNames) {
			drawText(ctx, this.x, this.y, this.drawNameSize, this.name, this.isSub);
			y += Math.max(this.s / 4.5, this.nameSize / 1.5);
		}

		if (settings.gameSettings.showMass) {
			const mass = Math.floor(this.s * this.s / 100).toString();
			if (mass > 50)
				drawText(ctx, this.x, y, this.drawNameSize / 2, mass, this.isSub);
		}
	}
})();

// Input improvements
(async function() {
    const { options } = await import('./assets/mjs/settings.mjs');
    const { C, sendMouseMove } = await import('./assets/mjs/ws.mjs');

	let lastW = performance.now();

	const nativeSend = WebSocket.prototype.send;
	WebSocket.prototype.send = function(buf) {
		let dv;
		if (buf instanceof Uint8Array) {
			dv = new DataView(buf.buffer);
		} else if (buf instanceof ArrayBuffer) {
			dv = new DataView(buf);
		} else {
			nativeSend.call(this, buf);
			return;
		}


		if (dv.getUint8(0) === C[17]) { // space key / split
			const { mainCanvas } = options.gameOptions;
			// before splitting, always send your exact mouse position.
			// splits are sent immediately, but mouse movements are normally not. so, you might split before
			// the server knows where you aimed.
			setTimeout(() => {
				// copy+pasted from source
				sendMouseMove(
					(options.gameOptions.mouseX - mainCanvas.width / 2) /
						options.camera.scale +
						options.camera.x,
					(options.gameOptions.mouseY - mainCanvas.height / 2) /
						options.camera.scale +
						options.camera.y
				);

				nativeSend.call(this, buf);
			});
			return;
		}

		if (dv.getUint8(0) === C[21]) { // W
			// SigMod for whatever reason sends ~300 W's per second when only
			// 25/s are ever registered. we ratelimit most of these sends.
			if (performance.now() - lastW < 30) return;
			lastW = performance.now();
		}

		nativeSend.call(this, buf);
	}
})();

// Lag improvements
(async function() {
    // note: no idea if this actually does anything lol
    const oldRequestAnimationFrame = requestAnimationFrame;
    window.requestAnimationFrame = function(fn) {
        if (document.visibilityState === 'hidden') // if tab is not visible (ctrl+w'd away or minimized)
            oldRequestAnimationFrame(() => requestAnimationFrame(fn)); // try rendering again next frame
        else
            oldRequestAnimationFrame(fn);
    }
})();

// Input improvements
(async function() {
	// Create a dialog when closing a tab, in case of an accident
	addEventListener('beforeunload', e => {
	    e.preventDefault();
	    e.returnValue = '';
	});

	// Disable Ctrl+W (only when in fullscreen) - helps when multiboxing quickly
	document.addEventListener('keydown', e => {
	    const code = e.which || e.keyCode;
	    if (e.ctrlKey && code === 87) { // ctrl+w
	        e.preventDefault();
	    } else if (e.ctrlKey && code === 9) { // ctrl+tab
	        e.returnValue = true;
	        e.stopImmediatePropagation();
	    }
	});

	// Sometimes, splits won't go through. Happens if you hold Space while switching tabs, which can happen
	// often if multiboxing quickly.
	addEventListener('blur', () => {
		const ev = new KeyboardEvent('keyup', { key: ' ', code: 'Space' });
		dispatchEvent(ev);
	});
})();

(async function() {
	// SigMod-specific fixes

	// forcefully enable "remove outlines" (a SigMod feature) when big,
	// as it's ugly AND massively impacts performance. (30 FPS on ball game w/ RTX 3060?????)
	setInterval(() => {
		const checkbox = document.querySelector('#fps-remOutlines');
		if (!checkbox) return;

		if (!checkbox.checked) {
			checkbox.click(); // enable the checkbox
			checkbox.disabled = true;
		}
	}, 500);
})();