Sigmally Fixes

Many necessary improvements for Sigmally and SigMod

当前为 2024-01-07 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Sigmally Fixes
// @namespace    https://8y8x.dev/sigmally-fixes
// @version      2024-01-07
// @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
	function drawText(ctx, x, y, size, text, isSub) {
		ctx.save();
		ctx.font = size + 'px Ubuntu';
		ctx.textBaseline = 'middle';
		ctx.textAlign = 'center';
		if (isSub) {
			ctx.fillStyle = '#f9bf0d';
			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);
		}
	}
})();
 
// Networking 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(() => { // timeout to allow mouse events to fire
				// 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() {
	const { C, wsSend } = await import('./assets/mjs/ws.mjs');

	// 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; // undo e.preventDefault()
	        e.stopImmediatePropagation(); // prevent sigmod from calling e.preventDefault() afterwards
	    }
	});
 
	// 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);
	});

	// send the release of the Q key, which allows you to switch spectate modes
	document.addEventListener('keyup', e => {
		if (e.key === 'q') {
			wsSend(new Uint8Array([ C[19] ]));
		}
	});
})();
 
(async function() {
	// SigMod-specific fixes
 
	// forcefully enable "remove outlines" (a SigMod feature) when big,
	// as it's ugly AND massively impacts performance.
	setInterval(() => {
		const checkbox = document.querySelector('#fps-remOutlines');
		if (!checkbox) return;
 
		if (!checkbox.checked) {
			checkbox.click(); // enable the checkbox
			checkbox.disabled = true;
		}
	}, 500);
})();