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