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/bundle.js CHANGED
@@ -34068,8 +34068,6 @@ void main() {
34068
34068
  });
34069
34069
  }
34070
34070
  function yieldToEventLoop() {
34071
- const scheduler = globalThis.scheduler;
34072
- if (scheduler?.yield) return scheduler.yield();
34073
34071
  return new Promise((resolve) => {
34074
34072
  if (typeof MessageChannel !== "undefined") {
34075
34073
  const channel = new MessageChannel();
@@ -103867,6 +103865,7 @@ gl_FragDepth=readDepth(vUv);
103867
103865
  // src/world/MineRenderWorld.ts
103868
103866
  init_log();
103869
103867
  var log21 = logger("MineRenderWorld");
103868
+ var YIELD_INTERVAL_MS = 8;
103870
103869
  var MineRenderWorld = class {
103871
103870
  constructor(scene, options = {}) {
103872
103871
  this._chunks = /* @__PURE__ */ new Map();
@@ -103980,7 +103979,7 @@ gl_FragDepth=readDepth(vUv);
103980
103979
  failures.set(chunk, error2);
103981
103980
  if (this._chunks.get(`${chunk.x}_${chunk.y}_${chunk.z}`) === chunk) this.pendingBiomes.set(chunk, void 0);
103982
103981
  }
103983
- if (performance.now() - sliceStart > 8) {
103982
+ if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
103984
103983
  await yieldToEventLoop();
103985
103984
  sliceStart = performance.now();
103986
103985
  }
@@ -104135,7 +104134,7 @@ gl_FragDepth=readDepth(vUv);
104135
104134
  )));
104136
104135
  for (const pos of positions) changes.set(`${pos.x},${pos.y},${pos.z}`, pos);
104137
104136
  }
104138
- if (performance.now() - sliceStart > 8) {
104137
+ if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
104139
104138
  await yieldToEventLoop();
104140
104139
  sliceStart = performance.now();
104141
104140
  }
@@ -104143,6 +104142,62 @@ gl_FragDepth=readDepth(vUv);
104143
104142
  }
104144
104143
  /** Replaces a chunk column's blocks, biomes, and saved light and, with `renderEntities`, its supported saved mobs. */
104145
104144
  async placeChunk(chunk, executor) {
104145
+ return this.placeColumn(chunk, executor);
104146
+ }
104147
+ /**
104148
+ * Replaces parsed columns in input order, then refreshes neighboring geometry once.
104149
+ * Duplicate coordinates retain their order. A failed column stops placement; queued refreshes finish before rejection.
104150
+ */
104151
+ async placeChunks(chunks) {
104152
+ const columns = [];
104153
+ for (const chunk of chunks) {
104154
+ const { x: x4, z: z4 } = chunk ?? {};
104155
+ if (![x4, z4].every(Number.isSafeInteger)) {
104156
+ throw new RangeError("Chunk column coordinates must be safe integers");
104157
+ }
104158
+ const sections = Array.from(chunk.sections, (section) => {
104159
+ const { y: y5 } = section ?? {};
104160
+ if (!Number.isSafeInteger(y5)) throw new RangeError("Chunk section coordinates must be safe integers");
104161
+ return { ...section, y: y5 };
104162
+ });
104163
+ columns.push({ ...chunk, x: x4, z: z4, sections });
104164
+ }
104165
+ if (!columns.length) return;
104166
+ await this.culling;
104167
+ let failed = false, failure, sliceStart = performance.now();
104168
+ try {
104169
+ for (const column of columns) {
104170
+ await this.placeColumn(column, void 0, true);
104171
+ if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
104172
+ await yieldToEventLoop();
104173
+ sliceStart = performance.now();
104174
+ }
104175
+ }
104176
+ } catch (error2) {
104177
+ failed = true;
104178
+ failure = error2;
104179
+ } finally {
104180
+ try {
104181
+ const failures = await this.refreshBiomes(true);
104182
+ if (failures.size) log21.warn("Failed to refresh biome colors", failures.values().next().value);
104183
+ } catch (error2) {
104184
+ if (!failed) {
104185
+ failed = true;
104186
+ failure = error2;
104187
+ }
104188
+ }
104189
+ try {
104190
+ await this.updateCulling([]);
104191
+ } catch (error2) {
104192
+ if (!failed) {
104193
+ failed = true;
104194
+ failure = error2;
104195
+ }
104196
+ }
104197
+ }
104198
+ if (failed) throw failure;
104199
+ }
104200
+ async placeColumn(chunk, executor, deferCulling = false) {
104146
104201
  if (![chunk.x, chunk.z].every(Number.isSafeInteger)) {
104147
104202
  throw new RangeError("Chunk column coordinates must be safe integers");
104148
104203
  }
@@ -104204,7 +104259,7 @@ gl_FragDepth=readDepth(vUv);
104204
104259
  } finally {
104205
104260
  this.markSectionChanged(target);
104206
104261
  }
104207
- if (performance.now() - sliceStart > 8) {
104262
+ if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
104208
104263
  await yieldToEventLoop();
104209
104264
  sliceStart = performance.now();
104210
104265
  }
@@ -104227,9 +104282,12 @@ gl_FragDepth=readDepth(vUv);
104227
104282
  await placeEntities?.();
104228
104283
  } finally {
104229
104284
  this.markBiomesChanged(chunk.x, chunk.z, void 0, false);
104230
- const failures = await this.refreshBiomes();
104231
- if (failures.size) log21.warn("Failed to refresh biome colors", failures.values().next().value);
104232
- await this.updateCulling([...changes.values()]);
104285
+ if (deferCulling) await this.updateCulling([...changes.values()], true);
104286
+ else {
104287
+ const failures = await this.refreshBiomes();
104288
+ if (failures.size) log21.warn("Failed to refresh biome colors", failures.values().next().value);
104289
+ await this.updateCulling([...changes.values()]);
104290
+ }
104233
104291
  }
104234
104292
  }
104235
104293
  /**
@@ -104259,7 +104317,7 @@ gl_FragDepth=readDepth(vUv);
104259
104317
  for (const { x: x4, z: z4 } of columns.values()) {
104260
104318
  this.entities?.clearColumn(x4, z4);
104261
104319
  await this.clearChunkColumn(x4, z4);
104262
- if (performance.now() - sliceStart > 8) {
104320
+ if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
104263
104321
  await yieldToEventLoop();
104264
104322
  sliceStart = performance.now();
104265
104323
  }
@@ -104398,15 +104456,25 @@ gl_FragDepth=readDepth(vUv);
104398
104456
  for (const [index, mask] of masks) await chunk.setCullMaskIndex(index, mask);
104399
104457
  await chunk.refreshLight(indices === "all" ? void 0 : indices);
104400
104458
  chunk.refreshAmbientOcclusion(this._ambientOcclusion);
104401
- if ((this._ambientOcclusion || this.ambientOcclusionPending) && performance.now() - sliceStart > 8) {
104459
+ if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
104402
104460
  await yieldToEventLoop();
104403
104461
  sliceStart = performance.now();
104404
104462
  }
104405
104463
  }
104406
- const builds = await Promise.allSettled([...batch.keys()].map(async (chunk) => {
104407
- await chunk.rebuildSectionMesh();
104408
- batch.delete(chunk);
104409
- }));
104464
+ const pending = [];
104465
+ for (const chunk of [...batch.keys()]) {
104466
+ const build = (async () => {
104467
+ await chunk.rebuildSectionMesh();
104468
+ batch.delete(chunk);
104469
+ })();
104470
+ void build.catch(() => void 0);
104471
+ pending.push(build);
104472
+ if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
104473
+ await yieldToEventLoop();
104474
+ sliceStart = performance.now();
104475
+ }
104476
+ }
104477
+ const builds = await Promise.allSettled(pending);
104410
104478
  const failure = builds.find((result) => result.status === "rejected");
104411
104479
  if (failure?.status === "rejected") throw failure.reason;
104412
104480
  } catch (error2) {
@@ -105898,6 +105966,7 @@ gl_FragDepth=readDepth(vUv);
105898
105966
  init_dirname();
105899
105967
  init_buffer2();
105900
105968
  init_process2();
105969
+ var MAX_PREFETCHED_COLUMNS = 4;
105901
105970
  var WorldStreamer = class {
105902
105971
  constructor(world, source, options = {}) {
105903
105972
  this.world = world;
@@ -105907,6 +105976,7 @@ gl_FragDepth=readDepth(vUv);
105907
105976
  this.missing = /* @__PURE__ */ new Map();
105908
105977
  this.failed = /* @__PURE__ */ new Map();
105909
105978
  this.desired = [];
105979
+ this.reads = /* @__PURE__ */ new Map();
105910
105980
  this.disposed = false;
105911
105981
  this.loadRadius = options.loadRadius ?? 2;
105912
105982
  this.unloadRadius = options.unloadRadius ?? Math.min(32, this.loadRadius + 1);
@@ -105951,6 +106021,12 @@ gl_FragDepth=readDepth(vUv);
105951
106021
  if (this.loading && !this.within(this.loading.position, this.loadRadius)) {
105952
106022
  this.loading.controller.abort();
105953
106023
  }
106024
+ for (const [key, read] of this.reads) {
106025
+ if (!this.within(read.position, this.loadRadius)) {
106026
+ read.controller.abort();
106027
+ if (read.ready) this.reads.delete(key);
106028
+ }
106029
+ }
105954
106030
  }
105955
106031
  return this.running ?? (this.running = Promise.resolve().then(() => this.drain()));
105956
106032
  }
@@ -105971,8 +106047,11 @@ gl_FragDepth=readDepth(vUv);
105971
106047
  this.desired = [];
105972
106048
  this.center = void 0;
105973
106049
  this.loading?.controller.abort();
106050
+ for (const read of this.reads.values()) read.controller.abort();
105974
106051
  return this.disposal ?? (this.disposal = Promise.resolve().then(async () => {
105975
106052
  await this.running?.catch(() => void 0);
106053
+ await Promise.all([...this.reads.values()].map((read) => read.promise));
106054
+ this.reads.clear();
105976
106055
  try {
105977
106056
  await this.unloadMany([...this.owned.values()]);
105978
106057
  } finally {
@@ -105998,6 +106077,10 @@ gl_FragDepth=readDepth(vUv);
105998
106077
  await this.unloadMany(obsolete);
105999
106078
  continue;
106000
106079
  }
106080
+ if (this.world.placeChunks) {
106081
+ if (await this.placeReadyChunks()) continue;
106082
+ return;
106083
+ }
106001
106084
  const position = this.desired.find((position2) => this.needsLoad(position2));
106002
106085
  if (!position) return;
106003
106086
  let chunk;
@@ -106020,26 +106103,105 @@ gl_FragDepth=readDepth(vUv);
106020
106103
  continue;
106021
106104
  }
106022
106105
  this.owned.set(this.key(position), position);
106106
+ await this.placeOwnedColumns([position], () => this.world.placeChunk(chunk));
106107
+ }
106108
+ } finally {
106109
+ for (const [key, read] of this.reads) {
106110
+ read.controller.abort();
106111
+ if (read.ready) this.reads.delete(key);
106112
+ }
106113
+ this.running = void 0;
106114
+ }
106115
+ }
106116
+ fillReadSlots() {
106117
+ while (!this.disposed && this.reads.size < MAX_PREFETCHED_COLUMNS) {
106118
+ const position = this.desired.find((position2) => this.needsLoad(position2) && !this.reads.has(this.key(position2)));
106119
+ if (!position) return;
106120
+ const key = this.key(position);
106121
+ const read = {
106122
+ position,
106123
+ controller: new AbortController(),
106124
+ promise: Promise.resolve(),
106125
+ ready: false,
106126
+ failed: false
106127
+ };
106128
+ this.reads.set(key, read);
106129
+ read.promise = (async () => {
106023
106130
  try {
106024
- await this.world.placeChunk(chunk);
106025
- this.loaded.set(this.key(position), position);
106131
+ read.chunk = await this.source.getChunk(position.x, position.z, read.controller.signal);
106132
+ if (read.chunk) this.validateChunk(read.chunk, position);
106026
106133
  } catch (error2) {
106027
- try {
106028
- await this.unloadMany([position]);
106029
- } catch (cleanupError) {
106030
- const failure = error2 instanceof Error ? error2 : new Error(String(error2));
106031
- try {
106032
- Object.defineProperty(failure, "cause", { value: cleanupError, configurable: true });
106033
- } catch {
106034
- throw Object.assign(new Error(failure.message), { cause: cleanupError });
106035
- }
106036
- throw failure;
106134
+ read.failed = true;
106135
+ read.error = error2;
106136
+ } finally {
106137
+ read.ready = true;
106138
+ if (read.controller.signal.aborted || this.disposed || !this.within(position, this.loadRadius)) {
106139
+ this.reads.delete(key);
106037
106140
  }
106038
- throw error2;
106141
+ }
106142
+ })();
106143
+ }
106144
+ }
106145
+ /** Places only the ready prefix, so later source reads cannot delay the nearest ready column. */
106146
+ async placeReadyChunks() {
106147
+ this.fillReadSlots();
106148
+ const position = this.desired.find((position2) => this.needsLoad(position2));
106149
+ if (!position) {
106150
+ if (!this.reads.size) return false;
106151
+ await Promise.all([...this.reads.values()].map((read) => read.promise));
106152
+ return true;
106153
+ }
106154
+ const head = this.reads.get(this.key(position));
106155
+ if (!head) {
106156
+ for (let index = this.desired.length - 1; index >= 0; index--) {
106157
+ const key = this.key(this.desired[index]);
106158
+ if (this.reads.get(key)?.ready) {
106159
+ this.reads.delete(key);
106160
+ return true;
106039
106161
  }
106040
106162
  }
106041
- } finally {
106042
- this.running = void 0;
106163
+ await Promise.race([...this.reads.values()].map((read) => read.promise));
106164
+ return true;
106165
+ }
106166
+ if (!head.ready) {
106167
+ await head.promise;
106168
+ return true;
106169
+ }
106170
+ const chunks = [], positions = [];
106171
+ for (const desired of this.desired) {
106172
+ if (!this.needsLoad(desired)) continue;
106173
+ const key = this.key(desired), read = this.reads.get(key);
106174
+ if (!read?.ready) break;
106175
+ this.reads.delete(key);
106176
+ if (read.failed) this.failed.set(key, { ...desired, error: read.error });
106177
+ else if (!read.chunk) this.missing.set(key, desired);
106178
+ else {
106179
+ chunks.push(read.chunk);
106180
+ positions.push(desired);
106181
+ }
106182
+ }
106183
+ if (!chunks.length) return true;
106184
+ for (const position2 of positions) this.owned.set(this.key(position2), position2);
106185
+ await this.placeOwnedColumns(positions, () => this.world.placeChunks(chunks));
106186
+ return true;
106187
+ }
106188
+ async placeOwnedColumns(positions, place) {
106189
+ try {
106190
+ await place();
106191
+ for (const position of positions) this.loaded.set(this.key(position), position);
106192
+ } catch (error2) {
106193
+ try {
106194
+ await this.unloadMany(positions);
106195
+ } catch (cleanupError) {
106196
+ const failure = error2 instanceof Error ? error2 : new Error(String(error2));
106197
+ try {
106198
+ Object.defineProperty(failure, "cause", { value: cleanupError, configurable: true });
106199
+ } catch {
106200
+ throw Object.assign(new Error(failure.message), { cause: cleanupError });
106201
+ }
106202
+ throw failure;
106203
+ }
106204
+ throw error2;
106043
106205
  }
106044
106206
  }
106045
106207
  needsLoad(position) {