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.
@@ -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
 
@@ -7243,15 +7239,12 @@ var Skins = class {
7243
7239
  };
7244
7240
 
7245
7241
  // src/world/Chunk.ts
7246
- import { Box3 as Box32, Object3D as Object3D6, Vector3 as Vector311 } from "three";
7242
+ import { Vector3 as Vector311 } from "three";
7247
7243
 
7248
7244
  // src/world/MineRenderWorld.ts
7249
7245
  import { Vector3 as Vector310 } from "three";
7250
7246
  var MineRenderWorld = class {
7251
7247
  constructor(scene) {
7252
- this._size = 4;
7253
- //TODO: expandable
7254
- this._blockSize = this._size * 16;
7255
7248
  this._chunks = /* @__PURE__ */ new Map();
7256
7249
  this.scene = scene;
7257
7250
  }
@@ -7262,12 +7255,8 @@ var MineRenderWorld = class {
7262
7255
  if (isTripleArray(posOrX)) {
7263
7256
  return this.getBlockAt(new Vector310(posOrX[0], posOrX[1], posOrX[2]));
7264
7257
  }
7265
- const chunk = this.getChunkAt(posOrX);
7266
- if (typeof chunk === "undefined") {
7267
- return void 0;
7268
- }
7269
7258
  this.validatePosBounds(posOrX);
7270
- return chunk.getBlockAt(posOrX);
7259
+ return this.getChunkAt(posOrX)?.getBlockAt(posOrX);
7271
7260
  }
7272
7261
  async setBlockAt(posOrX, yOrBlock, z, block) {
7273
7262
  if (typeof posOrX == "number") {
@@ -7277,32 +7266,48 @@ var MineRenderWorld = class {
7277
7266
  return this.setBlockAt(new Vector310(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
7278
7267
  }
7279
7268
  this.validatePosBounds(posOrX);
7280
- const chunk = this.getOrCreateChunkAt(posOrX);
7281
- return chunk.setBlockAt(posOrX, yOrBlock);
7282
- }
7283
- async placeMultiBlock(multiblock, useBatches = true, executor = new BatchedExecutor()) {
7284
- const keys = new Set(multiblock.blocks.map((block) => AssetKey.parse("blockstates", block.type)));
7285
- await BlockStates.getAll(keys);
7286
- const place = async (block) => {
7287
- if (useBatches && typeof executor !== "undefined") {
7288
- await new Promise((resolve, reject) => {
7289
- executor.submit(() => {
7290
- this.setBlockAt(block.position, block).then(resolve).catch(reject);
7291
- });
7292
- });
7293
- } else {
7294
- await this.setBlockAt(block.position, block);
7269
+ const value = yOrBlock;
7270
+ const chunk = Chunk.isAir(value) ? this.getChunkAt(posOrX) : this.getOrCreateChunkAt(posOrX);
7271
+ return chunk?.setBlockAt(posOrX, value);
7272
+ }
7273
+ async placeMultiBlock(multiblock, useBatches = true, executor) {
7274
+ const keys = /* @__PURE__ */ new Map();
7275
+ for (const block of multiblock.blocks) {
7276
+ if (Chunk.isAir(block)) continue;
7277
+ const key = AssetKey.parse("blockstates", block.type);
7278
+ keys.set(key.serialize(), key);
7279
+ }
7280
+ await BlockStates.getAll(keys.values());
7281
+ if (!useBatches) {
7282
+ for (const block of multiblock.blocks) await this.setBlockAt(block.position, block);
7283
+ return;
7284
+ }
7285
+ const positions = /* @__PURE__ */ new Map();
7286
+ for (const block of multiblock.blocks) {
7287
+ const key = block.position.join(",");
7288
+ const group = positions.get(key);
7289
+ if (group) group.push(block);
7290
+ else positions.set(key, [block]);
7291
+ }
7292
+ const groups = [...positions.values()];
7293
+ const queue = executor ?? new BatchedExecutor();
7294
+ try {
7295
+ for (let i = 0; i < groups.length; i += queue.batch) {
7296
+ const results = await Promise.allSettled(groups.slice(i, i + queue.batch).map(
7297
+ (blocks) => queue.submit(async () => {
7298
+ for (const block of blocks) await this.setBlockAt(block.position, block);
7299
+ })
7300
+ ));
7301
+ const failure = results.find((result) => result.status === "rejected");
7302
+ if (failure?.status === "rejected") throw failure.reason;
7295
7303
  }
7296
- };
7297
- for (let block of multiblock.blocks) {
7298
- await place(block);
7304
+ } finally {
7305
+ if (!executor) queue.stop();
7299
7306
  }
7300
7307
  }
7301
7308
  async clear() {
7302
- console.log("CHUNKS", this._chunks);
7303
- for (let chunk of this._chunks.values()) {
7304
- console.log("dispose chunk", chunk);
7305
- await chunk?.dispose();
7309
+ for (const chunk of this._chunks.values()) {
7310
+ await chunk.dispose();
7306
7311
  }
7307
7312
  this._chunks.clear();
7308
7313
  }
@@ -7316,8 +7321,7 @@ var MineRenderWorld = class {
7316
7321
  return chunk;
7317
7322
  }
7318
7323
  getChunkAt(pos) {
7319
- const index = this.worldPosToChunkIndex(pos);
7320
- return this._chunks[index];
7324
+ return this._chunks.get(this.worldPosToChunkKey(pos));
7321
7325
  }
7322
7326
  static worldToScenePosition(pos) {
7323
7327
  return new Vector310(
@@ -7333,12 +7337,6 @@ var MineRenderWorld = class {
7333
7337
  pos.z / 16
7334
7338
  );
7335
7339
  }
7336
- worldPosToChunkIndex(pos) {
7337
- const chunkX = Math.floor(pos.x / 16);
7338
- const chunkY = Math.floor(pos.y / 16);
7339
- const chunkZ = Math.floor(pos.z / 16);
7340
- return chunkZ * this._size * this._size + chunkY * this._size + chunkX;
7341
- }
7342
7340
  worldPosToChunkKey(pos) {
7343
7341
  const chunkX = Math.floor(pos.x / 16);
7344
7342
  const chunkY = Math.floor(pos.y / 16);
@@ -7346,12 +7344,9 @@ var MineRenderWorld = class {
7346
7344
  return `${chunkX}_${chunkY}_${chunkZ}`;
7347
7345
  }
7348
7346
  validatePosBounds(pos) {
7349
- if (pos.x < 0) throw new Error("x<0");
7350
- if (pos.y < 0) throw new Error("y<0");
7351
- if (pos.z < 0) throw new Error("z<0");
7352
- if (pos.x > this._blockSize) throw new Error("x>" + this._blockSize);
7353
- if (pos.y > this._blockSize) throw new Error("y>" + this._blockSize);
7354
- if (pos.z > this._blockSize) throw new Error("z>" + this._blockSize);
7347
+ if (![pos.x, pos.y, pos.z].every(Number.isInteger)) {
7348
+ throw new RangeError("Block coordinates must be integers");
7349
+ }
7355
7350
  }
7356
7351
  };
7357
7352
 
@@ -7364,16 +7359,6 @@ var Chunk = class _Chunk {
7364
7359
  this.x = x;
7365
7360
  this.y = y;
7366
7361
  this.z = z;
7367
- let anchor = new Object3D6();
7368
- this._anchor = anchor;
7369
- scene.add(anchor);
7370
- anchor.position.set(this.x * 256 + 128 - 8, this.y * 256 + 128 - 8, this.z * 256 + 128 - 8);
7371
- addBox3WireframeToObject(
7372
- new Box32(new Vector311(0, 0, 0), new Vector311(256, 256, 256)),
7373
- anchor,
7374
- 16776960,
7375
- 4
7376
- );
7377
7362
  }
7378
7363
  getBlockAt(posOrX, y, z) {
7379
7364
  if (typeof posOrX == "number") {
@@ -7399,7 +7384,7 @@ var Chunk = class _Chunk {
7399
7384
  return this.setBlockInChunkAt(pos, block, worldPos);
7400
7385
  }
7401
7386
  /**
7402
- * Set block at a _chunk_ position (0-16)
7387
+ * Set a block at integer chunk-local coordinates from 0 to 15.
7403
7388
  */
7404
7389
  async setBlockInChunkAt(pos, block, worldPos) {
7405
7390
  if (typeof worldPos === "undefined") {
@@ -7412,15 +7397,13 @@ var Chunk = class _Chunk {
7412
7397
  current.object.removeFromScene();
7413
7398
  delete this._blocks[index];
7414
7399
  }
7415
- if (typeof block === "undefined" || typeof block.type === "undefined" || block.type === "air") return void 0;
7400
+ if (!block || _Chunk.isAir(block)) return void 0;
7416
7401
  const blockState = await BlockStates.get(AssetKey.parse("blockstates", block.type));
7417
7402
  if (blockState) {
7418
7403
  const blockObject = await this.scene.addBlock(blockState, {
7419
7404
  mergeMeshes: true,
7420
7405
  instanceMeshes: true,
7421
- wireframe: true,
7422
7406
  maxInstanceCount: 2e3
7423
- //TODO: setting this to high kills FPS; increase on demand somehow?
7424
7407
  });
7425
7408
  if (block.properties) {
7426
7409
  await blockObject.setState(block.properties);
@@ -7436,23 +7419,21 @@ var Chunk = class _Chunk {
7436
7419
  return void 0;
7437
7420
  }
7438
7421
  async clear() {
7439
- let promises = [];
7440
- let pos = new Vector311();
7441
- for (let x = 0; x < 16; x++) {
7442
- for (let z = 0; z < 16; z++) {
7443
- for (let y = 0; y < 16; y++) {
7444
- promises.push(this.setBlockInChunkAt(pos.set(x, y, z), void 0));
7445
- }
7446
- }
7447
- }
7448
- return Promise.all(promises).then((ignored) => {
7449
- });
7422
+ this._blocks.forEach((block) => block.object.removeFromScene());
7423
+ this._blocks.length = 0;
7450
7424
  }
7451
7425
  async dispose() {
7452
7426
  await this.clear();
7453
- this.scene.remove(this._anchor);
7427
+ }
7428
+ static isAir(block) {
7429
+ if (typeof block === "undefined" || typeof block.type === "undefined") return true;
7430
+ const key = AssetKey.parse("blockstates", block.type);
7431
+ return key.namespace === "minecraft" && !key.type && ["air", "cave_air", "void_air"].includes(key.path);
7454
7432
  }
7455
7433
  static chunkPosToBlockIndex(pos) {
7434
+ if ([pos.x, pos.y, pos.z].some((value) => !Number.isInteger(value) || value < 0 || value >= 16)) {
7435
+ throw new RangeError("Block coordinates must be integers from 0 to 15 within a chunk");
7436
+ }
7456
7437
  return pos.y * 16 * 16 + pos.z * 16 + pos.x;
7457
7438
  }
7458
7439
  worldPosToChunkPos(pos) {