SOURCE / PINNED RELEASE
Made of little things.
Browser Side Quest
- Release
- b9d42f5768e4…
- Author-recorded commit
- c267690145fa…
- License
- LICENSE
- Author’s source reference
- nostr://npub1ye5ptcxfyyxl5vjvdjar2ua3f0hynkjzpx552mu5snj3qmx5pzjscpknpr/wss%3A%2F%2Fgit.shakespeare.diy%2F/browser-side-quest
Archive hash verified: 6d4b90b8a5f328f7…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
// Port of BrowserQuest server/js/format.js (MPL-2.0).
// HELLO gains an optional trailing string: the player's self-claimed Nostr pubkey.
import { Messages } from './gametypes.js';
type Field = 'n' | 's';
const formats: Record<number, { required: Field[]; optional?: Field[] }> = {
[Messages.HELLO]: { required: ['s', 'n', 'n'], optional: ['s'] },
[Messages.MOVE]: { required: ['n', 'n'] },
[Messages.LOOTMOVE]: { required: ['n', 'n', 'n'] },
[Messages.AGGRO]: { required: ['n'] },
[Messages.ATTACK]: { required: ['n'] },
[Messages.HIT]: { required: ['n'] },
[Messages.HURT]: { required: ['n'] },
[Messages.CHAT]: { required: ['s'] },
[Messages.LOOT]: { required: ['n'] },
[Messages.TELEPORT]: { required: ['n', 'n'] },
[Messages.ZONE]: { required: [] },
[Messages.OPEN]: { required: ['n'] },
[Messages.CHECK]: { required: ['n'] },
};
const isNumber = (v: unknown): v is number => typeof v === 'number' && Number.isFinite(v);
function matches(field: Field, value: unknown): boolean {
return field === 'n' ? isNumber(value) : typeof value === 'string';
}
/** Validate a client→world message. */
export function check(message: unknown): message is unknown[] {
if (!Array.isArray(message) || message.length === 0) return false;
const [type, ...args] = message;
if (!isNumber(type)) return false;
if (type === Messages.WHO) {
return args.length > 0 && args.every(isNumber);
}
const format = formats[type];
if (!format) return false;
const optional = format.optional ?? [];
if (args.length < format.required.length) return false;
if (args.length > format.required.length + optional.length) return false;
const fields = [...format.required, ...optional];
return args.every((value, i) => matches(fields[i], value));
}
