minerender 2.1.0-alpha.141 → 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/SectionMesh.mjs +65 -9
- package/dist/browser/esm/world/SectionMesh.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 +255 -37
- package/dist/browser/index.js.map +1 -1
- package/dist/bundle.js +257 -38
- 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 +255 -37
- package/dist/node/index.js.map +1 -1
- package/dist/node/index.mjs +271 -53
- package/dist/node/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/browser/index.js
CHANGED
|
@@ -264,8 +264,6 @@ async function sleep(timeout) {
|
|
|
264
264
|
});
|
|
265
265
|
}
|
|
266
266
|
function yieldToEventLoop() {
|
|
267
|
-
const scheduler = globalThis.scheduler;
|
|
268
|
-
if (scheduler?.yield) return scheduler.yield();
|
|
269
267
|
return new Promise((resolve) => {
|
|
270
268
|
if (typeof MessageChannel !== "undefined") {
|
|
271
269
|
const channel = new MessageChannel();
|
|
@@ -18669,6 +18667,7 @@ function skipsBlockFace(data, rule, block, neighbor, face) {
|
|
|
18669
18667
|
// src/world/MineRenderWorld.ts
|
|
18670
18668
|
init_log();
|
|
18671
18669
|
var log19 = logger("MineRenderWorld");
|
|
18670
|
+
var YIELD_INTERVAL_MS = 8;
|
|
18672
18671
|
var MineRenderWorld = class {
|
|
18673
18672
|
constructor(scene, options = {}) {
|
|
18674
18673
|
this._chunks = /* @__PURE__ */ new Map();
|
|
@@ -18782,7 +18781,7 @@ var MineRenderWorld = class {
|
|
|
18782
18781
|
failures.set(chunk, error);
|
|
18783
18782
|
if (this._chunks.get(`${chunk.x}_${chunk.y}_${chunk.z}`) === chunk) this.pendingBiomes.set(chunk, void 0);
|
|
18784
18783
|
}
|
|
18785
|
-
if (performance.now() - sliceStart >
|
|
18784
|
+
if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
|
|
18786
18785
|
await yieldToEventLoop();
|
|
18787
18786
|
sliceStart = performance.now();
|
|
18788
18787
|
}
|
|
@@ -18937,7 +18936,7 @@ var MineRenderWorld = class {
|
|
|
18937
18936
|
)));
|
|
18938
18937
|
for (const pos of positions) changes.set(`${pos.x},${pos.y},${pos.z}`, pos);
|
|
18939
18938
|
}
|
|
18940
|
-
if (performance.now() - sliceStart >
|
|
18939
|
+
if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
|
|
18941
18940
|
await yieldToEventLoop();
|
|
18942
18941
|
sliceStart = performance.now();
|
|
18943
18942
|
}
|
|
@@ -18945,6 +18944,62 @@ var MineRenderWorld = class {
|
|
|
18945
18944
|
}
|
|
18946
18945
|
/** Replaces a chunk column's blocks, biomes, and saved light and, with `renderEntities`, its supported saved mobs. */
|
|
18947
18946
|
async placeChunk(chunk, executor) {
|
|
18947
|
+
return this.placeColumn(chunk, executor);
|
|
18948
|
+
}
|
|
18949
|
+
/**
|
|
18950
|
+
* Replaces parsed columns in input order, then refreshes neighboring geometry once.
|
|
18951
|
+
* Duplicate coordinates retain their order. A failed column stops placement; queued refreshes finish before rejection.
|
|
18952
|
+
*/
|
|
18953
|
+
async placeChunks(chunks) {
|
|
18954
|
+
const columns = [];
|
|
18955
|
+
for (const chunk of chunks) {
|
|
18956
|
+
const { x, z } = chunk ?? {};
|
|
18957
|
+
if (![x, z].every(Number.isSafeInteger)) {
|
|
18958
|
+
throw new RangeError("Chunk column coordinates must be safe integers");
|
|
18959
|
+
}
|
|
18960
|
+
const sections = Array.from(chunk.sections, (section) => {
|
|
18961
|
+
const { y } = section ?? {};
|
|
18962
|
+
if (!Number.isSafeInteger(y)) throw new RangeError("Chunk section coordinates must be safe integers");
|
|
18963
|
+
return { ...section, y };
|
|
18964
|
+
});
|
|
18965
|
+
columns.push({ ...chunk, x, z, sections });
|
|
18966
|
+
}
|
|
18967
|
+
if (!columns.length) return;
|
|
18968
|
+
await this.culling;
|
|
18969
|
+
let failed = false, failure, sliceStart = performance.now();
|
|
18970
|
+
try {
|
|
18971
|
+
for (const column of columns) {
|
|
18972
|
+
await this.placeColumn(column, void 0, true);
|
|
18973
|
+
if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
|
|
18974
|
+
await yieldToEventLoop();
|
|
18975
|
+
sliceStart = performance.now();
|
|
18976
|
+
}
|
|
18977
|
+
}
|
|
18978
|
+
} catch (error) {
|
|
18979
|
+
failed = true;
|
|
18980
|
+
failure = error;
|
|
18981
|
+
} finally {
|
|
18982
|
+
try {
|
|
18983
|
+
const failures = await this.refreshBiomes(true);
|
|
18984
|
+
if (failures.size) log19.warn("Failed to refresh biome colors", failures.values().next().value);
|
|
18985
|
+
} catch (error) {
|
|
18986
|
+
if (!failed) {
|
|
18987
|
+
failed = true;
|
|
18988
|
+
failure = error;
|
|
18989
|
+
}
|
|
18990
|
+
}
|
|
18991
|
+
try {
|
|
18992
|
+
await this.updateCulling([]);
|
|
18993
|
+
} catch (error) {
|
|
18994
|
+
if (!failed) {
|
|
18995
|
+
failed = true;
|
|
18996
|
+
failure = error;
|
|
18997
|
+
}
|
|
18998
|
+
}
|
|
18999
|
+
}
|
|
19000
|
+
if (failed) throw failure;
|
|
19001
|
+
}
|
|
19002
|
+
async placeColumn(chunk, executor, deferCulling = false) {
|
|
18948
19003
|
if (![chunk.x, chunk.z].every(Number.isSafeInteger)) {
|
|
18949
19004
|
throw new RangeError("Chunk column coordinates must be safe integers");
|
|
18950
19005
|
}
|
|
@@ -19006,7 +19061,7 @@ var MineRenderWorld = class {
|
|
|
19006
19061
|
} finally {
|
|
19007
19062
|
this.markSectionChanged(target);
|
|
19008
19063
|
}
|
|
19009
|
-
if (performance.now() - sliceStart >
|
|
19064
|
+
if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
|
|
19010
19065
|
await yieldToEventLoop();
|
|
19011
19066
|
sliceStart = performance.now();
|
|
19012
19067
|
}
|
|
@@ -19029,9 +19084,12 @@ var MineRenderWorld = class {
|
|
|
19029
19084
|
await placeEntities?.();
|
|
19030
19085
|
} finally {
|
|
19031
19086
|
this.markBiomesChanged(chunk.x, chunk.z, void 0, false);
|
|
19032
|
-
|
|
19033
|
-
|
|
19034
|
-
|
|
19087
|
+
if (deferCulling) await this.updateCulling([...changes.values()], true);
|
|
19088
|
+
else {
|
|
19089
|
+
const failures = await this.refreshBiomes();
|
|
19090
|
+
if (failures.size) log19.warn("Failed to refresh biome colors", failures.values().next().value);
|
|
19091
|
+
await this.updateCulling([...changes.values()]);
|
|
19092
|
+
}
|
|
19035
19093
|
}
|
|
19036
19094
|
}
|
|
19037
19095
|
/**
|
|
@@ -19061,7 +19119,7 @@ var MineRenderWorld = class {
|
|
|
19061
19119
|
for (const { x, z } of columns.values()) {
|
|
19062
19120
|
this.entities?.clearColumn(x, z);
|
|
19063
19121
|
await this.clearChunkColumn(x, z);
|
|
19064
|
-
if (performance.now() - sliceStart >
|
|
19122
|
+
if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
|
|
19065
19123
|
await yieldToEventLoop();
|
|
19066
19124
|
sliceStart = performance.now();
|
|
19067
19125
|
}
|
|
@@ -19200,15 +19258,25 @@ var MineRenderWorld = class {
|
|
|
19200
19258
|
for (const [index, mask] of masks) await chunk.setCullMaskIndex(index, mask);
|
|
19201
19259
|
await chunk.refreshLight(indices === "all" ? void 0 : indices);
|
|
19202
19260
|
chunk.refreshAmbientOcclusion(this._ambientOcclusion);
|
|
19203
|
-
if (
|
|
19261
|
+
if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
|
|
19204
19262
|
await yieldToEventLoop();
|
|
19205
19263
|
sliceStart = performance.now();
|
|
19206
19264
|
}
|
|
19207
19265
|
}
|
|
19208
|
-
const
|
|
19209
|
-
|
|
19210
|
-
|
|
19211
|
-
|
|
19266
|
+
const pending = [];
|
|
19267
|
+
for (const chunk of [...batch.keys()]) {
|
|
19268
|
+
const build = (async () => {
|
|
19269
|
+
await chunk.rebuildSectionMesh();
|
|
19270
|
+
batch.delete(chunk);
|
|
19271
|
+
})();
|
|
19272
|
+
void build.catch(() => void 0);
|
|
19273
|
+
pending.push(build);
|
|
19274
|
+
if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
|
|
19275
|
+
await yieldToEventLoop();
|
|
19276
|
+
sliceStart = performance.now();
|
|
19277
|
+
}
|
|
19278
|
+
}
|
|
19279
|
+
const builds = await Promise.allSettled(pending);
|
|
19212
19280
|
const failure = builds.find((result) => result.status === "rejected");
|
|
19213
19281
|
if (failure?.status === "rejected") throw failure.reason;
|
|
19214
19282
|
} catch (error) {
|
|
@@ -19651,6 +19719,9 @@ _SectionWorker.initialized = false;
|
|
|
19651
19719
|
var SectionWorker = _SectionWorker;
|
|
19652
19720
|
|
|
19653
19721
|
// src/world/SectionMesh.ts
|
|
19722
|
+
var BLOCK_SIZE = 16;
|
|
19723
|
+
var QUAD_VERTICES = 4;
|
|
19724
|
+
var QUAD_INDICES = 6;
|
|
19654
19725
|
var templateCache = /* @__PURE__ */ new WeakMap();
|
|
19655
19726
|
var WATER_TINT = new import_three46.Color(4159204).toArray();
|
|
19656
19727
|
function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
@@ -19687,8 +19758,8 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
|
19687
19758
|
if (!data) {
|
|
19688
19759
|
const indices = template.geometry.getIndex();
|
|
19689
19760
|
const position = template.geometry.getAttribute("position");
|
|
19690
|
-
const quads = indices.count /
|
|
19691
|
-
if (indices.count %
|
|
19761
|
+
const quads = indices.count / QUAD_INDICES;
|
|
19762
|
+
if (indices.count % QUAD_INDICES !== 0 || position.count !== quads * QUAD_VERTICES) {
|
|
19692
19763
|
throw new RangeError("Section template geometry must hold four vertices and six indices per quad");
|
|
19693
19764
|
}
|
|
19694
19765
|
data = {
|
|
@@ -19698,8 +19769,8 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
|
19698
19769
|
uvs: new Float32Array(quads * 8),
|
|
19699
19770
|
uvBounds: new Float32Array(quads * 16),
|
|
19700
19771
|
colors: new Float32Array(quads * 12),
|
|
19701
|
-
shade: new Float32Array(quads *
|
|
19702
|
-
indices: new Uint16Array(quads *
|
|
19772
|
+
shade: new Float32Array(quads * QUAD_VERTICES),
|
|
19773
|
+
indices: new Uint16Array(quads * QUAD_INDICES),
|
|
19703
19774
|
cullFaces: new Uint8Array(template.cullFaces),
|
|
19704
19775
|
biomeTint: template.biomeTint ? new Uint8Array(template.biomeTint) : void 0,
|
|
19705
19776
|
atlas: 0,
|
|
@@ -19711,7 +19782,7 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
|
19711
19782
|
const bounds = template.geometry.getAttribute("uvBounds");
|
|
19712
19783
|
const color2 = template.geometry.getAttribute("color");
|
|
19713
19784
|
const shade = template.geometry.getAttribute("shade");
|
|
19714
|
-
for (let vertex = 0; vertex < quads *
|
|
19785
|
+
for (let vertex = 0; vertex < quads * QUAD_VERTICES; vertex++) {
|
|
19715
19786
|
data.positions.set([position.getX(vertex), position.getY(vertex), position.getZ(vertex)], vertex * 3);
|
|
19716
19787
|
data.normals.set([normal.getX(vertex), normal.getY(vertex), normal.getZ(vertex)], vertex * 3);
|
|
19717
19788
|
data.uvs.set([uv.getX(vertex), uv.getY(vertex)], vertex * 2);
|
|
@@ -19724,7 +19795,7 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
|
19724
19795
|
data.colors.set([color2?.getX(vertex) ?? 1, color2?.getY(vertex) ?? 1, color2?.getZ(vertex) ?? 1], vertex * 3);
|
|
19725
19796
|
data.shade[vertex] = shade?.getX(vertex) ?? 1;
|
|
19726
19797
|
}
|
|
19727
|
-
for (let i2 = 0; i2 < data.indices.length; i2++) data.indices[i2] = indices.getX(i2) -
|
|
19798
|
+
for (let i2 = 0; i2 < data.indices.length; i2++) data.indices[i2] = indices.getX(i2) - QUAD_VERTICES * Math.floor(i2 / QUAD_INDICES);
|
|
19728
19799
|
templateCache.set(template, data);
|
|
19729
19800
|
}
|
|
19730
19801
|
id = input.templateData.length;
|
|
@@ -19757,6 +19828,54 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
|
19757
19828
|
}
|
|
19758
19829
|
return { input, atlases: atlases2 };
|
|
19759
19830
|
}
|
|
19831
|
+
function sortTransparentQuads(mesh) {
|
|
19832
|
+
const index = mesh.geometry.getIndex();
|
|
19833
|
+
if (index.count <= QUAD_INDICES) return;
|
|
19834
|
+
index.setUsage(import_three46.DynamicDrawUsage);
|
|
19835
|
+
const count = index.count / QUAD_INDICES;
|
|
19836
|
+
let source, centers, depths, order;
|
|
19837
|
+
const view = new import_three46.Matrix4(), localCamera = new import_three46.Vector3(), key = new import_three46.Vector3(), previous = new import_three46.Vector3();
|
|
19838
|
+
let previousOrthographic;
|
|
19839
|
+
mesh.onBeforeRender = (_renderer, _scene, camera) => {
|
|
19840
|
+
localCamera.setFromMatrixPosition(camera.matrixWorld).applyMatrix4(view.copy(mesh.matrixWorld).invert());
|
|
19841
|
+
const orthographic = !!camera.isOrthographicCamera;
|
|
19842
|
+
if (orthographic) {
|
|
19843
|
+
view.multiplyMatrices(camera.matrixWorldInverse, mesh.matrixWorld);
|
|
19844
|
+
key.set(view.elements[2], view.elements[6], view.elements[10]);
|
|
19845
|
+
} else {
|
|
19846
|
+
key.copy(localCamera).divideScalar(BLOCK_SIZE).floor();
|
|
19847
|
+
}
|
|
19848
|
+
if (orthographic === previousOrthographic && key.equals(previous)) return;
|
|
19849
|
+
previous.copy(key);
|
|
19850
|
+
previousOrthographic = orthographic;
|
|
19851
|
+
if (!centers) {
|
|
19852
|
+
source = new Uint32Array(index.array);
|
|
19853
|
+
centers = new Float32Array(count * 3);
|
|
19854
|
+
depths = new Float32Array(count);
|
|
19855
|
+
order = Uint32Array.from({ length: count }, (_, quad) => quad);
|
|
19856
|
+
const positions = mesh.geometry.getAttribute("position");
|
|
19857
|
+
for (let quad = 0; quad < count; quad++) {
|
|
19858
|
+
for (let axis = 0; axis < 3; axis++) {
|
|
19859
|
+
let center = 0;
|
|
19860
|
+
for (let corner = 0; corner < QUAD_VERTICES; corner++) center += positions.getComponent(quad * QUAD_VERTICES + corner, axis);
|
|
19861
|
+
centers[quad * 3 + axis] = center / QUAD_VERTICES;
|
|
19862
|
+
}
|
|
19863
|
+
}
|
|
19864
|
+
}
|
|
19865
|
+
for (let quad = 0; quad < count; quad++) {
|
|
19866
|
+
const x = centers[quad * 3], y = centers[quad * 3 + 1], z = centers[quad * 3 + 2];
|
|
19867
|
+
depths[quad] = orthographic ? key.x * x + key.y * y + key.z * z : -((x - localCamera.x) ** 2 + (y - localCamera.y) ** 2 + (z - localCamera.z) ** 2);
|
|
19868
|
+
}
|
|
19869
|
+
order.sort((a, b) => depths[a] - depths[b] || a - b);
|
|
19870
|
+
if (!order.some((quad, offset) => index.array[offset * QUAD_INDICES] !== source[quad * QUAD_INDICES])) return;
|
|
19871
|
+
for (let offset = 0; offset < count; offset++) {
|
|
19872
|
+
for (let indexOffset = 0; indexOffset < QUAD_INDICES; indexOffset++) {
|
|
19873
|
+
index.array[offset * QUAD_INDICES + indexOffset] = source[order[offset] * QUAD_INDICES + indexOffset];
|
|
19874
|
+
}
|
|
19875
|
+
}
|
|
19876
|
+
index.needsUpdate = true;
|
|
19877
|
+
};
|
|
19878
|
+
}
|
|
19760
19879
|
var SectionMesh = class _SectionMesh extends import_three46.Group {
|
|
19761
19880
|
constructor() {
|
|
19762
19881
|
super(...arguments);
|
|
@@ -19766,7 +19885,11 @@ var SectionMesh = class _SectionMesh extends import_three46.Group {
|
|
|
19766
19885
|
/** Returns the owning section cell for a triangle hit on this mesh's merged geometry. */
|
|
19767
19886
|
getBlockIndex(hit) {
|
|
19768
19887
|
if (hit.faceIndex === void 0 || hit.faceIndex === null) return void 0;
|
|
19769
|
-
|
|
19888
|
+
const mesh = hit.object;
|
|
19889
|
+
const owners = this.blockIndices.get(mesh);
|
|
19890
|
+
if (!owners) return void 0;
|
|
19891
|
+
const vertex = mesh.geometry.getIndex().getX(hit.faceIndex * 3);
|
|
19892
|
+
return owners[Math.floor(vertex / QUAD_VERTICES)];
|
|
19770
19893
|
}
|
|
19771
19894
|
/** Builds section-local meshes from visible quads. `maxAtlasSize` limits each atlas dimension in pixels. */
|
|
19772
19895
|
static build(entries, maxAtlasSize = 2048, fluids, lighting) {
|
|
@@ -19802,13 +19925,14 @@ var SectionMesh = class _SectionMesh extends import_three46.Group {
|
|
|
19802
19925
|
const material = Materials.createShadedCanvasMaterial(canvas, transparent, false, true);
|
|
19803
19926
|
material.side = import_three46.FrontSide;
|
|
19804
19927
|
if (page.layer === 2) {
|
|
19928
|
+
material.depthWrite = !material.transparent;
|
|
19805
19929
|
material.side = import_three46.DoubleSide;
|
|
19806
19930
|
material.forceSinglePass = true;
|
|
19807
|
-
material.depthWrite = !material.transparent;
|
|
19808
19931
|
}
|
|
19809
19932
|
material.vertexColors = true;
|
|
19810
19933
|
const texture = material.uniforms?.map?.value ?? material.map;
|
|
19811
19934
|
const mesh = new import_three46.Mesh(geometry, material);
|
|
19935
|
+
if (transparent) sortTransparentQuads(mesh);
|
|
19812
19936
|
section.add(mesh);
|
|
19813
19937
|
section.blockIndices.set(mesh, page.blockIndices);
|
|
19814
19938
|
const unsubscribe = [];
|
|
@@ -20611,6 +20735,7 @@ var Chunk = class _Chunk {
|
|
|
20611
20735
|
};
|
|
20612
20736
|
|
|
20613
20737
|
// src/world/WorldStreamer.ts
|
|
20738
|
+
var MAX_PREFETCHED_COLUMNS = 4;
|
|
20614
20739
|
var WorldStreamer = class {
|
|
20615
20740
|
constructor(world, source, options = {}) {
|
|
20616
20741
|
this.world = world;
|
|
@@ -20620,6 +20745,7 @@ var WorldStreamer = class {
|
|
|
20620
20745
|
this.missing = /* @__PURE__ */ new Map();
|
|
20621
20746
|
this.failed = /* @__PURE__ */ new Map();
|
|
20622
20747
|
this.desired = [];
|
|
20748
|
+
this.reads = /* @__PURE__ */ new Map();
|
|
20623
20749
|
this.disposed = false;
|
|
20624
20750
|
this.loadRadius = options.loadRadius ?? 2;
|
|
20625
20751
|
this.unloadRadius = options.unloadRadius ?? Math.min(32, this.loadRadius + 1);
|
|
@@ -20664,6 +20790,12 @@ var WorldStreamer = class {
|
|
|
20664
20790
|
if (this.loading && !this.within(this.loading.position, this.loadRadius)) {
|
|
20665
20791
|
this.loading.controller.abort();
|
|
20666
20792
|
}
|
|
20793
|
+
for (const [key, read] of this.reads) {
|
|
20794
|
+
if (!this.within(read.position, this.loadRadius)) {
|
|
20795
|
+
read.controller.abort();
|
|
20796
|
+
if (read.ready) this.reads.delete(key);
|
|
20797
|
+
}
|
|
20798
|
+
}
|
|
20667
20799
|
}
|
|
20668
20800
|
return this.running ?? (this.running = Promise.resolve().then(() => this.drain()));
|
|
20669
20801
|
}
|
|
@@ -20684,8 +20816,11 @@ var WorldStreamer = class {
|
|
|
20684
20816
|
this.desired = [];
|
|
20685
20817
|
this.center = void 0;
|
|
20686
20818
|
this.loading?.controller.abort();
|
|
20819
|
+
for (const read of this.reads.values()) read.controller.abort();
|
|
20687
20820
|
return this.disposal ?? (this.disposal = Promise.resolve().then(async () => {
|
|
20688
20821
|
await this.running?.catch(() => void 0);
|
|
20822
|
+
await Promise.all([...this.reads.values()].map((read) => read.promise));
|
|
20823
|
+
this.reads.clear();
|
|
20689
20824
|
try {
|
|
20690
20825
|
await this.unloadMany([...this.owned.values()]);
|
|
20691
20826
|
} finally {
|
|
@@ -20711,6 +20846,10 @@ var WorldStreamer = class {
|
|
|
20711
20846
|
await this.unloadMany(obsolete);
|
|
20712
20847
|
continue;
|
|
20713
20848
|
}
|
|
20849
|
+
if (this.world.placeChunks) {
|
|
20850
|
+
if (await this.placeReadyChunks()) continue;
|
|
20851
|
+
return;
|
|
20852
|
+
}
|
|
20714
20853
|
const position = this.desired.find((position2) => this.needsLoad(position2));
|
|
20715
20854
|
if (!position) return;
|
|
20716
20855
|
let chunk;
|
|
@@ -20733,26 +20872,105 @@ var WorldStreamer = class {
|
|
|
20733
20872
|
continue;
|
|
20734
20873
|
}
|
|
20735
20874
|
this.owned.set(this.key(position), position);
|
|
20875
|
+
await this.placeOwnedColumns([position], () => this.world.placeChunk(chunk));
|
|
20876
|
+
}
|
|
20877
|
+
} finally {
|
|
20878
|
+
for (const [key, read] of this.reads) {
|
|
20879
|
+
read.controller.abort();
|
|
20880
|
+
if (read.ready) this.reads.delete(key);
|
|
20881
|
+
}
|
|
20882
|
+
this.running = void 0;
|
|
20883
|
+
}
|
|
20884
|
+
}
|
|
20885
|
+
fillReadSlots() {
|
|
20886
|
+
while (!this.disposed && this.reads.size < MAX_PREFETCHED_COLUMNS) {
|
|
20887
|
+
const position = this.desired.find((position2) => this.needsLoad(position2) && !this.reads.has(this.key(position2)));
|
|
20888
|
+
if (!position) return;
|
|
20889
|
+
const key = this.key(position);
|
|
20890
|
+
const read = {
|
|
20891
|
+
position,
|
|
20892
|
+
controller: new AbortController(),
|
|
20893
|
+
promise: Promise.resolve(),
|
|
20894
|
+
ready: false,
|
|
20895
|
+
failed: false
|
|
20896
|
+
};
|
|
20897
|
+
this.reads.set(key, read);
|
|
20898
|
+
read.promise = (async () => {
|
|
20736
20899
|
try {
|
|
20737
|
-
await this.
|
|
20738
|
-
this.
|
|
20900
|
+
read.chunk = await this.source.getChunk(position.x, position.z, read.controller.signal);
|
|
20901
|
+
if (read.chunk) this.validateChunk(read.chunk, position);
|
|
20739
20902
|
} catch (error) {
|
|
20740
|
-
|
|
20741
|
-
|
|
20742
|
-
|
|
20743
|
-
|
|
20744
|
-
|
|
20745
|
-
|
|
20746
|
-
} catch {
|
|
20747
|
-
throw Object.assign(new Error(failure.message), { cause: cleanupError });
|
|
20748
|
-
}
|
|
20749
|
-
throw failure;
|
|
20903
|
+
read.failed = true;
|
|
20904
|
+
read.error = error;
|
|
20905
|
+
} finally {
|
|
20906
|
+
read.ready = true;
|
|
20907
|
+
if (read.controller.signal.aborted || this.disposed || !this.within(position, this.loadRadius)) {
|
|
20908
|
+
this.reads.delete(key);
|
|
20750
20909
|
}
|
|
20751
|
-
|
|
20910
|
+
}
|
|
20911
|
+
})();
|
|
20912
|
+
}
|
|
20913
|
+
}
|
|
20914
|
+
/** Places only the ready prefix, so later source reads cannot delay the nearest ready column. */
|
|
20915
|
+
async placeReadyChunks() {
|
|
20916
|
+
this.fillReadSlots();
|
|
20917
|
+
const position = this.desired.find((position2) => this.needsLoad(position2));
|
|
20918
|
+
if (!position) {
|
|
20919
|
+
if (!this.reads.size) return false;
|
|
20920
|
+
await Promise.all([...this.reads.values()].map((read) => read.promise));
|
|
20921
|
+
return true;
|
|
20922
|
+
}
|
|
20923
|
+
const head = this.reads.get(this.key(position));
|
|
20924
|
+
if (!head) {
|
|
20925
|
+
for (let index = this.desired.length - 1; index >= 0; index--) {
|
|
20926
|
+
const key = this.key(this.desired[index]);
|
|
20927
|
+
if (this.reads.get(key)?.ready) {
|
|
20928
|
+
this.reads.delete(key);
|
|
20929
|
+
return true;
|
|
20752
20930
|
}
|
|
20753
20931
|
}
|
|
20754
|
-
|
|
20755
|
-
|
|
20932
|
+
await Promise.race([...this.reads.values()].map((read) => read.promise));
|
|
20933
|
+
return true;
|
|
20934
|
+
}
|
|
20935
|
+
if (!head.ready) {
|
|
20936
|
+
await head.promise;
|
|
20937
|
+
return true;
|
|
20938
|
+
}
|
|
20939
|
+
const chunks = [], positions = [];
|
|
20940
|
+
for (const desired of this.desired) {
|
|
20941
|
+
if (!this.needsLoad(desired)) continue;
|
|
20942
|
+
const key = this.key(desired), read = this.reads.get(key);
|
|
20943
|
+
if (!read?.ready) break;
|
|
20944
|
+
this.reads.delete(key);
|
|
20945
|
+
if (read.failed) this.failed.set(key, { ...desired, error: read.error });
|
|
20946
|
+
else if (!read.chunk) this.missing.set(key, desired);
|
|
20947
|
+
else {
|
|
20948
|
+
chunks.push(read.chunk);
|
|
20949
|
+
positions.push(desired);
|
|
20950
|
+
}
|
|
20951
|
+
}
|
|
20952
|
+
if (!chunks.length) return true;
|
|
20953
|
+
for (const position2 of positions) this.owned.set(this.key(position2), position2);
|
|
20954
|
+
await this.placeOwnedColumns(positions, () => this.world.placeChunks(chunks));
|
|
20955
|
+
return true;
|
|
20956
|
+
}
|
|
20957
|
+
async placeOwnedColumns(positions, place) {
|
|
20958
|
+
try {
|
|
20959
|
+
await place();
|
|
20960
|
+
for (const position of positions) this.loaded.set(this.key(position), position);
|
|
20961
|
+
} catch (error) {
|
|
20962
|
+
try {
|
|
20963
|
+
await this.unloadMany(positions);
|
|
20964
|
+
} catch (cleanupError) {
|
|
20965
|
+
const failure = error instanceof Error ? error : new Error(String(error));
|
|
20966
|
+
try {
|
|
20967
|
+
Object.defineProperty(failure, "cause", { value: cleanupError, configurable: true });
|
|
20968
|
+
} catch {
|
|
20969
|
+
throw Object.assign(new Error(failure.message), { cause: cleanupError });
|
|
20970
|
+
}
|
|
20971
|
+
throw failure;
|
|
20972
|
+
}
|
|
20973
|
+
throw error;
|
|
20756
20974
|
}
|
|
20757
20975
|
}
|
|
20758
20976
|
needsLoad(position) {
|