Sigmally Fixes

Many necessary improvements for Sigmally and SigMod

目前为 2024-01-04 提交的版本。查看 最新版本

// ==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);
})();