minerender 2.1.0-alpha.86 → 2.1.0-alpha.87
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 +103 -1
- package/dist/browser/index.d.ts +103 -1
- package/dist/browser/index.js +304 -0
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/index.mjs +304 -0
- package/dist/browser/index.mjs.map +1 -1
- package/dist/bundle.js +315 -0
- package/dist/bundle.js.map +1 -1
- package/dist/node/index.d.mts +103 -1
- package/dist/node/index.d.ts +103 -1
- package/dist/node/index.js +306 -0
- package/dist/node/index.js.map +1 -1
- package/dist/node/index.mjs +304 -0
- package/dist/node/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/bundle.js
CHANGED
|
@@ -106478,6 +106478,7 @@ ${shader.fragmentShader}`.replace(
|
|
|
106478
106478
|
__export(index_browser_exports, {
|
|
106479
106479
|
AXES: () => AXES,
|
|
106480
106480
|
AnvilParser: () => AnvilParser,
|
|
106481
|
+
AnvilWorldSource: () => AnvilWorldSource,
|
|
106481
106482
|
ArchiveAssetSource: () => ArchiveAssetSource,
|
|
106482
106483
|
AssetKey: () => AssetKey,
|
|
106483
106484
|
AssetLoadError: () => AssetLoadError,
|
|
@@ -106581,6 +106582,7 @@ ${shader.fragmentShader}`.replace(
|
|
|
106581
106582
|
TintColors: () => TintColors,
|
|
106582
106583
|
UVMapper: () => UVMapper,
|
|
106583
106584
|
VideoExporter: () => VideoExporter,
|
|
106585
|
+
WorldStreamer: () => WorldStreamer,
|
|
106584
106586
|
WrappedImage: () => WrappedImage,
|
|
106585
106587
|
addBox3WireframeToObject: () => addBox3WireframeToObject,
|
|
106586
106588
|
addWireframeToMesh: () => addWireframeToMesh,
|
|
@@ -119015,6 +119017,137 @@ gl_FragDepth=readDepth(vUv);
|
|
|
119015
119017
|
}
|
|
119016
119018
|
};
|
|
119017
119019
|
|
|
119020
|
+
// src/world/AnvilWorldSource.ts
|
|
119021
|
+
init_dirname();
|
|
119022
|
+
init_buffer2();
|
|
119023
|
+
init_process2();
|
|
119024
|
+
init_MineRenderError();
|
|
119025
|
+
async function abortable2(promise, signal) {
|
|
119026
|
+
if (!signal) return promise;
|
|
119027
|
+
let abort2;
|
|
119028
|
+
const cancelled = new Promise((_4, reject) => {
|
|
119029
|
+
abort2 = () => reject(signal.reason);
|
|
119030
|
+
signal.addEventListener("abort", abort2, { once: true });
|
|
119031
|
+
if (signal.aborted) abort2();
|
|
119032
|
+
});
|
|
119033
|
+
try {
|
|
119034
|
+
return await Promise.race([promise, cancelled]);
|
|
119035
|
+
} finally {
|
|
119036
|
+
signal.removeEventListener("abort", abort2);
|
|
119037
|
+
}
|
|
119038
|
+
}
|
|
119039
|
+
var AnvilWorldSource = class {
|
|
119040
|
+
constructor(readRegion, options = {}) {
|
|
119041
|
+
this.readRegion = readRegion;
|
|
119042
|
+
this.regions = /* @__PURE__ */ new Map();
|
|
119043
|
+
this.pending = /* @__PURE__ */ new Map();
|
|
119044
|
+
this.cachedBytes = 0;
|
|
119045
|
+
this.maxCachedRegions = options.maxCachedRegions ?? 4;
|
|
119046
|
+
this.maxCachedBytes = options.maxCachedBytes ?? 64 * 1024 * 1024;
|
|
119047
|
+
for (const [name2, value] of Object.entries({ maxCachedRegions: this.maxCachedRegions, maxCachedBytes: this.maxCachedBytes })) {
|
|
119048
|
+
if (!Number.isSafeInteger(value) || value < 0) throw new RangeError(`${name2} must be a nonnegative safe integer`);
|
|
119049
|
+
}
|
|
119050
|
+
}
|
|
119051
|
+
/** Decodes one column at absolute chunk coordinates. Aborting a caller leaves other shared readers active. */
|
|
119052
|
+
async getChunk(x4, z4, signal) {
|
|
119053
|
+
if (!Number.isSafeInteger(x4) || !Number.isSafeInteger(z4)) {
|
|
119054
|
+
throw new RangeError("Chunk column coordinates must be safe integers");
|
|
119055
|
+
}
|
|
119056
|
+
const regionX = Math.floor(x4 / 32), regionZ = Math.floor(z4 / 32);
|
|
119057
|
+
const data = await this.getRegion(regionX, regionZ, signal);
|
|
119058
|
+
signal?.throwIfAborted();
|
|
119059
|
+
if (!data) return void 0;
|
|
119060
|
+
const chunk = await abortable2(AnvilParser.parseChunk(data, x4 - regionX * 32, z4 - regionZ * 32), signal);
|
|
119061
|
+
signal?.throwIfAborted();
|
|
119062
|
+
if (chunk && (chunk.x !== x4 || chunk.z !== z4)) {
|
|
119063
|
+
throw new MineRenderError(`Anvil chunk coordinates ${chunk.x},${chunk.z} do not match requested column ${x4},${z4}`);
|
|
119064
|
+
}
|
|
119065
|
+
return chunk;
|
|
119066
|
+
}
|
|
119067
|
+
/** Releases cached regions. Reads already in progress finish without repopulating this cache. */
|
|
119068
|
+
clearCache() {
|
|
119069
|
+
this.regions.clear();
|
|
119070
|
+
this.pending.clear();
|
|
119071
|
+
this.cachedBytes = 0;
|
|
119072
|
+
}
|
|
119073
|
+
getRegion(x4, z4, signal) {
|
|
119074
|
+
signal?.throwIfAborted();
|
|
119075
|
+
const key = `${x4},${z4}`;
|
|
119076
|
+
const cached2 = this.regions.get(key);
|
|
119077
|
+
if (cached2) {
|
|
119078
|
+
this.regions.delete(key);
|
|
119079
|
+
this.regions.set(key, cached2);
|
|
119080
|
+
return Promise.resolve(cached2.data);
|
|
119081
|
+
}
|
|
119082
|
+
let loading = this.pending.get(key);
|
|
119083
|
+
if (!loading) {
|
|
119084
|
+
const controller = new AbortController();
|
|
119085
|
+
loading = {
|
|
119086
|
+
controller,
|
|
119087
|
+
waiters: 0,
|
|
119088
|
+
settled: false,
|
|
119089
|
+
promise: Promise.resolve().then(() => {
|
|
119090
|
+
controller.signal.throwIfAborted();
|
|
119091
|
+
return this.readRegion(x4, z4, controller.signal);
|
|
119092
|
+
}).then((data) => {
|
|
119093
|
+
controller.signal.throwIfAborted();
|
|
119094
|
+
let bytes = data instanceof Uint8Array ? data : data === void 0 ? void 0 : new Uint8Array(data);
|
|
119095
|
+
if (bytes && bytes.byteLength !== bytes.buffer.byteLength) bytes = new Uint8Array(bytes);
|
|
119096
|
+
if (bytes) AnvilParser.getChunkList(bytes);
|
|
119097
|
+
loading.settled = true;
|
|
119098
|
+
if (this.pending.get(key) === loading) this.cacheRegion(key, bytes);
|
|
119099
|
+
return bytes;
|
|
119100
|
+
}).finally(() => {
|
|
119101
|
+
loading.settled = true;
|
|
119102
|
+
if (this.pending.get(key) === loading) this.pending.delete(key);
|
|
119103
|
+
})
|
|
119104
|
+
};
|
|
119105
|
+
this.pending.set(key, loading);
|
|
119106
|
+
}
|
|
119107
|
+
const read = loading;
|
|
119108
|
+
read.waiters++;
|
|
119109
|
+
return new Promise((resolve, reject) => {
|
|
119110
|
+
let finished = false;
|
|
119111
|
+
const release2 = () => {
|
|
119112
|
+
if (finished) return false;
|
|
119113
|
+
finished = true;
|
|
119114
|
+
signal?.removeEventListener("abort", abort2);
|
|
119115
|
+
read.waiters--;
|
|
119116
|
+
return true;
|
|
119117
|
+
};
|
|
119118
|
+
const abort2 = () => {
|
|
119119
|
+
if (!release2()) return;
|
|
119120
|
+
if (!read.waiters && !read.settled) {
|
|
119121
|
+
if (this.pending.get(key) === read) this.pending.delete(key);
|
|
119122
|
+
read.controller.abort(signal.reason);
|
|
119123
|
+
}
|
|
119124
|
+
reject(signal.reason);
|
|
119125
|
+
};
|
|
119126
|
+
signal?.addEventListener("abort", abort2, { once: true });
|
|
119127
|
+
read.promise.then(
|
|
119128
|
+
(data) => {
|
|
119129
|
+
if (release2()) resolve(data);
|
|
119130
|
+
},
|
|
119131
|
+
(error2) => {
|
|
119132
|
+
if (release2()) reject(error2);
|
|
119133
|
+
}
|
|
119134
|
+
);
|
|
119135
|
+
if (signal?.aborted) abort2();
|
|
119136
|
+
});
|
|
119137
|
+
}
|
|
119138
|
+
cacheRegion(key, data) {
|
|
119139
|
+
const size = data?.byteLength ?? 0;
|
|
119140
|
+
if (!this.maxCachedRegions || size > this.maxCachedBytes) return;
|
|
119141
|
+
this.regions.set(key, { data, size });
|
|
119142
|
+
this.cachedBytes += size;
|
|
119143
|
+
while (this.regions.size > this.maxCachedRegions || this.cachedBytes > this.maxCachedBytes) {
|
|
119144
|
+
const oldest = this.regions.keys().next().value;
|
|
119145
|
+
this.cachedBytes -= this.regions.get(oldest).size;
|
|
119146
|
+
this.regions.delete(oldest);
|
|
119147
|
+
}
|
|
119148
|
+
}
|
|
119149
|
+
};
|
|
119150
|
+
|
|
119018
119151
|
// src/world/BlockInfo.ts
|
|
119019
119152
|
init_dirname();
|
|
119020
119153
|
init_buffer2();
|
|
@@ -119706,6 +119839,188 @@ gl_FragDepth=readDepth(vUv);
|
|
|
119706
119839
|
);
|
|
119707
119840
|
}
|
|
119708
119841
|
};
|
|
119842
|
+
|
|
119843
|
+
// src/world/WorldChunkSource.ts
|
|
119844
|
+
init_dirname();
|
|
119845
|
+
init_buffer2();
|
|
119846
|
+
init_process2();
|
|
119847
|
+
|
|
119848
|
+
// src/world/WorldStreamer.ts
|
|
119849
|
+
init_dirname();
|
|
119850
|
+
init_buffer2();
|
|
119851
|
+
init_process2();
|
|
119852
|
+
var WorldStreamer = class {
|
|
119853
|
+
constructor(world, source, options = {}) {
|
|
119854
|
+
this.world = world;
|
|
119855
|
+
this.source = source;
|
|
119856
|
+
this.loaded = /* @__PURE__ */ new Map();
|
|
119857
|
+
this.owned = /* @__PURE__ */ new Map();
|
|
119858
|
+
this.missing = /* @__PURE__ */ new Map();
|
|
119859
|
+
this.failed = /* @__PURE__ */ new Map();
|
|
119860
|
+
this.desired = [];
|
|
119861
|
+
this.disposed = false;
|
|
119862
|
+
this.loadRadius = options.loadRadius ?? 2;
|
|
119863
|
+
this.unloadRadius = options.unloadRadius ?? Math.min(32, this.loadRadius + 1);
|
|
119864
|
+
if (![this.loadRadius, this.unloadRadius].every((value) => Number.isInteger(value) && value >= 0 && value <= 32) || this.unloadRadius < this.loadRadius) {
|
|
119865
|
+
throw new RangeError("Chunk radii must be integers from 0 to 32, with unloadRadius at least loadRadius");
|
|
119866
|
+
}
|
|
119867
|
+
}
|
|
119868
|
+
/** Returns absolute coordinates of successfully placed columns, including retained columns. */
|
|
119869
|
+
get loadedChunks() {
|
|
119870
|
+
return [...this.loaded.values()].map((position) => ({ ...position }));
|
|
119871
|
+
}
|
|
119872
|
+
/** Returns source failures retained within the unload radius. Failed columns do not block other loads. */
|
|
119873
|
+
get failedChunks() {
|
|
119874
|
+
return [...this.failed.values()].map((failure) => ({ ...failure }));
|
|
119875
|
+
}
|
|
119876
|
+
/** Counts desired columns still waiting for a source read or placement, including the active column. */
|
|
119877
|
+
get pendingChunks() {
|
|
119878
|
+
return this.desired.filter((position) => this.needsLoad(position)).length;
|
|
119879
|
+
}
|
|
119880
|
+
/**
|
|
119881
|
+
* Updates the center in absolute chunk coordinates. Repeated calls share the active drain.
|
|
119882
|
+
* Source failures are recorded in failedChunks; other columns continue loading. Missing and failed
|
|
119883
|
+
* columns are remembered within the retention radius. Call retryFailedChunks to retry source failures.
|
|
119884
|
+
* Placement and unload failures reject the drain; call update again to retry those operations.
|
|
119885
|
+
* Reads outside the latest load radius receive an abort signal; placement finishes before unloading.
|
|
119886
|
+
* Sources should represent an unchanged world during streaming.
|
|
119887
|
+
*/
|
|
119888
|
+
async update(x4, z4) {
|
|
119889
|
+
if (this.disposed) throw new Error("WorldStreamer is disposed");
|
|
119890
|
+
if (![x4, z4].every((value) => Number.isSafeInteger(value) && Number.isSafeInteger(value - this.unloadRadius) && Number.isSafeInteger(value + this.unloadRadius))) {
|
|
119891
|
+
throw new RangeError("Chunk coordinates must be safe integers within the streaming radius");
|
|
119892
|
+
}
|
|
119893
|
+
if (!this.center || this.center.x !== x4 || this.center.z !== z4) {
|
|
119894
|
+
this.center = { x: x4, z: z4 };
|
|
119895
|
+
this.desired = [];
|
|
119896
|
+
for (let dz = -this.loadRadius; dz <= this.loadRadius; dz++) {
|
|
119897
|
+
for (let dx = -this.loadRadius; dx <= this.loadRadius; dx++) {
|
|
119898
|
+
this.desired.push({ x: x4 + dx, z: z4 + dz });
|
|
119899
|
+
}
|
|
119900
|
+
}
|
|
119901
|
+
this.desired.sort((a4, b4) => (a4.x - x4) ** 2 + (a4.z - z4) ** 2 - (b4.x - x4) ** 2 - (b4.z - z4) ** 2);
|
|
119902
|
+
if (this.loading && !this.within(this.loading.position, this.loadRadius)) {
|
|
119903
|
+
this.loading.controller.abort();
|
|
119904
|
+
}
|
|
119905
|
+
}
|
|
119906
|
+
return this.running ?? (this.running = Promise.resolve().then(() => this.drain()));
|
|
119907
|
+
}
|
|
119908
|
+
/** Updates from a camera or view-center position in scene units. One chunk spans 256 scene units. */
|
|
119909
|
+
async updatePosition(position) {
|
|
119910
|
+
if (![position.x, position.z].every(Number.isFinite)) throw new RangeError("Scene coordinates must be finite");
|
|
119911
|
+
return this.update(Math.floor(position.x / 256), Math.floor(position.z / 256));
|
|
119912
|
+
}
|
|
119913
|
+
/** Forgets recorded source failures and retries desired columns at the latest center. */
|
|
119914
|
+
async retryFailedChunks() {
|
|
119915
|
+
if (this.disposed) throw new Error("WorldStreamer is disposed");
|
|
119916
|
+
this.failed.clear();
|
|
119917
|
+
if (this.center) await this.update(this.center.x, this.center.z);
|
|
119918
|
+
}
|
|
119919
|
+
/** Aborts source reads, waits for active work, then unloads owned columns. The world and source remain caller-owned. */
|
|
119920
|
+
dispose() {
|
|
119921
|
+
this.disposed = true;
|
|
119922
|
+
this.desired = [];
|
|
119923
|
+
this.center = void 0;
|
|
119924
|
+
this.loading?.controller.abort();
|
|
119925
|
+
return this.disposal ?? (this.disposal = Promise.resolve().then(async () => {
|
|
119926
|
+
await this.running?.catch(() => void 0);
|
|
119927
|
+
let failure;
|
|
119928
|
+
let failed = false;
|
|
119929
|
+
for (const position of this.owned.values()) {
|
|
119930
|
+
try {
|
|
119931
|
+
await this.unload(position);
|
|
119932
|
+
} catch (error2) {
|
|
119933
|
+
failed = true;
|
|
119934
|
+
failure ?? (failure = error2);
|
|
119935
|
+
}
|
|
119936
|
+
}
|
|
119937
|
+
this.missing.clear();
|
|
119938
|
+
this.failed.clear();
|
|
119939
|
+
if (failed) throw failure;
|
|
119940
|
+
}).catch((error2) => {
|
|
119941
|
+
this.disposal = void 0;
|
|
119942
|
+
throw error2;
|
|
119943
|
+
}));
|
|
119944
|
+
}
|
|
119945
|
+
async drain() {
|
|
119946
|
+
try {
|
|
119947
|
+
while (!this.disposed) {
|
|
119948
|
+
for (const [key, position2] of this.missing) {
|
|
119949
|
+
if (!this.within(position2, this.unloadRadius)) this.missing.delete(key);
|
|
119950
|
+
}
|
|
119951
|
+
for (const [key, position2] of this.failed) {
|
|
119952
|
+
if (!this.within(position2, this.unloadRadius)) this.failed.delete(key);
|
|
119953
|
+
}
|
|
119954
|
+
const obsolete = [...this.owned.values()].find((position2) => !this.loaded.has(this.key(position2)) || !this.within(position2, this.unloadRadius));
|
|
119955
|
+
if (obsolete) {
|
|
119956
|
+
await this.unload(obsolete);
|
|
119957
|
+
continue;
|
|
119958
|
+
}
|
|
119959
|
+
const position = this.desired.find((position2) => this.needsLoad(position2));
|
|
119960
|
+
if (!position) return;
|
|
119961
|
+
let chunk;
|
|
119962
|
+
const controller = new AbortController();
|
|
119963
|
+
this.loading = { position, controller };
|
|
119964
|
+
try {
|
|
119965
|
+
chunk = await this.source.getChunk(position.x, position.z, controller.signal);
|
|
119966
|
+
if (chunk) this.validateChunk(chunk, position);
|
|
119967
|
+
} catch (error2) {
|
|
119968
|
+
if (!controller.signal.aborted && !this.disposed && this.within(position, this.loadRadius)) {
|
|
119969
|
+
this.failed.set(this.key(position), { ...position, error: error2 });
|
|
119970
|
+
}
|
|
119971
|
+
continue;
|
|
119972
|
+
} finally {
|
|
119973
|
+
this.loading = void 0;
|
|
119974
|
+
}
|
|
119975
|
+
if (controller.signal.aborted || this.disposed || !this.within(position, this.loadRadius)) continue;
|
|
119976
|
+
if (!chunk) {
|
|
119977
|
+
this.missing.set(this.key(position), position);
|
|
119978
|
+
continue;
|
|
119979
|
+
}
|
|
119980
|
+
this.owned.set(this.key(position), position);
|
|
119981
|
+
try {
|
|
119982
|
+
await this.world.placeChunk(chunk);
|
|
119983
|
+
this.loaded.set(this.key(position), position);
|
|
119984
|
+
} catch (error2) {
|
|
119985
|
+
try {
|
|
119986
|
+
await this.unload(position);
|
|
119987
|
+
} catch (cleanupError) {
|
|
119988
|
+
const failure = error2 instanceof Error ? error2 : new Error(String(error2));
|
|
119989
|
+
try {
|
|
119990
|
+
Object.defineProperty(failure, "cause", { value: cleanupError, configurable: true });
|
|
119991
|
+
} catch {
|
|
119992
|
+
throw Object.assign(new Error(failure.message), { cause: cleanupError });
|
|
119993
|
+
}
|
|
119994
|
+
throw failure;
|
|
119995
|
+
}
|
|
119996
|
+
throw error2;
|
|
119997
|
+
}
|
|
119998
|
+
}
|
|
119999
|
+
} finally {
|
|
120000
|
+
this.running = void 0;
|
|
120001
|
+
}
|
|
120002
|
+
}
|
|
120003
|
+
needsLoad(position) {
|
|
120004
|
+
const key = this.key(position);
|
|
120005
|
+
return !this.loaded.has(key) && !this.missing.has(key) && !this.failed.has(key);
|
|
120006
|
+
}
|
|
120007
|
+
async unload(position) {
|
|
120008
|
+
await this.world.unloadChunkColumn(position.x, position.z);
|
|
120009
|
+
this.owned.delete(this.key(position));
|
|
120010
|
+
this.loaded.delete(this.key(position));
|
|
120011
|
+
}
|
|
120012
|
+
validateChunk(chunk, position) {
|
|
120013
|
+
if (chunk.x !== position.x || chunk.z !== position.z) {
|
|
120014
|
+
throw new Error(`World source returned chunk ${chunk.x},${chunk.z} for ${position.x},${position.z}`);
|
|
120015
|
+
}
|
|
120016
|
+
}
|
|
120017
|
+
within(position, radius) {
|
|
120018
|
+
return !!this.center && Math.abs(position.x - this.center.x) <= radius && Math.abs(position.z - this.center.z) <= radius;
|
|
120019
|
+
}
|
|
120020
|
+
key(position) {
|
|
120021
|
+
return `${position.x},${position.z}`;
|
|
120022
|
+
}
|
|
120023
|
+
};
|
|
119709
120024
|
return __toCommonJS(index_browser_exports);
|
|
119710
120025
|
})();
|
|
119711
120026
|
/*! Bundled license information:
|