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.
@@ -584,13 +584,11 @@ declare class BatchedExecutor {
584
584
  readonly interval: number;
585
585
  readonly batch: number;
586
586
  private readonly queue;
587
- private readonly task;
588
587
  constructor(interval?: number, batch?: number);
589
- submit(task: Task): void;
590
- private run;
591
- private runNext;
588
+ submit<T>(task: Task<T>): Promise<T>;
589
+ stop(): void;
592
590
  }
593
- type Task = () => any | Promise<any>;
591
+ type Task<T = unknown> = () => T | Promise<T>;
594
592
 
595
593
  declare function prefix(prefix?: string): string;
596
594
 
@@ -1691,7 +1689,6 @@ declare class Chunk {
1691
1689
  readonly y: number;
1692
1690
  readonly z: number;
1693
1691
  private readonly _blocks;
1694
- private readonly _anchor;
1695
1692
  constructor(scene: MineRenderScene, x: number, y: number, z: number);
1696
1693
  getBlockAt(x: number, y: number, z: number): Maybe<BlockInfo>;
1697
1694
  getBlockAt(pos: Vector3): Maybe<BlockInfo>;
@@ -1703,11 +1700,12 @@ declare class Chunk {
1703
1700
  setBlockAt(pos: Vector3, block: Maybe<Block>): Promise<Maybe<BlockInfo>>;
1704
1701
  setBlockAt(pos: TripleArray, block: Maybe<Block>): Promise<Maybe<BlockInfo>>;
1705
1702
  /**
1706
- * Set block at a _chunk_ position (0-16)
1703
+ * Set a block at integer chunk-local coordinates from 0 to 15.
1707
1704
  */
1708
1705
  setBlockInChunkAt(pos: Vector3, block?: Block, worldPos?: Vector3): Promise<Maybe<BlockInfo>>;
1709
1706
  clear(): Promise<void>;
1710
1707
  dispose(): Promise<void>;
1708
+ static isAir(block: Maybe<Block>): boolean;
1711
1709
  static chunkPosToBlockIndex(pos: Vector3): number;
1712
1710
  worldPosToChunkPos(pos: Vector3): Vector3;
1713
1711
  chunkPosToWorldPos(pos: Vector3): Vector3;
@@ -1715,23 +1713,20 @@ declare class Chunk {
1715
1713
 
1716
1714
  declare class MineRenderWorld {
1717
1715
  readonly scene: MineRenderScene;
1718
- private _size;
1719
- private _blockSize;
1720
1716
  private readonly _chunks;
1721
1717
  constructor(scene: MineRenderScene);
1722
1718
  getBlockAt(x: number, y: number, z: number): Maybe<BlockInfo>;
1723
1719
  getBlockAt(pos: Vector3): Maybe<BlockInfo>;
1724
1720
  getBlockAt(pos: TripleArray): Maybe<BlockInfo>;
1725
- setBlockAt(x: number, y: number, z: number, block: Block): Promise<Maybe<BlockInfo>>;
1726
- setBlockAt(pos: Vector3, block: Block): Promise<Maybe<BlockInfo>>;
1727
- setBlockAt(pos: TripleArray, block: Block): Promise<Maybe<BlockInfo>>;
1721
+ setBlockAt(x: number, y: number, z: number, block: Maybe<Block>): Promise<Maybe<BlockInfo>>;
1722
+ setBlockAt(pos: Vector3, block: Maybe<Block>): Promise<Maybe<BlockInfo>>;
1723
+ setBlockAt(pos: TripleArray, block: Maybe<Block>): Promise<Maybe<BlockInfo>>;
1728
1724
  placeMultiBlock(multiblock: MultiBlockStructure, useBatches?: boolean, executor?: BatchedExecutor): Promise<void>;
1729
1725
  clear(): Promise<void>;
1730
1726
  private getOrCreateChunkAt;
1731
1727
  getChunkAt(pos: Vector3): Maybe<Chunk>;
1732
1728
  static worldToScenePosition(pos: Vector3): Vector3;
1733
1729
  static sceneToWorldPosition(pos: Vector3): Vector3;
1734
- worldPosToChunkIndex(pos: Vector3): number;
1735
1730
  worldPosToChunkKey(pos: Vector3): string;
1736
1731
  validatePosBounds(pos: Vector3): void;
1737
1732
  }
@@ -584,13 +584,11 @@ declare class BatchedExecutor {
584
584
  readonly interval: number;
585
585
  readonly batch: number;
586
586
  private readonly queue;
587
- private readonly task;
588
587
  constructor(interval?: number, batch?: number);
589
- submit(task: Task): void;
590
- private run;
591
- private runNext;
588
+ submit<T>(task: Task<T>): Promise<T>;
589
+ stop(): void;
592
590
  }
593
- type Task = () => any | Promise<any>;
591
+ type Task<T = unknown> = () => T | Promise<T>;
594
592
 
595
593
  declare function prefix(prefix?: string): string;
596
594
 
@@ -1691,7 +1689,6 @@ declare class Chunk {
1691
1689
  readonly y: number;
1692
1690
  readonly z: number;
1693
1691
  private readonly _blocks;
1694
- private readonly _anchor;
1695
1692
  constructor(scene: MineRenderScene, x: number, y: number, z: number);
1696
1693
  getBlockAt(x: number, y: number, z: number): Maybe<BlockInfo>;
1697
1694
  getBlockAt(pos: Vector3): Maybe<BlockInfo>;
@@ -1703,11 +1700,12 @@ declare class Chunk {
1703
1700
  setBlockAt(pos: Vector3, block: Maybe<Block>): Promise<Maybe<BlockInfo>>;
1704
1701
  setBlockAt(pos: TripleArray, block: Maybe<Block>): Promise<Maybe<BlockInfo>>;
1705
1702
  /**
1706
- * Set block at a _chunk_ position (0-16)
1703
+ * Set a block at integer chunk-local coordinates from 0 to 15.
1707
1704
  */
1708
1705
  setBlockInChunkAt(pos: Vector3, block?: Block, worldPos?: Vector3): Promise<Maybe<BlockInfo>>;
1709
1706
  clear(): Promise<void>;
1710
1707
  dispose(): Promise<void>;
1708
+ static isAir(block: Maybe<Block>): boolean;
1711
1709
  static chunkPosToBlockIndex(pos: Vector3): number;
1712
1710
  worldPosToChunkPos(pos: Vector3): Vector3;
1713
1711
  chunkPosToWorldPos(pos: Vector3): Vector3;
@@ -1715,23 +1713,20 @@ declare class Chunk {
1715
1713
 
1716
1714
  declare class MineRenderWorld {
1717
1715
  readonly scene: MineRenderScene;
1718
- private _size;
1719
- private _blockSize;
1720
1716
  private readonly _chunks;
1721
1717
  constructor(scene: MineRenderScene);
1722
1718
  getBlockAt(x: number, y: number, z: number): Maybe<BlockInfo>;
1723
1719
  getBlockAt(pos: Vector3): Maybe<BlockInfo>;
1724
1720
  getBlockAt(pos: TripleArray): Maybe<BlockInfo>;
1725
- setBlockAt(x: number, y: number, z: number, block: Block): Promise<Maybe<BlockInfo>>;
1726
- setBlockAt(pos: Vector3, block: Block): Promise<Maybe<BlockInfo>>;
1727
- setBlockAt(pos: TripleArray, block: Block): Promise<Maybe<BlockInfo>>;
1721
+ setBlockAt(x: number, y: number, z: number, block: Maybe<Block>): Promise<Maybe<BlockInfo>>;
1722
+ setBlockAt(pos: Vector3, block: Maybe<Block>): Promise<Maybe<BlockInfo>>;
1723
+ setBlockAt(pos: TripleArray, block: Maybe<Block>): Promise<Maybe<BlockInfo>>;
1728
1724
  placeMultiBlock(multiblock: MultiBlockStructure, useBatches?: boolean, executor?: BatchedExecutor): Promise<void>;
1729
1725
  clear(): Promise<void>;
1730
1726
  private getOrCreateChunkAt;
1731
1727
  getChunkAt(pos: Vector3): Maybe<Chunk>;
1732
1728
  static worldToScenePosition(pos: Vector3): Vector3;
1733
1729
  static sceneToWorldPosition(pos: Vector3): Vector3;
1734
- worldPosToChunkIndex(pos: Vector3): number;
1735
1730
  worldPosToChunkKey(pos: Vector3): string;
1736
1731
  validatePosBounds(pos: Vector3): void;
1737
1732
  }
@@ -1655,24 +1655,20 @@ var AssetLoadError = class extends Error {
1655
1655
  };
1656
1656
 
1657
1657
  // src/util/BatchedExecutor.ts
1658
+ var import_jobqu2 = require("jobqu");
1658
1659
  var BatchedExecutor = class {
1659
1660
  constructor(interval = 1, batch = 30) {
1661
+ if (!Number.isFinite(interval) || interval < 0) throw new RangeError("Batch interval must be finite and nonnegative");
1662
+ if (!Number.isInteger(batch) || batch < 1) throw new RangeError("Batch size must be a positive integer");
1660
1663
  this.interval = interval;
1661
1664
  this.batch = batch;
1662
- this.queue = [];
1663
- this.task = setInterval(() => this.run());
1665
+ this.queue = new import_jobqu2.JobQueue(async (task) => task(), { interval, maxPerRun: batch, maxActive: batch });
1664
1666
  }
1665
1667
  submit(task) {
1666
- this.queue.push(task);
1668
+ return this.queue.add(() => task());
1667
1669
  }
1668
- run() {
1669
- for (let i = 0; i < this.batch; i++) {
1670
- this.runNext();
1671
- }
1672
- }
1673
- runNext() {
1674
- let next = this.queue.shift();
1675
- if (next) next();
1670
+ stop() {
1671
+ this.queue.end();
1676
1672
  }
1677
1673
  };
1678
1674
 
@@ -7339,9 +7335,6 @@ var import_three25 = require("three");
7339
7335
  var import_three24 = require("three");
7340
7336
  var MineRenderWorld = class {
7341
7337
  constructor(scene) {
7342
- this._size = 4;
7343
- //TODO: expandable
7344
- this._blockSize = this._size * 16;
7345
7338
  this._chunks = /* @__PURE__ */ new Map();
7346
7339
  this.scene = scene;
7347
7340
  }
@@ -7352,12 +7345,8 @@ var MineRenderWorld = class {
7352
7345
  if (isTripleArray(posOrX)) {
7353
7346
  return this.getBlockAt(new import_three24.Vector3(posOrX[0], posOrX[1], posOrX[2]));
7354
7347
  }
7355
- const chunk = this.getChunkAt(posOrX);
7356
- if (typeof chunk === "undefined") {
7357
- return void 0;
7358
- }
7359
7348
  this.validatePosBounds(posOrX);
7360
- return chunk.getBlockAt(posOrX);
7349
+ return this.getChunkAt(posOrX)?.getBlockAt(posOrX);
7361
7350
  }
7362
7351
  async setBlockAt(posOrX, yOrBlock, z, block) {
7363
7352
  if (typeof posOrX == "number") {
@@ -7367,32 +7356,48 @@ var MineRenderWorld = class {
7367
7356
  return this.setBlockAt(new import_three24.Vector3(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
7368
7357
  }
7369
7358
  this.validatePosBounds(posOrX);
7370
- const chunk = this.getOrCreateChunkAt(posOrX);
7371
- return chunk.setBlockAt(posOrX, yOrBlock);
7372
- }
7373
- async placeMultiBlock(multiblock, useBatches = true, executor = new BatchedExecutor()) {
7374
- const keys = new Set(multiblock.blocks.map((block) => AssetKey.parse("blockstates", block.type)));
7375
- await BlockStates.getAll(keys);
7376
- const place = async (block) => {
7377
- if (useBatches && typeof executor !== "undefined") {
7378
- await new Promise((resolve, reject) => {
7379
- executor.submit(() => {
7380
- this.setBlockAt(block.position, block).then(resolve).catch(reject);
7381
- });
7382
- });
7383
- } else {
7384
- await this.setBlockAt(block.position, block);
7359
+ const value = yOrBlock;
7360
+ const chunk = Chunk.isAir(value) ? this.getChunkAt(posOrX) : this.getOrCreateChunkAt(posOrX);
7361
+ return chunk?.setBlockAt(posOrX, value);
7362
+ }
7363
+ async placeMultiBlock(multiblock, useBatches = true, executor) {
7364
+ const keys = /* @__PURE__ */ new Map();
7365
+ for (const block of multiblock.blocks) {
7366
+ if (Chunk.isAir(block)) continue;
7367
+ const key = AssetKey.parse("blockstates", block.type);
7368
+ keys.set(key.serialize(), key);
7369
+ }
7370
+ await BlockStates.getAll(keys.values());
7371
+ if (!useBatches) {
7372
+ for (const block of multiblock.blocks) await this.setBlockAt(block.position, block);
7373
+ return;
7374
+ }
7375
+ const positions = /* @__PURE__ */ new Map();
7376
+ for (const block of multiblock.blocks) {
7377
+ const key = block.position.join(",");
7378
+ const group = positions.get(key);
7379
+ if (group) group.push(block);
7380
+ else positions.set(key, [block]);
7381
+ }
7382
+ const groups = [...positions.values()];
7383
+ const queue = executor ?? new BatchedExecutor();
7384
+ try {
7385
+ for (let i = 0; i < groups.length; i += queue.batch) {
7386
+ const results = await Promise.allSettled(groups.slice(i, i + queue.batch).map(
7387
+ (blocks) => queue.submit(async () => {
7388
+ for (const block of blocks) await this.setBlockAt(block.position, block);
7389
+ })
7390
+ ));
7391
+ const failure = results.find((result) => result.status === "rejected");
7392
+ if (failure?.status === "rejected") throw failure.reason;
7385
7393
  }
7386
- };
7387
- for (let block of multiblock.blocks) {
7388
- await place(block);
7394
+ } finally {
7395
+ if (!executor) queue.stop();
7389
7396
  }
7390
7397
  }
7391
7398
  async clear() {
7392
- console.log("CHUNKS", this._chunks);
7393
- for (let chunk of this._chunks.values()) {
7394
- console.log("dispose chunk", chunk);
7395
- await chunk?.dispose();
7399
+ for (const chunk of this._chunks.values()) {
7400
+ await chunk.dispose();
7396
7401
  }
7397
7402
  this._chunks.clear();
7398
7403
  }
@@ -7406,8 +7411,7 @@ var MineRenderWorld = class {
7406
7411
  return chunk;
7407
7412
  }
7408
7413
  getChunkAt(pos) {
7409
- const index = this.worldPosToChunkIndex(pos);
7410
- return this._chunks[index];
7414
+ return this._chunks.get(this.worldPosToChunkKey(pos));
7411
7415
  }
7412
7416
  static worldToScenePosition(pos) {
7413
7417
  return new import_three24.Vector3(
@@ -7423,12 +7427,6 @@ var MineRenderWorld = class {
7423
7427
  pos.z / 16
7424
7428
  );
7425
7429
  }
7426
- worldPosToChunkIndex(pos) {
7427
- const chunkX = Math.floor(pos.x / 16);
7428
- const chunkY = Math.floor(pos.y / 16);
7429
- const chunkZ = Math.floor(pos.z / 16);
7430
- return chunkZ * this._size * this._size + chunkY * this._size + chunkX;
7431
- }
7432
7430
  worldPosToChunkKey(pos) {
7433
7431
  const chunkX = Math.floor(pos.x / 16);
7434
7432
  const chunkY = Math.floor(pos.y / 16);
@@ -7436,12 +7434,9 @@ var MineRenderWorld = class {
7436
7434
  return `${chunkX}_${chunkY}_${chunkZ}`;
7437
7435
  }
7438
7436
  validatePosBounds(pos) {
7439
- if (pos.x < 0) throw new Error("x<0");
7440
- if (pos.y < 0) throw new Error("y<0");
7441
- if (pos.z < 0) throw new Error("z<0");
7442
- if (pos.x > this._blockSize) throw new Error("x>" + this._blockSize);
7443
- if (pos.y > this._blockSize) throw new Error("y>" + this._blockSize);
7444
- if (pos.z > this._blockSize) throw new Error("z>" + this._blockSize);
7437
+ if (![pos.x, pos.y, pos.z].every(Number.isInteger)) {
7438
+ throw new RangeError("Block coordinates must be integers");
7439
+ }
7445
7440
  }
7446
7441
  };
7447
7442
 
@@ -7454,16 +7449,6 @@ var Chunk = class _Chunk {
7454
7449
  this.x = x;
7455
7450
  this.y = y;
7456
7451
  this.z = z;
7457
- let anchor = new import_three25.Object3D();
7458
- this._anchor = anchor;
7459
- scene.add(anchor);
7460
- anchor.position.set(this.x * 256 + 128 - 8, this.y * 256 + 128 - 8, this.z * 256 + 128 - 8);
7461
- addBox3WireframeToObject(
7462
- new import_three25.Box3(new import_three25.Vector3(0, 0, 0), new import_three25.Vector3(256, 256, 256)),
7463
- anchor,
7464
- 16776960,
7465
- 4
7466
- );
7467
7452
  }
7468
7453
  getBlockAt(posOrX, y, z) {
7469
7454
  if (typeof posOrX == "number") {
@@ -7489,7 +7474,7 @@ var Chunk = class _Chunk {
7489
7474
  return this.setBlockInChunkAt(pos, block, worldPos);
7490
7475
  }
7491
7476
  /**
7492
- * Set block at a _chunk_ position (0-16)
7477
+ * Set a block at integer chunk-local coordinates from 0 to 15.
7493
7478
  */
7494
7479
  async setBlockInChunkAt(pos, block, worldPos) {
7495
7480
  if (typeof worldPos === "undefined") {
@@ -7502,15 +7487,13 @@ var Chunk = class _Chunk {
7502
7487
  current.object.removeFromScene();
7503
7488
  delete this._blocks[index];
7504
7489
  }
7505
- if (typeof block === "undefined" || typeof block.type === "undefined" || block.type === "air") return void 0;
7490
+ if (!block || _Chunk.isAir(block)) return void 0;
7506
7491
  const blockState = await BlockStates.get(AssetKey.parse("blockstates", block.type));
7507
7492
  if (blockState) {
7508
7493
  const blockObject = await this.scene.addBlock(blockState, {
7509
7494
  mergeMeshes: true,
7510
7495
  instanceMeshes: true,
7511
- wireframe: true,
7512
7496
  maxInstanceCount: 2e3
7513
- //TODO: setting this to high kills FPS; increase on demand somehow?
7514
7497
  });
7515
7498
  if (block.properties) {
7516
7499
  await blockObject.setState(block.properties);
@@ -7526,23 +7509,21 @@ var Chunk = class _Chunk {
7526
7509
  return void 0;
7527
7510
  }
7528
7511
  async clear() {
7529
- let promises = [];
7530
- let pos = new import_three25.Vector3();
7531
- for (let x = 0; x < 16; x++) {
7532
- for (let z = 0; z < 16; z++) {
7533
- for (let y = 0; y < 16; y++) {
7534
- promises.push(this.setBlockInChunkAt(pos.set(x, y, z), void 0));
7535
- }
7536
- }
7537
- }
7538
- return Promise.all(promises).then((ignored) => {
7539
- });
7512
+ this._blocks.forEach((block) => block.object.removeFromScene());
7513
+ this._blocks.length = 0;
7540
7514
  }
7541
7515
  async dispose() {
7542
7516
  await this.clear();
7543
- this.scene.remove(this._anchor);
7517
+ }
7518
+ static isAir(block) {
7519
+ if (typeof block === "undefined" || typeof block.type === "undefined") return true;
7520
+ const key = AssetKey.parse("blockstates", block.type);
7521
+ return key.namespace === "minecraft" && !key.type && ["air", "cave_air", "void_air"].includes(key.path);
7544
7522
  }
7545
7523
  static chunkPosToBlockIndex(pos) {
7524
+ if ([pos.x, pos.y, pos.z].some((value) => !Number.isInteger(value) || value < 0 || value >= 16)) {
7525
+ throw new RangeError("Block coordinates must be integers from 0 to 15 within a chunk");
7526
+ }
7546
7527
  return pos.y * 16 * 16 + pos.z * 16 + pos.x;
7547
7528
  }
7548
7529
  worldPosToChunkPos(pos) {