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.
@@ -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
 
@@ -7193,15 +7189,12 @@ var Skins = class {
7193
7189
  };
7194
7190
 
7195
7191
  // src/world/Chunk.ts
7196
- import { Box3 as Box32, Object3D as Object3D6, Vector3 as Vector311 } from "three";
7192
+ import { Vector3 as Vector311 } from "three";
7197
7193
 
7198
7194
  // src/world/MineRenderWorld.ts
7199
7195
  import { Vector3 as Vector310 } from "three";
7200
7196
  var MineRenderWorld = class {
7201
7197
  constructor(scene) {
7202
- this._size = 4;
7203
- //TODO: expandable
7204
- this._blockSize = this._size * 16;
7205
7198
  this._chunks = /* @__PURE__ */ new Map();
7206
7199
  this.scene = scene;
7207
7200
  }
@@ -7212,12 +7205,8 @@ var MineRenderWorld = class {
7212
7205
  if (isTripleArray(posOrX)) {
7213
7206
  return this.getBlockAt(new Vector310(posOrX[0], posOrX[1], posOrX[2]));
7214
7207
  }
7215
- const chunk = this.getChunkAt(posOrX);
7216
- if (typeof chunk === "undefined") {
7217
- return void 0;
7218
- }
7219
7208
  this.validatePosBounds(posOrX);
7220
- return chunk.getBlockAt(posOrX);
7209
+ return this.getChunkAt(posOrX)?.getBlockAt(posOrX);
7221
7210
  }
7222
7211
  async setBlockAt(posOrX, yOrBlock, z, block) {
7223
7212
  if (typeof posOrX == "number") {
@@ -7227,32 +7216,48 @@ var MineRenderWorld = class {
7227
7216
  return this.setBlockAt(new Vector310(posOrX[0], posOrX[1], posOrX[2]), yOrBlock);
7228
7217
  }
7229
7218
  this.validatePosBounds(posOrX);
7230
- const chunk = this.getOrCreateChunkAt(posOrX);
7231
- return chunk.setBlockAt(posOrX, yOrBlock);
7232
- }
7233
- async placeMultiBlock(multiblock, useBatches = true, executor = new BatchedExecutor()) {
7234
- const keys = new Set(multiblock.blocks.map((block) => AssetKey.parse("blockstates", block.type)));
7235
- await BlockStates.getAll(keys);
7236
- const place = async (block) => {
7237
- if (useBatches && typeof executor !== "undefined") {
7238
- await new Promise((resolve, reject) => {
7239
- executor.submit(() => {
7240
- this.setBlockAt(block.position, block).then(resolve).catch(reject);
7241
- });
7242
- });
7243
- } else {
7244
- await this.setBlockAt(block.position, block);
7219
+ const value = yOrBlock;
7220
+ const chunk = Chunk.isAir(value) ? this.getChunkAt(posOrX) : this.getOrCreateChunkAt(posOrX);
7221
+ return chunk?.setBlockAt(posOrX, value);
7222
+ }
7223
+ async placeMultiBlock(multiblock, useBatches = true, executor) {
7224
+ const keys = /* @__PURE__ */ new Map();
7225
+ for (const block of multiblock.blocks) {
7226
+ if (Chunk.isAir(block)) continue;
7227
+ const key = AssetKey.parse("blockstates", block.type);
7228
+ keys.set(key.serialize(), key);
7229
+ }
7230
+ await BlockStates.getAll(keys.values());
7231
+ if (!useBatches) {
7232
+ for (const block of multiblock.blocks) await this.setBlockAt(block.position, block);
7233
+ return;
7234
+ }
7235
+ const positions = /* @__PURE__ */ new Map();
7236
+ for (const block of multiblock.blocks) {
7237
+ const key = block.position.join(",");
7238
+ const group = positions.get(key);
7239
+ if (group) group.push(block);
7240
+ else positions.set(key, [block]);
7241
+ }
7242
+ const groups = [...positions.values()];
7243
+ const queue = executor ?? new BatchedExecutor();
7244
+ try {
7245
+ for (let i = 0; i < groups.length; i += queue.batch) {
7246
+ const results = await Promise.allSettled(groups.slice(i, i + queue.batch).map(
7247
+ (blocks) => queue.submit(async () => {
7248
+ for (const block of blocks) await this.setBlockAt(block.position, block);
7249
+ })
7250
+ ));
7251
+ const failure = results.find((result) => result.status === "rejected");
7252
+ if (failure?.status === "rejected") throw failure.reason;
7245
7253
  }
7246
- };
7247
- for (let block of multiblock.blocks) {
7248
- await place(block);
7254
+ } finally {
7255
+ if (!executor) queue.stop();
7249
7256
  }
7250
7257
  }
7251
7258
  async clear() {
7252
- console.log("CHUNKS", this._chunks);
7253
- for (let chunk of this._chunks.values()) {
7254
- console.log("dispose chunk", chunk);
7255
- await chunk?.dispose();
7259
+ for (const chunk of this._chunks.values()) {
7260
+ await chunk.dispose();
7256
7261
  }
7257
7262
  this._chunks.clear();
7258
7263
  }
@@ -7266,8 +7271,7 @@ var MineRenderWorld = class {
7266
7271
  return chunk;
7267
7272
  }
7268
7273
  getChunkAt(pos) {
7269
- const index = this.worldPosToChunkIndex(pos);
7270
- return this._chunks[index];
7274
+ return this._chunks.get(this.worldPosToChunkKey(pos));
7271
7275
  }
7272
7276
  static worldToScenePosition(pos) {
7273
7277
  return new Vector310(
@@ -7283,12 +7287,6 @@ var MineRenderWorld = class {
7283
7287
  pos.z / 16
7284
7288
  );
7285
7289
  }
7286
- worldPosToChunkIndex(pos) {
7287
- const chunkX = Math.floor(pos.x / 16);
7288
- const chunkY = Math.floor(pos.y / 16);
7289
- const chunkZ = Math.floor(pos.z / 16);
7290
- return chunkZ * this._size * this._size + chunkY * this._size + chunkX;
7291
- }
7292
7290
  worldPosToChunkKey(pos) {
7293
7291
  const chunkX = Math.floor(pos.x / 16);
7294
7292
  const chunkY = Math.floor(pos.y / 16);
@@ -7296,12 +7294,9 @@ var MineRenderWorld = class {
7296
7294
  return `${chunkX}_${chunkY}_${chunkZ}`;
7297
7295
  }
7298
7296
  validatePosBounds(pos) {
7299
- if (pos.x < 0) throw new Error("x<0");
7300
- if (pos.y < 0) throw new Error("y<0");
7301
- if (pos.z < 0) throw new Error("z<0");
7302
- if (pos.x > this._blockSize) throw new Error("x>" + this._blockSize);
7303
- if (pos.y > this._blockSize) throw new Error("y>" + this._blockSize);
7304
- if (pos.z > this._blockSize) throw new Error("z>" + this._blockSize);
7297
+ if (![pos.x, pos.y, pos.z].every(Number.isInteger)) {
7298
+ throw new RangeError("Block coordinates must be integers");
7299
+ }
7305
7300
  }
7306
7301
  };
7307
7302
 
@@ -7314,16 +7309,6 @@ var Chunk = class _Chunk {
7314
7309
  this.x = x;
7315
7310
  this.y = y;
7316
7311
  this.z = z;
7317
- let anchor = new Object3D6();
7318
- this._anchor = anchor;
7319
- scene.add(anchor);
7320
- anchor.position.set(this.x * 256 + 128 - 8, this.y * 256 + 128 - 8, this.z * 256 + 128 - 8);
7321
- addBox3WireframeToObject(
7322
- new Box32(new Vector311(0, 0, 0), new Vector311(256, 256, 256)),
7323
- anchor,
7324
- 16776960,
7325
- 4
7326
- );
7327
7312
  }
7328
7313
  getBlockAt(posOrX, y, z) {
7329
7314
  if (typeof posOrX == "number") {
@@ -7349,7 +7334,7 @@ var Chunk = class _Chunk {
7349
7334
  return this.setBlockInChunkAt(pos, block, worldPos);
7350
7335
  }
7351
7336
  /**
7352
- * Set block at a _chunk_ position (0-16)
7337
+ * Set a block at integer chunk-local coordinates from 0 to 15.
7353
7338
  */
7354
7339
  async setBlockInChunkAt(pos, block, worldPos) {
7355
7340
  if (typeof worldPos === "undefined") {
@@ -7362,15 +7347,13 @@ var Chunk = class _Chunk {
7362
7347
  current.object.removeFromScene();
7363
7348
  delete this._blocks[index];
7364
7349
  }
7365
- if (typeof block === "undefined" || typeof block.type === "undefined" || block.type === "air") return void 0;
7350
+ if (!block || _Chunk.isAir(block)) return void 0;
7366
7351
  const blockState = await BlockStates.get(AssetKey.parse("blockstates", block.type));
7367
7352
  if (blockState) {
7368
7353
  const blockObject = await this.scene.addBlock(blockState, {
7369
7354
  mergeMeshes: true,
7370
7355
  instanceMeshes: true,
7371
- wireframe: true,
7372
7356
  maxInstanceCount: 2e3
7373
- //TODO: setting this to high kills FPS; increase on demand somehow?
7374
7357
  });
7375
7358
  if (block.properties) {
7376
7359
  await blockObject.setState(block.properties);
@@ -7386,23 +7369,21 @@ var Chunk = class _Chunk {
7386
7369
  return void 0;
7387
7370
  }
7388
7371
  async clear() {
7389
- let promises = [];
7390
- let pos = new Vector311();
7391
- for (let x = 0; x < 16; x++) {
7392
- for (let z = 0; z < 16; z++) {
7393
- for (let y = 0; y < 16; y++) {
7394
- promises.push(this.setBlockInChunkAt(pos.set(x, y, z), void 0));
7395
- }
7396
- }
7397
- }
7398
- return Promise.all(promises).then((ignored) => {
7399
- });
7372
+ this._blocks.forEach((block) => block.object.removeFromScene());
7373
+ this._blocks.length = 0;
7400
7374
  }
7401
7375
  async dispose() {
7402
7376
  await this.clear();
7403
- this.scene.remove(this._anchor);
7377
+ }
7378
+ static isAir(block) {
7379
+ if (typeof block === "undefined" || typeof block.type === "undefined") return true;
7380
+ const key = AssetKey.parse("blockstates", block.type);
7381
+ return key.namespace === "minecraft" && !key.type && ["air", "cave_air", "void_air"].includes(key.path);
7404
7382
  }
7405
7383
  static chunkPosToBlockIndex(pos) {
7384
+ if ([pos.x, pos.y, pos.z].some((value) => !Number.isInteger(value) || value < 0 || value >= 16)) {
7385
+ throw new RangeError("Block coordinates must be integers from 0 to 15 within a chunk");
7386
+ }
7406
7387
  return pos.y * 16 * 16 + pos.z * 16 + pos.x;
7407
7388
  }
7408
7389
  worldPosToChunkPos(pos) {