Back to Limb Race
SOURCE / PINNED RELEASE

Made of little things.

Limb Race

Release
6a8ae4fc626e…
Author-recorded commit
008c01ee6454…
License
LICENSE
Author’s source reference
nostr://npub182jczunncwe0jn6frpqwq3e0qjws7yqqnc3auccqv9nte2dnd63scjm4rf/wss%3A%2F%2Fgit.napplet.soy%2F/n-c2869f12ad8

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

src/scores.ts
import { cvm, storage } from '@napplet/sdk';
import {encodeDrawing, decodeDrawing} from './race-data.js';
import {validateSnapshot, type LevelSnapshot} from './level-model.js';
import backend from '../soy-backend.json';
import { runtimeHasDomain } from './domain-availability.js';
import { validDesign, TRACKS, PHYSICS_VERSION, type Design, type Mode, type Material } from './physics.js';
export type Edit={at:number;design:Design};
export type Run={id:string;track:string;mode:Mode;material:Material;physics:number;ms:number;name:string;actor?:string;created:number;design:Design;edits:Edit[];level?:LevelSnapshot;shared?:boolean};
export type BoardRow={actor:string;score:number;name:string;updated:number;revision:string;hasData:boolean};
export type Board={rows:BoardRow[];own:BoardRow|null;trust:string;nextOffset:number|null};
const KEY='limb-race-v2';
export function boardId(track:string,mode:Mode,material:Material):string {return `${track}-${mode}-${material}-v${PHYSICS_VERSION}-draw1`;}
export function hasSharedBoard(track:string,mode:Mode,material:Material):boolean {return backend.boards.includes(boardId(track,mode,material));}
export async function callBoard(tool:string,args:Record<string,unknown>={}):Promise<unknown> {
  if(!runtimeHasDomain('cvm'))throw new Error('Shared scores are unavailable in this host. Personal races still work.');
  if(!await cvm.registry.has('soy.boards.v2'))throw new Error('Shared drawings need an updated score service (soy.boards.v2). Your race is kept locally.');
  const provider=(backend as {provider?:{pubkey:string;relays:string[]}}).provider;
  const result=provider?await cvm.callTool(provider,tool,args):await cvm.registry.call('soy.boards.v2',tool,args);
  if(result.isError)throw new Error(result.content?.filter(x=>x.type==='text').map(x=>x.text).join(' ').slice(0,180)||'Score service unavailable. Try again.');
  return result.structuredContent;
}
export async function readBoard(track:string,mode:Mode,material:Material,offset=0):Promise<Board> {
  if(!hasSharedBoard(track,mode,material))throw new Error('This course keeps personal records only.');
  const value=await callBoard('soy_board_read',{napplet:backend.napplet,board:boardId(track,mode,material),limit:20,offset}) as Board;
  if(!value||!Array.isArray(value.rows))throw new Error('The score service returned an unreadable board.');
  return {...value,rows:value.rows.filter(r=>Number.isFinite(r.score)&&r.score>0&&r.score<=180000&&typeof r.actor==='string'&&typeof r.name==='string'&&typeof r.revision==='string'&&typeof r.hasData==='boolean').sort((a,b)=>a.score-b.score)};
}
export async function submitRun(run:Run):Promise<void> {
  if(!hasSharedBoard(run.track,run.mode,run.material))throw new Error('This course keeps personal records only.');
  await callBoard('soy_board_submit',{napplet:backend.napplet,board:boardId(run.track,run.mode,run.material),score:run.ms,name:run.name.slice(0,40),data:encodeDrawing(run.design)});
}
export async function readSharedRun(track:string,mode:Mode,material:Material,row:BoardRow):Promise<Run> {
  const result=await callBoard('soy_board_entry',{napplet:backend.napplet,board:boardId(track,mode,material),actor:row.actor,revision:row.revision}) as {stale:boolean;entry:(BoardRow&{data:unknown})|null};
  if(result?.stale)throw new Error('This racer has a newer best. Refresh the board and select it again.');
  const entry=result?.entry;
  if(!entry||entry.revision!==row.revision||entry.actor!==row.actor||entry.score!==row.score||typeof entry.name!=='string'||!Number.isFinite(entry.updated))throw new Error('This run is no longer available. Refresh the board.');
  return {id:entry.revision,track,mode,material,physics:PHYSICS_VERSION,ms:entry.score,name:entry.name,actor:entry.actor,created:entry.updated,design:decodeDrawing(entry.data),edits:[],shared:true};
}
function validRunLevel(run:Run):boolean {
  if(TRACKS.some(t=>t.id===run.track))return true;
  try{return !!run.level&&validateSnapshot(run.level).key===run.track;}catch{return false;}
}
export async function loadHistory():Promise<{runs:Run[];design:Design;name:string}|null> {
  if(!runtimeHasDomain('storage'))return null;
  const raw=await storage.getItem(KEY);
  const legacy=raw?null:await storage.getItem('limb-race-v1');
  if(!raw&&!legacy)return null;
  const data=JSON.parse(raw??legacy!);
  if(!raw)data.runs=[]; // Original times remain in their untouched v1 store.
  return {runs:Array.isArray(data.runs)?data.runs.filter((r:Run)=>r&&typeof r.id==='string'&&typeof r.name==='string'&&Number.isFinite(r.ms)&&r.ms>0&&r.ms<=180000&&validRunLevel(r)&&r.physics===PHYSICS_VERSION&&['rigid','bendy'].includes(r.material)&&['classic','live'].includes(r.mode)&&validDesign(r.design)&&Array.isArray(r.edits)&&r.edits.length<=100&&r.edits.every(e=>Number.isFinite(e.at)&&validDesign(e.design))).slice(0,40):[],design:validDesign(data.design)?data.design:[],name:typeof data.name==='string'?data.name.slice(0,24):'Guest racer'};
}
export async function saveHistory(runs:Run[],design:Design,name:string):Promise<boolean> {
  if(!runtimeHasDomain('storage'))return false;
  // A bounded timeline and history keep the 512 KB storage budget predictable.
  let kept=runs.slice(0,40),json=JSON.stringify({runs:kept,design,name});
  while(json.length>230000&&kept.length>1){kept=kept.slice(0,-1);json=JSON.stringify({runs:kept,design,name});}
  await storage.setItem(KEY,json);return true;
}