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
  };
@@ -1172,12 +1172,12 @@ var AssetKey = class _AssetKey extends BasicAssetKey {
1172
1172
  return a.join("/");
1173
1173
  }
1174
1174
  getFullPath() {
1175
- let p12 = [];
1175
+ let p11 = [];
1176
1176
  if (this.type) {
1177
- p12.push(this.type);
1177
+ p11.push(this.type);
1178
1178
  }
1179
- p12.push(this.path);
1180
- return p12.join("/");
1179
+ p11.push(this.path);
1180
+ return p11.join("/");
1181
1181
  }
1182
1182
  toNamespacedString() {
1183
1183
  return this.namespace + ":" + this.getFullPath();
@@ -1264,7 +1264,7 @@ var ModelMerger = class {
1264
1264
  merged.display = { ...inheritedDisplay, ...DisplayTransforms.withHandFallbacks(source.display) };
1265
1265
  }
1266
1266
  }
1267
- merged.hierarchy = models.map((m) => m.parent).filter((p12) => `${p12}`);
1267
+ merged.hierarchy = models.map((m) => m.parent).filter((p11) => `${p11}`);
1268
1268
  merged.hierarchy.push(`${merged.parent}`);
1269
1269
  return merged;
1270
1270
  }
@@ -1504,24 +1504,20 @@ var AssetLoadError = class extends Error {
1504
1504
  };
1505
1505
 
1506
1506
  // src/util/BatchedExecutor.ts
1507
+ import { JobQueue as JobQueue2 } from "jobqu";
1507
1508
  var BatchedExecutor = class {
1508
1509
  constructor(interval = 1, batch = 30) {
1510
+ if (!Number.isFinite(interval) || interval < 0) throw new RangeError("Batch interval must be finite and nonnegative");
1511
+ if (!Number.isInteger(batch) || batch < 1) throw new RangeError("Batch size must be a positive integer");
1509
1512
  this.interval = interval;
1510
1513
  this.batch = batch;
1511
- this.queue = [];
1512
- this.task = setInterval(() => this.run());
1514
+ this.queue = new JobQueue2(async (task) => task(), { interval, maxPerRun: batch, maxActive: batch });
1513
1515
  }
1514
1516
  submit(task) {
1515
- this.queue.push(task);
1517
+ return this.queue.add(() => task());
1516
1518
  }
1517
- run() {
1518
- for (let i = 0; i < this.batch; i++) {
1519
- this.runNext();
1520
- }
1521
- }
1522
- runNext() {
1523
- let next = this.queue.shift();
1524
- if (next) next();
1519
+ stop() {
1520
+ this.queue.end();
1525
1521
  }
1526
1522
  };
1527
1523
 
@@ -7245,6 +7241,87 @@ var Skins = class {
7245
7241
  // src/world/Chunk.ts
7246
7242
  import { Vector3 as Vector311 } from "three";
7247
7243
 
7244
+ // src/world/ChunkData.ts
7245
+ var ChunkData = class _ChunkData {
7246
+ constructor() {
7247
+ this.indices = new Uint16Array(4096);
7248
+ this.palette = [void 0];
7249
+ this.paletteIds = /* @__PURE__ */ new Map();
7250
+ this.freeIds = [];
7251
+ this.nbt = /* @__PURE__ */ new Map();
7252
+ }
7253
+ get(index) {
7254
+ _ChunkData.validateIndex(index);
7255
+ const entry = this.palette[this.indices[index]];
7256
+ return entry ? _ChunkData.copyBlock(entry.state, this.nbt.get(index)) : void 0;
7257
+ }
7258
+ snapshot(index) {
7259
+ _ChunkData.validateIndex(index);
7260
+ const entry = this.palette[this.indices[index]];
7261
+ return entry ? _ChunkData.copyBlock.bind(void 0, entry.state, this.nbt.get(index)) : void 0;
7262
+ }
7263
+ set(index, block) {
7264
+ _ChunkData.validateIndex(index);
7265
+ let state;
7266
+ let key;
7267
+ let nbt;
7268
+ if (block && !_ChunkData.isAir(block)) {
7269
+ const type = AssetKey.parse("blockstates", block.type).toNamespacedString();
7270
+ const properties = Object.fromEntries(Object.keys(block.properties ?? {}).sort().map((name) => [name, block.properties[name]]));
7271
+ state = Object.freeze(Object.keys(properties).length ? { type, properties: Object.freeze(properties) } : { type });
7272
+ key = JSON.stringify([type, properties]);
7273
+ if (block.nbt !== void 0) nbt = structuredClone(block.nbt);
7274
+ }
7275
+ const previousId = this.indices[index];
7276
+ if (previousId !== 0) {
7277
+ const previous = this.palette[previousId];
7278
+ if (--previous.references === 0) {
7279
+ this.paletteIds.delete(previous.key);
7280
+ this.palette[previousId] = void 0;
7281
+ this.freeIds.push(previousId);
7282
+ }
7283
+ }
7284
+ let id = 0;
7285
+ if (state && key !== void 0) {
7286
+ const existingId = this.paletteIds.get(key);
7287
+ if (existingId !== void 0) {
7288
+ id = existingId;
7289
+ this.palette[id].references++;
7290
+ } else {
7291
+ id = this.freeIds.pop() ?? this.palette.length;
7292
+ this.palette[id] = { key, state, references: 1 };
7293
+ this.paletteIds.set(key, id);
7294
+ }
7295
+ }
7296
+ this.indices[index] = id;
7297
+ if (nbt !== void 0) this.nbt.set(index, nbt);
7298
+ else this.nbt.delete(index);
7299
+ }
7300
+ clear() {
7301
+ this.indices.fill(0);
7302
+ this.palette.length = 1;
7303
+ this.paletteIds.clear();
7304
+ this.freeIds.length = 0;
7305
+ this.nbt.clear();
7306
+ }
7307
+ static isAir(block) {
7308
+ if (typeof block === "undefined" || typeof block.type === "undefined") return true;
7309
+ const key = AssetKey.parse("blockstates", block.type);
7310
+ return key.namespace === "minecraft" && !key.type && ["air", "cave_air", "void_air"].includes(key.path);
7311
+ }
7312
+ static copyBlock(state, nbt) {
7313
+ const block = { type: state.type };
7314
+ if (state.properties) block.properties = { ...state.properties };
7315
+ if (nbt !== void 0) block.nbt = structuredClone(nbt);
7316
+ return block;
7317
+ }
7318
+ static validateIndex(index) {
7319
+ if (!Number.isInteger(index) || index < 0 || index >= 4096) {
7320
+ throw new RangeError("Block index must be an integer from 0 to 4095");
7321
+ }
7322
+ }
7323
+ };
7324
+
7248
7325
  // src/world/MineRenderWorld.ts
7249
7326
  import { Vector3 as Vector310 } from "three";
7250
7327
  var MineRenderWorld = class {
@@ -7274,22 +7351,39 @@ var MineRenderWorld = class {
7274
7351
  const chunk = Chunk.isAir(value) ? this.getChunkAt(posOrX) : this.getOrCreateChunkAt(posOrX);
7275
7352
  return chunk?.setBlockAt(posOrX, value);
7276
7353
  }
7277
- async placeMultiBlock(multiblock, useBatches = true, executor = new BatchedExecutor()) {
7278
- const keys = new Set(multiblock.blocks.map((block) => AssetKey.parse("blockstates", block.type)));
7279
- await BlockStates.getAll(keys);
7280
- const place = async (block) => {
7281
- if (useBatches && typeof executor !== "undefined") {
7282
- await new Promise((resolve, reject) => {
7283
- executor.submit(() => {
7284
- this.setBlockAt(block.position, block).then(resolve).catch(reject);
7285
- });
7286
- });
7287
- } else {
7288
- await this.setBlockAt(block.position, block);
7354
+ async placeMultiBlock(multiblock, useBatches = true, executor) {
7355
+ const keys = /* @__PURE__ */ new Map();
7356
+ for (const block of multiblock.blocks) {
7357
+ if (Chunk.isAir(block)) continue;
7358
+ const key = AssetKey.parse("blockstates", block.type);
7359
+ keys.set(key.serialize(), key);
7360
+ }
7361
+ await BlockStates.getAll(keys.values());
7362
+ if (!useBatches) {
7363
+ for (const block of multiblock.blocks) await this.setBlockAt(block.position, block);
7364
+ return;
7365
+ }
7366
+ const positions = /* @__PURE__ */ new Map();
7367
+ for (const block of multiblock.blocks) {
7368
+ const key = block.position.join(",");
7369
+ const group = positions.get(key);
7370
+ if (group) group.push(block);
7371
+ else positions.set(key, [block]);
7372
+ }
7373
+ const groups = [...positions.values()];
7374
+ const queue = executor ?? new BatchedExecutor();
7375
+ try {
7376
+ for (let i = 0; i < groups.length; i += queue.batch) {
7377
+ const results = await Promise.allSettled(groups.slice(i, i + queue.batch).map(
7378
+ (blocks) => queue.submit(async () => {
7379
+ for (const block of blocks) await this.setBlockAt(block.position, block);
7380
+ })
7381
+ ));
7382
+ const failure = results.find((result) => result.status === "rejected");
7383
+ if (failure?.status === "rejected") throw failure.reason;
7289
7384
  }
7290
- };
7291
- for (let block of multiblock.blocks) {
7292
- await place(block);
7385
+ } finally {
7386
+ if (!executor) queue.stop();
7293
7387
  }
7294
7388
  }
7295
7389
  async clear() {
@@ -7338,10 +7432,10 @@ var MineRenderWorld = class {
7338
7432
  };
7339
7433
 
7340
7434
  // src/world/Chunk.ts
7341
- var p11 = prefix("Chunk");
7342
7435
  var Chunk = class _Chunk {
7343
7436
  constructor(scene, x, y, z) {
7344
- this._blocks = [];
7437
+ this.data = new ChunkData();
7438
+ this.renderedBlocks = /* @__PURE__ */ new Map();
7345
7439
  this.scene = scene;
7346
7440
  this.x = x;
7347
7441
  this.y = y;
@@ -7356,7 +7450,7 @@ var Chunk = class _Chunk {
7356
7450
  }
7357
7451
  const pos = this.worldPosToChunkPos(posOrX);
7358
7452
  const index = _Chunk.chunkPosToBlockIndex(pos);
7359
- return this._blocks[index];
7453
+ return this.renderedBlocks.get(index);
7360
7454
  }
7361
7455
  async setBlockAt(posOrX, yOrBlock, z, block) {
7362
7456
  if (typeof posOrX == "number") {
@@ -7378,44 +7472,47 @@ var Chunk = class _Chunk {
7378
7472
  worldPos = this.chunkPosToWorldPos(pos);
7379
7473
  }
7380
7474
  const index = _Chunk.chunkPosToBlockIndex(pos);
7381
- const current = this._blocks[index];
7382
- if (typeof current !== "undefined") {
7383
- console.debug(p11, "deleting existing block at", pos, worldPos, index);
7384
- current.object.removeFromScene();
7385
- delete this._blocks[index];
7386
- }
7387
- if (!block || _Chunk.isAir(block)) return void 0;
7388
- const blockState = await BlockStates.get(AssetKey.parse("blockstates", block.type));
7389
- if (blockState) {
7390
- const blockObject = await this.scene.addBlock(blockState, {
7475
+ this.data.set(index, block);
7476
+ const readBlock = this.data.snapshot(index);
7477
+ this.renderedBlocks.get(index)?.object.removeFromScene();
7478
+ this.renderedBlocks.delete(index);
7479
+ if (!readBlock) return void 0;
7480
+ const stored = readBlock();
7481
+ let object;
7482
+ try {
7483
+ const blockState = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
7484
+ if (!blockState) {
7485
+ this.data.set(index, void 0);
7486
+ return void 0;
7487
+ }
7488
+ object = await this.scene.addBlock(blockState, {
7391
7489
  mergeMeshes: true,
7392
7490
  instanceMeshes: true,
7393
7491
  maxInstanceCount: 2e3
7394
7492
  });
7395
- if (block.properties) {
7396
- await blockObject.setState(block.properties);
7397
- }
7398
- const scenePos = MineRenderWorld.worldToScenePosition(worldPos);
7399
- blockObject.setPosition(scenePos);
7400
- this._blocks[index] = {
7401
- block,
7402
- object: blockObject
7403
- };
7404
- return this._blocks[index];
7493
+ if (stored.properties) await object.setState(stored.properties);
7494
+ object.setPosition(MineRenderWorld.worldToScenePosition(worldPos));
7495
+ const info = { get block() {
7496
+ return readBlock();
7497
+ }, object };
7498
+ this.renderedBlocks.set(index, info);
7499
+ return info;
7500
+ } catch (error) {
7501
+ this.data.set(index, void 0);
7502
+ object?.removeFromScene();
7503
+ throw error;
7405
7504
  }
7406
- return void 0;
7407
7505
  }
7408
7506
  async clear() {
7409
- this._blocks.forEach((block) => block.object.removeFromScene());
7410
- this._blocks.length = 0;
7507
+ for (const info of this.renderedBlocks.values()) info.object.removeFromScene();
7508
+ this.renderedBlocks.clear();
7509
+ this.data.clear();
7411
7510
  }
7412
7511
  async dispose() {
7413
7512
  await this.clear();
7414
7513
  }
7415
7514
  static isAir(block) {
7416
- if (typeof block === "undefined" || typeof block.type === "undefined") return true;
7417
- const key = AssetKey.parse("blockstates", block.type);
7418
- return key.namespace === "minecraft" && !key.type && ["air", "cave_air", "void_air"].includes(key.path);
7515
+ return ChunkData.isAir(block);
7419
7516
  }
7420
7517
  static chunkPosToBlockIndex(pos) {
7421
7518
  if ([pos.x, pos.y, pos.z].some((value) => !Number.isInteger(value) || value < 0 || value >= 16)) {
@@ -7460,6 +7557,7 @@ export {
7460
7557
  Caching,
7461
7558
  CanvasImage,
7462
7559
  Chunk,
7560
+ ChunkData,
7463
7561
  CubeFace,
7464
7562
  CubeFaceIndex,
7465
7563
  DEFAULT_ELEMENTS,