Skip to content

Instantly share code, notes, and snippets.

@JeremyRH
JeremyRH / date-utils.ts
Last active October 7, 2024 03:11
date-utils.ts
const datePartMapping = [
["year", "UTCFullYear"],
["month", "UTCMonth"],
["day", "UTCDate"],
["hour", "UTCHours"],
["minute", "UTCMinutes"],
["second", "UTCSeconds"],
] as const;
const dateValuesKeys = datePartMapping.map((m) => m[0]);
// ==UserScript==
// @name TagPro show 4 key-presses (spectator only)
// @version 1.0.0
// @description Shows up to four arrows on every player, one for every direction being pressed.
// @author github.com/JeremyRH
// @match https://tagpro.koalabeast.com/game
// @match https://bash-tp.github.io/tagpro-vcr/game.html
// @match https://keratagpro.github.io/tagpro-vcr/game.html
// @grant none
// ==/UserScript==

Bypassing a Browser Game's Client-Side Security

Not long ago, I used to play a real-time multiplayer browser game called TagPro. The architecture is fairly standard for real-time multiplayer games. The browser opens a websocket connection to a server and user input is sent over the open connection. The server runs all the game logic and sends the game state to each browser several times a second.

Client-Side Security

There are two separate versions of the JavaScript bundle that powers the TagPro client: a competitive version with client-side security and a casual version without. A toggle in private games enables the competitive version. The JavaScript for TagPro's competitive scene has a few security measures in place to prevent third-party script execution. The first security measure is not exposing the game object globally by wrapping the game.js bundle in a self-executing function:

(function init() {
  // game.js
  var tagpro = {
 ...
import { useState, useEffect } from 'react';
function sharedState(getState) {
const setters = new Set();
return [
() => {
const currentState = getState();
const setter = useState(currentState)[1];
useEffect(() => {
setters.add(setter);
@JeremyRH
JeremyRH / event-delegation-performance.md
Last active October 22, 2024 19:42
Does event delegation actually improve performance?

Does event delegation actually improve performance?

Event delegation works by attaching a single event listener to a parent element to catch events bubbling up from the children. Many people believe this is more performant than attaching event listeners to each child. I am not convinced this is always true.

Let's start with a common example of event delegation. Here we have a list of elements:

<ul id="item-list">
  <li data-cost="12">Item 1</li>
  <li data-cost="18">Item 2</li>
  <li data-cost="6">Item 3</li>
 ...
const divisibleBy = (divisor, int) => int % divisor === 0;
const fizzOrEmptyString = int => divisibleBy(3, int) ? "Fizz" : "";
const buzzOrEmptyString = int => divisibleBy(5, int) ? "Buzz" : "";
const fizzBuzz = int => fizzOrEmptyString(int) + buzzOrEmptyString(int) || String(int);
console.log(Array.from({ length: 100 }, (v, i) => fizzBuzz(i + 1)));
const applyBind = Function.prototype.bind.bind(Function.prototype.apply);
const mapSetApply = applyBind(Map.prototype.set);
const hijackedFns = new Map();
function hijackFunction(context, propName, replacer) {
mapSetApply(hijackedFns, [replacer, context[propName]]);
context[propName] = replacer;
}
const fnToStringApply = applyBind(Function.prototype.toString);
const mapGetApply = applyBind(Map.prototype.get);
hijackFunction(Function.prototype, 'toString', function toString() {
function getElementsByText(text, rootNode = document.documentElement) {
const nodeIterator = document.createNodeIterator(
rootNode,
NodeFilter.SHOW_TEXT,
(node) => node.nodeValue.trim() === text ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT
);
const matchingElements = [];
let currentNode;
while (currentNode = nodeIterator.nextNode()) {
const parentElement = currentNode.parentElement;