minerender 2.1.0-alpha.35 → 2.1.0-alpha.37

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.
@@ -32,8 +32,8 @@ var require_dist = __commonJS({
32
32
  __assign = Object.assign || function(t) {
33
33
  for (var s, i = 1, n = arguments.length; i < n; i++) {
34
34
  s = arguments[i];
35
- for (var p12 in s) if (Object.prototype.hasOwnProperty.call(s, p12))
36
- t[p12] = s[p12];
35
+ for (var p11 in s) if (Object.prototype.hasOwnProperty.call(s, p11))
36
+ t[p11] = s[p11];
37
37
  }
38
38
  return t;
39
39
  };
@@ -1165,12 +1165,12 @@ var AssetKey = class _AssetKey extends BasicAssetKey {
1165
1165
  return a.join("/");
1166
1166
  }
1167
1167
  getFullPath() {
1168
- let p12 = [];
1168
+ let p11 = [];
1169
1169
  if (this.type) {
1170
- p12.push(this.type);
1170
+ p11.push(this.type);
1171
1171
  }
1172
- p12.push(this.path);
1173
- return p12.join("/");
1172
+ p11.push(this.path);
1173
+ return p11.join("/");
1174
1174
  }
1175
1175
  toNamespacedString() {
1176
1176
  return this.namespace + ":" + this.getFullPath();
@@ -1257,7 +1257,7 @@ var ModelMerger = class {
1257
1257
  merged.display = { ...inheritedDisplay, ...DisplayTransforms.withHandFallbacks(source.display) };
1258
1258
  }
1259
1259
  }
1260
- merged.hierarchy = models.map((m) => m.parent).filter((p12) => `${p12}`);
1260
+ merged.hierarchy = models.map((m) => m.parent).filter((p11) => `${p11}`);
1261
1261
  merged.hierarchy.push(`${merged.parent}`);
1262
1262
  return merged;
1263
1263
  }
@@ -1497,24 +1497,20 @@ var AssetLoadError = class extends Error {
1497
1497
  };
1498
1498
 
1499
1499
  // src/util/BatchedExecutor.ts
1500
+ import { JobQueue as JobQueue2 } from "jobqu";
1500
1501
  var BatchedExecutor = class {
1501
1502
  constructor(interval = 1, batch = 30) {
1503
+ if (!Number.isFinite(interval) || interval < 0) throw new RangeError("Batch interval must be finite and nonnegative");
1504
+ if (!Number.isInteger(batch) || batch < 1) throw new RangeError("Batch size must be a positive integer");
1502
1505
  this.interval = interval;
1503
1506
  this.batch = batch;
1504
- this.queue = [];
1505
- this.task = setInterval(() => this.run());
1507
+ this.queue = new JobQueue2(async (task) => task(), { interval, maxPerRun: batch, maxActive: batch });
1506
1508
  }
1507
1509
  submit(task) {
1508
- this.queue.push(task);
1510
+ return this.queue.add(() => task());
1509
1511
  }
1510
- run() {
1511
- for (let i = 0; i < this.batch; i++) {
1512
- this.runNext();
1513
- }
1514
- }
1515
- runNext() {
1516
- let next = this.queue.shift();
1517
- if (next) next();
1512
+ stop() {
1513
+ this.queue.end();
1518
1514
  }
1519
1515
  };
1520
1516
 
@@ -7195,6 +7191,87 @@ var Skins = class {
7195
7191
  // src/world/Chunk.ts
7196
7192
  import { Vector3 as Vector311 } from "three";
7197
7193
 
7194
+ // src/world/ChunkData.ts
7195
+ var ChunkData = class _ChunkData {
7196
+ constructor() {
7197
+ this.indices = new Uint16Array(4096);
7198
+ this.palette = [void 0];
7199
+ this.paletteIds = /* @__PURE__ */ new Map();
7200
+ this.freeIds = [];
7201
+ this.nbt = /* @__PURE__ */ new Map();
7202
+ }
7203
+ get(index) {
7204
+ _ChunkData.validateIndex(index);
7205
+ const entry = this.palette[this.indices[index]];
7206
+ return entry ? _ChunkData.copyBlock(entry.state, this.nbt.get(index)) : void 0;
7207
+ }
7208
+ snapshot(index) {
7209
+ _ChunkData.validateIndex(index);
7210
+ const entry = this.palette[this.indices[index]];
7211
+ return entry ? _ChunkData.copyBlock.bind(void 0, entry.state, this.nbt.get(index)) : void 0;
7212
+ }
7213
+ set(index, block) {
7214
+ _ChunkData.validateIndex(index);
7215
+ let state;
7216
+ let key;
7217
+ let nbt;
7218
+ if (block && !_ChunkData.isAir(block)) {
7219
+ const type = AssetKey.parse("blockstates", block.type).toNamespacedString();
7220
+ const properties = Object.fromEntries(Object.keys(block.properties ?? {}).sort().map((name) => [name, block.properties[name]]));
7221
+ state = Object.freeze(Object.keys(properties).length ? { type, properties: Object.freeze(properties) } : { type });
7222
+ key = JSON.stringify([type, properties]);
7223
+ if (block.nbt !== void 0) nbt = structuredClone(block.nbt);
7224
+ }
7225
+ const previousId = this.indices[index];
7226
+ if (previousId !== 0) {
7227
+ const previous = this.palette[previousId];
7228
+ if (--previous.references === 0) {
7229
+ this.paletteIds.delete(previous.key);
7230
+ this.palette[previousId] = void 0;
7231
+ this.freeIds.push(previousId);
7232
+ }
7233
+ }
7234
+ let id = 0;
7235
+ if (state && key !== void 0) {
7236
+ const existingId = this.paletteIds.get(key);
7237
+ if (existingId !== void 0) {
7238
+ id = existingId;
7239
+ this.palette[id].references++;
7240
+ } else {
7241
+ id = this.freeIds.pop() ?? this.palette.length;
7242
+ this.palette[id] = { key, state, references: 1 };
7243
+ this.paletteIds.set(key, id);
7244
+ }
7245
+ }
7246
+ this.indices[index] = id;
7247
+ if (nbt !== void 0) this.nbt.set(index, nbt);
7248
+ else this.nbt.delete(index);
7249
+ }
7250
+ clear() {
7251
+ this.indices.fill(0);
7252
+ this.palette.length = 1;
7253
+ this.paletteIds.clear();
7254
+ this.freeIds.length = 0;
7255
+ this.nbt.clear();
7256
+ }
7257
+ static isAir(block) {
7258
+ if (typeof block === "undefined" || typeof block.type === "undefined") return true;
7259
+ const key = AssetKey.parse("blockstates", block.type);
7260
+ return key.namespace === "minecraft" && !key.type && ["air", "cave_air", "void_air"].includes(key.path);
7261
+ }
7262
+ static copyBlock(state, nbt) {
7263
+ const block = { type: state.type };
7264
+ if (state.properties) block.properties = { ...state.properties };
7265
+ if (nbt !== void 0) block.nbt = structuredClone(nbt);
7266
+ return block;
7267
+ }
7268
+ static validateIndex(index) {
7269
+ if (!Number.isInteger(index) || index < 0 || index >= 4096) {
7270
+ throw new RangeError("Block index must be an integer from 0 to 4095");
7271
+ }
7272
+ }
7273
+ };
7274
+
7198
7275
  // src/world/MineRenderWorld.ts
7199
7276
  import { Vector3 as Vector310 } from "three";
7200
7277
  var MineRenderWorld = class {
@@ -7224,22 +7301,39 @@ var MineRenderWorld = class {
7224
7301
  const chunk = Chunk.isAir(value) ? this.getChunkAt(posOrX) : this.getOrCreateChunkAt(posOrX);
7225
7302
  return chunk?.setBlockAt(posOrX, value);
7226
7303
  }
7227
- async placeMultiBlock(multiblock, useBatches = true, executor = new BatchedExecutor()) {
7228
- const keys = new Set(multiblock.blocks.map((block) => AssetKey.parse("blockstates", block.type)));
7229
- await BlockStates.getAll(keys);
7230
- const place = async (block) => {
7231
- if (useBatches && typeof executor !== "undefined") {
7232
- await new Promise((resolve, reject) => {
7233
- executor.submit(() => {
7234
- this.setBlockAt(block.position, block).then(resolve).catch(reject);
7235
- });
7236
- });
7237
- } else {
7238
- await this.setBlockAt(block.position, block);
7304
+ async placeMultiBlock(multiblock, useBatches = true, executor) {
7305
+ const keys = /* @__PURE__ */ new Map();
7306
+ for (const block of multiblock.blocks) {
7307
+ if (Chunk.isAir(block)) continue;
7308
+ const key = AssetKey.parse("blockstates", block.type);
7309
+ keys.set(key.serialize(), key);
7310
+ }
7311
+ await BlockStates.getAll(keys.values());
7312
+ if (!useBatches) {
7313
+ for (const block of multiblock.blocks) await this.setBlockAt(block.position, block);
7314
+ return;
7315
+ }
7316
+ const positions = /* @__PURE__ */ new Map();
7317
+ for (const block of multiblock.blocks) {
7318
+ const key = block.position.join(",");
7319
+ const group = positions.get(key);
7320
+ if (group) group.push(block);
7321
+ else positions.set(key, [block]);
7322
+ }
7323
+ const groups = [...positions.values()];
7324
+ const queue = executor ?? new BatchedExecutor();
7325
+ try {
7326
+ for (let i = 0; i < groups.length; i += queue.batch) {
7327
+ const results = await Promise.allSettled(groups.slice(i, i + queue.batch).map(
7328
+ (blocks) => queue.submit(async () => {
7329
+ for (const block of blocks) await this.setBlockAt(block.position, block);
7330
+ })
7331
+ ));
7332
+ const failure = results.find((result) => result.status === "rejected");
7333
+ if (failure?.status === "rejected") throw failure.reason;
7239
7334
  }
7240
- };
7241
- for (let block of multiblock.blocks) {
7242
- await place(block);
7335
+ } finally {
7336
+ if (!executor) queue.stop();
7243
7337
  }
7244
7338
  }
7245
7339
  async clear() {
@@ -7288,10 +7382,10 @@ var MineRenderWorld = class {
7288
7382
  };
7289
7383
 
7290
7384
  // src/world/Chunk.ts
7291
- var p11 = prefix("Chunk");
7292
7385
  var Chunk = class _Chunk {
7293
7386
  constructor(scene, x, y, z) {
7294
- this._blocks = [];
7387
+ this.data = new ChunkData();
7388
+ this.renderedBlocks = /* @__PURE__ */ new Map();
7295
7389
  this.scene = scene;
7296
7390
  this.x = x;
7297
7391
  this.y = y;
@@ -7306,7 +7400,7 @@ var Chunk = class _Chunk {
7306
7400
  }
7307
7401
  const pos = this.worldPosToChunkPos(posOrX);
7308
7402
  const index = _Chunk.chunkPosToBlockIndex(pos);
7309
- return this._blocks[index];
7403
+ return this.renderedBlocks.get(index);
7310
7404
  }
7311
7405
  async setBlockAt(posOrX, yOrBlock, z, block) {
7312
7406
  if (typeof posOrX == "number") {
@@ -7328,44 +7422,47 @@ var Chunk = class _Chunk {
7328
7422
  worldPos = this.chunkPosToWorldPos(pos);
7329
7423
  }
7330
7424
  const index = _Chunk.chunkPosToBlockIndex(pos);
7331
- const current = this._blocks[index];
7332
- if (typeof current !== "undefined") {
7333
- console.debug(p11, "deleting existing block at", pos, worldPos, index);
7334
- current.object.removeFromScene();
7335
- delete this._blocks[index];
7336
- }
7337
- if (!block || _Chunk.isAir(block)) return void 0;
7338
- const blockState = await BlockStates.get(AssetKey.parse("blockstates", block.type));
7339
- if (blockState) {
7340
- const blockObject = await this.scene.addBlock(blockState, {
7425
+ this.data.set(index, block);
7426
+ const readBlock = this.data.snapshot(index);
7427
+ this.renderedBlocks.get(index)?.object.removeFromScene();
7428
+ this.renderedBlocks.delete(index);
7429
+ if (!readBlock) return void 0;
7430
+ const stored = readBlock();
7431
+ let object;
7432
+ try {
7433
+ const blockState = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
7434
+ if (!blockState) {
7435
+ this.data.set(index, void 0);
7436
+ return void 0;
7437
+ }
7438
+ object = await this.scene.addBlock(blockState, {
7341
7439
  mergeMeshes: true,
7342
7440
  instanceMeshes: true,
7343
7441
  maxInstanceCount: 2e3
7344
7442
  });
7345
- if (block.properties) {
7346
- await blockObject.setState(block.properties);
7347
- }
7348
- const scenePos = MineRenderWorld.worldToScenePosition(worldPos);
7349
- blockObject.setPosition(scenePos);
7350
- this._blocks[index] = {
7351
- block,
7352
- object: blockObject
7353
- };
7354
- return this._blocks[index];
7443
+ if (stored.properties) await object.setState(stored.properties);
7444
+ object.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
7445
+ const info = { get block() {
7446
+ return readBlock();
7447
+ }, object };
7448
+ this.renderedBlocks.set(index, info);
7449
+ return info;
7450
+ } catch (error) {
7451
+ this.data.set(index, void 0);
7452
+ object?.removeFromScene();
7453
+ throw error;
7355
7454
  }
7356
- return void 0;
7357
7455
  }
7358
7456
  async clear() {
7359
- this._blocks.forEach((block) => block.object.removeFromScene());
7360
- this._blocks.length = 0;
7457
+ for (const info of this.renderedBlocks.values()) info.object.removeFromScene();
7458
+ this.renderedBlocks.clear();
7459
+ this.data.clear();
7361
7460
  }
7362
7461
  async dispose() {
7363
7462
  await this.clear();
7364
7463
  }
7365
7464
  static isAir(block) {
7366
- if (typeof block === "undefined" || typeof block.type === "undefined") return true;
7367
- const key = AssetKey.parse("blockstates", block.type);
7368
- return key.namespace === "minecraft" && !key.type && ["air", "cave_air", "void_air"].includes(key.path);
7465
+ return ChunkData.isAir(block);
7369
7466
  }
7370
7467
  static chunkPosToBlockIndex(pos) {
7371
7468
  if ([pos.x, pos.y, pos.z].some((value) => !Number.isInteger(value) || value < 0 || value >= 16)) {
@@ -7412,6 +7509,7 @@ export {
7412
7509
  Caching,
7413
7510
  CanvasImage,
7414
7511
  Chunk,
7512
+ ChunkData,
7415
7513
  CubeFace,
7416
7514
  CubeFaceIndex,
7417
7515
  DEFAULT_ELEMENTS,