Back to Supersonic RC Revive
SOURCE / PINNED RELEASE

Made of little things.

Supersonic RC Revive

Release
1ba42f1ca1d6…
Author-recorded commit
baecad10b1cd…
License
LICENSE
Author’s source reference
nostr://npub1ye5ptcxfyyxl5vjvdjar2ua3f0hynkjzpx552mu5snj3qmx5pzjscpknpr/wss%3A%2F%2Fgit.napplet.soy%2F/n-143146b0d6f

Archive hash verified: 90d22b206672eba4…. The source-to-build association is the author’s claim; it has not been independently rebuilt.

tools/build/rapier-slim.ts
// Strip the parts of Rapier's WASM this game never calls (soft bodies, joints,
// character/vehicle controllers, scene queries, serialization…): ~3.1 MB →
// ~2.1 MB before gzip.
//
// An export survives when any of these hold:
//  - it is wasm-bindgen plumbing (not a `raw*` binding), a constructor or a destructor;
//  - tools/build/rapier-exports.json lists it (recorded from real runs by
//    tools/build/record-rapier-exports.mjs);
//  - its method name matches a `.method(` call in src/game/physics/*.ts, so a
//    newly used Rapier method is kept even before the list is re-recorded.
// A binding that was stripped anyway fails loudly: "wasm.raw…_… is not a function".
// Set SSRC_RAPIER_FULL=1 to embed the untouched module.
import { readdirSync, readFileSync } from 'node:fs';
import { join } from 'node:path';
import binaryen from 'binaryen';

export function rapierKeepList(root: string): Set<string> {
  const keep = new Set<string>(JSON.parse(readFileSync(join(root, 'tools/build/rapier-exports.json'), 'utf8')).exports);
  const physics = join(root, 'src/game/physics');
  for (const file of readdirSync(physics).filter((f) => f.endsWith('.ts'))) {
    for (const m of readFileSync(join(physics, file), 'utf8').matchAll(/\.([A-Za-z]\w*)\(/g)) keep.add(`method:${m[1].toLowerCase()}`);
  }
  return keep;
}

function kept(name: string, keep: Set<string>): boolean {
  if (!name.startsWith('raw') || keep.has(name)) return true;
  if (/_new$|_free$/.test(name)) return true;
  // rawrigidbodyset_rbSetLinvel → setlinvel; rawcolliderset_coSetFriction → setfriction.
  const method = /^raw[a-z0-9]+_(?:rb|co)?([A-Za-z]\w*)$/.exec(name)?.[1];
  return method !== undefined && keep.has(`method:${method.toLowerCase()}`);
}

export function slimRapierWasm(wasm: Uint8Array, keep: Set<string>): Uint8Array {
  const mod = binaryen.readBinary(wasm);
  try {
    // Only features every current browser ships: binaryen's `All` would also
    // switch on newer binary encodings (e.g. compact imports) that browsers reject.
    const F = binaryen.Features;
    mod.setFeatures(F.MutableGlobals | F.NontrappingFPToInt | F.BulkMemory | F.BulkMemoryOpt | F.SignExt | F.ReferenceTypes | F.Multivalue);
    if (!mod.validate()) throw new Error('rapier-slim: the Rapier module needs WASM features outside the browser baseline');
    const drop: string[] = [];
    for (let i = 0; i < mod.getNumExports(); i++) {
      const info = binaryen.getExportInfo(mod.getExportByIndex(i));
      if (info.kind === binaryen.ExternalFunction && !kept(info.name, keep)) drop.push(info.name);
    }
    for (const name of drop) mod.removeExport(name);
    mod.runPasses(['remove-unused-module-elements']);
    return mod.emitBinary();
  } finally {
    mod.dispose();
  }
}