Huge world size, and saves are gzip-compressed (about 60x smaller) (t3)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-24 17:51:42 -04:00
co-authored by Claude Opus 5.5
parent 4a8825415d
commit 56c93ae135
4 changed files with 65 additions and 10 deletions
+27 -4
View File
@@ -45,7 +45,7 @@ async function worldNewest(id) {
const sv = a && b ? (a.savedAt >= b.savedAt ? a : b) : a || b;
if (!sv) return null;
const entry = (await listWorlds()).find(w => w.id === id);
return { ...sv, worldId: id, worldName: entry ? entry.name : 'World' };
return { ...sv, buffers: await unpackBuffers(sv.buffers), worldId: id, worldName: entry ? entry.name : 'World' };
}
function deleteWorld(id) {
return worldWrite(async () => {
@@ -75,8 +75,9 @@ async function migrateWorlds() {
for (const [slot, old] of [['main', a], ['auto', b]]) if (old) {
const copy = await DB.get(`${id}:${slot}`);
if (!copy || JSON.stringify(copy.meta) !== JSON.stringify(old.meta) || copy.savedAt !== old.savedAt || copy.playTime !== old.playTime) throw new Error('Save copy failed.');
for (const k in old.buffers) {
const x = old.buffers[k], y = copy.buffers[k];
const original = await unpackBuffers(old.buffers), copied = await unpackBuffers(copy.buffers);
for (const k in original) {
const x = original[k], y = copied[k];
if (!y || x.constructor !== y.constructor || x.length !== y.length || !x.every((v, i) => v === y[i])) throw new Error('Save copy failed.');
}
}
@@ -91,12 +92,13 @@ async function saveWorld(slot, quiet) {
const s = serializeWorld(), savedAt = Date.now(), playTime = GAME.playTime;
if (!GAME.worldId) { GAME.worldId = newWorldId(); GAME.worldName = `World ${(await listWorlds()).length + 1}`; }
const id = GAME.worldId, name = GAME.worldName;
const buffers = await packBuffers(s.buffers);
await worldWrite(async () => {
const list = await listWorlds();
let entry = list.find(w => w.id === id);
if (!entry) { entry = { id, name, createdAt: savedAt }; list.push(entry); }
Object.assign(entry, { savedAt, playTime, seed: s.meta.seed, w: s.meta.W, h: s.meta.H });
await DB.op('readwrite', st => { st.put({ meta: s.meta, buffers: s.buffers, savedAt, playTime }, `${id}:${slot}`); st.put(list, 'worlds'); });
await DB.op('readwrite', st => { st.put({ meta: s.meta, buffers, savedAt, playTime }, `${id}:${slot}`); st.put(list, 'worlds'); });
});
if (!quiet) toast(slot === 'auto' ? 'Autosaved.' : 'World saved.');
return true;
@@ -116,6 +118,27 @@ async function gz(u8, inflate) {
const st = new Blob([u8]).stream().pipeThrough(inflate ? new DecompressionStream('gzip') : new CompressionStream('gzip'));
return new Uint8Array(await new Response(st).arrayBuffer());
}
async function packBuffers(buffers) {
const packed = {};
for (const k in buffers) {
const arr = buffers[k], bytes = await gz(new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength));
packed[k] = bytes ? { type: arr.constructor.name, gz: true, bytes } : arr;
}
return packed;
}
async function unpackBuffers(buffers) {
const raw = {}, types = { Uint8Array, Uint8ClampedArray, Int8Array, Uint16Array, Int16Array, Uint32Array, Int32Array, Float32Array, Float64Array, BigInt64Array, BigUint64Array };
for (const k in buffers) {
const b = buffers[k];
if (ArrayBuffer.isView(b)) { raw[k] = b; continue; }
if (!b || b.gz !== true) throw new Error('Invalid saved buffer: ' + k);
if (!Object.hasOwn(types, b.type)) throw new Error('Unknown saved buffer type: ' + b.type);
const bytes = await gz(b.bytes, true), C = types[b.type];
if (!bytes) throw new Error('This browser cannot decompress saved worlds.');
raw[k] = new C(bytes.buffer, bytes.byteOffset, bytes.byteLength / C.BYTES_PER_ELEMENT);
}
return raw;
}
async function exportWorld() {
const s = serializeWorld();
const out = { format: 'deepgrain-world', v: 1, meta: s.meta, buffers: {} };