minerender 2.1.0-alpha.142 → 2.1.0-alpha.144
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/README.md +10 -1
- package/dist/browser/esm/index.browser.d.mts +21 -4
- package/dist/browser/esm/util/util.mjs +0 -2
- package/dist/browser/esm/util/util.mjs.map +1 -1
- package/dist/browser/esm/world/Chunk.mjs +62 -33
- package/dist/browser/esm/world/Chunk.mjs.map +1 -1
- package/dist/browser/esm/world/MineRenderWorld.mjs +98 -13
- package/dist/browser/esm/world/MineRenderWorld.mjs.map +1 -1
- package/dist/browser/esm/world/SectionModels.mjs +21 -15
- package/dist/browser/esm/world/SectionModels.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 +21 -4
- package/dist/browser/index.js +289 -78
- package/dist/browser/index.js.map +1 -1
- package/dist/bundle.js +289 -78
- package/dist/bundle.js.map +1 -1
- package/dist/node/index.d.mts +21 -4
- package/dist/node/index.d.ts +21 -4
- package/dist/node/index.js +289 -78
- package/dist/node/index.js.map +1 -1
- package/dist/node/index.mjs +289 -78
- 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,6 +17922,7 @@ var AnvilWorldSource = class {
|
|
|
17924
17922
|
};
|
|
17925
17923
|
|
|
17926
17924
|
// src/world/Chunk.ts
|
|
17925
|
+
init_BlockObject();
|
|
17927
17926
|
import { Color as Color13, Vector3 as Vector321 } from "three";
|
|
17928
17927
|
init_BlockStates();
|
|
17929
17928
|
init_AssetKey();
|
|
@@ -17934,6 +17933,7 @@ init_util();
|
|
|
17934
17933
|
import { Vector3 as Vector319 } from "three";
|
|
17935
17934
|
init_AssetKey();
|
|
17936
17935
|
init_BlockStates();
|
|
17936
|
+
init_BlockEntities();
|
|
17937
17937
|
init_CubeFace();
|
|
17938
17938
|
|
|
17939
17939
|
// src/world/SectionModels.ts
|
|
@@ -18031,6 +18031,10 @@ var SectionModels = class {
|
|
|
18031
18031
|
biomeTint: source ? [...templates.values()].some((template) => template.biomeTint?.some((value) => value === 1)) || void 0 : void 0
|
|
18032
18032
|
};
|
|
18033
18033
|
}
|
|
18034
|
+
/** Starts loading the state's models and atlases so a later `prepareState` with the same properties finds them loaded. */
|
|
18035
|
+
async preload(blockState3, properties = {}) {
|
|
18036
|
+
await this.state(blockState3, properties);
|
|
18037
|
+
}
|
|
18034
18038
|
async state(blockState3, properties) {
|
|
18035
18039
|
let states = this.states.get(blockState3);
|
|
18036
18040
|
if (!states) this.states.set(blockState3, states = /* @__PURE__ */ new Map());
|
|
@@ -18053,22 +18057,24 @@ var SectionModels = class {
|
|
|
18053
18057
|
async prepare(blockState3, properties) {
|
|
18054
18058
|
const state = { ...await BlockStateResolver.defaults(blockState3), ...properties };
|
|
18055
18059
|
const groups = BlockStateResolver.matching(blockState3, state);
|
|
18056
|
-
if (!groups.length) return void 0;
|
|
18060
|
+
if (!groups.length || groups.some((group) => Array.isArray(group) && !group.length)) return void 0;
|
|
18061
|
+
const variants = groups.flatMap((group) => Array.isArray(group) ? group : [group]);
|
|
18062
|
+
if (variants.some((variant) => !variant.model)) return void 0;
|
|
18063
|
+
const settled = await Promise.allSettled(variants.map(async (variant) => {
|
|
18064
|
+
const modelKey = AssetKey.parse("models", variant.model);
|
|
18065
|
+
modelKey.root = blockState3.key?.root;
|
|
18066
|
+
const model = await Models.getMerged(modelKey);
|
|
18067
|
+
if (!model?.elements?.length) return void 0;
|
|
18068
|
+
const atlas = await UVMapper.getAtlas(model);
|
|
18069
|
+
const rotation = BlockStateResolver.rotation(variant);
|
|
18070
|
+
if (!atlas || atlas.image.width > this.maxAtlasSize || atlas.image.height > this.maxAtlasSize) return void 0;
|
|
18071
|
+
return { model, atlas, rotation };
|
|
18072
|
+
}));
|
|
18057
18073
|
const prepared = /* @__PURE__ */ new Map();
|
|
18058
|
-
for (const
|
|
18059
|
-
|
|
18060
|
-
if (!
|
|
18061
|
-
|
|
18062
|
-
if (!variant.model) return void 0;
|
|
18063
|
-
const modelKey = AssetKey.parse("models", variant.model);
|
|
18064
|
-
modelKey.root = blockState3.key?.root;
|
|
18065
|
-
const model = await Models.getMerged(modelKey);
|
|
18066
|
-
if (!model?.elements?.length) return void 0;
|
|
18067
|
-
const atlas = await UVMapper.getAtlas(model);
|
|
18068
|
-
const rotation = BlockStateResolver.rotation(variant);
|
|
18069
|
-
if (!atlas || atlas.image.width > this.maxAtlasSize || atlas.image.height > this.maxAtlasSize) return void 0;
|
|
18070
|
-
prepared.set(variant, { model, atlas, rotation });
|
|
18071
|
-
}
|
|
18074
|
+
for (const [i, result] of settled.entries()) {
|
|
18075
|
+
if (result.status === "rejected") throw result.reason;
|
|
18076
|
+
if (!result.value) return void 0;
|
|
18077
|
+
prepared.set(variants[i], result.value);
|
|
18072
18078
|
}
|
|
18073
18079
|
return { state, groups, choices: prepared };
|
|
18074
18080
|
}
|
|
@@ -18528,6 +18534,7 @@ function skipsBlockFace(data, rule, block, neighbor, face) {
|
|
|
18528
18534
|
// src/world/MineRenderWorld.ts
|
|
18529
18535
|
init_log();
|
|
18530
18536
|
var log18 = logger("MineRenderWorld");
|
|
18537
|
+
var YIELD_INTERVAL_MS = 8;
|
|
18531
18538
|
var MineRenderWorld = class {
|
|
18532
18539
|
constructor(scene, options = {}) {
|
|
18533
18540
|
this._chunks = /* @__PURE__ */ new Map();
|
|
@@ -18641,7 +18648,7 @@ var MineRenderWorld = class {
|
|
|
18641
18648
|
failures.set(chunk, error);
|
|
18642
18649
|
if (this._chunks.get(`${chunk.x}_${chunk.y}_${chunk.z}`) === chunk) this.pendingBiomes.set(chunk, void 0);
|
|
18643
18650
|
}
|
|
18644
|
-
if (performance.now() - sliceStart >
|
|
18651
|
+
if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
|
|
18645
18652
|
await yieldToEventLoop();
|
|
18646
18653
|
sliceStart = performance.now();
|
|
18647
18654
|
}
|
|
@@ -18742,15 +18749,18 @@ var MineRenderWorld = class {
|
|
|
18742
18749
|
const key = AssetKey.parse("blockstates", block.type);
|
|
18743
18750
|
keys.set(key.serialize(), key);
|
|
18744
18751
|
}
|
|
18745
|
-
await BlockStates.getAll(keys.values());
|
|
18752
|
+
const loaded = await BlockStates.getAll(keys.values());
|
|
18753
|
+
const states = new Map([...keys.keys()].map((key, i) => [key, loaded[i]]));
|
|
18746
18754
|
if (!useBatches) {
|
|
18747
18755
|
for (const block of multiblock.blocks) await place(block);
|
|
18748
18756
|
return;
|
|
18749
18757
|
}
|
|
18750
18758
|
if (!executor) {
|
|
18751
18759
|
const groups2 = /* @__PURE__ */ new Map();
|
|
18760
|
+
const distinct = /* @__PURE__ */ new Map();
|
|
18752
18761
|
const position = new Vector319();
|
|
18753
18762
|
for (const block of multiblock.blocks) {
|
|
18763
|
+
if (this.sectionModels && !Chunk.isAir(block)) distinct.set(ChunkData.paletteKey(block), block);
|
|
18754
18764
|
const [x, y, z] = block.position;
|
|
18755
18765
|
this.validatePosBounds(position.set(x, y, z));
|
|
18756
18766
|
const cx = Math.floor(x / 16), cy = Math.floor(y / 16), cz = Math.floor(z / 16);
|
|
@@ -18761,6 +18771,16 @@ var MineRenderWorld = class {
|
|
|
18761
18771
|
if (!group) groups2.set(chunk, group = []);
|
|
18762
18772
|
group.push({ index: (y - cy * 16) * 256 + (z - cz * 16) * 16 + x - cx * 16, block });
|
|
18763
18773
|
}
|
|
18774
|
+
if (this.sectionModels) {
|
|
18775
|
+
for (const block of distinct.values()) {
|
|
18776
|
+
const state = states.get(AssetKey.parse("blockstates", block.type).serialize());
|
|
18777
|
+
if (!state) continue;
|
|
18778
|
+
void (async () => {
|
|
18779
|
+
if (state.key && BlockEntities.entry(await BlockEntities.getIndex(state.key.root), state.key.toNamespacedString())) return;
|
|
18780
|
+
await this.sectionModels.preload(state, block.properties);
|
|
18781
|
+
})().catch(() => void 0);
|
|
18782
|
+
}
|
|
18783
|
+
}
|
|
18764
18784
|
await this.placeChunkGroups(groups2, changes);
|
|
18765
18785
|
return;
|
|
18766
18786
|
}
|
|
@@ -18796,7 +18816,7 @@ var MineRenderWorld = class {
|
|
|
18796
18816
|
));
|
|
18797
18817
|
for (const pos of positions) changes.set(`${pos.x},${pos.y},${pos.z}`, pos);
|
|
18798
18818
|
}
|
|
18799
|
-
if (performance.now() - sliceStart >
|
|
18819
|
+
if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
|
|
18800
18820
|
await yieldToEventLoop();
|
|
18801
18821
|
sliceStart = performance.now();
|
|
18802
18822
|
}
|
|
@@ -18804,6 +18824,62 @@ var MineRenderWorld = class {
|
|
|
18804
18824
|
}
|
|
18805
18825
|
/** Replaces a chunk column's blocks, biomes, and saved light and, with `renderEntities`, its supported saved mobs. */
|
|
18806
18826
|
async placeChunk(chunk, executor) {
|
|
18827
|
+
return this.placeColumn(chunk, executor);
|
|
18828
|
+
}
|
|
18829
|
+
/**
|
|
18830
|
+
* Replaces parsed columns in input order, then refreshes neighboring geometry once.
|
|
18831
|
+
* Duplicate coordinates retain their order. A failed column stops placement; queued refreshes finish before rejection.
|
|
18832
|
+
*/
|
|
18833
|
+
async placeChunks(chunks) {
|
|
18834
|
+
const columns = [];
|
|
18835
|
+
for (const chunk of chunks) {
|
|
18836
|
+
const { x, z } = chunk ?? {};
|
|
18837
|
+
if (![x, z].every(Number.isSafeInteger)) {
|
|
18838
|
+
throw new RangeError("Chunk column coordinates must be safe integers");
|
|
18839
|
+
}
|
|
18840
|
+
const sections = Array.from(chunk.sections, (section) => {
|
|
18841
|
+
const { y } = section ?? {};
|
|
18842
|
+
if (!Number.isSafeInteger(y)) throw new RangeError("Chunk section coordinates must be safe integers");
|
|
18843
|
+
return { ...section, y };
|
|
18844
|
+
});
|
|
18845
|
+
columns.push({ ...chunk, x, z, sections });
|
|
18846
|
+
}
|
|
18847
|
+
if (!columns.length) return;
|
|
18848
|
+
await this.culling;
|
|
18849
|
+
let failed = false, failure, sliceStart = performance.now();
|
|
18850
|
+
try {
|
|
18851
|
+
for (const column of columns) {
|
|
18852
|
+
await this.placeColumn(column, void 0, true);
|
|
18853
|
+
if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
|
|
18854
|
+
await yieldToEventLoop();
|
|
18855
|
+
sliceStart = performance.now();
|
|
18856
|
+
}
|
|
18857
|
+
}
|
|
18858
|
+
} catch (error) {
|
|
18859
|
+
failed = true;
|
|
18860
|
+
failure = error;
|
|
18861
|
+
} finally {
|
|
18862
|
+
try {
|
|
18863
|
+
const failures = await this.refreshBiomes(true);
|
|
18864
|
+
if (failures.size) log18.warn("Failed to refresh biome colors", failures.values().next().value);
|
|
18865
|
+
} catch (error) {
|
|
18866
|
+
if (!failed) {
|
|
18867
|
+
failed = true;
|
|
18868
|
+
failure = error;
|
|
18869
|
+
}
|
|
18870
|
+
}
|
|
18871
|
+
try {
|
|
18872
|
+
await this.updateCulling([]);
|
|
18873
|
+
} catch (error) {
|
|
18874
|
+
if (!failed) {
|
|
18875
|
+
failed = true;
|
|
18876
|
+
failure = error;
|
|
18877
|
+
}
|
|
18878
|
+
}
|
|
18879
|
+
}
|
|
18880
|
+
if (failed) throw failure;
|
|
18881
|
+
}
|
|
18882
|
+
async placeColumn(chunk, executor, deferCulling = false) {
|
|
18807
18883
|
if (![chunk.x, chunk.z].every(Number.isSafeInteger)) {
|
|
18808
18884
|
throw new RangeError("Chunk column coordinates must be safe integers");
|
|
18809
18885
|
}
|
|
@@ -18865,7 +18941,7 @@ var MineRenderWorld = class {
|
|
|
18865
18941
|
} finally {
|
|
18866
18942
|
this.markSectionChanged(target);
|
|
18867
18943
|
}
|
|
18868
|
-
if (performance.now() - sliceStart >
|
|
18944
|
+
if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
|
|
18869
18945
|
await yieldToEventLoop();
|
|
18870
18946
|
sliceStart = performance.now();
|
|
18871
18947
|
}
|
|
@@ -18888,9 +18964,12 @@ var MineRenderWorld = class {
|
|
|
18888
18964
|
await placeEntities?.();
|
|
18889
18965
|
} finally {
|
|
18890
18966
|
this.markBiomesChanged(chunk.x, chunk.z, void 0, false);
|
|
18891
|
-
|
|
18892
|
-
|
|
18893
|
-
|
|
18967
|
+
if (deferCulling) await this.updateCulling([...changes.values()], true);
|
|
18968
|
+
else {
|
|
18969
|
+
const failures = await this.refreshBiomes();
|
|
18970
|
+
if (failures.size) log18.warn("Failed to refresh biome colors", failures.values().next().value);
|
|
18971
|
+
await this.updateCulling([...changes.values()]);
|
|
18972
|
+
}
|
|
18894
18973
|
}
|
|
18895
18974
|
}
|
|
18896
18975
|
/**
|
|
@@ -18920,7 +18999,7 @@ var MineRenderWorld = class {
|
|
|
18920
18999
|
for (const { x, z } of columns.values()) {
|
|
18921
19000
|
this.entities?.clearColumn(x, z);
|
|
18922
19001
|
await this.clearChunkColumn(x, z);
|
|
18923
|
-
if (performance.now() - sliceStart >
|
|
19002
|
+
if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
|
|
18924
19003
|
await yieldToEventLoop();
|
|
18925
19004
|
sliceStart = performance.now();
|
|
18926
19005
|
}
|
|
@@ -19059,15 +19138,25 @@ var MineRenderWorld = class {
|
|
|
19059
19138
|
for (const [index, mask] of masks) await chunk.setCullMaskIndex(index, mask);
|
|
19060
19139
|
await chunk.refreshLight(indices === "all" ? void 0 : indices);
|
|
19061
19140
|
chunk.refreshAmbientOcclusion(this._ambientOcclusion);
|
|
19062
|
-
if (
|
|
19141
|
+
if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
|
|
19063
19142
|
await yieldToEventLoop();
|
|
19064
19143
|
sliceStart = performance.now();
|
|
19065
19144
|
}
|
|
19066
19145
|
}
|
|
19067
|
-
const
|
|
19068
|
-
|
|
19069
|
-
|
|
19070
|
-
|
|
19146
|
+
const pending = [];
|
|
19147
|
+
for (const chunk of [...batch.keys()]) {
|
|
19148
|
+
const build = (async () => {
|
|
19149
|
+
await chunk.rebuildSectionMesh();
|
|
19150
|
+
batch.delete(chunk);
|
|
19151
|
+
})();
|
|
19152
|
+
void build.catch(() => void 0);
|
|
19153
|
+
pending.push(build);
|
|
19154
|
+
if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
|
|
19155
|
+
await yieldToEventLoop();
|
|
19156
|
+
sliceStart = performance.now();
|
|
19157
|
+
}
|
|
19158
|
+
}
|
|
19159
|
+
const builds = await Promise.allSettled(pending);
|
|
19071
19160
|
const failure = builds.find((result) => result.status === "rejected");
|
|
19072
19161
|
if (failure?.status === "rejected") throw failure.reason;
|
|
19073
19162
|
} catch (error) {
|
|
@@ -20019,8 +20108,10 @@ var Chunk = class _Chunk {
|
|
|
20019
20108
|
if (rejected?.status === "rejected") throw rejected.reason;
|
|
20020
20109
|
}
|
|
20021
20110
|
const positions = [];
|
|
20022
|
-
|
|
20023
|
-
|
|
20111
|
+
const jobs = /* @__PURE__ */ new Map();
|
|
20112
|
+
let failureOrder = Infinity, failure;
|
|
20113
|
+
for (const [order, { index, key, block }] of placements.entries()) {
|
|
20114
|
+
jobs.delete(index);
|
|
20024
20115
|
this.hiddenBlocks.delete(index);
|
|
20025
20116
|
this.data.assign(index, this.data.intern(block), block?.nbt);
|
|
20026
20117
|
const readBlock = this.data.snapshot(index);
|
|
@@ -20036,7 +20127,6 @@ var Chunk = class _Chunk {
|
|
|
20036
20127
|
this.z * 16 + Math.floor(index / 16) % 16
|
|
20037
20128
|
);
|
|
20038
20129
|
positions.push(worldPos);
|
|
20039
|
-
let object4;
|
|
20040
20130
|
try {
|
|
20041
20131
|
if (!readBlock) continue;
|
|
20042
20132
|
const resolved = resolutions.get(key);
|
|
@@ -20058,7 +20148,7 @@ var Chunk = class _Chunk {
|
|
|
20058
20148
|
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0, biomeTint: context?.tint })));
|
|
20059
20149
|
this.meshDirty = true;
|
|
20060
20150
|
} else {
|
|
20061
|
-
|
|
20151
|
+
jobs.set(index, { order, readBlock, blockState: blockState3, position: MineRenderWorld.worldToScenePosition(worldPos), options: {
|
|
20062
20152
|
mergeMeshes: true,
|
|
20063
20153
|
instanceMeshes: true,
|
|
20064
20154
|
maxInstanceCount: 2e3,
|
|
@@ -20066,13 +20156,12 @@ var Chunk = class _Chunk {
|
|
|
20066
20156
|
variantPosition,
|
|
20067
20157
|
biome: context?.biome,
|
|
20068
20158
|
biomePosition: this.biomePosition(index, blockState3, stored)
|
|
20069
|
-
});
|
|
20070
|
-
|
|
20159
|
+
} });
|
|
20160
|
+
continue;
|
|
20071
20161
|
}
|
|
20072
20162
|
this.renderedBlocks.set(index, { get block() {
|
|
20073
20163
|
return readBlock();
|
|
20074
|
-
}, object:
|
|
20075
|
-
if (object4) this.objectBlocks.add(index);
|
|
20164
|
+
}, object: void 0 });
|
|
20076
20165
|
} catch (error) {
|
|
20077
20166
|
if (preserveDataOnFailure && readBlock) {
|
|
20078
20167
|
this.renderedBlocks.set(index, { get block() {
|
|
@@ -20080,12 +20169,35 @@ var Chunk = class _Chunk {
|
|
|
20080
20169
|
}, object: void 0 });
|
|
20081
20170
|
} else this.data.assign(index, 0);
|
|
20082
20171
|
this.fluidCells[index] = 0;
|
|
20083
|
-
|
|
20084
|
-
|
|
20085
|
-
|
|
20172
|
+
if (order < failureOrder) {
|
|
20173
|
+
failureOrder = order;
|
|
20174
|
+
failure = error;
|
|
20175
|
+
}
|
|
20086
20176
|
}
|
|
20087
20177
|
}
|
|
20088
|
-
|
|
20178
|
+
const pending = [...jobs];
|
|
20179
|
+
const objects = await Promise.allSettled(pending.map(([, job]) => this.createBlockObject(job.blockState, job.position, job.options)));
|
|
20180
|
+
for (const [i, [index, { order, readBlock }]] of pending.entries()) {
|
|
20181
|
+
const result = objects[i];
|
|
20182
|
+
if (result.status === "fulfilled") {
|
|
20183
|
+
this.renderedBlocks.set(index, { get block() {
|
|
20184
|
+
return readBlock();
|
|
20185
|
+
}, object: result.value });
|
|
20186
|
+
this.objectBlocks.add(index);
|
|
20187
|
+
} else {
|
|
20188
|
+
if (preserveDataOnFailure) {
|
|
20189
|
+
this.renderedBlocks.set(index, { get block() {
|
|
20190
|
+
return readBlock();
|
|
20191
|
+
}, object: void 0 });
|
|
20192
|
+
} else this.data.assign(index, 0);
|
|
20193
|
+
this.fluidCells[index] = 0;
|
|
20194
|
+
if (order < failureOrder) {
|
|
20195
|
+
failureOrder = order;
|
|
20196
|
+
failure = result.reason;
|
|
20197
|
+
}
|
|
20198
|
+
}
|
|
20199
|
+
}
|
|
20200
|
+
if (failureOrder < Infinity) throw failure;
|
|
20089
20201
|
return positions;
|
|
20090
20202
|
}
|
|
20091
20203
|
/** Replaces every cell from parsed section data. The caller refreshes neighbor culling. */
|
|
@@ -20156,37 +20268,42 @@ var Chunk = class _Chunk {
|
|
|
20156
20268
|
failed = true;
|
|
20157
20269
|
}
|
|
20158
20270
|
}
|
|
20159
|
-
|
|
20160
|
-
|
|
20161
|
-
|
|
20162
|
-
|
|
20163
|
-
|
|
20164
|
-
|
|
20165
|
-
|
|
20166
|
-
|
|
20167
|
-
|
|
20168
|
-
|
|
20169
|
-
|
|
20170
|
-
|
|
20171
|
-
|
|
20172
|
-
|
|
20173
|
-
|
|
20174
|
-
|
|
20175
|
-
)
|
|
20176
|
-
this.renderedBlocks.get(index).object = object4;
|
|
20271
|
+
const objects = await Promise.allSettled(perBlocks.map(({ index, blockState: blockState3, biome }) => this.createBlockObject(blockState3, new Vector321(
|
|
20272
|
+
this.x * 256 + index % 16 * 16,
|
|
20273
|
+
this.y * 256 + Math.floor(index / 256) * 16,
|
|
20274
|
+
this.z * 256 + Math.floor(index / 16) % 16 * 16
|
|
20275
|
+
), {
|
|
20276
|
+
mergeMeshes: true,
|
|
20277
|
+
instanceMeshes: true,
|
|
20278
|
+
maxInstanceCount: 2e3,
|
|
20279
|
+
initialState: this.data.stateAt(this.data.idAt(index)).properties,
|
|
20280
|
+
variantPosition: this.positionAt(index),
|
|
20281
|
+
biome,
|
|
20282
|
+
biomePosition: this.biomePosition(index, blockState3, this.data.stateAt(this.data.idAt(index)))
|
|
20283
|
+
})));
|
|
20284
|
+
for (const [i, { index }] of perBlocks.entries()) {
|
|
20285
|
+
const result = objects[i];
|
|
20286
|
+
if (result.status === "fulfilled") {
|
|
20287
|
+
this.renderedBlocks.get(index).object = result.value;
|
|
20177
20288
|
this.objectBlocks.add(index);
|
|
20178
|
-
}
|
|
20289
|
+
} else {
|
|
20179
20290
|
this.data.assign(index, 0);
|
|
20180
20291
|
this.fluidCells[index] = 0;
|
|
20181
20292
|
this.renderedBlocks.delete(index);
|
|
20182
20293
|
this.objectBlocks.delete(index);
|
|
20183
|
-
|
|
20184
|
-
if (!failed) failure = error;
|
|
20294
|
+
if (!failed) failure = result.reason;
|
|
20185
20295
|
failed = true;
|
|
20186
20296
|
}
|
|
20187
20297
|
}
|
|
20188
20298
|
if (failed) throw failure;
|
|
20189
20299
|
}
|
|
20300
|
+
createBlockObject(blockState3, position, options) {
|
|
20301
|
+
return this.scene.addSceneObject(blockState3, () => {
|
|
20302
|
+
const object4 = new BlockObject(blockState3, options);
|
|
20303
|
+
object4.position.copy(position);
|
|
20304
|
+
return object4;
|
|
20305
|
+
}, options);
|
|
20306
|
+
}
|
|
20190
20307
|
positionAt(index) {
|
|
20191
20308
|
return [this.x * 16 + index % 16, this.y * 16 + Math.floor(index / 256), this.z * 16 + Math.floor(index / 16) % 16];
|
|
20192
20309
|
}
|
|
@@ -20527,6 +20644,7 @@ var Chunk = class _Chunk {
|
|
|
20527
20644
|
};
|
|
20528
20645
|
|
|
20529
20646
|
// src/world/WorldStreamer.ts
|
|
20647
|
+
var MAX_PREFETCHED_COLUMNS = 4;
|
|
20530
20648
|
var WorldStreamer = class {
|
|
20531
20649
|
constructor(world, source, options = {}) {
|
|
20532
20650
|
this.world = world;
|
|
@@ -20536,6 +20654,7 @@ var WorldStreamer = class {
|
|
|
20536
20654
|
this.missing = /* @__PURE__ */ new Map();
|
|
20537
20655
|
this.failed = /* @__PURE__ */ new Map();
|
|
20538
20656
|
this.desired = [];
|
|
20657
|
+
this.reads = /* @__PURE__ */ new Map();
|
|
20539
20658
|
this.disposed = false;
|
|
20540
20659
|
this.loadRadius = options.loadRadius ?? 2;
|
|
20541
20660
|
this.unloadRadius = options.unloadRadius ?? Math.min(32, this.loadRadius + 1);
|
|
@@ -20580,6 +20699,12 @@ var WorldStreamer = class {
|
|
|
20580
20699
|
if (this.loading && !this.within(this.loading.position, this.loadRadius)) {
|
|
20581
20700
|
this.loading.controller.abort();
|
|
20582
20701
|
}
|
|
20702
|
+
for (const [key, read] of this.reads) {
|
|
20703
|
+
if (!this.within(read.position, this.loadRadius)) {
|
|
20704
|
+
read.controller.abort();
|
|
20705
|
+
if (read.ready) this.reads.delete(key);
|
|
20706
|
+
}
|
|
20707
|
+
}
|
|
20583
20708
|
}
|
|
20584
20709
|
return this.running ??= Promise.resolve().then(() => this.drain());
|
|
20585
20710
|
}
|
|
@@ -20600,8 +20725,11 @@ var WorldStreamer = class {
|
|
|
20600
20725
|
this.desired = [];
|
|
20601
20726
|
this.center = void 0;
|
|
20602
20727
|
this.loading?.controller.abort();
|
|
20728
|
+
for (const read of this.reads.values()) read.controller.abort();
|
|
20603
20729
|
return this.disposal ??= Promise.resolve().then(async () => {
|
|
20604
20730
|
await this.running?.catch(() => void 0);
|
|
20731
|
+
await Promise.all([...this.reads.values()].map((read) => read.promise));
|
|
20732
|
+
this.reads.clear();
|
|
20605
20733
|
try {
|
|
20606
20734
|
await this.unloadMany([...this.owned.values()]);
|
|
20607
20735
|
} finally {
|
|
@@ -20627,6 +20755,10 @@ var WorldStreamer = class {
|
|
|
20627
20755
|
await this.unloadMany(obsolete);
|
|
20628
20756
|
continue;
|
|
20629
20757
|
}
|
|
20758
|
+
if (this.world.placeChunks) {
|
|
20759
|
+
if (await this.placeReadyChunks()) continue;
|
|
20760
|
+
return;
|
|
20761
|
+
}
|
|
20630
20762
|
const position = this.desired.find((position2) => this.needsLoad(position2));
|
|
20631
20763
|
if (!position) return;
|
|
20632
20764
|
let chunk;
|
|
@@ -20649,26 +20781,105 @@ var WorldStreamer = class {
|
|
|
20649
20781
|
continue;
|
|
20650
20782
|
}
|
|
20651
20783
|
this.owned.set(this.key(position), position);
|
|
20784
|
+
await this.placeOwnedColumns([position], () => this.world.placeChunk(chunk));
|
|
20785
|
+
}
|
|
20786
|
+
} finally {
|
|
20787
|
+
for (const [key, read] of this.reads) {
|
|
20788
|
+
read.controller.abort();
|
|
20789
|
+
if (read.ready) this.reads.delete(key);
|
|
20790
|
+
}
|
|
20791
|
+
this.running = void 0;
|
|
20792
|
+
}
|
|
20793
|
+
}
|
|
20794
|
+
fillReadSlots() {
|
|
20795
|
+
while (!this.disposed && this.reads.size < MAX_PREFETCHED_COLUMNS) {
|
|
20796
|
+
const position = this.desired.find((position2) => this.needsLoad(position2) && !this.reads.has(this.key(position2)));
|
|
20797
|
+
if (!position) return;
|
|
20798
|
+
const key = this.key(position);
|
|
20799
|
+
const read = {
|
|
20800
|
+
position,
|
|
20801
|
+
controller: new AbortController(),
|
|
20802
|
+
promise: Promise.resolve(),
|
|
20803
|
+
ready: false,
|
|
20804
|
+
failed: false
|
|
20805
|
+
};
|
|
20806
|
+
this.reads.set(key, read);
|
|
20807
|
+
read.promise = (async () => {
|
|
20652
20808
|
try {
|
|
20653
|
-
await this.
|
|
20654
|
-
this.
|
|
20809
|
+
read.chunk = await this.source.getChunk(position.x, position.z, read.controller.signal);
|
|
20810
|
+
if (read.chunk) this.validateChunk(read.chunk, position);
|
|
20655
20811
|
} catch (error) {
|
|
20656
|
-
|
|
20657
|
-
|
|
20658
|
-
|
|
20659
|
-
|
|
20660
|
-
|
|
20661
|
-
|
|
20662
|
-
} catch {
|
|
20663
|
-
throw Object.assign(new Error(failure.message), { cause: cleanupError });
|
|
20664
|
-
}
|
|
20665
|
-
throw failure;
|
|
20812
|
+
read.failed = true;
|
|
20813
|
+
read.error = error;
|
|
20814
|
+
} finally {
|
|
20815
|
+
read.ready = true;
|
|
20816
|
+
if (read.controller.signal.aborted || this.disposed || !this.within(position, this.loadRadius)) {
|
|
20817
|
+
this.reads.delete(key);
|
|
20666
20818
|
}
|
|
20667
|
-
|
|
20819
|
+
}
|
|
20820
|
+
})();
|
|
20821
|
+
}
|
|
20822
|
+
}
|
|
20823
|
+
/** Places only the ready prefix, so later source reads cannot delay the nearest ready column. */
|
|
20824
|
+
async placeReadyChunks() {
|
|
20825
|
+
this.fillReadSlots();
|
|
20826
|
+
const position = this.desired.find((position2) => this.needsLoad(position2));
|
|
20827
|
+
if (!position) {
|
|
20828
|
+
if (!this.reads.size) return false;
|
|
20829
|
+
await Promise.all([...this.reads.values()].map((read) => read.promise));
|
|
20830
|
+
return true;
|
|
20831
|
+
}
|
|
20832
|
+
const head = this.reads.get(this.key(position));
|
|
20833
|
+
if (!head) {
|
|
20834
|
+
for (let index = this.desired.length - 1; index >= 0; index--) {
|
|
20835
|
+
const key = this.key(this.desired[index]);
|
|
20836
|
+
if (this.reads.get(key)?.ready) {
|
|
20837
|
+
this.reads.delete(key);
|
|
20838
|
+
return true;
|
|
20668
20839
|
}
|
|
20669
20840
|
}
|
|
20670
|
-
|
|
20671
|
-
|
|
20841
|
+
await Promise.race([...this.reads.values()].map((read) => read.promise));
|
|
20842
|
+
return true;
|
|
20843
|
+
}
|
|
20844
|
+
if (!head.ready) {
|
|
20845
|
+
await head.promise;
|
|
20846
|
+
return true;
|
|
20847
|
+
}
|
|
20848
|
+
const chunks = [], positions = [];
|
|
20849
|
+
for (const desired of this.desired) {
|
|
20850
|
+
if (!this.needsLoad(desired)) continue;
|
|
20851
|
+
const key = this.key(desired), read = this.reads.get(key);
|
|
20852
|
+
if (!read?.ready) break;
|
|
20853
|
+
this.reads.delete(key);
|
|
20854
|
+
if (read.failed) this.failed.set(key, { ...desired, error: read.error });
|
|
20855
|
+
else if (!read.chunk) this.missing.set(key, desired);
|
|
20856
|
+
else {
|
|
20857
|
+
chunks.push(read.chunk);
|
|
20858
|
+
positions.push(desired);
|
|
20859
|
+
}
|
|
20860
|
+
}
|
|
20861
|
+
if (!chunks.length) return true;
|
|
20862
|
+
for (const position2 of positions) this.owned.set(this.key(position2), position2);
|
|
20863
|
+
await this.placeOwnedColumns(positions, () => this.world.placeChunks(chunks));
|
|
20864
|
+
return true;
|
|
20865
|
+
}
|
|
20866
|
+
async placeOwnedColumns(positions, place) {
|
|
20867
|
+
try {
|
|
20868
|
+
await place();
|
|
20869
|
+
for (const position of positions) this.loaded.set(this.key(position), position);
|
|
20870
|
+
} catch (error) {
|
|
20871
|
+
try {
|
|
20872
|
+
await this.unloadMany(positions);
|
|
20873
|
+
} catch (cleanupError) {
|
|
20874
|
+
const failure = error instanceof Error ? error : new Error(String(error));
|
|
20875
|
+
try {
|
|
20876
|
+
Object.defineProperty(failure, "cause", { value: cleanupError, configurable: true });
|
|
20877
|
+
} catch {
|
|
20878
|
+
throw Object.assign(new Error(failure.message), { cause: cleanupError });
|
|
20879
|
+
}
|
|
20880
|
+
throw failure;
|
|
20881
|
+
}
|
|
20882
|
+
throw error;
|
|
20672
20883
|
}
|
|
20673
20884
|
}
|
|
20674
20885
|
needsLoad(position) {
|