minerender 2.1.0-alpha.34 → 2.1.0-alpha.36

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.
@@ -585,13 +585,11 @@ declare class BatchedExecutor {
585
585
  readonly interval: number;
586
586
  readonly batch: number;
587
587
  private readonly queue;
588
- private readonly task;
589
588
  constructor(interval?: number, batch?: number);
590
- submit(task: Task): void;
591
- private run;
592
- private runNext;
589
+ submit<T>(task: Task<T>): Promise<T>;
590
+ stop(): void;
593
591
  }
594
- type Task = () => any | Promise<any>;
592
+ type Task<T = unknown> = () => T | Promise<T>;
595
593
 
596
594
  declare function prefix(prefix?: string): string;
597
595
 
@@ -1692,7 +1690,6 @@ declare class Chunk {
1692
1690
  readonly y: number;
1693
1691
  readonly z: number;
1694
1692
  private readonly _blocks;
1695
- private readonly _anchor;
1696
1693
  constructor(scene: MineRenderScene, x: number, y: number, z: number);
1697
1694
  getBlockAt(x: number, y: number, z: number): Maybe<BlockInfo>;
1698
1695
  getBlockAt(pos: Vector3): Maybe<BlockInfo>;
@@ -1704,11 +1701,12 @@ declare class Chunk {
1704
1701
  setBlockAt(pos: Vector3, block: Maybe<Block>): Promise<Maybe<BlockInfo>>;
1705
1702
  setBlockAt(pos: TripleArray, block: Maybe<Block>): Promise<Maybe<BlockInfo>>;
1706
1703
  /**
1707
- * Set block at a _chunk_ position (0-16)
1704
+ * Set a block at integer chunk-local coordinates from 0 to 15.
1708
1705
  */
1709
1706
  setBlockInChunkAt(pos: Vector3, block?: Block, worldPos?: Vector3): Promise<Maybe<BlockInfo>>;
1710
1707
  clear(): Promise<void>;
1711
1708
  dispose(): Promise<void>;
1709
+ static isAir(block: Maybe<Block>): boolean;
1712
1710
  static chunkPosToBlockIndex(pos: Vector3): number;
1713
1711
  worldPosToChunkPos(pos: Vector3): Vector3;
1714
1712
  chunkPosToWorldPos(pos: Vector3): Vector3;
@@ -1716,23 +1714,20 @@ declare class Chunk {
1716
1714
 
1717
1715
  declare class MineRenderWorld {
1718
1716
  readonly scene: MineRenderScene;
1719
- private _size;
1720
- private _blockSize;
1721
1717
  private readonly _chunks;
1722
1718
  constructor(scene: MineRenderScene);
1723
1719
  getBlockAt(x: number, y: number, z: number): Maybe<BlockInfo>;
1724
1720
  getBlockAt(pos: Vector3): Maybe<BlockInfo>;
1725
1721
  getBlockAt(pos: TripleArray): Maybe<BlockInfo>;
1726
- setBlockAt(x: number, y: number, z: number, block: Block): Promise<Maybe<BlockInfo>>;
1727
- setBlockAt(pos: Vector3, block: Block): Promise<Maybe<BlockInfo>>;
1728
- setBlockAt(pos: TripleArray, block: Block): Promise<Maybe<BlockInfo>>;
1722
+ setBlockAt(x: number, y: number, z: number, block: Maybe<Block>): Promise<Maybe<BlockInfo>>;
1723
+ setBlockAt(pos: Vector3, block: Maybe<Block>): Promise<Maybe<BlockInfo>>;
1724
+ setBlockAt(pos: TripleArray, block: Maybe<Block>): Promise<Maybe<BlockInfo>>;
1729
1725
  placeMultiBlock(multiblock: MultiBlockStructure, useBatches?: boolean, executor?: BatchedExecutor): Promise<void>;
1730
1726
  clear(): Promise<void>;
1731
1727
  private getOrCreateChunkAt;
1732
1728
  getChunkAt(pos: Vector3): Maybe<Chunk>;
1733
1729
  static worldToScenePosition(pos: Vector3): Vector3;
1734
1730
  static sceneToWorldPosition(pos: Vector3): Vector3;
1735
- worldPosToChunkIndex(pos: Vector3): number;
1736
1731
  worldPosToChunkKey(pos: Vector3): string;
1737
1732
  validatePosBounds(pos: Vector3): void;
1738
1733
  }
@@ -585,13 +585,11 @@ declare class BatchedExecutor {
585
585
  readonly interval: number;
586
586
  readonly batch: number;
587
587
  private readonly queue;
588
- private readonly task;
589
588
  constructor(interval?: number, batch?: number);
590
- submit(task: Task): void;
591
- private run;
592
- private runNext;
589
+ submit<T>(task: Task<T>): Promise<T>;
590
+ stop(): void;
593
591
  }
594
- type Task = () => any | Promise<any>;
592
+ type Task<T = unknown> = () => T | Promise<T>;
595
593
 
596
594
  declare function prefix(prefix?: string): string;
597
595
 
@@ -1692,7 +1690,6 @@ declare class Chunk {
1692
1690
  readonly y: number;
1693
1691
  readonly z: number;
1694
1692
  private readonly _blocks;
1695
- private readonly _anchor;
1696
1693
  constructor(scene: MineRenderScene, x: number, y: number, z: number);
1697
1694
  getBlockAt(x: number, y: number, z: number): Maybe<BlockInfo>;
1698
1695
  getBlockAt(pos: Vector3): Maybe<BlockInfo>;
@@ -1704,11 +1701,12 @@ declare class Chunk {
1704
1701
  setBlockAt(pos: Vector3, block: Maybe<Block>): Promise<Maybe<BlockInfo>>;
1705
1702
  setBlockAt(pos: TripleArray, block: Maybe<Block>): Promise<Maybe<BlockInfo>>;
1706
1703
  /**
1707
- * Set block at a _chunk_ position (0-16)
1704
+ * Set a block at integer chunk-local coordinates from 0 to 15.
1708
1705
  */
1709
1706
  setBlockInChunkAt(pos: Vector3, block?: Block, worldPos?: Vector3): Promise<Maybe<BlockInfo>>;
1710
1707
  clear(): Promise<void>;
1711
1708
  dispose(): Promise<void>;
1709
+ static isAir(block: Maybe<Block>): boolean;
1712
1710
  static chunkPosToBlockIndex(pos: Vector3): number;
1713
1711
  worldPosToChunkPos(pos: Vector3): Vector3;
1714
1712
  chunkPosToWorldPos(pos: Vector3): Vector3;
@@ -1716,23 +1714,20 @@ declare class Chunk {
1716
1714
 
1717
1715
  declare class MineRenderWorld {
1718
1716
  readonly scene: MineRenderScene;
1719
- private _size;
1720
- private _blockSize;
1721
1717
  private readonly _chunks;
1722
1718
  constructor(scene: MineRenderScene);
1723
1719
  getBlockAt(x: number, y: number, z: number): Maybe<BlockInfo>;
1724
1720
  getBlockAt(pos: Vector3): Maybe<BlockInfo>;
1725
1721
  getBlockAt(pos: TripleArray): Maybe<BlockInfo>;
1726
- setBlockAt(x: number, y: number, z: number, block: Block): Promise<Maybe<BlockInfo>>;
1727
- setBlockAt(pos: Vector3, block: Block): Promise<Maybe<BlockInfo>>;
1728
- setBlockAt(pos: TripleArray, block: Block): Promise<Maybe<BlockInfo>>;
1722
+ setBlockAt(x: number, y: number, z: number, block: Maybe<Block>): Promise<Maybe<BlockInfo>>;
1723
+ setBlockAt(pos: Vector3, block: Maybe<Block>): Promise<Maybe<BlockInfo>>;
1724
+ setBlockAt(pos: TripleArray, block: Maybe<Block>): Promise<Maybe<BlockInfo>>;
1729
1725
  placeMultiBlock(multiblock: MultiBlockStructure, useBatches?: boolean, executor?: BatchedExecutor): Promise<void>;
1730
1726
  clear(): Promise<void>;
1731
1727
  private getOrCreateChunkAt;
1732
1728
  getChunkAt(pos: Vector3): Maybe<Chunk>;
1733
1729
  static worldToScenePosition(pos: Vector3): Vector3;
1734
1730
  static sceneToWorldPosition(pos: Vector3): Vector3;
1735
- worldPosToChunkIndex(pos: Vector3): number;
1736
1731
  worldPosToChunkKey(pos: Vector3): string;
1737
1732
  validatePosBounds(pos: Vector3): void;
1738
1733
  }
@@ -1661,24 +1661,20 @@ var AssetLoadError = class extends Error {
1661
1661
  };
1662
1662
 
1663
1663
  // src/util/BatchedExecutor.ts
1664
+ var import_jobqu2 = require("jobqu");
1664
1665
  var BatchedExecutor = class {
1665
1666
  constructor(interval = 1, batch = 30) {
1667
+ if (!Number.isFinite(interval) || interval < 0) throw new RangeError("Batch interval must be finite and nonnegative");
1668
+ if (!Number.isInteger(batch) || batch < 1) throw new RangeError("Batch size must be a positive integer");
1666
1669
  this.interval = interval;
1667
1670
  this.batch = batch;
1668
- this.queue = [];
1669
- this.task = setInterval(() => this.run());
1671
+ this.queue = new import_jobqu2.JobQueue(async (task) => task(), { interval, maxPerRun: batch, maxActive: batch });
1670
1672
  }
1671
1673
  submit(task) {
1672
- this.queue.push(task);
1674
+ return this.queue.add(() => task());
1673
1675
  }
1674
- run() {
1675
- for (let i = 0; i < this.batch; i++) {
1676
- this.runNext();
1677
- }
1678
- }
1679
- runNext() {
1680
- let next = this.queue.shift();
1681
- if (next) next();
1676
+ stop() {
1677
+ this.queue.end();
1682
1678
  }
1683
1679
  };
1684
1680
 
@@ -7388,9 +7384,6 @@ var import_three25 = require("three");
7388
7384
  var import_three24 = require("three");
7389
7385
  var MineRenderWorld = class {
7390
7386
  constructor(scene) {
7391
- this._size = 4;
7392
- //TODO: expandable
7393
- this._blockSize = this._size * 16;
7394
7387
  this._chunks = /* @__PURE__ */ new Map();
7395
7388
  this.scene = scene;
7396
7389
  }
@@ -7401,12 +7394,8 @@ var MineRenderWorld = class {
7401
7394
  if (isTripleArray(posOrX)) {
7402
7395
  return this.getBlockAt(new import_three24.Vector3(posOrX[0], posOrX[1], posOrX[2]));
7403
7396
  }
7404
- const chunk = this.getChunkAt(posOrX);
7405
- if (typeof chunk === "undefined") {
7406
- return void 0;
7407
- }
7408
7397
  this.validatePosBounds(posOrX);
7409
- return chunk.getBlockAt(posOrX);
7398
+ return this.getChunkAt(posOrX)?.getBlockAt(posOrX);
7410
7399
  }
7411
7400
  async setBlockAt(posOrX, yOrBlock, z, block) {
7412
7401
  if (typeof posOrX == "number") {
@@ -7416,32 +7405,48 @@ var MineRenderWorld = class {
7416
7405
  return this.setBlockAt(new import_three24.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
7417
7406
  }
7418
7407
  this.validatePosBounds(posOrX);
7419
- const chunk = this.getOrCreateChunkAt(posOrX);
7420
- return chunk.setBlockAt(posOrX, yOrBlock);
7421
- }
7422
- async placeMultiBlock(multiblock, useBatches = true, executor = new BatchedExecutor()) {
7423
- const keys = new Set(multiblock.blocks.map((block) => AssetKey.parse("blockstates", block.type)));
7424
- await BlockStates.getAll(keys);
7425
- const place = async (block) => {
7426
- if (useBatches && typeof executor !== "undefined") {
7427
- await new Promise((resolve, reject) => {
7428
- executor.submit(() => {
7429
- this.setBlockAt(block.position, block).then(resolve).catch(reject);
7430
- });
7431
- });
7432
- } else {
7433
- await this.setBlockAt(block.position, block);
7408
+ const value = yOrBlock;
7409
+ const chunk = Chunk.isAir(value) ? this.getChunkAt(posOrX) : this.getOrCreateChunkAt(posOrX);
7410
+ return chunk?.setBlockAt(posOrX, value);
7411
+ }
7412
+ async placeMultiBlock(multiblock, useBatches = true, executor) {
7413
+ const keys = /* @__PURE__ */ new Map();
7414
+ for (const block of multiblock.blocks) {
7415
+ if (Chunk.isAir(block)) continue;
7416
+ const key = AssetKey.parse("blockstates", block.type);
7417
+ keys.set(key.serialize(), key);
7418
+ }
7419
+ await BlockStates.getAll(keys.values());
7420
+ if (!useBatches) {
7421
+ for (const block of multiblock.blocks) await this.setBlockAt(block.position, block);
7422
+ return;
7423
+ }
7424
+ const positions = /* @__PURE__ */ new Map();
7425
+ for (const block of multiblock.blocks) {
7426
+ const key = block.position.join(",");
7427
+ const group = positions.get(key);
7428
+ if (group) group.push(block);
7429
+ else positions.set(key, [block]);
7430
+ }
7431
+ const groups = [...positions.values()];
7432
+ const queue = executor ?? new BatchedExecutor();
7433
+ try {
7434
+ for (let i = 0; i < groups.length; i += queue.batch) {
7435
+ const results = await Promise.allSettled(groups.slice(i, i + queue.batch).map(
7436
+ (blocks) => queue.submit(async () => {
7437
+ for (const block of blocks) await this.setBlockAt(block.position, block);
7438
+ })
7439
+ ));
7440
+ const failure = results.find((result) => result.status === "rejected");
7441
+ if (failure?.status === "rejected") throw failure.reason;
7434
7442
  }
7435
- };
7436
- for (let block of multiblock.blocks) {
7437
- await place(block);
7443
+ } finally {
7444
+ if (!executor) queue.stop();
7438
7445
  }
7439
7446
  }
7440
7447
  async clear() {
7441
- console.log("CHUNKS", this._chunks);
7442
- for (let chunk of this._chunks.values()) {
7443
- console.log("dispose chunk", chunk);
7444
- await chunk?.dispose();
7448
+ for (const chunk of this._chunks.values()) {
7449
+ await chunk.dispose();
7445
7450
  }
7446
7451
  this._chunks.clear();
7447
7452
  }
@@ -7455,8 +7460,7 @@ var MineRenderWorld = class {
7455
7460
  return chunk;
7456
7461
  }
7457
7462
  getChunkAt(pos) {
7458
- const index = this.worldPosToChunkIndex(pos);
7459
- return this._chunks[index];
7463
+ return this._chunks.get(this.worldPosToChunkKey(pos));
7460
7464
  }
7461
7465
  static worldToScenePosition(pos) {
7462
7466
  return new import_three24.Vector3(
@@ -7472,12 +7476,6 @@ var MineRenderWorld = class {
7472
7476
  pos.z / 16
7473
7477
  );
7474
7478
  }
7475
- worldPosToChunkIndex(pos) {
7476
- const chunkX = Math.floor(pos.x / 16);
7477
- const chunkY = Math.floor(pos.y / 16);
7478
- const chunkZ = Math.floor(pos.z / 16);
7479
- return chunkZ * this._size * this._size + chunkY * this._size + chunkX;
7480
- }
7481
7479
  worldPosToChunkKey(pos) {
7482
7480
  const chunkX = Math.floor(pos.x / 16);
7483
7481
  const chunkY = Math.floor(pos.y / 16);
@@ -7485,12 +7483,9 @@ var MineRenderWorld = class {
7485
7483
  return `${chunkX}_${chunkY}_${chunkZ}`;
7486
7484
  }
7487
7485
  validatePosBounds(pos) {
7488
- if (pos.x < 0) throw new Error("x<0");
7489
- if (pos.y < 0) throw new Error("y<0");
7490
- if (pos.z < 0) throw new Error("z<0");
7491
- if (pos.x > this._blockSize) throw new Error("x>" + this._blockSize);
7492
- if (pos.y > this._blockSize) throw new Error("y>" + this._blockSize);
7493
- if (pos.z > this._blockSize) throw new Error("z>" + this._blockSize);
7486
+ if (![pos.x, pos.y, pos.z].every(Number.isInteger)) {
7487
+ throw new RangeError("Block coordinates must be integers");
7488
+ }
7494
7489
  }
7495
7490
  };
7496
7491
 
@@ -7503,16 +7498,6 @@ var Chunk = class _Chunk {
7503
7498
  this.x = x;
7504
7499
  this.y = y;
7505
7500
  this.z = z;
7506
- let anchor = new import_three25.Object3D();
7507
- this._anchor = anchor;
7508
- scene.add(anchor);
7509
- anchor.position.set(this.x * 256 + 128 - 8, this.y * 256 + 128 - 8, this.z * 256 + 128 - 8);
7510
- addBox3WireframeToObject(
7511
- new import_three25.Box3(new import_three25.Vector3(0, 0, 0), new import_three25.Vector3(256, 256, 256)),
7512
- anchor,
7513
- 16776960,
7514
- 4
7515
- );
7516
7501
  }
7517
7502
  getBlockAt(posOrX, y, z) {
7518
7503
  if (typeof posOrX == "number") {
@@ -7538,7 +7523,7 @@ var Chunk = class _Chunk {
7538
7523
  return this.setBlockInChunkAt(pos, block, worldPos);
7539
7524
  }
7540
7525
  /**
7541
- * Set block at a _chunk_ position (0-16)
7526
+ * Set a block at integer chunk-local coordinates from 0 to 15.
7542
7527
  */
7543
7528
  async setBlockInChunkAt(pos, block, worldPos) {
7544
7529
  if (typeof worldPos === "undefined") {
@@ -7551,15 +7536,13 @@ var Chunk = class _Chunk {
7551
7536
  current.object.removeFromScene();
7552
7537
  delete this._blocks[index];
7553
7538
  }
7554
- if (typeof block === "undefined" || typeof block.type === "undefined" || block.type === "air") return void 0;
7539
+ if (!block || _Chunk.isAir(block)) return void 0;
7555
7540
  const blockState = await BlockStates.get(AssetKey.parse("blockstates", block.type));
7556
7541
  if (blockState) {
7557
7542
  const blockObject = await this.scene.addBlock(blockState, {
7558
7543
  mergeMeshes: true,
7559
7544
  instanceMeshes: true,
7560
- wireframe: true,
7561
7545
  maxInstanceCount: 2e3
7562
- //TODO: setting this to high kills FPS; increase on demand somehow?
7563
7546
  });
7564
7547
  if (block.properties) {
7565
7548
  await blockObject.setState(block.properties);
@@ -7575,23 +7558,21 @@ var Chunk = class _Chunk {
7575
7558
  return void 0;
7576
7559
  }
7577
7560
  async clear() {
7578
- let promises = [];
7579
- let pos = new import_three25.Vector3();
7580
- for (let x = 0; x < 16; x++) {
7581
- for (let z = 0; z < 16; z++) {
7582
- for (let y = 0; y < 16; y++) {
7583
- promises.push(this.setBlockInChunkAt(pos.set(x, y, z), void 0));
7584
- }
7585
- }
7586
- }
7587
- return Promise.all(promises).then((ignored) => {
7588
- });
7561
+ this._blocks.forEach((block) => block.object.removeFromScene());
7562
+ this._blocks.length = 0;
7589
7563
  }
7590
7564
  async dispose() {
7591
7565
  await this.clear();
7592
- this.scene.remove(this._anchor);
7566
+ }
7567
+ static isAir(block) {
7568
+ if (typeof block === "undefined" || typeof block.type === "undefined") return true;
7569
+ const key = AssetKey.parse("blockstates", block.type);
7570
+ return key.namespace === "minecraft" && !key.type && ["air", "cave_air", "void_air"].includes(key.path);
7593
7571
  }
7594
7572
  static chunkPosToBlockIndex(pos) {
7573
+ if ([pos.x, pos.y, pos.z].some((value) => !Number.isInteger(value) || value < 0 || value >= 16)) {
7574
+ throw new RangeError("Block coordinates must be integers from 0 to 15 within a chunk");
7575
+ }
7595
7576
  return pos.y * 16 * 16 + pos.z * 16 + pos.x;
7596
7577
  }
7597
7578
  worldPosToChunkPos(pos) {