minerender 2.1.0-alpha.90 → 2.1.0-alpha.92

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.
package/dist/bundle.js CHANGED
@@ -119191,17 +119191,141 @@ gl_FragDepth=readDepth(vUv);
119191
119191
  }
119192
119192
  };
119193
119193
 
119194
+ // src/world/_compression/Lz4.ts
119195
+ init_dirname();
119196
+ init_buffer2();
119197
+ init_process2();
119198
+ init_MineRenderError();
119199
+ function decodeAnvilLz4(data) {
119200
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
119201
+ const blocks = [];
119202
+ let cursor = 0, totalSize = 0;
119203
+ while (true) {
119204
+ if (cursor + 21 > data.length) throw invalid("truncated block header or missing terminator");
119205
+ if (view.getUint32(cursor) !== 1280980034 || view.getUint32(cursor + 4) !== 1819239275) {
119206
+ throw invalid("invalid block signature");
119207
+ }
119208
+ const token = data[cursor + 8];
119209
+ const method = token & 240;
119210
+ const size = view.getInt32(cursor + 9, true);
119211
+ const decodedSize = view.getInt32(cursor + 13, true);
119212
+ const checksum = view.getUint32(cursor + 17, true);
119213
+ cursor += 21;
119214
+ if (method !== 16 && method !== 32) throw invalid("unsupported block method");
119215
+ if (size < 0 || decodedSize < 0 || decodedSize > 2 ** (10 + (token & 15)) || size === 0 !== (decodedSize === 0) || method === 16 && size !== decodedSize) {
119216
+ throw invalid("invalid block lengths");
119217
+ }
119218
+ if (size === 0) {
119219
+ if (checksum !== 0 || cursor !== data.length) throw invalid("invalid terminator or trailing bytes");
119220
+ break;
119221
+ }
119222
+ if (cursor + size > data.length || decodedSize > size * 255) throw invalid("invalid block lengths");
119223
+ blocks.push({ start: cursor, size, decodedSize, raw: method === 16, checksum });
119224
+ totalSize += decodedSize;
119225
+ cursor += size;
119226
+ }
119227
+ const output = new Uint8Array(totalSize);
119228
+ let offset = 0;
119229
+ for (const block of blocks) {
119230
+ const source = data.subarray(block.start, block.start + block.size);
119231
+ const target = output.subarray(offset, offset + block.decodedSize);
119232
+ if (block.raw) target.set(source);
119233
+ else decodeBlock(source, target);
119234
+ if (checksum32(target) !== block.checksum) throw invalid("block checksum mismatch");
119235
+ offset += block.decodedSize;
119236
+ }
119237
+ return output;
119238
+ }
119239
+ function decodeBlock(source, target) {
119240
+ let input = 0, output = 0, matched = false;
119241
+ const length = (nibble) => {
119242
+ let value = nibble;
119243
+ if (value === 15) {
119244
+ let extension;
119245
+ do {
119246
+ if (input === source.length) throw invalid("truncated sequence length");
119247
+ extension = source[input++];
119248
+ value += extension;
119249
+ } while (extension === 255);
119250
+ }
119251
+ return value;
119252
+ };
119253
+ while (input < source.length) {
119254
+ const token = source[input++];
119255
+ const literals = length(token >>> 4);
119256
+ if (input + literals > source.length || output + literals > target.length) {
119257
+ throw invalid("literal sequence exceeds block bounds");
119258
+ }
119259
+ target.set(source.subarray(input, input + literals), output);
119260
+ input += literals;
119261
+ output += literals;
119262
+ if (input === source.length) {
119263
+ if (output !== target.length || matched && literals < 5) throw invalid("invalid final literal sequence");
119264
+ return;
119265
+ }
119266
+ if (input + 2 > source.length) throw invalid("truncated match offset");
119267
+ const distance = source[input] | source[input + 1] << 8;
119268
+ input += 2;
119269
+ if (distance === 0 || distance > output) throw invalid("match offset exceeds block history");
119270
+ const count = length(token & 15) + 4;
119271
+ if (output > target.length - 12 || output + count > target.length - 5) {
119272
+ throw invalid("match exceeds block bounds");
119273
+ }
119274
+ for (let end = output + count; output < end; output++) target[output] = target[output - distance];
119275
+ matched = true;
119276
+ }
119277
+ throw invalid("missing final literal sequence");
119278
+ }
119279
+ function checksum32(data) {
119280
+ const prime1 = 2654435761, prime2 = 2246822519, prime3 = 3266489917, prime4 = 668265263, prime5 = 374761393;
119281
+ const seed = 2538058380;
119282
+ const rotate = (value, bits2) => value << bits2 | value >>> 32 - bits2;
119283
+ const round = (value, word) => Math.imul(rotate(value + Math.imul(word, prime2), 13), prime1);
119284
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
119285
+ let cursor = 0, hash2;
119286
+ if (data.length >= 16) {
119287
+ let a4 = seed + prime1 + prime2, b4 = seed + prime2, c4 = seed, d4 = seed - prime1;
119288
+ while (cursor + 16 <= data.length) {
119289
+ a4 = round(a4, view.getUint32(cursor, true));
119290
+ b4 = round(b4, view.getUint32(cursor + 4, true));
119291
+ c4 = round(c4, view.getUint32(cursor + 8, true));
119292
+ d4 = round(d4, view.getUint32(cursor + 12, true));
119293
+ cursor += 16;
119294
+ }
119295
+ hash2 = rotate(a4, 1) + rotate(b4, 7) + rotate(c4, 12) + rotate(d4, 18);
119296
+ } else {
119297
+ hash2 = seed + prime5;
119298
+ }
119299
+ hash2 += data.length;
119300
+ while (cursor + 4 <= data.length) {
119301
+ hash2 = Math.imul(rotate(hash2 + Math.imul(view.getUint32(cursor, true), prime3), 17), prime4);
119302
+ cursor += 4;
119303
+ }
119304
+ while (cursor < data.length) {
119305
+ hash2 = Math.imul(rotate(hash2 + Math.imul(data[cursor++], prime5), 11), prime1);
119306
+ }
119307
+ hash2 = Math.imul(hash2 ^ hash2 >>> 15, prime2);
119308
+ hash2 = Math.imul(hash2 ^ hash2 >>> 13, prime3);
119309
+ return (hash2 ^ hash2 >>> 16) & 268435455;
119310
+ }
119311
+ function invalid(detail) {
119312
+ return new MineRenderError(`Invalid Anvil LZ4 stream: ${detail}`);
119313
+ }
119314
+
119194
119315
  // src/world/AnvilParser.ts
119195
119316
  var AnvilParser = class {
119196
119317
  /** Lists occupied chunk positions within the region, using local coordinates from 0 to 31. */
119197
119318
  static getChunkList(data) {
119198
119319
  return this.locations(this.bytes(data)).map(({ x: x4, z: z4 }) => ({ x: x4, z: z4 }));
119199
119320
  }
119200
- /** Decodes all stored chunks. Gzip, zlib, and uncompressed payloads are supported. */
119201
- static async parse(data) {
119321
+ /** Decodes all stored chunks. Gzip, zlib, LZ4, and uncompressed payloads are supported. */
119322
+ static async parse(data, options = {}) {
119323
+ options.signal?.throwIfAborted();
119202
119324
  const bytes = this.bytes(data);
119203
119325
  const chunks = [];
119204
- for (const location of this.locations(bytes)) chunks.push(await this.readChunk(bytes, location));
119326
+ for (const location of this.locations(bytes)) {
119327
+ chunks.push(await this.abortable(this.readChunk(bytes, location, options), options.signal));
119328
+ }
119205
119329
  return { chunks };
119206
119330
  }
119207
119331
  /**
@@ -119210,13 +119334,28 @@ gl_FragDepth=readDepth(vUv);
119210
119334
  * @param localZ - Chunk z within the region, from 0 to 31.
119211
119335
  * @returns The chunk with absolute coordinates, or `undefined` if the region has no chunk there.
119212
119336
  */
119213
- static async parseChunk(data, localX, localZ) {
119337
+ static async parseChunk(data, localX, localZ, options = {}) {
119338
+ options.signal?.throwIfAborted();
119214
119339
  if (![localX, localZ].every((value) => Number.isInteger(value) && value >= 0 && value < 32)) {
119215
119340
  throw new RangeError("Region-local chunk coordinates must be integers from 0 to 31");
119216
119341
  }
119217
119342
  const bytes = this.bytes(data);
119218
119343
  const location = this.locations(bytes).find(({ x: x4, z: z4 }) => x4 === localX && z4 === localZ);
119219
- return location ? this.readChunk(bytes, location) : void 0;
119344
+ return location ? this.abortable(this.readChunk(bytes, location, options), options.signal) : void 0;
119345
+ }
119346
+ static async abortable(promise, signal) {
119347
+ if (!signal) return promise;
119348
+ let abort2;
119349
+ const cancelled = new Promise((_4, reject) => {
119350
+ abort2 = () => reject(signal.reason);
119351
+ signal.addEventListener("abort", abort2, { once: true });
119352
+ if (signal.aborted) abort2();
119353
+ });
119354
+ try {
119355
+ return await Promise.race([promise, cancelled]);
119356
+ } finally {
119357
+ signal.removeEventListener("abort", abort2);
119358
+ }
119220
119359
  }
119221
119360
  static bytes(data) {
119222
119361
  return data instanceof Uint8Array ? data : new Uint8Array(data);
@@ -119242,18 +119381,41 @@ gl_FragDepth=readDepth(vUv);
119242
119381
  }
119243
119382
  return locations;
119244
119383
  }
119245
- static async readChunk(data, location) {
119246
- const compression = data[location.offset + 4];
119247
- if (compression & 128) throw new MineRenderError("External Anvil .mcc chunks are not supported");
119248
- if (compression !== 1 && compression !== 2 && compression !== 3) {
119249
- throw new MineRenderError(`Unsupported Anvil compression ${compression}${compression === 4 ? " (LZ4)" : ""}`);
119384
+ static async readChunk(data, location, options) {
119385
+ options.signal?.throwIfAborted();
119386
+ const format3 = data[location.offset + 4];
119387
+ const external = (format3 & 128) !== 0;
119388
+ const compression = format3 & 127;
119389
+ if (compression !== 1 && compression !== 2 && compression !== 3 && compression !== 4) {
119390
+ throw new MineRenderError(`Unsupported Anvil compression ${compression}`);
119250
119391
  }
119251
119392
  let payload = data.subarray(location.offset + 5, location.offset + 4 + location.length);
119252
- if (compression !== 3) {
119393
+ let expected;
119394
+ if (external) {
119395
+ if (!options.region || !options.readExternalChunk) {
119396
+ throw new MineRenderError("External Anvil .mcc chunks require region coordinates and readExternalChunk");
119397
+ }
119398
+ if (![options.region.x, options.region.z].every(Number.isSafeInteger)) {
119399
+ throw new RangeError("Anvil region coordinates must be safe integers");
119400
+ }
119401
+ expected = { x: options.region.x * 32 + location.x, z: options.region.z * 32 + location.z };
119402
+ if (![expected.x, expected.z].every(Number.isSafeInteger)) {
119403
+ throw new RangeError("External Anvil chunk coordinates must be safe integers");
119404
+ }
119405
+ const externalData = await options.readExternalChunk(expected.x, expected.z, options.signal);
119406
+ options.signal?.throwIfAborted();
119407
+ if (externalData === void 0) throw new MineRenderError(`External Anvil chunk c.${expected.x}.${expected.z}.mcc is missing`);
119408
+ payload = this.bytes(externalData);
119409
+ }
119410
+ if (compression === 4) {
119411
+ payload = decodeAnvilLz4(payload);
119412
+ } else if (compression !== 3) {
119253
119413
  const stream = new Blob([payload]).stream().pipeThrough(new DecompressionStream(compression === 1 ? "gzip" : "deflate"));
119254
119414
  payload = new Uint8Array(await new Response(stream).arrayBuffer());
119255
119415
  }
119416
+ options.signal?.throwIfAborted();
119256
119417
  const nbt = await NBTHelper.fromBuffer(payload, "big");
119418
+ options.signal?.throwIfAborted();
119257
119419
  const root = nbt.value;
119258
119420
  const level = root.Level?.type === "compound" ? root.Level.value : root;
119259
119421
  const dataVersion = root.DataVersion?.type === "int" ? root.DataVersion.value : void 0;
@@ -119266,6 +119428,9 @@ gl_FragDepth=readDepth(vUv);
119266
119428
  if ((chunk.x % 32 + 32) % 32 !== location.x || (chunk.z % 32 + 32) % 32 !== location.z) {
119267
119429
  throw new MineRenderError("Anvil chunk coordinates do not match its region location");
119268
119430
  }
119431
+ if (expected && (chunk.x !== expected.x || chunk.z !== expected.z)) {
119432
+ throw new MineRenderError(`Anvil chunk coordinates do not match external file c.${expected.x}.${expected.z}.mcc`);
119433
+ }
119269
119434
  for (const section of this.compounds(level.sections ?? level.Sections, "sections")) {
119270
119435
  const y5 = this.integer(section.Y, "section Y");
119271
119436
  if (section.Blocks || section.Data || section.Add) {
@@ -119400,6 +119565,7 @@ gl_FragDepth=readDepth(vUv);
119400
119565
  this.cachedBytes = 0;
119401
119566
  this.maxCachedRegions = options.maxCachedRegions ?? 4;
119402
119567
  this.maxCachedBytes = options.maxCachedBytes ?? 64 * 1024 * 1024;
119568
+ this.readExternalChunk = options.readExternalChunk;
119403
119569
  for (const [name2, value] of Object.entries({ maxCachedRegions: this.maxCachedRegions, maxCachedBytes: this.maxCachedBytes })) {
119404
119570
  if (!Number.isSafeInteger(value) || value < 0) throw new RangeError(`${name2} must be a nonnegative safe integer`);
119405
119571
  }
@@ -119413,7 +119579,11 @@ gl_FragDepth=readDepth(vUv);
119413
119579
  const data = await this.getRegion(regionX, regionZ, signal);
119414
119580
  signal?.throwIfAborted();
119415
119581
  if (!data) return void 0;
119416
- const chunk = await abortable2(AnvilParser.parseChunk(data, x4 - regionX * 32, z4 - regionZ * 32), signal);
119582
+ const chunk = await AnvilParser.parseChunk(data, x4 - regionX * 32, z4 - regionZ * 32, {
119583
+ region: { x: regionX, z: regionZ },
119584
+ readExternalChunk: this.readExternalChunk,
119585
+ signal
119586
+ });
119417
119587
  signal?.throwIfAborted();
119418
119588
  if (chunk && (chunk.x !== x4 || chunk.z !== z4)) {
119419
119589
  throw new MineRenderError(`Anvil chunk coordinates ${chunk.x},${chunk.z} do not match requested column ${x4},${z4}`);
@@ -119444,7 +119614,7 @@ gl_FragDepth=readDepth(vUv);
119444
119614
  settled: false,
119445
119615
  promise: Promise.resolve().then(() => {
119446
119616
  controller.signal.throwIfAborted();
119447
- return this.readRegion(x4, z4, controller.signal);
119617
+ return abortable2(this.readRegion(x4, z4, controller.signal), controller.signal);
119448
119618
  }).then((data) => {
119449
119619
  controller.signal.throwIfAborted();
119450
119620
  let bytes = data instanceof Uint8Array ? data : data === void 0 ? void 0 : new Uint8Array(data);