SOURCE / PINNED RELEASE
Made of little things.
Outbreak: Soycoon City
- Release
- db7750b1d009…
- Author-recorded commit
- 3ee3f5fd27a6…
- License
- LICENSE
- Author’s source reference
- nostr://npub182jczunncwe0jn6frpqwq3e0qjws7yqqnc3auccqv9nte2dnd63scjm4rf/wss%3A%2F%2Fgit.napplet.soy%2F/n-b87c04571a9
Archive hash verified: dbebdc772b142a1c…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
import { readFile, readdir } from 'node:fs/promises';
import { createHash } from 'node:crypto';
import assert from 'node:assert/strict';
import { gameModule } from './game-module.mjs';
const { parseStage, signature } = await gameModule('../src/world.ts');
const hash=bytes=>createHash('sha256').update(bytes).digest('hex');
function jpegSize(bytes){
assert.equal(bytes.readUInt16BE(0),0xffd8);
for(let at=2;at+9<bytes.length;){
assert.equal(bytes[at],0xff);const marker=bytes[at+1];
if(marker===0xc0||marker===0xc2)return [bytes.readUInt16BE(at+7),bytes.readUInt16BE(at+5)];
const length=bytes.readUInt16BE(at+2);assert(length>=2);at+=length+2;
}
throw new Error('JPEG has no supported size header.');
}
for (const name of (await readdir('levels')).filter(n => n.endsWith('.json') && !n.endsWith('.schema.json'))) {
const level = parseStage(await readFile(`levels/${name}`, 'utf8'));
for (const asset of level.assets ?? []) {
assert(asset.path, `${name}: repository assets need a path`);
const bytes = await readFile(asset.path);
assert.equal(bytes.length, asset.size, `${asset.id}: size changed`);
assert.equal(createHash('sha256').update(bytes).digest('hex'), asset.sha256, `${asset.id}: bytes changed; update the asset inventory`);
}
for (const room of level.rooms) for (const camera of room.cameras) if (camera.background) {
assert.equal(camera.background.signature, signature(room, camera), `${room.id}/${camera.id}: background no longer matches; regenerate/review art before stamping`);
}
if(name==='demo.json'){
const renders=JSON.parse(await readFile('docs/room-render-manifest.json','utf8'));
const cameras=level.rooms.flatMap(r=>r.cameras.map(c=>({room:r.id,camera:c.id,binding:c.background})));
assert.equal(renders.shots.length,cameras.length);assert.equal(new Set(renders.shots.map(s=>`${s.room}/${s.camera}`)).size,cameras.length);
for(const c of cameras){
assert(c.binding,`${c.room}/${c.camera}: demo view has no rendered background`);
const shot=renders.shots.find(s=>s.room===c.room&&s.camera===c.camera),asset=level.assets.find(a=>a.id===c.binding.asset);assert(shot);assert(asset);
assert.equal(shot.image,asset.path);assert.equal(shot.sha256,asset.sha256);assert.equal(shot.size,asset.size);
assert.deepEqual(jpegSize(await readFile(shot.image)),[640,480]);
const schema=await readFile(shot.schema);assert.equal(hash(schema),shot.schemaSha256);assert.equal(schema.readUInt32BE(16),640);assert.equal(schema.readUInt32BE(20),480);
assert((await readFile(shot.prompt,'utf8')).length>100);
}
}
console.log(`PASS ${name}: ${level.rooms.length} rooms, ${level.assets?.length ?? 0} verified assets`);
}
