SOURCE / PINNED RELEASE
Made of little things.
Napcraft
- Release
- ee47ac6a25df…
- Author-recorded commit
- 410dea87e109…
- License
- LICENSE
- Author’s source reference
- nostr://npub182jczunncwe0jn6frpqwq3e0qjws7yqqnc3auccqv9nte2dnd63scjm4rf/wss%3A%2F%2Fgit.napplet.soy%2F/n-52f9e22f5ce
Archive hash verified: 9e6e0cabb18d0d46…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
// Self-contained soy-ts-quickjs-v1 module. Only the provider supplies authority.
import type { BackendContext } from '../docs/examples/backend-context';
type Edit = {x:number;y:number;z:number;block:number;expected:number};
type Change = {n:number;x:number;y:number;z:number;block:number};
type World = {name:string;blocks:number[];edits:number;locked:boolean};
function delta(world:World, changes:Change[], after:number) {
const reset = after > world.edits || after < world.edits - changes.length;
return {from:after,edits:world.edits,locked:world.locked,reset,changes:reset?[]:changes.filter(c=>c.n>after)};
}
const W = 18,
H = 12;
function index(x: number, y: number, z: number) {
return x + z * W + y * W * W;
}
export async function handle(ctx: BackendContext, input: any) {
if (ctx.operation === 'createWorld') {
const blocks = Array(W * W * H).fill(0);
const put = (x: number, y: number, z: number, b: number) => {
blocks[index(x, y, z)] = b;
};
for (let x = 0; x < W; x++)
for (let z = 0; z < W; z++) {
put(x, 0, z, 2);
put(x, 1, z, 2);
put(x, 2, z, x < 2 || z < 2 || x > 15 || z > 15 ? 6 : 1);
if (x > 12 && z < 6) put(x, 3, z, 1);
}
for (const [x, z] of [
[3, 4],
[13, 12],
[5, 14],
]) {
put(x, 3, z, 3);
put(x, 4, z, 3);
for (let dx = -1; dx <= 1; dx++)
for (let dz = -1; dz <= 1; dz++) put(x + dx, 5, z + dz, 1);
put(x, 6, z, 1);
}
// A tiny welcoming pavilion; the rest is an open building canvas.
for (let x = 8; x <= 11; x++) for (let z = 3; z <= 6; z++) put(x, 3, z, 3);
for (const [x, z] of [
[8, 3],
[11, 3],
[8, 6],
[11, 6],
]) {
put(x, 4, z, 4);
put(x, 5, z, 4);
}
for (let x = 8; x <= 11; x++) for (let z = 3; z <= 6; z++) put(x, 6, z, 5);
const world = {
name: input.name.trim() || 'Our little island',
blocks,
edits: 0,
locked: false,
};
await ctx.state.setAccess({
visibility: 'public',
building: 'everyone',
guestsMayBuild: true,
members: {},
});
await ctx.state.set('worlds', 'island', world);
await ctx.state.set('editLog', 'recent', {changes:[]});
return world;
}
const world = await ctx.state.get<World>('worlds', 'island');
if (!world) throw new Error('NOT_FOUND: This island is unavailable.');
if (ctx.operation === 'readWorld') return world;
const journal = await ctx.state.get<{changes:Change[]}>('editLog','recent') ?? {changes:[]};
if (ctx.operation === 'readUpdates') return delta(world,journal.changes,input.after);
if (ctx.operation === 'setLocked') {
if (!ctx.account || ctx.principal !== ctx.owner)
throw new Error('FORBIDDEN: Only the world owner can lock building.');
world.locked = input.locked;
} else if (ctx.operation === 'editBlock' || ctx.operation === 'editBlocks') {
if (world.locked)
throw new Error('FORBIDDEN: The owner has paused building.');
const edits: Edit[] = ctx.operation === 'editBlocks' ? input.edits : [input];
if (!Array.isArray(edits) || edits.length < 1 || edits.length > 8) throw new Error('INVALID: Use 1–8 edits per batch.');
// Validate sequentially on a private copy. No records are written until ALL pass.
world.blocks = [...world.blocks];
for (const { x, y, z, block, expected } of edits) {
if (
![x, y, z, block, expected].every(Number.isInteger) ||
x < 0 ||
x >= W ||
z < 0 ||
z >= W ||
y < 2 ||
y >= H ||
block < 0 ||
block > 6
)
throw new Error('INVALID: Outside the buildable island.');
const i = index(x, y, z);
if (world.blocks[i] !== expected)
throw new Error(
'CONFLICT: This block changed. Look again before editing.',
);
if (block === expected)
throw new Error('INVALID: This block is already that material.');
if (block !== 0 && expected !== 0)
throw new Error('CONFLICT: Remove the existing block first.');
if (block !== 0 && world.blocks[index(x, y - 1, z)] === 0)
throw new Error('INVALID: Build on an existing block.');
if (block === 0 && y < H - 1 && world.blocks[index(x, y + 1, z)] !== 0)
throw new Error('INVALID: Remove the top block first.');
world.blocks[i] = block;
world.edits++;
journal.changes.push({n:world.edits,x,y,z,block});
}
journal.changes = journal.changes.slice(-128);
await ctx.state.set('editLog','recent',journal);
} else throw new Error('INVALID: Unknown operation.');
await ctx.state.set('worlds', 'island', world);
return ctx.operation === 'editBlocks' ? delta(world,journal.changes,input.after) : world;
}
