minerender 2.1.0-alpha.142 → 2.1.0-alpha.143
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/esm/index.browser.d.mts +16 -2
- package/dist/browser/esm/util/util.mjs +0 -2
- package/dist/browser/esm/util/util.mjs.map +1 -1
- package/dist/browser/esm/world/MineRenderWorld.mjs +82 -12
- package/dist/browser/esm/world/MineRenderWorld.mjs.map +1 -1
- package/dist/browser/esm/world/WorldStreamer.mjs +109 -15
- package/dist/browser/esm/world/WorldStreamer.mjs.map +1 -1
- package/dist/browser/index.d.ts +16 -2
- package/dist/browser/index.js +191 -29
- package/dist/browser/index.js.map +1 -1
- package/dist/bundle.js +191 -29
- package/dist/bundle.js.map +1 -1
- package/dist/node/index.d.mts +16 -2
- package/dist/node/index.d.ts +16 -2
- package/dist/node/index.js +191 -29
- package/dist/node/index.js.map +1 -1
- package/dist/node/index.mjs +191 -29
- package/dist/node/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/node/index.d.mts
CHANGED
|
@@ -785,7 +785,10 @@ declare function toRadians(degrees: number): number;
|
|
|
785
785
|
declare function toDegrees(radians: number): number;
|
|
786
786
|
/** Resolves after a delay in milliseconds. */
|
|
787
787
|
declare function sleep(timeout: number): Promise<void>;
|
|
788
|
-
/**
|
|
788
|
+
/**
|
|
789
|
+
* Yields to the next macrotask without timer clamping when the platform supports it.
|
|
790
|
+
* Not `scheduler.yield()`: Chrome runs its continuations ahead of rendering, so chained yields delay frames.
|
|
791
|
+
*/
|
|
789
792
|
declare function yieldToEventLoop(): Promise<void>;
|
|
790
793
|
/** Reduces an angle modulo 360, retaining a negative sign for negative inputs. */
|
|
791
794
|
declare function clampRotationDegrees(deg: number): number;
|
|
@@ -4284,6 +4287,12 @@ declare class MineRenderWorld<SectionMeshing extends boolean = false> {
|
|
|
4284
4287
|
private placeChunkGroups;
|
|
4285
4288
|
/** Replaces a chunk column's blocks, biomes, and saved light and, with `renderEntities`, its supported saved mobs. */
|
|
4286
4289
|
placeChunk(chunk: AnvilChunk, executor?: BatchedExecutor): Promise<void>;
|
|
4290
|
+
/**
|
|
4291
|
+
* Replaces parsed columns in input order, then refreshes neighboring geometry once.
|
|
4292
|
+
* Duplicate coordinates retain their order. A failed column stops placement; queued refreshes finish before rejection.
|
|
4293
|
+
*/
|
|
4294
|
+
placeChunks(chunks: readonly AnvilChunk[]): Promise<void>;
|
|
4295
|
+
private placeColumn;
|
|
4287
4296
|
/**
|
|
4288
4297
|
* Removes blocks and owned entities at chunk-column coordinates, then refreshes neighboring faces and fluids.
|
|
4289
4298
|
* Coordinates match AnvilChunk.x/z, not block positions. Missing columns are ignored.
|
|
@@ -4371,10 +4380,11 @@ declare class WorldStreamer {
|
|
|
4371
4380
|
private desired;
|
|
4372
4381
|
private center?;
|
|
4373
4382
|
private loading?;
|
|
4383
|
+
private readonly reads;
|
|
4374
4384
|
private running?;
|
|
4375
4385
|
private disposal?;
|
|
4376
4386
|
private disposed;
|
|
4377
|
-
constructor(world: Pick<MineRenderWorld<boolean>, "placeChunk" | "unloadChunkColumns">, source: WorldChunkSource, options?: WorldStreamerOptions);
|
|
4387
|
+
constructor(world: Pick<MineRenderWorld<boolean>, "placeChunk" | "unloadChunkColumns"> & Partial<Pick<MineRenderWorld<boolean>, "placeChunks">>, source: WorldChunkSource, options?: WorldStreamerOptions);
|
|
4378
4388
|
/** Returns absolute coordinates of successfully placed columns, including retained columns. */
|
|
4379
4389
|
get loadedChunks(): readonly {
|
|
4380
4390
|
x: number;
|
|
@@ -4407,6 +4417,10 @@ declare class WorldStreamer {
|
|
|
4407
4417
|
/** Aborts source reads, waits for active work, then unloads owned columns. The world and source remain caller-owned. */
|
|
4408
4418
|
dispose(): Promise<void>;
|
|
4409
4419
|
private drain;
|
|
4420
|
+
private fillReadSlots;
|
|
4421
|
+
/** Places only the ready prefix, so later source reads cannot delay the nearest ready column. */
|
|
4422
|
+
private placeReadyChunks;
|
|
4423
|
+
private placeOwnedColumns;
|
|
4410
4424
|
private needsLoad;
|
|
4411
4425
|
private unloadMany;
|
|
4412
4426
|
private validateChunk;
|
package/dist/node/index.d.ts
CHANGED
|
@@ -785,7 +785,10 @@ declare function toRadians(degrees: number): number;
|
|
|
785
785
|
declare function toDegrees(radians: number): number;
|
|
786
786
|
/** Resolves after a delay in milliseconds. */
|
|
787
787
|
declare function sleep(timeout: number): Promise<void>;
|
|
788
|
-
/**
|
|
788
|
+
/**
|
|
789
|
+
* Yields to the next macrotask without timer clamping when the platform supports it.
|
|
790
|
+
* Not `scheduler.yield()`: Chrome runs its continuations ahead of rendering, so chained yields delay frames.
|
|
791
|
+
*/
|
|
789
792
|
declare function yieldToEventLoop(): Promise<void>;
|
|
790
793
|
/** Reduces an angle modulo 360, retaining a negative sign for negative inputs. */
|
|
791
794
|
declare function clampRotationDegrees(deg: number): number;
|
|
@@ -4284,6 +4287,12 @@ declare class MineRenderWorld<SectionMeshing extends boolean = false> {
|
|
|
4284
4287
|
private placeChunkGroups;
|
|
4285
4288
|
/** Replaces a chunk column's blocks, biomes, and saved light and, with `renderEntities`, its supported saved mobs. */
|
|
4286
4289
|
placeChunk(chunk: AnvilChunk, executor?: BatchedExecutor): Promise<void>;
|
|
4290
|
+
/**
|
|
4291
|
+
* Replaces parsed columns in input order, then refreshes neighboring geometry once.
|
|
4292
|
+
* Duplicate coordinates retain their order. A failed column stops placement; queued refreshes finish before rejection.
|
|
4293
|
+
*/
|
|
4294
|
+
placeChunks(chunks: readonly AnvilChunk[]): Promise<void>;
|
|
4295
|
+
private placeColumn;
|
|
4287
4296
|
/**
|
|
4288
4297
|
* Removes blocks and owned entities at chunk-column coordinates, then refreshes neighboring faces and fluids.
|
|
4289
4298
|
* Coordinates match AnvilChunk.x/z, not block positions. Missing columns are ignored.
|
|
@@ -4371,10 +4380,11 @@ declare class WorldStreamer {
|
|
|
4371
4380
|
private desired;
|
|
4372
4381
|
private center?;
|
|
4373
4382
|
private loading?;
|
|
4383
|
+
private readonly reads;
|
|
4374
4384
|
private running?;
|
|
4375
4385
|
private disposal?;
|
|
4376
4386
|
private disposed;
|
|
4377
|
-
constructor(world: Pick<MineRenderWorld<boolean>, "placeChunk" | "unloadChunkColumns">, source: WorldChunkSource, options?: WorldStreamerOptions);
|
|
4387
|
+
constructor(world: Pick<MineRenderWorld<boolean>, "placeChunk" | "unloadChunkColumns"> & Partial<Pick<MineRenderWorld<boolean>, "placeChunks">>, source: WorldChunkSource, options?: WorldStreamerOptions);
|
|
4378
4388
|
/** Returns absolute coordinates of successfully placed columns, including retained columns. */
|
|
4379
4389
|
get loadedChunks(): readonly {
|
|
4380
4390
|
x: number;
|
|
@@ -4407,6 +4417,10 @@ declare class WorldStreamer {
|
|
|
4407
4417
|
/** Aborts source reads, waits for active work, then unloads owned columns. The world and source remain caller-owned. */
|
|
4408
4418
|
dispose(): Promise<void>;
|
|
4409
4419
|
private drain;
|
|
4420
|
+
private fillReadSlots;
|
|
4421
|
+
/** Places only the ready prefix, so later source reads cannot delay the nearest ready column. */
|
|
4422
|
+
private placeReadyChunks;
|
|
4423
|
+
private placeOwnedColumns;
|
|
4410
4424
|
private needsLoad;
|
|
4411
4425
|
private unloadMany;
|
|
4412
4426
|
private validateChunk;
|
package/dist/node/index.js
CHANGED
|
@@ -184,8 +184,6 @@ async function sleep(timeout) {
|
|
|
184
184
|
});
|
|
185
185
|
}
|
|
186
186
|
function yieldToEventLoop() {
|
|
187
|
-
const scheduler = globalThis.scheduler;
|
|
188
|
-
if (scheduler?.yield) return scheduler.yield();
|
|
189
187
|
return new Promise((resolve) => {
|
|
190
188
|
if (typeof MessageChannel !== "undefined") {
|
|
191
189
|
const channel = new MessageChannel();
|
|
@@ -18696,6 +18694,7 @@ function skipsBlockFace(data, rule, block, neighbor, face) {
|
|
|
18696
18694
|
// src/world/MineRenderWorld.ts
|
|
18697
18695
|
init_log();
|
|
18698
18696
|
var log18 = logger("MineRenderWorld");
|
|
18697
|
+
var YIELD_INTERVAL_MS = 8;
|
|
18699
18698
|
var MineRenderWorld = class {
|
|
18700
18699
|
constructor(scene, options = {}) {
|
|
18701
18700
|
this._chunks = /* @__PURE__ */ new Map();
|
|
@@ -18809,7 +18808,7 @@ var MineRenderWorld = class {
|
|
|
18809
18808
|
failures.set(chunk, error);
|
|
18810
18809
|
if (this._chunks.get(`${chunk.x}_${chunk.y}_${chunk.z}`) === chunk) this.pendingBiomes.set(chunk, void 0);
|
|
18811
18810
|
}
|
|
18812
|
-
if (performance.now() - sliceStart >
|
|
18811
|
+
if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
|
|
18813
18812
|
await yieldToEventLoop();
|
|
18814
18813
|
sliceStart = performance.now();
|
|
18815
18814
|
}
|
|
@@ -18964,7 +18963,7 @@ var MineRenderWorld = class {
|
|
|
18964
18963
|
));
|
|
18965
18964
|
for (const pos of positions) changes.set(`${pos.x},${pos.y},${pos.z}`, pos);
|
|
18966
18965
|
}
|
|
18967
|
-
if (performance.now() - sliceStart >
|
|
18966
|
+
if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
|
|
18968
18967
|
await yieldToEventLoop();
|
|
18969
18968
|
sliceStart = performance.now();
|
|
18970
18969
|
}
|
|
@@ -18972,6 +18971,62 @@ var MineRenderWorld = class {
|
|
|
18972
18971
|
}
|
|
18973
18972
|
/** Replaces a chunk column's blocks, biomes, and saved light and, with `renderEntities`, its supported saved mobs. */
|
|
18974
18973
|
async placeChunk(chunk, executor) {
|
|
18974
|
+
return this.placeColumn(chunk, executor);
|
|
18975
|
+
}
|
|
18976
|
+
/**
|
|
18977
|
+
* Replaces parsed columns in input order, then refreshes neighboring geometry once.
|
|
18978
|
+
* Duplicate coordinates retain their order. A failed column stops placement; queued refreshes finish before rejection.
|
|
18979
|
+
*/
|
|
18980
|
+
async placeChunks(chunks) {
|
|
18981
|
+
const columns = [];
|
|
18982
|
+
for (const chunk of chunks) {
|
|
18983
|
+
const { x, z } = chunk ?? {};
|
|
18984
|
+
if (![x, z].every(Number.isSafeInteger)) {
|
|
18985
|
+
throw new RangeError("Chunk column coordinates must be safe integers");
|
|
18986
|
+
}
|
|
18987
|
+
const sections = Array.from(chunk.sections, (section) => {
|
|
18988
|
+
const { y } = section ?? {};
|
|
18989
|
+
if (!Number.isSafeInteger(y)) throw new RangeError("Chunk section coordinates must be safe integers");
|
|
18990
|
+
return { ...section, y };
|
|
18991
|
+
});
|
|
18992
|
+
columns.push({ ...chunk, x, z, sections });
|
|
18993
|
+
}
|
|
18994
|
+
if (!columns.length) return;
|
|
18995
|
+
await this.culling;
|
|
18996
|
+
let failed = false, failure, sliceStart = performance.now();
|
|
18997
|
+
try {
|
|
18998
|
+
for (const column of columns) {
|
|
18999
|
+
await this.placeColumn(column, void 0, true);
|
|
19000
|
+
if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
|
|
19001
|
+
await yieldToEventLoop();
|
|
19002
|
+
sliceStart = performance.now();
|
|
19003
|
+
}
|
|
19004
|
+
}
|
|
19005
|
+
} catch (error) {
|
|
19006
|
+
failed = true;
|
|
19007
|
+
failure = error;
|
|
19008
|
+
} finally {
|
|
19009
|
+
try {
|
|
19010
|
+
const failures = await this.refreshBiomes(true);
|
|
19011
|
+
if (failures.size) log18.warn("Failed to refresh biome colors", failures.values().next().value);
|
|
19012
|
+
} catch (error) {
|
|
19013
|
+
if (!failed) {
|
|
19014
|
+
failed = true;
|
|
19015
|
+
failure = error;
|
|
19016
|
+
}
|
|
19017
|
+
}
|
|
19018
|
+
try {
|
|
19019
|
+
await this.updateCulling([]);
|
|
19020
|
+
} catch (error) {
|
|
19021
|
+
if (!failed) {
|
|
19022
|
+
failed = true;
|
|
19023
|
+
failure = error;
|
|
19024
|
+
}
|
|
19025
|
+
}
|
|
19026
|
+
}
|
|
19027
|
+
if (failed) throw failure;
|
|
19028
|
+
}
|
|
19029
|
+
async placeColumn(chunk, executor, deferCulling = false) {
|
|
18975
19030
|
if (![chunk.x, chunk.z].every(Number.isSafeInteger)) {
|
|
18976
19031
|
throw new RangeError("Chunk column coordinates must be safe integers");
|
|
18977
19032
|
}
|
|
@@ -19033,7 +19088,7 @@ var MineRenderWorld = class {
|
|
|
19033
19088
|
} finally {
|
|
19034
19089
|
this.markSectionChanged(target);
|
|
19035
19090
|
}
|
|
19036
|
-
if (performance.now() - sliceStart >
|
|
19091
|
+
if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
|
|
19037
19092
|
await yieldToEventLoop();
|
|
19038
19093
|
sliceStart = performance.now();
|
|
19039
19094
|
}
|
|
@@ -19056,9 +19111,12 @@ var MineRenderWorld = class {
|
|
|
19056
19111
|
await placeEntities?.();
|
|
19057
19112
|
} finally {
|
|
19058
19113
|
this.markBiomesChanged(chunk.x, chunk.z, void 0, false);
|
|
19059
|
-
|
|
19060
|
-
|
|
19061
|
-
|
|
19114
|
+
if (deferCulling) await this.updateCulling([...changes.values()], true);
|
|
19115
|
+
else {
|
|
19116
|
+
const failures = await this.refreshBiomes();
|
|
19117
|
+
if (failures.size) log18.warn("Failed to refresh biome colors", failures.values().next().value);
|
|
19118
|
+
await this.updateCulling([...changes.values()]);
|
|
19119
|
+
}
|
|
19062
19120
|
}
|
|
19063
19121
|
}
|
|
19064
19122
|
/**
|
|
@@ -19088,7 +19146,7 @@ var MineRenderWorld = class {
|
|
|
19088
19146
|
for (const { x, z } of columns.values()) {
|
|
19089
19147
|
this.entities?.clearColumn(x, z);
|
|
19090
19148
|
await this.clearChunkColumn(x, z);
|
|
19091
|
-
if (performance.now() - sliceStart >
|
|
19149
|
+
if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
|
|
19092
19150
|
await yieldToEventLoop();
|
|
19093
19151
|
sliceStart = performance.now();
|
|
19094
19152
|
}
|
|
@@ -19227,15 +19285,25 @@ var MineRenderWorld = class {
|
|
|
19227
19285
|
for (const [index, mask] of masks) await chunk.setCullMaskIndex(index, mask);
|
|
19228
19286
|
await chunk.refreshLight(indices === "all" ? void 0 : indices);
|
|
19229
19287
|
chunk.refreshAmbientOcclusion(this._ambientOcclusion);
|
|
19230
|
-
if (
|
|
19288
|
+
if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
|
|
19231
19289
|
await yieldToEventLoop();
|
|
19232
19290
|
sliceStart = performance.now();
|
|
19233
19291
|
}
|
|
19234
19292
|
}
|
|
19235
|
-
const
|
|
19236
|
-
|
|
19237
|
-
|
|
19238
|
-
|
|
19293
|
+
const pending = [];
|
|
19294
|
+
for (const chunk of [...batch.keys()]) {
|
|
19295
|
+
const build = (async () => {
|
|
19296
|
+
await chunk.rebuildSectionMesh();
|
|
19297
|
+
batch.delete(chunk);
|
|
19298
|
+
})();
|
|
19299
|
+
void build.catch(() => void 0);
|
|
19300
|
+
pending.push(build);
|
|
19301
|
+
if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
|
|
19302
|
+
await yieldToEventLoop();
|
|
19303
|
+
sliceStart = performance.now();
|
|
19304
|
+
}
|
|
19305
|
+
}
|
|
19306
|
+
const builds = await Promise.allSettled(pending);
|
|
19239
19307
|
const failure = builds.find((result) => result.status === "rejected");
|
|
19240
19308
|
if (failure?.status === "rejected") throw failure.reason;
|
|
19241
19309
|
} catch (error) {
|
|
@@ -20695,6 +20763,7 @@ var Chunk = class _Chunk {
|
|
|
20695
20763
|
};
|
|
20696
20764
|
|
|
20697
20765
|
// src/world/WorldStreamer.ts
|
|
20766
|
+
var MAX_PREFETCHED_COLUMNS = 4;
|
|
20698
20767
|
var WorldStreamer = class {
|
|
20699
20768
|
constructor(world, source, options = {}) {
|
|
20700
20769
|
this.world = world;
|
|
@@ -20704,6 +20773,7 @@ var WorldStreamer = class {
|
|
|
20704
20773
|
this.missing = /* @__PURE__ */ new Map();
|
|
20705
20774
|
this.failed = /* @__PURE__ */ new Map();
|
|
20706
20775
|
this.desired = [];
|
|
20776
|
+
this.reads = /* @__PURE__ */ new Map();
|
|
20707
20777
|
this.disposed = false;
|
|
20708
20778
|
this.loadRadius = options.loadRadius ?? 2;
|
|
20709
20779
|
this.unloadRadius = options.unloadRadius ?? Math.min(32, this.loadRadius + 1);
|
|
@@ -20748,6 +20818,12 @@ var WorldStreamer = class {
|
|
|
20748
20818
|
if (this.loading && !this.within(this.loading.position, this.loadRadius)) {
|
|
20749
20819
|
this.loading.controller.abort();
|
|
20750
20820
|
}
|
|
20821
|
+
for (const [key, read] of this.reads) {
|
|
20822
|
+
if (!this.within(read.position, this.loadRadius)) {
|
|
20823
|
+
read.controller.abort();
|
|
20824
|
+
if (read.ready) this.reads.delete(key);
|
|
20825
|
+
}
|
|
20826
|
+
}
|
|
20751
20827
|
}
|
|
20752
20828
|
return this.running ??= Promise.resolve().then(() => this.drain());
|
|
20753
20829
|
}
|
|
@@ -20768,8 +20844,11 @@ var WorldStreamer = class {
|
|
|
20768
20844
|
this.desired = [];
|
|
20769
20845
|
this.center = void 0;
|
|
20770
20846
|
this.loading?.controller.abort();
|
|
20847
|
+
for (const read of this.reads.values()) read.controller.abort();
|
|
20771
20848
|
return this.disposal ??= Promise.resolve().then(async () => {
|
|
20772
20849
|
await this.running?.catch(() => void 0);
|
|
20850
|
+
await Promise.all([...this.reads.values()].map((read) => read.promise));
|
|
20851
|
+
this.reads.clear();
|
|
20773
20852
|
try {
|
|
20774
20853
|
await this.unloadMany([...this.owned.values()]);
|
|
20775
20854
|
} finally {
|
|
@@ -20795,6 +20874,10 @@ var WorldStreamer = class {
|
|
|
20795
20874
|
await this.unloadMany(obsolete);
|
|
20796
20875
|
continue;
|
|
20797
20876
|
}
|
|
20877
|
+
if (this.world.placeChunks) {
|
|
20878
|
+
if (await this.placeReadyChunks()) continue;
|
|
20879
|
+
return;
|
|
20880
|
+
}
|
|
20798
20881
|
const position = this.desired.find((position2) => this.needsLoad(position2));
|
|
20799
20882
|
if (!position) return;
|
|
20800
20883
|
let chunk;
|
|
@@ -20817,26 +20900,105 @@ var WorldStreamer = class {
|
|
|
20817
20900
|
continue;
|
|
20818
20901
|
}
|
|
20819
20902
|
this.owned.set(this.key(position), position);
|
|
20903
|
+
await this.placeOwnedColumns([position], () => this.world.placeChunk(chunk));
|
|
20904
|
+
}
|
|
20905
|
+
} finally {
|
|
20906
|
+
for (const [key, read] of this.reads) {
|
|
20907
|
+
read.controller.abort();
|
|
20908
|
+
if (read.ready) this.reads.delete(key);
|
|
20909
|
+
}
|
|
20910
|
+
this.running = void 0;
|
|
20911
|
+
}
|
|
20912
|
+
}
|
|
20913
|
+
fillReadSlots() {
|
|
20914
|
+
while (!this.disposed && this.reads.size < MAX_PREFETCHED_COLUMNS) {
|
|
20915
|
+
const position = this.desired.find((position2) => this.needsLoad(position2) && !this.reads.has(this.key(position2)));
|
|
20916
|
+
if (!position) return;
|
|
20917
|
+
const key = this.key(position);
|
|
20918
|
+
const read = {
|
|
20919
|
+
position,
|
|
20920
|
+
controller: new AbortController(),
|
|
20921
|
+
promise: Promise.resolve(),
|
|
20922
|
+
ready: false,
|
|
20923
|
+
failed: false
|
|
20924
|
+
};
|
|
20925
|
+
this.reads.set(key, read);
|
|
20926
|
+
read.promise = (async () => {
|
|
20820
20927
|
try {
|
|
20821
|
-
await this.
|
|
20822
|
-
this.
|
|
20928
|
+
read.chunk = await this.source.getChunk(position.x, position.z, read.controller.signal);
|
|
20929
|
+
if (read.chunk) this.validateChunk(read.chunk, position);
|
|
20823
20930
|
} catch (error) {
|
|
20824
|
-
|
|
20825
|
-
|
|
20826
|
-
|
|
20827
|
-
|
|
20828
|
-
|
|
20829
|
-
|
|
20830
|
-
} catch {
|
|
20831
|
-
throw Object.assign(new Error(failure.message), { cause: cleanupError });
|
|
20832
|
-
}
|
|
20833
|
-
throw failure;
|
|
20931
|
+
read.failed = true;
|
|
20932
|
+
read.error = error;
|
|
20933
|
+
} finally {
|
|
20934
|
+
read.ready = true;
|
|
20935
|
+
if (read.controller.signal.aborted || this.disposed || !this.within(position, this.loadRadius)) {
|
|
20936
|
+
this.reads.delete(key);
|
|
20834
20937
|
}
|
|
20835
|
-
|
|
20938
|
+
}
|
|
20939
|
+
})();
|
|
20940
|
+
}
|
|
20941
|
+
}
|
|
20942
|
+
/** Places only the ready prefix, so later source reads cannot delay the nearest ready column. */
|
|
20943
|
+
async placeReadyChunks() {
|
|
20944
|
+
this.fillReadSlots();
|
|
20945
|
+
const position = this.desired.find((position2) => this.needsLoad(position2));
|
|
20946
|
+
if (!position) {
|
|
20947
|
+
if (!this.reads.size) return false;
|
|
20948
|
+
await Promise.all([...this.reads.values()].map((read) => read.promise));
|
|
20949
|
+
return true;
|
|
20950
|
+
}
|
|
20951
|
+
const head = this.reads.get(this.key(position));
|
|
20952
|
+
if (!head) {
|
|
20953
|
+
for (let index = this.desired.length - 1; index >= 0; index--) {
|
|
20954
|
+
const key = this.key(this.desired[index]);
|
|
20955
|
+
if (this.reads.get(key)?.ready) {
|
|
20956
|
+
this.reads.delete(key);
|
|
20957
|
+
return true;
|
|
20836
20958
|
}
|
|
20837
20959
|
}
|
|
20838
|
-
|
|
20839
|
-
|
|
20960
|
+
await Promise.race([...this.reads.values()].map((read) => read.promise));
|
|
20961
|
+
return true;
|
|
20962
|
+
}
|
|
20963
|
+
if (!head.ready) {
|
|
20964
|
+
await head.promise;
|
|
20965
|
+
return true;
|
|
20966
|
+
}
|
|
20967
|
+
const chunks = [], positions = [];
|
|
20968
|
+
for (const desired of this.desired) {
|
|
20969
|
+
if (!this.needsLoad(desired)) continue;
|
|
20970
|
+
const key = this.key(desired), read = this.reads.get(key);
|
|
20971
|
+
if (!read?.ready) break;
|
|
20972
|
+
this.reads.delete(key);
|
|
20973
|
+
if (read.failed) this.failed.set(key, { ...desired, error: read.error });
|
|
20974
|
+
else if (!read.chunk) this.missing.set(key, desired);
|
|
20975
|
+
else {
|
|
20976
|
+
chunks.push(read.chunk);
|
|
20977
|
+
positions.push(desired);
|
|
20978
|
+
}
|
|
20979
|
+
}
|
|
20980
|
+
if (!chunks.length) return true;
|
|
20981
|
+
for (const position2 of positions) this.owned.set(this.key(position2), position2);
|
|
20982
|
+
await this.placeOwnedColumns(positions, () => this.world.placeChunks(chunks));
|
|
20983
|
+
return true;
|
|
20984
|
+
}
|
|
20985
|
+
async placeOwnedColumns(positions, place) {
|
|
20986
|
+
try {
|
|
20987
|
+
await place();
|
|
20988
|
+
for (const position of positions) this.loaded.set(this.key(position), position);
|
|
20989
|
+
} catch (error) {
|
|
20990
|
+
try {
|
|
20991
|
+
await this.unloadMany(positions);
|
|
20992
|
+
} catch (cleanupError) {
|
|
20993
|
+
const failure = error instanceof Error ? error : new Error(String(error));
|
|
20994
|
+
try {
|
|
20995
|
+
Object.defineProperty(failure, "cause", { value: cleanupError, configurable: true });
|
|
20996
|
+
} catch {
|
|
20997
|
+
throw Object.assign(new Error(failure.message), { cause: cleanupError });
|
|
20998
|
+
}
|
|
20999
|
+
throw failure;
|
|
21000
|
+
}
|
|
21001
|
+
throw error;
|
|
20840
21002
|
}
|
|
20841
21003
|
}
|
|
20842
21004
|
needsLoad(position) {
|