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/node/index.mjs
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();
|
|
@@ -17924,7 +17922,7 @@ var AnvilWorldSource = class {
|
|
|
17924
17922
|
};
|
|
17925
17923
|
|
|
17926
17924
|
// src/world/Chunk.ts
|
|
17927
|
-
import { Color as Color13, Vector3 as
|
|
17925
|
+
import { Color as Color13, Vector3 as Vector321 } from "three";
|
|
17928
17926
|
init_BlockStates();
|
|
17929
17927
|
init_AssetKey();
|
|
17930
17928
|
|
|
@@ -18528,6 +18526,7 @@ function skipsBlockFace(data, rule, block, neighbor, face) {
|
|
|
18528
18526
|
// src/world/MineRenderWorld.ts
|
|
18529
18527
|
init_log();
|
|
18530
18528
|
var log18 = logger("MineRenderWorld");
|
|
18529
|
+
var YIELD_INTERVAL_MS = 8;
|
|
18531
18530
|
var MineRenderWorld = class {
|
|
18532
18531
|
constructor(scene, options = {}) {
|
|
18533
18532
|
this._chunks = /* @__PURE__ */ new Map();
|
|
@@ -18641,7 +18640,7 @@ var MineRenderWorld = class {
|
|
|
18641
18640
|
failures.set(chunk, error);
|
|
18642
18641
|
if (this._chunks.get(`${chunk.x}_${chunk.y}_${chunk.z}`) === chunk) this.pendingBiomes.set(chunk, void 0);
|
|
18643
18642
|
}
|
|
18644
|
-
if (performance.now() - sliceStart >
|
|
18643
|
+
if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
|
|
18645
18644
|
await yieldToEventLoop();
|
|
18646
18645
|
sliceStart = performance.now();
|
|
18647
18646
|
}
|
|
@@ -18796,7 +18795,7 @@ var MineRenderWorld = class {
|
|
|
18796
18795
|
));
|
|
18797
18796
|
for (const pos of positions) changes.set(`${pos.x},${pos.y},${pos.z}`, pos);
|
|
18798
18797
|
}
|
|
18799
|
-
if (performance.now() - sliceStart >
|
|
18798
|
+
if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
|
|
18800
18799
|
await yieldToEventLoop();
|
|
18801
18800
|
sliceStart = performance.now();
|
|
18802
18801
|
}
|
|
@@ -18804,6 +18803,62 @@ var MineRenderWorld = class {
|
|
|
18804
18803
|
}
|
|
18805
18804
|
/** Replaces a chunk column's blocks, biomes, and saved light and, with `renderEntities`, its supported saved mobs. */
|
|
18806
18805
|
async placeChunk(chunk, executor) {
|
|
18806
|
+
return this.placeColumn(chunk, executor);
|
|
18807
|
+
}
|
|
18808
|
+
/**
|
|
18809
|
+
* Replaces parsed columns in input order, then refreshes neighboring geometry once.
|
|
18810
|
+
* Duplicate coordinates retain their order. A failed column stops placement; queued refreshes finish before rejection.
|
|
18811
|
+
*/
|
|
18812
|
+
async placeChunks(chunks) {
|
|
18813
|
+
const columns = [];
|
|
18814
|
+
for (const chunk of chunks) {
|
|
18815
|
+
const { x, z } = chunk ?? {};
|
|
18816
|
+
if (![x, z].every(Number.isSafeInteger)) {
|
|
18817
|
+
throw new RangeError("Chunk column coordinates must be safe integers");
|
|
18818
|
+
}
|
|
18819
|
+
const sections = Array.from(chunk.sections, (section) => {
|
|
18820
|
+
const { y } = section ?? {};
|
|
18821
|
+
if (!Number.isSafeInteger(y)) throw new RangeError("Chunk section coordinates must be safe integers");
|
|
18822
|
+
return { ...section, y };
|
|
18823
|
+
});
|
|
18824
|
+
columns.push({ ...chunk, x, z, sections });
|
|
18825
|
+
}
|
|
18826
|
+
if (!columns.length) return;
|
|
18827
|
+
await this.culling;
|
|
18828
|
+
let failed = false, failure, sliceStart = performance.now();
|
|
18829
|
+
try {
|
|
18830
|
+
for (const column of columns) {
|
|
18831
|
+
await this.placeColumn(column, void 0, true);
|
|
18832
|
+
if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
|
|
18833
|
+
await yieldToEventLoop();
|
|
18834
|
+
sliceStart = performance.now();
|
|
18835
|
+
}
|
|
18836
|
+
}
|
|
18837
|
+
} catch (error) {
|
|
18838
|
+
failed = true;
|
|
18839
|
+
failure = error;
|
|
18840
|
+
} finally {
|
|
18841
|
+
try {
|
|
18842
|
+
const failures = await this.refreshBiomes(true);
|
|
18843
|
+
if (failures.size) log18.warn("Failed to refresh biome colors", failures.values().next().value);
|
|
18844
|
+
} catch (error) {
|
|
18845
|
+
if (!failed) {
|
|
18846
|
+
failed = true;
|
|
18847
|
+
failure = error;
|
|
18848
|
+
}
|
|
18849
|
+
}
|
|
18850
|
+
try {
|
|
18851
|
+
await this.updateCulling([]);
|
|
18852
|
+
} catch (error) {
|
|
18853
|
+
if (!failed) {
|
|
18854
|
+
failed = true;
|
|
18855
|
+
failure = error;
|
|
18856
|
+
}
|
|
18857
|
+
}
|
|
18858
|
+
}
|
|
18859
|
+
if (failed) throw failure;
|
|
18860
|
+
}
|
|
18861
|
+
async placeColumn(chunk, executor, deferCulling = false) {
|
|
18807
18862
|
if (![chunk.x, chunk.z].every(Number.isSafeInteger)) {
|
|
18808
18863
|
throw new RangeError("Chunk column coordinates must be safe integers");
|
|
18809
18864
|
}
|
|
@@ -18865,7 +18920,7 @@ var MineRenderWorld = class {
|
|
|
18865
18920
|
} finally {
|
|
18866
18921
|
this.markSectionChanged(target);
|
|
18867
18922
|
}
|
|
18868
|
-
if (performance.now() - sliceStart >
|
|
18923
|
+
if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
|
|
18869
18924
|
await yieldToEventLoop();
|
|
18870
18925
|
sliceStart = performance.now();
|
|
18871
18926
|
}
|
|
@@ -18888,9 +18943,12 @@ var MineRenderWorld = class {
|
|
|
18888
18943
|
await placeEntities?.();
|
|
18889
18944
|
} finally {
|
|
18890
18945
|
this.markBiomesChanged(chunk.x, chunk.z, void 0, false);
|
|
18891
|
-
|
|
18892
|
-
|
|
18893
|
-
|
|
18946
|
+
if (deferCulling) await this.updateCulling([...changes.values()], true);
|
|
18947
|
+
else {
|
|
18948
|
+
const failures = await this.refreshBiomes();
|
|
18949
|
+
if (failures.size) log18.warn("Failed to refresh biome colors", failures.values().next().value);
|
|
18950
|
+
await this.updateCulling([...changes.values()]);
|
|
18951
|
+
}
|
|
18894
18952
|
}
|
|
18895
18953
|
}
|
|
18896
18954
|
/**
|
|
@@ -18920,7 +18978,7 @@ var MineRenderWorld = class {
|
|
|
18920
18978
|
for (const { x, z } of columns.values()) {
|
|
18921
18979
|
this.entities?.clearColumn(x, z);
|
|
18922
18980
|
await this.clearChunkColumn(x, z);
|
|
18923
|
-
if (performance.now() - sliceStart >
|
|
18981
|
+
if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
|
|
18924
18982
|
await yieldToEventLoop();
|
|
18925
18983
|
sliceStart = performance.now();
|
|
18926
18984
|
}
|
|
@@ -19059,15 +19117,25 @@ var MineRenderWorld = class {
|
|
|
19059
19117
|
for (const [index, mask] of masks) await chunk.setCullMaskIndex(index, mask);
|
|
19060
19118
|
await chunk.refreshLight(indices === "all" ? void 0 : indices);
|
|
19061
19119
|
chunk.refreshAmbientOcclusion(this._ambientOcclusion);
|
|
19062
|
-
if (
|
|
19120
|
+
if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
|
|
19063
19121
|
await yieldToEventLoop();
|
|
19064
19122
|
sliceStart = performance.now();
|
|
19065
19123
|
}
|
|
19066
19124
|
}
|
|
19067
|
-
const
|
|
19068
|
-
|
|
19069
|
-
|
|
19070
|
-
|
|
19125
|
+
const pending = [];
|
|
19126
|
+
for (const chunk of [...batch.keys()]) {
|
|
19127
|
+
const build = (async () => {
|
|
19128
|
+
await chunk.rebuildSectionMesh();
|
|
19129
|
+
batch.delete(chunk);
|
|
19130
|
+
})();
|
|
19131
|
+
void build.catch(() => void 0);
|
|
19132
|
+
pending.push(build);
|
|
19133
|
+
if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
|
|
19134
|
+
await yieldToEventLoop();
|
|
19135
|
+
sliceStart = performance.now();
|
|
19136
|
+
}
|
|
19137
|
+
}
|
|
19138
|
+
const builds = await Promise.allSettled(pending);
|
|
19071
19139
|
const failure = builds.find((result) => result.status === "rejected");
|
|
19072
19140
|
if (failure?.status === "rejected") throw failure.reason;
|
|
19073
19141
|
} catch (error) {
|
|
@@ -19184,7 +19252,7 @@ init_BlockEntities();
|
|
|
19184
19252
|
// src/world/SectionMesh.ts
|
|
19185
19253
|
init_CanvasCompat();
|
|
19186
19254
|
init_Materials();
|
|
19187
|
-
import { BufferGeometry as BufferGeometry9, Color as Color12, DoubleSide as DoubleSide8, Float32BufferAttribute as Float32BufferAttribute10, FrontSide as FrontSide4, Group as Group3, Mesh as Mesh12, Uint32BufferAttribute } from "three";
|
|
19255
|
+
import { BufferGeometry as BufferGeometry9, Color as Color12, DoubleSide as DoubleSide8, DynamicDrawUsage, Float32BufferAttribute as Float32BufferAttribute10, FrontSide as FrontSide4, Group as Group3, Matrix4 as Matrix413, Mesh as Mesh12, Uint32BufferAttribute, Vector3 as Vector320 } from "three";
|
|
19188
19256
|
|
|
19189
19257
|
// src/world/SectionGeometry.ts
|
|
19190
19258
|
init_FluidQuads();
|
|
@@ -19511,6 +19579,9 @@ var SectionWorker = class _SectionWorker {
|
|
|
19511
19579
|
};
|
|
19512
19580
|
|
|
19513
19581
|
// src/world/SectionMesh.ts
|
|
19582
|
+
var BLOCK_SIZE = 16;
|
|
19583
|
+
var QUAD_VERTICES = 4;
|
|
19584
|
+
var QUAD_INDICES = 6;
|
|
19514
19585
|
var templateCache = /* @__PURE__ */ new WeakMap();
|
|
19515
19586
|
var WATER_TINT = new Color12(4159204).toArray();
|
|
19516
19587
|
function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
@@ -19547,8 +19618,8 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
|
19547
19618
|
if (!data) {
|
|
19548
19619
|
const indices = template.geometry.getIndex();
|
|
19549
19620
|
const position = template.geometry.getAttribute("position");
|
|
19550
|
-
const quads = indices.count /
|
|
19551
|
-
if (indices.count %
|
|
19621
|
+
const quads = indices.count / QUAD_INDICES;
|
|
19622
|
+
if (indices.count % QUAD_INDICES !== 0 || position.count !== quads * QUAD_VERTICES) {
|
|
19552
19623
|
throw new RangeError("Section template geometry must hold four vertices and six indices per quad");
|
|
19553
19624
|
}
|
|
19554
19625
|
data = {
|
|
@@ -19558,8 +19629,8 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
|
19558
19629
|
uvs: new Float32Array(quads * 8),
|
|
19559
19630
|
uvBounds: new Float32Array(quads * 16),
|
|
19560
19631
|
colors: new Float32Array(quads * 12),
|
|
19561
|
-
shade: new Float32Array(quads *
|
|
19562
|
-
indices: new Uint16Array(quads *
|
|
19632
|
+
shade: new Float32Array(quads * QUAD_VERTICES),
|
|
19633
|
+
indices: new Uint16Array(quads * QUAD_INDICES),
|
|
19563
19634
|
cullFaces: new Uint8Array(template.cullFaces),
|
|
19564
19635
|
biomeTint: template.biomeTint ? new Uint8Array(template.biomeTint) : void 0,
|
|
19565
19636
|
atlas: 0,
|
|
@@ -19571,7 +19642,7 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
|
19571
19642
|
const bounds = template.geometry.getAttribute("uvBounds");
|
|
19572
19643
|
const color2 = template.geometry.getAttribute("color");
|
|
19573
19644
|
const shade = template.geometry.getAttribute("shade");
|
|
19574
|
-
for (let vertex = 0; vertex < quads *
|
|
19645
|
+
for (let vertex = 0; vertex < quads * QUAD_VERTICES; vertex++) {
|
|
19575
19646
|
data.positions.set([position.getX(vertex), position.getY(vertex), position.getZ(vertex)], vertex * 3);
|
|
19576
19647
|
data.normals.set([normal.getX(vertex), normal.getY(vertex), normal.getZ(vertex)], vertex * 3);
|
|
19577
19648
|
data.uvs.set([uv.getX(vertex), uv.getY(vertex)], vertex * 2);
|
|
@@ -19584,7 +19655,7 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
|
19584
19655
|
data.colors.set([color2?.getX(vertex) ?? 1, color2?.getY(vertex) ?? 1, color2?.getZ(vertex) ?? 1], vertex * 3);
|
|
19585
19656
|
data.shade[vertex] = shade?.getX(vertex) ?? 1;
|
|
19586
19657
|
}
|
|
19587
|
-
for (let i2 = 0; i2 < data.indices.length; i2++) data.indices[i2] = indices.getX(i2) -
|
|
19658
|
+
for (let i2 = 0; i2 < data.indices.length; i2++) data.indices[i2] = indices.getX(i2) - QUAD_VERTICES * Math.floor(i2 / QUAD_INDICES);
|
|
19588
19659
|
templateCache.set(template, data);
|
|
19589
19660
|
}
|
|
19590
19661
|
id = input.templateData.length;
|
|
@@ -19617,6 +19688,54 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
|
19617
19688
|
}
|
|
19618
19689
|
return { input, atlases: atlases2 };
|
|
19619
19690
|
}
|
|
19691
|
+
function sortTransparentQuads(mesh) {
|
|
19692
|
+
const index = mesh.geometry.getIndex();
|
|
19693
|
+
if (index.count <= QUAD_INDICES) return;
|
|
19694
|
+
index.setUsage(DynamicDrawUsage);
|
|
19695
|
+
const count = index.count / QUAD_INDICES;
|
|
19696
|
+
let source, centers, depths, order;
|
|
19697
|
+
const view = new Matrix413(), localCamera = new Vector320(), key = new Vector320(), previous = new Vector320();
|
|
19698
|
+
let previousOrthographic;
|
|
19699
|
+
mesh.onBeforeRender = (_renderer, _scene, camera) => {
|
|
19700
|
+
localCamera.setFromMatrixPosition(camera.matrixWorld).applyMatrix4(view.copy(mesh.matrixWorld).invert());
|
|
19701
|
+
const orthographic = !!camera.isOrthographicCamera;
|
|
19702
|
+
if (orthographic) {
|
|
19703
|
+
view.multiplyMatrices(camera.matrixWorldInverse, mesh.matrixWorld);
|
|
19704
|
+
key.set(view.elements[2], view.elements[6], view.elements[10]);
|
|
19705
|
+
} else {
|
|
19706
|
+
key.copy(localCamera).divideScalar(BLOCK_SIZE).floor();
|
|
19707
|
+
}
|
|
19708
|
+
if (orthographic === previousOrthographic && key.equals(previous)) return;
|
|
19709
|
+
previous.copy(key);
|
|
19710
|
+
previousOrthographic = orthographic;
|
|
19711
|
+
if (!centers) {
|
|
19712
|
+
source = new Uint32Array(index.array);
|
|
19713
|
+
centers = new Float32Array(count * 3);
|
|
19714
|
+
depths = new Float32Array(count);
|
|
19715
|
+
order = Uint32Array.from({ length: count }, (_, quad) => quad);
|
|
19716
|
+
const positions = mesh.geometry.getAttribute("position");
|
|
19717
|
+
for (let quad = 0; quad < count; quad++) {
|
|
19718
|
+
for (let axis = 0; axis < 3; axis++) {
|
|
19719
|
+
let center = 0;
|
|
19720
|
+
for (let corner = 0; corner < QUAD_VERTICES; corner++) center += positions.getComponent(quad * QUAD_VERTICES + corner, axis);
|
|
19721
|
+
centers[quad * 3 + axis] = center / QUAD_VERTICES;
|
|
19722
|
+
}
|
|
19723
|
+
}
|
|
19724
|
+
}
|
|
19725
|
+
for (let quad = 0; quad < count; quad++) {
|
|
19726
|
+
const x = centers[quad * 3], y = centers[quad * 3 + 1], z = centers[quad * 3 + 2];
|
|
19727
|
+
depths[quad] = orthographic ? key.x * x + key.y * y + key.z * z : -((x - localCamera.x) ** 2 + (y - localCamera.y) ** 2 + (z - localCamera.z) ** 2);
|
|
19728
|
+
}
|
|
19729
|
+
order.sort((a, b) => depths[a] - depths[b] || a - b);
|
|
19730
|
+
if (!order.some((quad, offset) => index.array[offset * QUAD_INDICES] !== source[quad * QUAD_INDICES])) return;
|
|
19731
|
+
for (let offset = 0; offset < count; offset++) {
|
|
19732
|
+
for (let indexOffset = 0; indexOffset < QUAD_INDICES; indexOffset++) {
|
|
19733
|
+
index.array[offset * QUAD_INDICES + indexOffset] = source[order[offset] * QUAD_INDICES + indexOffset];
|
|
19734
|
+
}
|
|
19735
|
+
}
|
|
19736
|
+
index.needsUpdate = true;
|
|
19737
|
+
};
|
|
19738
|
+
}
|
|
19620
19739
|
var SectionMesh = class _SectionMesh extends Group3 {
|
|
19621
19740
|
constructor() {
|
|
19622
19741
|
super(...arguments);
|
|
@@ -19626,7 +19745,11 @@ var SectionMesh = class _SectionMesh extends Group3 {
|
|
|
19626
19745
|
/** Returns the owning section cell for a triangle hit on this mesh's merged geometry. */
|
|
19627
19746
|
getBlockIndex(hit) {
|
|
19628
19747
|
if (hit.faceIndex === void 0 || hit.faceIndex === null) return void 0;
|
|
19629
|
-
|
|
19748
|
+
const mesh = hit.object;
|
|
19749
|
+
const owners = this.blockIndices.get(mesh);
|
|
19750
|
+
if (!owners) return void 0;
|
|
19751
|
+
const vertex = mesh.geometry.getIndex().getX(hit.faceIndex * 3);
|
|
19752
|
+
return owners[Math.floor(vertex / QUAD_VERTICES)];
|
|
19630
19753
|
}
|
|
19631
19754
|
/** Builds section-local meshes from visible quads. `maxAtlasSize` limits each atlas dimension in pixels. */
|
|
19632
19755
|
static build(entries, maxAtlasSize = 2048, fluids, lighting) {
|
|
@@ -19662,13 +19785,14 @@ var SectionMesh = class _SectionMesh extends Group3 {
|
|
|
19662
19785
|
const material = Materials.createShadedCanvasMaterial(canvas, transparent, false, true);
|
|
19663
19786
|
material.side = FrontSide4;
|
|
19664
19787
|
if (page.layer === 2) {
|
|
19788
|
+
material.depthWrite = !material.transparent;
|
|
19665
19789
|
material.side = DoubleSide8;
|
|
19666
19790
|
material.forceSinglePass = true;
|
|
19667
|
-
material.depthWrite = !material.transparent;
|
|
19668
19791
|
}
|
|
19669
19792
|
material.vertexColors = true;
|
|
19670
19793
|
const texture = material.uniforms?.map?.value ?? material.map;
|
|
19671
19794
|
const mesh = new Mesh12(geometry, material);
|
|
19795
|
+
if (transparent) sortTransparentQuads(mesh);
|
|
19672
19796
|
section.add(mesh);
|
|
19673
19797
|
section.blockIndices.set(mesh, page.blockIndices);
|
|
19674
19798
|
const unsubscribe = [];
|
|
@@ -19727,7 +19851,7 @@ var Chunk = class _Chunk {
|
|
|
19727
19851
|
this.sectionBlocks = /* @__PURE__ */ new Map();
|
|
19728
19852
|
this.hiddenBlocks = /* @__PURE__ */ new Set();
|
|
19729
19853
|
this.fluidCells = new Uint8Array(4096);
|
|
19730
|
-
this.blockPosition = new
|
|
19854
|
+
this.blockPosition = new Vector321();
|
|
19731
19855
|
this.meshDirty = false;
|
|
19732
19856
|
this.meshGeneration = 0;
|
|
19733
19857
|
this.blockChanges = /* @__PURE__ */ new Set();
|
|
@@ -19787,7 +19911,7 @@ var Chunk = class _Chunk {
|
|
|
19787
19911
|
const info = owner.chunk.renderedBlocks.get(owner.index);
|
|
19788
19912
|
if (!info) continue;
|
|
19789
19913
|
return {
|
|
19790
|
-
position: new
|
|
19914
|
+
position: new Vector321(
|
|
19791
19915
|
owner.chunk.x * 16 + owner.index % 16,
|
|
19792
19916
|
owner.chunk.y * 16 + Math.floor(owner.index / 256),
|
|
19793
19917
|
owner.chunk.z * 16 + Math.floor(owner.index / 16) % 16
|
|
@@ -19802,18 +19926,18 @@ var Chunk = class _Chunk {
|
|
|
19802
19926
|
}
|
|
19803
19927
|
getBlockAt(posOrX, y, z) {
|
|
19804
19928
|
if (typeof posOrX == "number") {
|
|
19805
|
-
return this.getBlockAt(new
|
|
19929
|
+
return this.getBlockAt(new Vector321(posOrX, y, z));
|
|
19806
19930
|
}
|
|
19807
19931
|
if (isTripleArray(posOrX)) {
|
|
19808
|
-
return this.getBlockAt(new
|
|
19932
|
+
return this.getBlockAt(new Vector321(posOrX[0], posOrX[1], posOrX[2]));
|
|
19809
19933
|
}
|
|
19810
19934
|
const pos = this.blockPosition.set(posOrX.x - this.x * 16, posOrX.y - this.y * 16, posOrX.z - this.z * 16);
|
|
19811
19935
|
const index = _Chunk.chunkPosToBlockIndex(pos);
|
|
19812
19936
|
return this.renderedBlocks.get(index);
|
|
19813
19937
|
}
|
|
19814
19938
|
getBiomeAt(posOrX, y, z) {
|
|
19815
|
-
if (typeof posOrX === "number") return this.getBiomeAt(new
|
|
19816
|
-
if (isTripleArray(posOrX)) return this.getBiomeAt(new
|
|
19939
|
+
if (typeof posOrX === "number") return this.getBiomeAt(new Vector321(posOrX, y, z));
|
|
19940
|
+
if (isTripleArray(posOrX)) return this.getBiomeAt(new Vector321(...posOrX));
|
|
19817
19941
|
const pos = this.worldPosToChunkPos(posOrX);
|
|
19818
19942
|
_Chunk.chunkPosToBlockIndex(pos);
|
|
19819
19943
|
return this.biomeAt ? this.biomeAt(posOrX.x, posOrX.y, posOrX.z) : this.biomes?.[Math.floor(pos.x / 4) + Math.floor(pos.z / 4) * 4 + Math.floor(pos.y / 4) * 16];
|
|
@@ -19891,17 +20015,17 @@ var Chunk = class _Chunk {
|
|
|
19891
20015
|
this.hiddenBlocks.add(index);
|
|
19892
20016
|
this.renderedBlocks.get(index)?.object?.setVisible(false);
|
|
19893
20017
|
}
|
|
19894
|
-
const positions = blocks.map(({ index }) => new
|
|
20018
|
+
const positions = blocks.map(({ index }) => new Vector321(...this.positionAt(index)));
|
|
19895
20019
|
if (this.onBlocksChanged) await this.onBlocksChanged(positions, deferCulling);
|
|
19896
20020
|
else await this.rebuildSectionMesh();
|
|
19897
20021
|
}
|
|
19898
20022
|
}
|
|
19899
20023
|
async setBlockAt(posOrX, yOrBlock, z, block) {
|
|
19900
20024
|
if (typeof posOrX == "number") {
|
|
19901
|
-
return this.setBlockAt(new
|
|
20025
|
+
return this.setBlockAt(new Vector321(posOrX, yOrBlock, z), block);
|
|
19902
20026
|
}
|
|
19903
20027
|
if (isTripleArray(posOrX)) {
|
|
19904
|
-
return this.setBlockAt(new
|
|
20028
|
+
return this.setBlockAt(new Vector321(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
|
|
19905
20029
|
}
|
|
19906
20030
|
const worldPos = posOrX;
|
|
19907
20031
|
const pos = this.worldPosToChunkPos(worldPos);
|
|
@@ -19974,7 +20098,7 @@ var Chunk = class _Chunk {
|
|
|
19974
20098
|
this.fluidCells[index] = 0;
|
|
19975
20099
|
this.renderedBlocks.delete(index);
|
|
19976
20100
|
this.objectBlocks.delete(index);
|
|
19977
|
-
const worldPos = new
|
|
20101
|
+
const worldPos = new Vector321(
|
|
19978
20102
|
this.x * 16 + index % 16,
|
|
19979
20103
|
this.y * 16 + Math.floor(index / 256),
|
|
19980
20104
|
this.z * 16 + Math.floor(index / 16) % 16
|
|
@@ -20112,7 +20236,7 @@ var Chunk = class _Chunk {
|
|
|
20112
20236
|
biome,
|
|
20113
20237
|
biomePosition: this.biomePosition(index, blockState3, this.data.stateAt(this.data.idAt(index)))
|
|
20114
20238
|
});
|
|
20115
|
-
object4.setPosition(new
|
|
20239
|
+
object4.setPosition(new Vector321(
|
|
20116
20240
|
this.x * 256 + index % 16 * 16,
|
|
20117
20241
|
this.y * 256 + Math.floor(index / 256) * 16,
|
|
20118
20242
|
this.z * 256 + Math.floor(index / 16) % 16 * 16
|
|
@@ -20419,7 +20543,7 @@ var Chunk = class _Chunk {
|
|
|
20419
20543
|
async clear(notify = true) {
|
|
20420
20544
|
if (this.blockChanges.size) await Promise.allSettled(this.blockChanges);
|
|
20421
20545
|
const positions = notify && this.onBlocksChanged ? [...this.renderedBlocks.keys()].map((index) => this.chunkPosToWorldPos(
|
|
20422
|
-
new
|
|
20546
|
+
new Vector321(index % 16, Math.floor(index / 256), Math.floor(index / 16) % 16)
|
|
20423
20547
|
)) : [];
|
|
20424
20548
|
for (const info of this.renderedBlocks.values()) info.object?.removeFromScene();
|
|
20425
20549
|
this.sectionBlocks.clear();
|
|
@@ -20455,14 +20579,14 @@ var Chunk = class _Chunk {
|
|
|
20455
20579
|
return pos.y * 16 * 16 + pos.z * 16 + pos.x;
|
|
20456
20580
|
}
|
|
20457
20581
|
worldPosToChunkPos(pos) {
|
|
20458
|
-
return new
|
|
20582
|
+
return new Vector321(
|
|
20459
20583
|
pos.x - this.x * 16,
|
|
20460
20584
|
pos.y - this.y * 16,
|
|
20461
20585
|
pos.z - this.z * 16
|
|
20462
20586
|
);
|
|
20463
20587
|
}
|
|
20464
20588
|
chunkPosToWorldPos(pos) {
|
|
20465
|
-
return new
|
|
20589
|
+
return new Vector321(
|
|
20466
20590
|
this.x * 16 + pos.x,
|
|
20467
20591
|
this.y * 16 + pos.y,
|
|
20468
20592
|
this.z * 16 + pos.z
|
|
@@ -20471,6 +20595,7 @@ var Chunk = class _Chunk {
|
|
|
20471
20595
|
};
|
|
20472
20596
|
|
|
20473
20597
|
// src/world/WorldStreamer.ts
|
|
20598
|
+
var MAX_PREFETCHED_COLUMNS = 4;
|
|
20474
20599
|
var WorldStreamer = class {
|
|
20475
20600
|
constructor(world, source, options = {}) {
|
|
20476
20601
|
this.world = world;
|
|
@@ -20480,6 +20605,7 @@ var WorldStreamer = class {
|
|
|
20480
20605
|
this.missing = /* @__PURE__ */ new Map();
|
|
20481
20606
|
this.failed = /* @__PURE__ */ new Map();
|
|
20482
20607
|
this.desired = [];
|
|
20608
|
+
this.reads = /* @__PURE__ */ new Map();
|
|
20483
20609
|
this.disposed = false;
|
|
20484
20610
|
this.loadRadius = options.loadRadius ?? 2;
|
|
20485
20611
|
this.unloadRadius = options.unloadRadius ?? Math.min(32, this.loadRadius + 1);
|
|
@@ -20524,6 +20650,12 @@ var WorldStreamer = class {
|
|
|
20524
20650
|
if (this.loading && !this.within(this.loading.position, this.loadRadius)) {
|
|
20525
20651
|
this.loading.controller.abort();
|
|
20526
20652
|
}
|
|
20653
|
+
for (const [key, read] of this.reads) {
|
|
20654
|
+
if (!this.within(read.position, this.loadRadius)) {
|
|
20655
|
+
read.controller.abort();
|
|
20656
|
+
if (read.ready) this.reads.delete(key);
|
|
20657
|
+
}
|
|
20658
|
+
}
|
|
20527
20659
|
}
|
|
20528
20660
|
return this.running ??= Promise.resolve().then(() => this.drain());
|
|
20529
20661
|
}
|
|
@@ -20544,8 +20676,11 @@ var WorldStreamer = class {
|
|
|
20544
20676
|
this.desired = [];
|
|
20545
20677
|
this.center = void 0;
|
|
20546
20678
|
this.loading?.controller.abort();
|
|
20679
|
+
for (const read of this.reads.values()) read.controller.abort();
|
|
20547
20680
|
return this.disposal ??= Promise.resolve().then(async () => {
|
|
20548
20681
|
await this.running?.catch(() => void 0);
|
|
20682
|
+
await Promise.all([...this.reads.values()].map((read) => read.promise));
|
|
20683
|
+
this.reads.clear();
|
|
20549
20684
|
try {
|
|
20550
20685
|
await this.unloadMany([...this.owned.values()]);
|
|
20551
20686
|
} finally {
|
|
@@ -20571,6 +20706,10 @@ var WorldStreamer = class {
|
|
|
20571
20706
|
await this.unloadMany(obsolete);
|
|
20572
20707
|
continue;
|
|
20573
20708
|
}
|
|
20709
|
+
if (this.world.placeChunks) {
|
|
20710
|
+
if (await this.placeReadyChunks()) continue;
|
|
20711
|
+
return;
|
|
20712
|
+
}
|
|
20574
20713
|
const position = this.desired.find((position2) => this.needsLoad(position2));
|
|
20575
20714
|
if (!position) return;
|
|
20576
20715
|
let chunk;
|
|
@@ -20593,26 +20732,105 @@ var WorldStreamer = class {
|
|
|
20593
20732
|
continue;
|
|
20594
20733
|
}
|
|
20595
20734
|
this.owned.set(this.key(position), position);
|
|
20735
|
+
await this.placeOwnedColumns([position], () => this.world.placeChunk(chunk));
|
|
20736
|
+
}
|
|
20737
|
+
} finally {
|
|
20738
|
+
for (const [key, read] of this.reads) {
|
|
20739
|
+
read.controller.abort();
|
|
20740
|
+
if (read.ready) this.reads.delete(key);
|
|
20741
|
+
}
|
|
20742
|
+
this.running = void 0;
|
|
20743
|
+
}
|
|
20744
|
+
}
|
|
20745
|
+
fillReadSlots() {
|
|
20746
|
+
while (!this.disposed && this.reads.size < MAX_PREFETCHED_COLUMNS) {
|
|
20747
|
+
const position = this.desired.find((position2) => this.needsLoad(position2) && !this.reads.has(this.key(position2)));
|
|
20748
|
+
if (!position) return;
|
|
20749
|
+
const key = this.key(position);
|
|
20750
|
+
const read = {
|
|
20751
|
+
position,
|
|
20752
|
+
controller: new AbortController(),
|
|
20753
|
+
promise: Promise.resolve(),
|
|
20754
|
+
ready: false,
|
|
20755
|
+
failed: false
|
|
20756
|
+
};
|
|
20757
|
+
this.reads.set(key, read);
|
|
20758
|
+
read.promise = (async () => {
|
|
20596
20759
|
try {
|
|
20597
|
-
await this.
|
|
20598
|
-
this.
|
|
20760
|
+
read.chunk = await this.source.getChunk(position.x, position.z, read.controller.signal);
|
|
20761
|
+
if (read.chunk) this.validateChunk(read.chunk, position);
|
|
20599
20762
|
} catch (error) {
|
|
20600
|
-
|
|
20601
|
-
|
|
20602
|
-
|
|
20603
|
-
|
|
20604
|
-
|
|
20605
|
-
|
|
20606
|
-
} catch {
|
|
20607
|
-
throw Object.assign(new Error(failure.message), { cause: cleanupError });
|
|
20608
|
-
}
|
|
20609
|
-
throw failure;
|
|
20763
|
+
read.failed = true;
|
|
20764
|
+
read.error = error;
|
|
20765
|
+
} finally {
|
|
20766
|
+
read.ready = true;
|
|
20767
|
+
if (read.controller.signal.aborted || this.disposed || !this.within(position, this.loadRadius)) {
|
|
20768
|
+
this.reads.delete(key);
|
|
20610
20769
|
}
|
|
20611
|
-
|
|
20770
|
+
}
|
|
20771
|
+
})();
|
|
20772
|
+
}
|
|
20773
|
+
}
|
|
20774
|
+
/** Places only the ready prefix, so later source reads cannot delay the nearest ready column. */
|
|
20775
|
+
async placeReadyChunks() {
|
|
20776
|
+
this.fillReadSlots();
|
|
20777
|
+
const position = this.desired.find((position2) => this.needsLoad(position2));
|
|
20778
|
+
if (!position) {
|
|
20779
|
+
if (!this.reads.size) return false;
|
|
20780
|
+
await Promise.all([...this.reads.values()].map((read) => read.promise));
|
|
20781
|
+
return true;
|
|
20782
|
+
}
|
|
20783
|
+
const head = this.reads.get(this.key(position));
|
|
20784
|
+
if (!head) {
|
|
20785
|
+
for (let index = this.desired.length - 1; index >= 0; index--) {
|
|
20786
|
+
const key = this.key(this.desired[index]);
|
|
20787
|
+
if (this.reads.get(key)?.ready) {
|
|
20788
|
+
this.reads.delete(key);
|
|
20789
|
+
return true;
|
|
20612
20790
|
}
|
|
20613
20791
|
}
|
|
20614
|
-
|
|
20615
|
-
|
|
20792
|
+
await Promise.race([...this.reads.values()].map((read) => read.promise));
|
|
20793
|
+
return true;
|
|
20794
|
+
}
|
|
20795
|
+
if (!head.ready) {
|
|
20796
|
+
await head.promise;
|
|
20797
|
+
return true;
|
|
20798
|
+
}
|
|
20799
|
+
const chunks = [], positions = [];
|
|
20800
|
+
for (const desired of this.desired) {
|
|
20801
|
+
if (!this.needsLoad(desired)) continue;
|
|
20802
|
+
const key = this.key(desired), read = this.reads.get(key);
|
|
20803
|
+
if (!read?.ready) break;
|
|
20804
|
+
this.reads.delete(key);
|
|
20805
|
+
if (read.failed) this.failed.set(key, { ...desired, error: read.error });
|
|
20806
|
+
else if (!read.chunk) this.missing.set(key, desired);
|
|
20807
|
+
else {
|
|
20808
|
+
chunks.push(read.chunk);
|
|
20809
|
+
positions.push(desired);
|
|
20810
|
+
}
|
|
20811
|
+
}
|
|
20812
|
+
if (!chunks.length) return true;
|
|
20813
|
+
for (const position2 of positions) this.owned.set(this.key(position2), position2);
|
|
20814
|
+
await this.placeOwnedColumns(positions, () => this.world.placeChunks(chunks));
|
|
20815
|
+
return true;
|
|
20816
|
+
}
|
|
20817
|
+
async placeOwnedColumns(positions, place) {
|
|
20818
|
+
try {
|
|
20819
|
+
await place();
|
|
20820
|
+
for (const position of positions) this.loaded.set(this.key(position), position);
|
|
20821
|
+
} catch (error) {
|
|
20822
|
+
try {
|
|
20823
|
+
await this.unloadMany(positions);
|
|
20824
|
+
} catch (cleanupError) {
|
|
20825
|
+
const failure = error instanceof Error ? error : new Error(String(error));
|
|
20826
|
+
try {
|
|
20827
|
+
Object.defineProperty(failure, "cause", { value: cleanupError, configurable: true });
|
|
20828
|
+
} catch {
|
|
20829
|
+
throw Object.assign(new Error(failure.message), { cause: cleanupError });
|
|
20830
|
+
}
|
|
20831
|
+
throw failure;
|
|
20832
|
+
}
|
|
20833
|
+
throw error;
|
|
20616
20834
|
}
|
|
20617
20835
|
}
|
|
20618
20836
|
needsLoad(position) {
|