A loaded world starts around the player first, the rest a few areas a tick (t21)

- Areas within 150 grains of the bot start at once; the others 12 a tick, nearest first, and any
  the bot flies toward start straight away. A Huge world is fully started after 17 s.
- Huge world, first 600 ticks: average 15.2 -> 5.5 ms/tick, worst 79.7 -> 16.2.
- New test/t_load.js.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-24 22:41:57 -04:00
co-authored by Claude Opus 5.5
parent 7fa1056e09
commit 0a8847594e
7 changed files with 137 additions and 12 deletions
+43 -7
View File
@@ -117,13 +117,6 @@ function deserializeWorld(meta, buffers) {
seen = new Uint8Array(NMAT); meta.seen.forEach((v, k) => { if (k < NMAT) seen[k] = v; });
ledger = meta.ledger || {};
Object.assign(stats, meta.stats || {});
awake.fill(3);
for (let c = 0; c < NCH; c++) {
const cx = c % CW, cy = (c / CW) | 0, x0 = cx << 5, y0 = cy << 5, x1 = Math.min(W, x0 + 32), y1 = Math.min(H, y0 + 32);
let dev = 0;
for (let y = y0; y < y1 && dev < 2; y++) for (let x = x0; x < x1; x++) { const d = Math.abs(temp[y * W + x] - AMB[y]); if (d > dev) dev = d; if (flags[y * W + x] & F_BURN) dev = 99; }
if (dev >= 2) { hot[c] = 6; hotLvl[c] = 2; }
}
powerDirty = true; rebuildPower(); updateRoof();
if (!weather.base) weather.base = worldWater(); // older saves: take the water it has now as its starting amount
if (!meta.specks) clearHardSpecks(true); // older saves: tidy away tiny specks of too-hard rock once
@@ -137,5 +130,48 @@ function deserializeWorld(meta, buffers) {
for (const mo of molds) for (const i of moldCellsAt(mo.kind, mo.x, mo.y).walls) fogRevealAround(i % W, (i / W) | 0, 12);
}
fogLook(bot.x + bot.w / 2, bot.y + bot.h / 2);
startLoadWake();
EV.length = 0;
}
// Keep the load-time heat scan, but start distant areas in order instead of all at once.
function startLoadWake() {
awake.fill(0); hot.fill(0); hotLvl.fill(0);
loadWakePending = new Uint8Array(NCH).fill(1);
for (let c = 0; c < NCH; c++) {
const cx = c % CW, cy = (c / CW) | 0, x0 = cx << 5, y0 = cy << 5, x1 = Math.min(W, x0 + 32), y1 = Math.min(H, y0 + 32);
let dev = 0;
for (let y = y0; y < y1 && dev < 2; y++) for (let x = x0; x < x1; x++) { const d = Math.abs(temp[y * W + x] - AMB[y]); if (d > dev) dev = d; if (flags[y * W + x] & F_BURN) dev = 99; }
if (dev >= 2) loadWakePending[c] = 2;
}
const x = bot.x + bot.w / 2, y = bot.y + bot.h / 2;
const distance = c => (c % CW * 32 + 16 - x) ** 2 + (((c / CW) | 0) * 32 + 16 - y) ** 2;
loadWakeOrder = Uint32Array.from({ length: NCH }, (_, c) => c);
loadWakeOrder.sort((a, b) => distance(a) - distance(b));
loadWakeIndex = 0;
wakeLoadNearBot();
}
function wakeLoadArea(c) {
const pending = loadWakePending[c];
if (!pending) return;
loadWakePending[c] = 0;
awake[c] = 3;
if (pending === 2) { hot[c] = 6; hotLvl[c] = 2; }
}
function wakeLoadNearBot() {
const x = bot.x + bot.w / 2, y = bot.y + bot.h / 2;
const x0 = Math.max(0, Math.floor((x - 150) / 32)), x1 = Math.min(CW - 1, Math.floor((x + 150) / 32));
const y0 = Math.max(0, Math.floor((y - 150) / 32)), y1 = Math.min(CH - 1, Math.floor((y + 150) / 32));
for (let cy = y0; cy <= y1; cy++) for (let cx = x0; cx <= x1; cx++) {
const dx = Math.max(cx * 32 - x, 0, x - (cx * 32 + 31));
const dy = Math.max(cy * 32 - y, 0, y - (cy * 32 + 31));
if (dx * dx + dy * dy <= 150 * 150) wakeLoadArea(cy * CW + cx);
}
}
function loadWakeTick() {
if (tick % 8 === 0) wakeLoadNearBot();
// Twelve per tick finishes a Huge world in 1024 ticks, and a Small one in 171.
const end = Math.min(loadWakeIndex + 12, loadWakeOrder.length);
while (loadWakeIndex < end) wakeLoadArea(loadWakeOrder[loadWakeIndex++]);
if (loadWakeIndex === loadWakeOrder.length) loadWakeOrder = loadWakePending = null;
}
+1
View File
@@ -3,6 +3,7 @@
function simTick() {
tick++;
tickStamp = (tickStamp % 255) + 1;
if (loadWakeOrder) loadWakeTick();
if (tick % 20 === 1) updateDirtyRoof();
weatherTick();
machinesTick();
+2
View File
@@ -14,6 +14,7 @@ let bgm; // background layer: trees. Liquids, powders and gases pass in front of
let flyers = []; // grains in flight, blown out of the bot
let ghostBlocks = new Map(), ghostMolds = [];
let awake, hot, hotLvl, powderArea, roofDirty;
let loadWakeOrder = null, loadWakePending = null, loadWakeIndex = 0;
let tick = 0, tickStamp = 1;
let machines = [null], molds = [], nets = [null], pcells = new Set(), powerDirty = true;
let beltL = [], beltR = [], tubeLists = [[], [], [], [], []];
@@ -49,6 +50,7 @@ function alloc(w, h) {
surfBase = Math.round(Math.min(h * 0.27, 175)); underD = h - surfBase; shallowD = 0; lapse = 0.36;
surfY = new Int16Array(w); roofY = new Int16Array(w); dirtY = new Int16Array(w); graniteY = new Int16Array(w); basaltY = new Int16Array(w); magmaY = new Int16Array(w);
AMB = new Float32Array(h);
loadWakeOrder = loadWakePending = null; loadWakeIndex = 0;
awake = new Uint8Array(NCH); hot = new Uint8Array(NCH); hotLvl = new Uint8Array(NCH);
roofDirty = new Uint8Array(CW).fill(1);
powderArea = new Uint8Array(NCH).fill(1); // unknown areas get one visit before being ruled out