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/browser/index.d.mts +22 -5
- package/dist/browser/index.d.ts +22 -5
- package/dist/browser/index.js +180 -13
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/index.mjs +180 -13
- package/dist/browser/index.mjs.map +1 -1
- package/dist/bundle.js +183 -13
- package/dist/bundle.js.map +1 -1
- package/dist/node/index.d.mts +22 -5
- package/dist/node/index.d.ts +22 -5
- package/dist/node/index.js +180 -13
- package/dist/node/index.js.map +1 -1
- package/dist/node/index.mjs +180 -13
- package/dist/node/index.mjs.map +1 -1
- package/package.json +1 -1
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))
|
|
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
|
-
|
|
119247
|
-
|
|
119248
|
-
|
|
119249
|
-
|
|
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
|
-
|
|
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
|
|
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);
|