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.js
CHANGED
|
@@ -184,8 +184,6 @@ async function sleep(timeout) {
|
|
|
184
184
|
});
|
|
185
185
|
}
|
|
186
186
|
function yieldToEventLoop() {
|
|
187
|
-
const scheduler = globalThis.scheduler;
|
|
188
|
-
if (scheduler?.yield) return scheduler.yield();
|
|
189
187
|
return new Promise((resolve) => {
|
|
190
188
|
if (typeof MessageChannel !== "undefined") {
|
|
191
189
|
const channel = new MessageChannel();
|
|
@@ -18092,6 +18090,7 @@ var AnvilWorldSource = class {
|
|
|
18092
18090
|
};
|
|
18093
18091
|
|
|
18094
18092
|
// src/world/Chunk.ts
|
|
18093
|
+
init_BlockObject();
|
|
18095
18094
|
var import_three47 = require("three");
|
|
18096
18095
|
init_BlockStates();
|
|
18097
18096
|
init_AssetKey();
|
|
@@ -18102,6 +18101,7 @@ init_Model();
|
|
|
18102
18101
|
init_util();
|
|
18103
18102
|
init_AssetKey();
|
|
18104
18103
|
init_BlockStates();
|
|
18104
|
+
init_BlockEntities();
|
|
18105
18105
|
init_CubeFace();
|
|
18106
18106
|
|
|
18107
18107
|
// src/world/SectionModels.ts
|
|
@@ -18199,6 +18199,10 @@ var SectionModels = class {
|
|
|
18199
18199
|
biomeTint: source ? [...templates.values()].some((template) => template.biomeTint?.some((value) => value === 1)) || void 0 : void 0
|
|
18200
18200
|
};
|
|
18201
18201
|
}
|
|
18202
|
+
/** Starts loading the state's models and atlases so a later `prepareState` with the same properties finds them loaded. */
|
|
18203
|
+
async preload(blockState3, properties = {}) {
|
|
18204
|
+
await this.state(blockState3, properties);
|
|
18205
|
+
}
|
|
18202
18206
|
async state(blockState3, properties) {
|
|
18203
18207
|
let states = this.states.get(blockState3);
|
|
18204
18208
|
if (!states) this.states.set(blockState3, states = /* @__PURE__ */ new Map());
|
|
@@ -18221,22 +18225,24 @@ var SectionModels = class {
|
|
|
18221
18225
|
async prepare(blockState3, properties) {
|
|
18222
18226
|
const state = { ...await BlockStateResolver.defaults(blockState3), ...properties };
|
|
18223
18227
|
const groups = BlockStateResolver.matching(blockState3, state);
|
|
18224
|
-
if (!groups.length) return void 0;
|
|
18228
|
+
if (!groups.length || groups.some((group) => Array.isArray(group) && !group.length)) return void 0;
|
|
18229
|
+
const variants = groups.flatMap((group) => Array.isArray(group) ? group : [group]);
|
|
18230
|
+
if (variants.some((variant) => !variant.model)) return void 0;
|
|
18231
|
+
const settled = await Promise.allSettled(variants.map(async (variant) => {
|
|
18232
|
+
const modelKey = AssetKey.parse("models", variant.model);
|
|
18233
|
+
modelKey.root = blockState3.key?.root;
|
|
18234
|
+
const model = await Models.getMerged(modelKey);
|
|
18235
|
+
if (!model?.elements?.length) return void 0;
|
|
18236
|
+
const atlas = await UVMapper.getAtlas(model);
|
|
18237
|
+
const rotation = BlockStateResolver.rotation(variant);
|
|
18238
|
+
if (!atlas || atlas.image.width > this.maxAtlasSize || atlas.image.height > this.maxAtlasSize) return void 0;
|
|
18239
|
+
return { model, atlas, rotation };
|
|
18240
|
+
}));
|
|
18225
18241
|
const prepared = /* @__PURE__ */ new Map();
|
|
18226
|
-
for (const
|
|
18227
|
-
|
|
18228
|
-
if (!
|
|
18229
|
-
|
|
18230
|
-
if (!variant.model) return void 0;
|
|
18231
|
-
const modelKey = AssetKey.parse("models", variant.model);
|
|
18232
|
-
modelKey.root = blockState3.key?.root;
|
|
18233
|
-
const model = await Models.getMerged(modelKey);
|
|
18234
|
-
if (!model?.elements?.length) return void 0;
|
|
18235
|
-
const atlas = await UVMapper.getAtlas(model);
|
|
18236
|
-
const rotation = BlockStateResolver.rotation(variant);
|
|
18237
|
-
if (!atlas || atlas.image.width > this.maxAtlasSize || atlas.image.height > this.maxAtlasSize) return void 0;
|
|
18238
|
-
prepared.set(variant, { model, atlas, rotation });
|
|
18239
|
-
}
|
|
18242
|
+
for (const [i, result] of settled.entries()) {
|
|
18243
|
+
if (result.status === "rejected") throw result.reason;
|
|
18244
|
+
if (!result.value) return void 0;
|
|
18245
|
+
prepared.set(variants[i], result.value);
|
|
18240
18246
|
}
|
|
18241
18247
|
return { state, groups, choices: prepared };
|
|
18242
18248
|
}
|
|
@@ -18696,6 +18702,7 @@ function skipsBlockFace(data, rule, block, neighbor, face) {
|
|
|
18696
18702
|
// src/world/MineRenderWorld.ts
|
|
18697
18703
|
init_log();
|
|
18698
18704
|
var log18 = logger("MineRenderWorld");
|
|
18705
|
+
var YIELD_INTERVAL_MS = 8;
|
|
18699
18706
|
var MineRenderWorld = class {
|
|
18700
18707
|
constructor(scene, options = {}) {
|
|
18701
18708
|
this._chunks = /* @__PURE__ */ new Map();
|
|
@@ -18809,7 +18816,7 @@ var MineRenderWorld = class {
|
|
|
18809
18816
|
failures.set(chunk, error);
|
|
18810
18817
|
if (this._chunks.get(`${chunk.x}_${chunk.y}_${chunk.z}`) === chunk) this.pendingBiomes.set(chunk, void 0);
|
|
18811
18818
|
}
|
|
18812
|
-
if (performance.now() - sliceStart >
|
|
18819
|
+
if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
|
|
18813
18820
|
await yieldToEventLoop();
|
|
18814
18821
|
sliceStart = performance.now();
|
|
18815
18822
|
}
|
|
@@ -18910,15 +18917,18 @@ var MineRenderWorld = class {
|
|
|
18910
18917
|
const key = AssetKey.parse("blockstates", block.type);
|
|
18911
18918
|
keys.set(key.serialize(), key);
|
|
18912
18919
|
}
|
|
18913
|
-
await BlockStates.getAll(keys.values());
|
|
18920
|
+
const loaded = await BlockStates.getAll(keys.values());
|
|
18921
|
+
const states = new Map([...keys.keys()].map((key, i) => [key, loaded[i]]));
|
|
18914
18922
|
if (!useBatches) {
|
|
18915
18923
|
for (const block of multiblock.blocks) await place(block);
|
|
18916
18924
|
return;
|
|
18917
18925
|
}
|
|
18918
18926
|
if (!executor) {
|
|
18919
18927
|
const groups2 = /* @__PURE__ */ new Map();
|
|
18928
|
+
const distinct = /* @__PURE__ */ new Map();
|
|
18920
18929
|
const position = new import_three45.Vector3();
|
|
18921
18930
|
for (const block of multiblock.blocks) {
|
|
18931
|
+
if (this.sectionModels && !Chunk.isAir(block)) distinct.set(ChunkData.paletteKey(block), block);
|
|
18922
18932
|
const [x, y, z] = block.position;
|
|
18923
18933
|
this.validatePosBounds(position.set(x, y, z));
|
|
18924
18934
|
const cx = Math.floor(x / 16), cy = Math.floor(y / 16), cz = Math.floor(z / 16);
|
|
@@ -18929,6 +18939,16 @@ var MineRenderWorld = class {
|
|
|
18929
18939
|
if (!group) groups2.set(chunk, group = []);
|
|
18930
18940
|
group.push({ index: (y - cy * 16) * 256 + (z - cz * 16) * 16 + x - cx * 16, block });
|
|
18931
18941
|
}
|
|
18942
|
+
if (this.sectionModels) {
|
|
18943
|
+
for (const block of distinct.values()) {
|
|
18944
|
+
const state = states.get(AssetKey.parse("blockstates", block.type).serialize());
|
|
18945
|
+
if (!state) continue;
|
|
18946
|
+
void (async () => {
|
|
18947
|
+
if (state.key && BlockEntities.entry(await BlockEntities.getIndex(state.key.root), state.key.toNamespacedString())) return;
|
|
18948
|
+
await this.sectionModels.preload(state, block.properties);
|
|
18949
|
+
})().catch(() => void 0);
|
|
18950
|
+
}
|
|
18951
|
+
}
|
|
18932
18952
|
await this.placeChunkGroups(groups2, changes);
|
|
18933
18953
|
return;
|
|
18934
18954
|
}
|
|
@@ -18964,7 +18984,7 @@ var MineRenderWorld = class {
|
|
|
18964
18984
|
));
|
|
18965
18985
|
for (const pos of positions) changes.set(`${pos.x},${pos.y},${pos.z}`, pos);
|
|
18966
18986
|
}
|
|
18967
|
-
if (performance.now() - sliceStart >
|
|
18987
|
+
if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
|
|
18968
18988
|
await yieldToEventLoop();
|
|
18969
18989
|
sliceStart = performance.now();
|
|
18970
18990
|
}
|
|
@@ -18972,6 +18992,62 @@ var MineRenderWorld = class {
|
|
|
18972
18992
|
}
|
|
18973
18993
|
/** Replaces a chunk column's blocks, biomes, and saved light and, with `renderEntities`, its supported saved mobs. */
|
|
18974
18994
|
async placeChunk(chunk, executor) {
|
|
18995
|
+
return this.placeColumn(chunk, executor);
|
|
18996
|
+
}
|
|
18997
|
+
/**
|
|
18998
|
+
* Replaces parsed columns in input order, then refreshes neighboring geometry once.
|
|
18999
|
+
* Duplicate coordinates retain their order. A failed column stops placement; queued refreshes finish before rejection.
|
|
19000
|
+
*/
|
|
19001
|
+
async placeChunks(chunks) {
|
|
19002
|
+
const columns = [];
|
|
19003
|
+
for (const chunk of chunks) {
|
|
19004
|
+
const { x, z } = chunk ?? {};
|
|
19005
|
+
if (![x, z].every(Number.isSafeInteger)) {
|
|
19006
|
+
throw new RangeError("Chunk column coordinates must be safe integers");
|
|
19007
|
+
}
|
|
19008
|
+
const sections = Array.from(chunk.sections, (section) => {
|
|
19009
|
+
const { y } = section ?? {};
|
|
19010
|
+
if (!Number.isSafeInteger(y)) throw new RangeError("Chunk section coordinates must be safe integers");
|
|
19011
|
+
return { ...section, y };
|
|
19012
|
+
});
|
|
19013
|
+
columns.push({ ...chunk, x, z, sections });
|
|
19014
|
+
}
|
|
19015
|
+
if (!columns.length) return;
|
|
19016
|
+
await this.culling;
|
|
19017
|
+
let failed = false, failure, sliceStart = performance.now();
|
|
19018
|
+
try {
|
|
19019
|
+
for (const column of columns) {
|
|
19020
|
+
await this.placeColumn(column, void 0, true);
|
|
19021
|
+
if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
|
|
19022
|
+
await yieldToEventLoop();
|
|
19023
|
+
sliceStart = performance.now();
|
|
19024
|
+
}
|
|
19025
|
+
}
|
|
19026
|
+
} catch (error) {
|
|
19027
|
+
failed = true;
|
|
19028
|
+
failure = error;
|
|
19029
|
+
} finally {
|
|
19030
|
+
try {
|
|
19031
|
+
const failures = await this.refreshBiomes(true);
|
|
19032
|
+
if (failures.size) log18.warn("Failed to refresh biome colors", failures.values().next().value);
|
|
19033
|
+
} catch (error) {
|
|
19034
|
+
if (!failed) {
|
|
19035
|
+
failed = true;
|
|
19036
|
+
failure = error;
|
|
19037
|
+
}
|
|
19038
|
+
}
|
|
19039
|
+
try {
|
|
19040
|
+
await this.updateCulling([]);
|
|
19041
|
+
} catch (error) {
|
|
19042
|
+
if (!failed) {
|
|
19043
|
+
failed = true;
|
|
19044
|
+
failure = error;
|
|
19045
|
+
}
|
|
19046
|
+
}
|
|
19047
|
+
}
|
|
19048
|
+
if (failed) throw failure;
|
|
19049
|
+
}
|
|
19050
|
+
async placeColumn(chunk, executor, deferCulling = false) {
|
|
18975
19051
|
if (![chunk.x, chunk.z].every(Number.isSafeInteger)) {
|
|
18976
19052
|
throw new RangeError("Chunk column coordinates must be safe integers");
|
|
18977
19053
|
}
|
|
@@ -19033,7 +19109,7 @@ var MineRenderWorld = class {
|
|
|
19033
19109
|
} finally {
|
|
19034
19110
|
this.markSectionChanged(target);
|
|
19035
19111
|
}
|
|
19036
|
-
if (performance.now() - sliceStart >
|
|
19112
|
+
if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
|
|
19037
19113
|
await yieldToEventLoop();
|
|
19038
19114
|
sliceStart = performance.now();
|
|
19039
19115
|
}
|
|
@@ -19056,9 +19132,12 @@ var MineRenderWorld = class {
|
|
|
19056
19132
|
await placeEntities?.();
|
|
19057
19133
|
} finally {
|
|
19058
19134
|
this.markBiomesChanged(chunk.x, chunk.z, void 0, false);
|
|
19059
|
-
|
|
19060
|
-
|
|
19061
|
-
|
|
19135
|
+
if (deferCulling) await this.updateCulling([...changes.values()], true);
|
|
19136
|
+
else {
|
|
19137
|
+
const failures = await this.refreshBiomes();
|
|
19138
|
+
if (failures.size) log18.warn("Failed to refresh biome colors", failures.values().next().value);
|
|
19139
|
+
await this.updateCulling([...changes.values()]);
|
|
19140
|
+
}
|
|
19062
19141
|
}
|
|
19063
19142
|
}
|
|
19064
19143
|
/**
|
|
@@ -19088,7 +19167,7 @@ var MineRenderWorld = class {
|
|
|
19088
19167
|
for (const { x, z } of columns.values()) {
|
|
19089
19168
|
this.entities?.clearColumn(x, z);
|
|
19090
19169
|
await this.clearChunkColumn(x, z);
|
|
19091
|
-
if (performance.now() - sliceStart >
|
|
19170
|
+
if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
|
|
19092
19171
|
await yieldToEventLoop();
|
|
19093
19172
|
sliceStart = performance.now();
|
|
19094
19173
|
}
|
|
@@ -19227,15 +19306,25 @@ var MineRenderWorld = class {
|
|
|
19227
19306
|
for (const [index, mask] of masks) await chunk.setCullMaskIndex(index, mask);
|
|
19228
19307
|
await chunk.refreshLight(indices === "all" ? void 0 : indices);
|
|
19229
19308
|
chunk.refreshAmbientOcclusion(this._ambientOcclusion);
|
|
19230
|
-
if (
|
|
19309
|
+
if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
|
|
19231
19310
|
await yieldToEventLoop();
|
|
19232
19311
|
sliceStart = performance.now();
|
|
19233
19312
|
}
|
|
19234
19313
|
}
|
|
19235
|
-
const
|
|
19236
|
-
|
|
19237
|
-
|
|
19238
|
-
|
|
19314
|
+
const pending = [];
|
|
19315
|
+
for (const chunk of [...batch.keys()]) {
|
|
19316
|
+
const build = (async () => {
|
|
19317
|
+
await chunk.rebuildSectionMesh();
|
|
19318
|
+
batch.delete(chunk);
|
|
19319
|
+
})();
|
|
19320
|
+
void build.catch(() => void 0);
|
|
19321
|
+
pending.push(build);
|
|
19322
|
+
if (performance.now() - sliceStart > YIELD_INTERVAL_MS) {
|
|
19323
|
+
await yieldToEventLoop();
|
|
19324
|
+
sliceStart = performance.now();
|
|
19325
|
+
}
|
|
19326
|
+
}
|
|
19327
|
+
const builds = await Promise.allSettled(pending);
|
|
19239
19328
|
const failure = builds.find((result) => result.status === "rejected");
|
|
19240
19329
|
if (failure?.status === "rejected") throw failure.reason;
|
|
19241
19330
|
} catch (error) {
|
|
@@ -20187,8 +20276,10 @@ var Chunk = class _Chunk {
|
|
|
20187
20276
|
if (rejected?.status === "rejected") throw rejected.reason;
|
|
20188
20277
|
}
|
|
20189
20278
|
const positions = [];
|
|
20190
|
-
|
|
20191
|
-
|
|
20279
|
+
const jobs = /* @__PURE__ */ new Map();
|
|
20280
|
+
let failureOrder = Infinity, failure;
|
|
20281
|
+
for (const [order, { index, key, block }] of placements.entries()) {
|
|
20282
|
+
jobs.delete(index);
|
|
20192
20283
|
this.hiddenBlocks.delete(index);
|
|
20193
20284
|
this.data.assign(index, this.data.intern(block), block?.nbt);
|
|
20194
20285
|
const readBlock = this.data.snapshot(index);
|
|
@@ -20204,7 +20295,6 @@ var Chunk = class _Chunk {
|
|
|
20204
20295
|
this.z * 16 + Math.floor(index / 16) % 16
|
|
20205
20296
|
);
|
|
20206
20297
|
positions.push(worldPos);
|
|
20207
|
-
let object4;
|
|
20208
20298
|
try {
|
|
20209
20299
|
if (!readBlock) continue;
|
|
20210
20300
|
const resolved = resolutions.get(key);
|
|
@@ -20226,7 +20316,7 @@ var Chunk = class _Chunk {
|
|
|
20226
20316
|
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0, biomeTint: context?.tint })));
|
|
20227
20317
|
this.meshDirty = true;
|
|
20228
20318
|
} else {
|
|
20229
|
-
|
|
20319
|
+
jobs.set(index, { order, readBlock, blockState: blockState3, position: MineRenderWorld.worldToScenePosition(worldPos), options: {
|
|
20230
20320
|
mergeMeshes: true,
|
|
20231
20321
|
instanceMeshes: true,
|
|
20232
20322
|
maxInstanceCount: 2e3,
|
|
@@ -20234,13 +20324,12 @@ var Chunk = class _Chunk {
|
|
|
20234
20324
|
variantPosition,
|
|
20235
20325
|
biome: context?.biome,
|
|
20236
20326
|
biomePosition: this.biomePosition(index, blockState3, stored)
|
|
20237
|
-
});
|
|
20238
|
-
|
|
20327
|
+
} });
|
|
20328
|
+
continue;
|
|
20239
20329
|
}
|
|
20240
20330
|
this.renderedBlocks.set(index, { get block() {
|
|
20241
20331
|
return readBlock();
|
|
20242
|
-
}, object:
|
|
20243
|
-
if (object4) this.objectBlocks.add(index);
|
|
20332
|
+
}, object: void 0 });
|
|
20244
20333
|
} catch (error) {
|
|
20245
20334
|
if (preserveDataOnFailure && readBlock) {
|
|
20246
20335
|
this.renderedBlocks.set(index, { get block() {
|
|
@@ -20248,12 +20337,35 @@ var Chunk = class _Chunk {
|
|
|
20248
20337
|
}, object: void 0 });
|
|
20249
20338
|
} else this.data.assign(index, 0);
|
|
20250
20339
|
this.fluidCells[index] = 0;
|
|
20251
|
-
|
|
20252
|
-
|
|
20253
|
-
|
|
20340
|
+
if (order < failureOrder) {
|
|
20341
|
+
failureOrder = order;
|
|
20342
|
+
failure = error;
|
|
20343
|
+
}
|
|
20254
20344
|
}
|
|
20255
20345
|
}
|
|
20256
|
-
|
|
20346
|
+
const pending = [...jobs];
|
|
20347
|
+
const objects = await Promise.allSettled(pending.map(([, job]) => this.createBlockObject(job.blockState, job.position, job.options)));
|
|
20348
|
+
for (const [i, [index, { order, readBlock }]] of pending.entries()) {
|
|
20349
|
+
const result = objects[i];
|
|
20350
|
+
if (result.status === "fulfilled") {
|
|
20351
|
+
this.renderedBlocks.set(index, { get block() {
|
|
20352
|
+
return readBlock();
|
|
20353
|
+
}, object: result.value });
|
|
20354
|
+
this.objectBlocks.add(index);
|
|
20355
|
+
} else {
|
|
20356
|
+
if (preserveDataOnFailure) {
|
|
20357
|
+
this.renderedBlocks.set(index, { get block() {
|
|
20358
|
+
return readBlock();
|
|
20359
|
+
}, object: void 0 });
|
|
20360
|
+
} else this.data.assign(index, 0);
|
|
20361
|
+
this.fluidCells[index] = 0;
|
|
20362
|
+
if (order < failureOrder) {
|
|
20363
|
+
failureOrder = order;
|
|
20364
|
+
failure = result.reason;
|
|
20365
|
+
}
|
|
20366
|
+
}
|
|
20367
|
+
}
|
|
20368
|
+
if (failureOrder < Infinity) throw failure;
|
|
20257
20369
|
return positions;
|
|
20258
20370
|
}
|
|
20259
20371
|
/** Replaces every cell from parsed section data. The caller refreshes neighbor culling. */
|
|
@@ -20324,37 +20436,42 @@ var Chunk = class _Chunk {
|
|
|
20324
20436
|
failed = true;
|
|
20325
20437
|
}
|
|
20326
20438
|
}
|
|
20327
|
-
|
|
20328
|
-
|
|
20329
|
-
|
|
20330
|
-
|
|
20331
|
-
|
|
20332
|
-
|
|
20333
|
-
|
|
20334
|
-
|
|
20335
|
-
|
|
20336
|
-
|
|
20337
|
-
|
|
20338
|
-
|
|
20339
|
-
|
|
20340
|
-
|
|
20341
|
-
|
|
20342
|
-
|
|
20343
|
-
)
|
|
20344
|
-
this.renderedBlocks.get(index).object = object4;
|
|
20439
|
+
const objects = await Promise.allSettled(perBlocks.map(({ index, blockState: blockState3, biome }) => this.createBlockObject(blockState3, new import_three47.Vector3(
|
|
20440
|
+
this.x * 256 + index % 16 * 16,
|
|
20441
|
+
this.y * 256 + Math.floor(index / 256) * 16,
|
|
20442
|
+
this.z * 256 + Math.floor(index / 16) % 16 * 16
|
|
20443
|
+
), {
|
|
20444
|
+
mergeMeshes: true,
|
|
20445
|
+
instanceMeshes: true,
|
|
20446
|
+
maxInstanceCount: 2e3,
|
|
20447
|
+
initialState: this.data.stateAt(this.data.idAt(index)).properties,
|
|
20448
|
+
variantPosition: this.positionAt(index),
|
|
20449
|
+
biome,
|
|
20450
|
+
biomePosition: this.biomePosition(index, blockState3, this.data.stateAt(this.data.idAt(index)))
|
|
20451
|
+
})));
|
|
20452
|
+
for (const [i, { index }] of perBlocks.entries()) {
|
|
20453
|
+
const result = objects[i];
|
|
20454
|
+
if (result.status === "fulfilled") {
|
|
20455
|
+
this.renderedBlocks.get(index).object = result.value;
|
|
20345
20456
|
this.objectBlocks.add(index);
|
|
20346
|
-
}
|
|
20457
|
+
} else {
|
|
20347
20458
|
this.data.assign(index, 0);
|
|
20348
20459
|
this.fluidCells[index] = 0;
|
|
20349
20460
|
this.renderedBlocks.delete(index);
|
|
20350
20461
|
this.objectBlocks.delete(index);
|
|
20351
|
-
|
|
20352
|
-
if (!failed) failure = error;
|
|
20462
|
+
if (!failed) failure = result.reason;
|
|
20353
20463
|
failed = true;
|
|
20354
20464
|
}
|
|
20355
20465
|
}
|
|
20356
20466
|
if (failed) throw failure;
|
|
20357
20467
|
}
|
|
20468
|
+
createBlockObject(blockState3, position, options) {
|
|
20469
|
+
return this.scene.addSceneObject(blockState3, () => {
|
|
20470
|
+
const object4 = new BlockObject(blockState3, options);
|
|
20471
|
+
object4.position.copy(position);
|
|
20472
|
+
return object4;
|
|
20473
|
+
}, options);
|
|
20474
|
+
}
|
|
20358
20475
|
positionAt(index) {
|
|
20359
20476
|
return [this.x * 16 + index % 16, this.y * 16 + Math.floor(index / 256), this.z * 16 + Math.floor(index / 16) % 16];
|
|
20360
20477
|
}
|
|
@@ -20695,6 +20812,7 @@ var Chunk = class _Chunk {
|
|
|
20695
20812
|
};
|
|
20696
20813
|
|
|
20697
20814
|
// src/world/WorldStreamer.ts
|
|
20815
|
+
var MAX_PREFETCHED_COLUMNS = 4;
|
|
20698
20816
|
var WorldStreamer = class {
|
|
20699
20817
|
constructor(world, source, options = {}) {
|
|
20700
20818
|
this.world = world;
|
|
@@ -20704,6 +20822,7 @@ var WorldStreamer = class {
|
|
|
20704
20822
|
this.missing = /* @__PURE__ */ new Map();
|
|
20705
20823
|
this.failed = /* @__PURE__ */ new Map();
|
|
20706
20824
|
this.desired = [];
|
|
20825
|
+
this.reads = /* @__PURE__ */ new Map();
|
|
20707
20826
|
this.disposed = false;
|
|
20708
20827
|
this.loadRadius = options.loadRadius ?? 2;
|
|
20709
20828
|
this.unloadRadius = options.unloadRadius ?? Math.min(32, this.loadRadius + 1);
|
|
@@ -20748,6 +20867,12 @@ var WorldStreamer = class {
|
|
|
20748
20867
|
if (this.loading && !this.within(this.loading.position, this.loadRadius)) {
|
|
20749
20868
|
this.loading.controller.abort();
|
|
20750
20869
|
}
|
|
20870
|
+
for (const [key, read] of this.reads) {
|
|
20871
|
+
if (!this.within(read.position, this.loadRadius)) {
|
|
20872
|
+
read.controller.abort();
|
|
20873
|
+
if (read.ready) this.reads.delete(key);
|
|
20874
|
+
}
|
|
20875
|
+
}
|
|
20751
20876
|
}
|
|
20752
20877
|
return this.running ??= Promise.resolve().then(() => this.drain());
|
|
20753
20878
|
}
|
|
@@ -20768,8 +20893,11 @@ var WorldStreamer = class {
|
|
|
20768
20893
|
this.desired = [];
|
|
20769
20894
|
this.center = void 0;
|
|
20770
20895
|
this.loading?.controller.abort();
|
|
20896
|
+
for (const read of this.reads.values()) read.controller.abort();
|
|
20771
20897
|
return this.disposal ??= Promise.resolve().then(async () => {
|
|
20772
20898
|
await this.running?.catch(() => void 0);
|
|
20899
|
+
await Promise.all([...this.reads.values()].map((read) => read.promise));
|
|
20900
|
+
this.reads.clear();
|
|
20773
20901
|
try {
|
|
20774
20902
|
await this.unloadMany([...this.owned.values()]);
|
|
20775
20903
|
} finally {
|
|
@@ -20795,6 +20923,10 @@ var WorldStreamer = class {
|
|
|
20795
20923
|
await this.unloadMany(obsolete);
|
|
20796
20924
|
continue;
|
|
20797
20925
|
}
|
|
20926
|
+
if (this.world.placeChunks) {
|
|
20927
|
+
if (await this.placeReadyChunks()) continue;
|
|
20928
|
+
return;
|
|
20929
|
+
}
|
|
20798
20930
|
const position = this.desired.find((position2) => this.needsLoad(position2));
|
|
20799
20931
|
if (!position) return;
|
|
20800
20932
|
let chunk;
|
|
@@ -20817,26 +20949,105 @@ var WorldStreamer = class {
|
|
|
20817
20949
|
continue;
|
|
20818
20950
|
}
|
|
20819
20951
|
this.owned.set(this.key(position), position);
|
|
20952
|
+
await this.placeOwnedColumns([position], () => this.world.placeChunk(chunk));
|
|
20953
|
+
}
|
|
20954
|
+
} finally {
|
|
20955
|
+
for (const [key, read] of this.reads) {
|
|
20956
|
+
read.controller.abort();
|
|
20957
|
+
if (read.ready) this.reads.delete(key);
|
|
20958
|
+
}
|
|
20959
|
+
this.running = void 0;
|
|
20960
|
+
}
|
|
20961
|
+
}
|
|
20962
|
+
fillReadSlots() {
|
|
20963
|
+
while (!this.disposed && this.reads.size < MAX_PREFETCHED_COLUMNS) {
|
|
20964
|
+
const position = this.desired.find((position2) => this.needsLoad(position2) && !this.reads.has(this.key(position2)));
|
|
20965
|
+
if (!position) return;
|
|
20966
|
+
const key = this.key(position);
|
|
20967
|
+
const read = {
|
|
20968
|
+
position,
|
|
20969
|
+
controller: new AbortController(),
|
|
20970
|
+
promise: Promise.resolve(),
|
|
20971
|
+
ready: false,
|
|
20972
|
+
failed: false
|
|
20973
|
+
};
|
|
20974
|
+
this.reads.set(key, read);
|
|
20975
|
+
read.promise = (async () => {
|
|
20820
20976
|
try {
|
|
20821
|
-
await this.
|
|
20822
|
-
this.
|
|
20977
|
+
read.chunk = await this.source.getChunk(position.x, position.z, read.controller.signal);
|
|
20978
|
+
if (read.chunk) this.validateChunk(read.chunk, position);
|
|
20823
20979
|
} catch (error) {
|
|
20824
|
-
|
|
20825
|
-
|
|
20826
|
-
|
|
20827
|
-
|
|
20828
|
-
|
|
20829
|
-
|
|
20830
|
-
} catch {
|
|
20831
|
-
throw Object.assign(new Error(failure.message), { cause: cleanupError });
|
|
20832
|
-
}
|
|
20833
|
-
throw failure;
|
|
20980
|
+
read.failed = true;
|
|
20981
|
+
read.error = error;
|
|
20982
|
+
} finally {
|
|
20983
|
+
read.ready = true;
|
|
20984
|
+
if (read.controller.signal.aborted || this.disposed || !this.within(position, this.loadRadius)) {
|
|
20985
|
+
this.reads.delete(key);
|
|
20834
20986
|
}
|
|
20835
|
-
|
|
20987
|
+
}
|
|
20988
|
+
})();
|
|
20989
|
+
}
|
|
20990
|
+
}
|
|
20991
|
+
/** Places only the ready prefix, so later source reads cannot delay the nearest ready column. */
|
|
20992
|
+
async placeReadyChunks() {
|
|
20993
|
+
this.fillReadSlots();
|
|
20994
|
+
const position = this.desired.find((position2) => this.needsLoad(position2));
|
|
20995
|
+
if (!position) {
|
|
20996
|
+
if (!this.reads.size) return false;
|
|
20997
|
+
await Promise.all([...this.reads.values()].map((read) => read.promise));
|
|
20998
|
+
return true;
|
|
20999
|
+
}
|
|
21000
|
+
const head = this.reads.get(this.key(position));
|
|
21001
|
+
if (!head) {
|
|
21002
|
+
for (let index = this.desired.length - 1; index >= 0; index--) {
|
|
21003
|
+
const key = this.key(this.desired[index]);
|
|
21004
|
+
if (this.reads.get(key)?.ready) {
|
|
21005
|
+
this.reads.delete(key);
|
|
21006
|
+
return true;
|
|
20836
21007
|
}
|
|
20837
21008
|
}
|
|
20838
|
-
|
|
20839
|
-
|
|
21009
|
+
await Promise.race([...this.reads.values()].map((read) => read.promise));
|
|
21010
|
+
return true;
|
|
21011
|
+
}
|
|
21012
|
+
if (!head.ready) {
|
|
21013
|
+
await head.promise;
|
|
21014
|
+
return true;
|
|
21015
|
+
}
|
|
21016
|
+
const chunks = [], positions = [];
|
|
21017
|
+
for (const desired of this.desired) {
|
|
21018
|
+
if (!this.needsLoad(desired)) continue;
|
|
21019
|
+
const key = this.key(desired), read = this.reads.get(key);
|
|
21020
|
+
if (!read?.ready) break;
|
|
21021
|
+
this.reads.delete(key);
|
|
21022
|
+
if (read.failed) this.failed.set(key, { ...desired, error: read.error });
|
|
21023
|
+
else if (!read.chunk) this.missing.set(key, desired);
|
|
21024
|
+
else {
|
|
21025
|
+
chunks.push(read.chunk);
|
|
21026
|
+
positions.push(desired);
|
|
21027
|
+
}
|
|
21028
|
+
}
|
|
21029
|
+
if (!chunks.length) return true;
|
|
21030
|
+
for (const position2 of positions) this.owned.set(this.key(position2), position2);
|
|
21031
|
+
await this.placeOwnedColumns(positions, () => this.world.placeChunks(chunks));
|
|
21032
|
+
return true;
|
|
21033
|
+
}
|
|
21034
|
+
async placeOwnedColumns(positions, place) {
|
|
21035
|
+
try {
|
|
21036
|
+
await place();
|
|
21037
|
+
for (const position of positions) this.loaded.set(this.key(position), position);
|
|
21038
|
+
} catch (error) {
|
|
21039
|
+
try {
|
|
21040
|
+
await this.unloadMany(positions);
|
|
21041
|
+
} catch (cleanupError) {
|
|
21042
|
+
const failure = error instanceof Error ? error : new Error(String(error));
|
|
21043
|
+
try {
|
|
21044
|
+
Object.defineProperty(failure, "cause", { value: cleanupError, configurable: true });
|
|
21045
|
+
} catch {
|
|
21046
|
+
throw Object.assign(new Error(failure.message), { cause: cleanupError });
|
|
21047
|
+
}
|
|
21048
|
+
throw failure;
|
|
21049
|
+
}
|
|
21050
|
+
throw error;
|
|
20840
21051
|
}
|
|
20841
21052
|
}
|
|
20842
21053
|
needsLoad(position) {
|