minerender 2.1.0-alpha.127 → 2.1.0-alpha.129
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 +77 -5
- package/dist/browser/index.d.ts +77 -5
- package/dist/browser/index.js +653 -287
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/index.mjs +653 -287
- package/dist/browser/index.mjs.map +1 -1
- package/dist/browser/section.worker.js +7 -5
- package/dist/browser/section.worker.js.map +1 -1
- package/dist/browser/section.worker.mjs +7 -5
- package/dist/browser/section.worker.mjs.map +1 -1
- package/dist/bundle.js +665 -293
- package/dist/bundle.js.map +1 -1
- package/dist/node/index.d.mts +77 -5
- package/dist/node/index.d.ts +77 -5
- package/dist/node/index.js +656 -287
- package/dist/node/index.js.map +1 -1
- package/dist/node/index.mjs +655 -287
- package/dist/node/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/browser/index.mjs
CHANGED
|
@@ -263,6 +263,10 @@ var _Caching, Caching;
|
|
|
263
263
|
var init_Caching = __esm({
|
|
264
264
|
"src/cache/Caching.ts"() {
|
|
265
265
|
_Caching = class _Caching {
|
|
266
|
+
/** Changes when shared assets are cleared, so derived caches can discard their old values. */
|
|
267
|
+
static get generation() {
|
|
268
|
+
return this._generation;
|
|
269
|
+
}
|
|
266
270
|
static createAssetCache() {
|
|
267
271
|
return Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(30)).buildAsync();
|
|
268
272
|
}
|
|
@@ -298,6 +302,7 @@ var init_Caching = __esm({
|
|
|
298
302
|
}
|
|
299
303
|
/** Invalidates in-memory entries so later lookups reload them. Persisted entries are retained. */
|
|
300
304
|
static clear() {
|
|
305
|
+
this._generation++;
|
|
301
306
|
for (const cache of this.all) {
|
|
302
307
|
cache.invalidateAll();
|
|
303
308
|
}
|
|
@@ -309,6 +314,7 @@ var init_Caching = __esm({
|
|
|
309
314
|
* Use {@link shutdown} to also stop requests and the shared Ticker.
|
|
310
315
|
*/
|
|
311
316
|
static end() {
|
|
317
|
+
this._generation++;
|
|
312
318
|
for (const cache of this.all) {
|
|
313
319
|
cache.invalidateAll();
|
|
314
320
|
cache.end();
|
|
@@ -320,6 +326,7 @@ var init_Caching = __esm({
|
|
|
320
326
|
};
|
|
321
327
|
}
|
|
322
328
|
};
|
|
329
|
+
_Caching._generation = 0;
|
|
323
330
|
_Caching.rawImageCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(5)).buildAsync();
|
|
324
331
|
_Caching.imageDataCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(10)).buildAsync();
|
|
325
332
|
_Caching.canvasImageDataCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(10)).buildAsync();
|
|
@@ -1208,118 +1215,6 @@ var init_Materials = __esm({
|
|
|
1208
1215
|
}
|
|
1209
1216
|
});
|
|
1210
1217
|
|
|
1211
|
-
// src/model/Model.ts
|
|
1212
|
-
function isDoubleArray(obj) {
|
|
1213
|
-
return Array.isArray(obj) && obj.length === 2;
|
|
1214
|
-
}
|
|
1215
|
-
function isTripleArray(obj) {
|
|
1216
|
-
return Array.isArray(obj) && obj.length === 3;
|
|
1217
|
-
}
|
|
1218
|
-
function isQuadArray(obj) {
|
|
1219
|
-
return Array.isArray(obj) && obj.length === 4;
|
|
1220
|
-
}
|
|
1221
|
-
var ITEM_GENERATED, BUILTIN_GENERATED, BUILTIN_ENTITY, DEFAULT_ELEMENTS;
|
|
1222
|
-
var init_Model = __esm({
|
|
1223
|
-
"src/model/Model.ts"() {
|
|
1224
|
-
ITEM_GENERATED = "item/generated";
|
|
1225
|
-
BUILTIN_GENERATED = "builtin/generated";
|
|
1226
|
-
BUILTIN_ENTITY = "builtin/entity";
|
|
1227
|
-
DEFAULT_ELEMENTS = [];
|
|
1228
|
-
}
|
|
1229
|
-
});
|
|
1230
|
-
|
|
1231
|
-
// ../../node_modules/ts-deepmerge/dist/index.js
|
|
1232
|
-
var require_dist = __commonJS({
|
|
1233
|
-
"../../node_modules/ts-deepmerge/dist/index.js"(exports) {
|
|
1234
|
-
"use strict";
|
|
1235
|
-
var __assign = exports && exports.__assign || function() {
|
|
1236
|
-
__assign = Object.assign || function(t) {
|
|
1237
|
-
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
1238
|
-
s = arguments[i];
|
|
1239
|
-
for (var p11 in s) if (Object.prototype.hasOwnProperty.call(s, p11))
|
|
1240
|
-
t[p11] = s[p11];
|
|
1241
|
-
}
|
|
1242
|
-
return t;
|
|
1243
|
-
};
|
|
1244
|
-
return __assign.apply(this, arguments);
|
|
1245
|
-
};
|
|
1246
|
-
var __read = exports && exports.__read || function(o, n) {
|
|
1247
|
-
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
1248
|
-
if (!m) return o;
|
|
1249
|
-
var i = m.call(o), r, ar = [], e;
|
|
1250
|
-
try {
|
|
1251
|
-
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
1252
|
-
} catch (error) {
|
|
1253
|
-
e = { error };
|
|
1254
|
-
} finally {
|
|
1255
|
-
try {
|
|
1256
|
-
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
1257
|
-
} finally {
|
|
1258
|
-
if (e) throw e.error;
|
|
1259
|
-
}
|
|
1260
|
-
}
|
|
1261
|
-
return ar;
|
|
1262
|
-
};
|
|
1263
|
-
var __spreadArray = exports && exports.__spreadArray || function(to, from, pack) {
|
|
1264
|
-
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
1265
|
-
if (ar || !(i in from)) {
|
|
1266
|
-
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
1267
|
-
ar[i] = from[i];
|
|
1268
|
-
}
|
|
1269
|
-
}
|
|
1270
|
-
return to.concat(ar || Array.prototype.slice.call(from));
|
|
1271
|
-
};
|
|
1272
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1273
|
-
var isObject = function(obj) {
|
|
1274
|
-
if (typeof obj === "object" && obj !== null) {
|
|
1275
|
-
if (typeof Object.getPrototypeOf === "function") {
|
|
1276
|
-
var prototype = Object.getPrototypeOf(obj);
|
|
1277
|
-
return prototype === Object.prototype || prototype === null;
|
|
1278
|
-
}
|
|
1279
|
-
return Object.prototype.toString.call(obj) === "[object Object]";
|
|
1280
|
-
}
|
|
1281
|
-
return false;
|
|
1282
|
-
};
|
|
1283
|
-
var PROTECTED_KEYS = ["__proto__"];
|
|
1284
|
-
var merge10 = function() {
|
|
1285
|
-
var objects = [];
|
|
1286
|
-
for (var _i = 0; _i < arguments.length; _i++) {
|
|
1287
|
-
objects[_i] = arguments[_i];
|
|
1288
|
-
}
|
|
1289
|
-
return objects.reduce(function(result, current) {
|
|
1290
|
-
Object.keys(current).forEach(function(key) {
|
|
1291
|
-
if (PROTECTED_KEYS.includes(key)) {
|
|
1292
|
-
return;
|
|
1293
|
-
}
|
|
1294
|
-
if (Array.isArray(result[key]) && Array.isArray(current[key])) {
|
|
1295
|
-
result[key] = merge10.options.mergeArrays ? Array.from(new Set(result[key].concat(current[key]))) : current[key];
|
|
1296
|
-
} else if (isObject(result[key]) && isObject(current[key])) {
|
|
1297
|
-
result[key] = merge10(result[key], current[key]);
|
|
1298
|
-
} else {
|
|
1299
|
-
result[key] = current[key];
|
|
1300
|
-
}
|
|
1301
|
-
});
|
|
1302
|
-
return result;
|
|
1303
|
-
}, {});
|
|
1304
|
-
};
|
|
1305
|
-
var defaultOptions = {
|
|
1306
|
-
mergeArrays: true
|
|
1307
|
-
};
|
|
1308
|
-
merge10.options = defaultOptions;
|
|
1309
|
-
merge10.withOptions = function(options) {
|
|
1310
|
-
var objects = [];
|
|
1311
|
-
for (var _i = 1; _i < arguments.length; _i++) {
|
|
1312
|
-
objects[_i - 1] = arguments[_i];
|
|
1313
|
-
}
|
|
1314
|
-
merge10.options = __assign({ mergeArrays: true }, options);
|
|
1315
|
-
var result = merge10.apply(void 0, __spreadArray([], __read(objects), false));
|
|
1316
|
-
merge10.options = defaultOptions;
|
|
1317
|
-
return result;
|
|
1318
|
-
};
|
|
1319
|
-
exports.default = merge10;
|
|
1320
|
-
}
|
|
1321
|
-
});
|
|
1322
|
-
|
|
1323
1218
|
// src/assets/AssetKey.ts
|
|
1324
1219
|
function isResourceLocation(value) {
|
|
1325
1220
|
return typeof value === "string" && /^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(value);
|
|
@@ -1422,17 +1317,306 @@ var init_AssetKey = __esm({
|
|
|
1422
1317
|
if (this.type) {
|
|
1423
1318
|
p11.push(this.type);
|
|
1424
1319
|
}
|
|
1425
|
-
p11.push(this.path);
|
|
1426
|
-
return p11.join("/");
|
|
1320
|
+
p11.push(this.path);
|
|
1321
|
+
return p11.join("/");
|
|
1322
|
+
}
|
|
1323
|
+
toNamespacedString() {
|
|
1324
|
+
return this.namespace + ":" + this.getFullPath();
|
|
1325
|
+
}
|
|
1326
|
+
/** Returns a cache identifier that includes the explicit root or {@link AssetLoader.ROOT}. */
|
|
1327
|
+
serialize() {
|
|
1328
|
+
return this.toString();
|
|
1329
|
+
}
|
|
1330
|
+
};
|
|
1331
|
+
}
|
|
1332
|
+
});
|
|
1333
|
+
|
|
1334
|
+
// src/assets/source/parser/AssetParsers.ts
|
|
1335
|
+
var AssetParser;
|
|
1336
|
+
var init_AssetParsers = __esm({
|
|
1337
|
+
"src/assets/source/parser/AssetParsers.ts"() {
|
|
1338
|
+
AssetParser = /* @__PURE__ */ ((AssetParser2) => {
|
|
1339
|
+
AssetParser2["MODEL"] = "model";
|
|
1340
|
+
AssetParser2["BLOCKSTATE"] = "blockstate";
|
|
1341
|
+
AssetParser2["META"] = "meta";
|
|
1342
|
+
AssetParser2["NBT"] = "nbt";
|
|
1343
|
+
AssetParser2["IMAGE"] = "image";
|
|
1344
|
+
AssetParser2["LIST"] = "list";
|
|
1345
|
+
AssetParser2["JSON"] = "json";
|
|
1346
|
+
return AssetParser2;
|
|
1347
|
+
})(AssetParser || {});
|
|
1348
|
+
}
|
|
1349
|
+
});
|
|
1350
|
+
|
|
1351
|
+
// src/assets/MineRenderData.ts
|
|
1352
|
+
var object, release, compare, MineRenderData;
|
|
1353
|
+
var init_MineRenderData = __esm({
|
|
1354
|
+
"src/assets/MineRenderData.ts"() {
|
|
1355
|
+
init_AssetKey();
|
|
1356
|
+
init_AssetLoader();
|
|
1357
|
+
init_AssetParsers();
|
|
1358
|
+
init_Caching();
|
|
1359
|
+
object = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1360
|
+
release = (value) => /^\d+\.\d+(?:\.\d+)?$/.test(value) ? value.split(".").map(Number) : void 0;
|
|
1361
|
+
compare = (left, right) => {
|
|
1362
|
+
for (let i = 0; i < 3; i++) {
|
|
1363
|
+
const difference = (left[i] ?? 0) - (right[i] ?? 0);
|
|
1364
|
+
if (difference) return difference;
|
|
1365
|
+
}
|
|
1366
|
+
return 0;
|
|
1367
|
+
};
|
|
1368
|
+
MineRenderData = class {
|
|
1369
|
+
/** Loads one registry. Missing files and invalid data reject the request. */
|
|
1370
|
+
static async get(dataset, root) {
|
|
1371
|
+
root = root?.replace(/\/+$/, "");
|
|
1372
|
+
const scope = AssetLoader.persistentScope;
|
|
1373
|
+
const defaultRoot = AssetLoader.ROOT;
|
|
1374
|
+
const resolved = await this.resolve(root);
|
|
1375
|
+
const entry = resolved.manifest.datasets[dataset];
|
|
1376
|
+
if (!entry) throw new Error(`MineRender ${dataset} data is unavailable for ${resolved.version}: ${resolved.manifest.unavailable[dataset] ?? "not listed in the manifest"}`);
|
|
1377
|
+
const file = entry.file;
|
|
1378
|
+
const requestRoot = resolved.root === (root ?? defaultRoot).replace(/\/+$/, "") ? root : resolved.root;
|
|
1379
|
+
return await this.cached(`${requestRoot === void 0 ? "sources" : "explicit"}:${resolved.root}/${file}:${entry.sha256}`, scope, defaultRoot, async () => {
|
|
1380
|
+
const data = await this.load(file.slice(0, -5), requestRoot);
|
|
1381
|
+
if (!this.validDataset(dataset, data)) throw new Error(`Invalid MineRender ${dataset} data at ${resolved.root}/minerender-data/${file}`);
|
|
1382
|
+
return data;
|
|
1383
|
+
});
|
|
1384
|
+
}
|
|
1385
|
+
/**
|
|
1386
|
+
* Uses the exact version when published. Otherwise, selects the nearest patch in the same
|
|
1387
|
+
* release line (older on ties), then the latest earlier release or the oldest published release.
|
|
1388
|
+
* Unpublished snapshots reject.
|
|
1389
|
+
*/
|
|
1390
|
+
static async resolve(root) {
|
|
1391
|
+
const requestRoot = root?.replace(/\/+$/, "");
|
|
1392
|
+
const defaultRoot = AssetLoader.ROOT;
|
|
1393
|
+
root = (root ?? defaultRoot).replace(/\/+$/, "");
|
|
1394
|
+
const scope = AssetLoader.persistentScope;
|
|
1395
|
+
return this.cached(`resolve:${requestRoot === void 0 ? "sources" : "explicit"}:${root}`, scope, defaultRoot, async () => {
|
|
1396
|
+
const requestedVersion = root.slice(root.lastIndexOf("/") + 1);
|
|
1397
|
+
let manifest = await this.manifest(root, requestRoot !== void 0 && release(requestedVersion) ? requestedVersion : void 0, requestRoot);
|
|
1398
|
+
if (manifest) return { requestedVersion, version: manifest.minecraftVersion, root, manifest };
|
|
1399
|
+
const requested = release(requestedVersion);
|
|
1400
|
+
if (!requested) {
|
|
1401
|
+
throw new Error(`No MineRender data is published for ${requestedVersion}; automatic fallback requires a release`);
|
|
1402
|
+
}
|
|
1403
|
+
const base = root.slice(0, root.lastIndexOf("/"));
|
|
1404
|
+
const index = await this.cached(`versions:${base}`, scope, defaultRoot, async () => {
|
|
1405
|
+
const value = await this.load("versions", base);
|
|
1406
|
+
if (!object(value) || !Array.isArray(value.versions) || !value.versions.every((entry) => object(entry) && typeof entry.name === "string")) {
|
|
1407
|
+
throw new Error(`Invalid MineRender version index at ${base}/minerender-data/versions.json`);
|
|
1408
|
+
}
|
|
1409
|
+
return value.versions;
|
|
1410
|
+
});
|
|
1411
|
+
const versions = index.map((entry) => ({ name: entry.name, number: release(entry.name) })).filter((entry) => !!entry.number).sort((left, right) => compare(left.number, right.number));
|
|
1412
|
+
const sameLine = versions.filter((entry) => entry.number[0] === requested[0] && entry.number[1] === requested[1]);
|
|
1413
|
+
const selected = sameLine.sort((left, right) => Math.abs((left.number[2] ?? 0) - (requested[2] ?? 0)) - Math.abs((right.number[2] ?? 0) - (requested[2] ?? 0)) || compare(left.number, right.number))[0] ?? versions.filter((entry) => compare(entry.number, requested) <= 0).pop() ?? versions[0];
|
|
1414
|
+
if (!selected) throw new Error(`No compatible MineRender data is published for ${requestedVersion}`);
|
|
1415
|
+
const resolvedRoot = `${base}/${selected.name}`;
|
|
1416
|
+
manifest = await this.manifest(resolvedRoot, selected.name, resolvedRoot);
|
|
1417
|
+
if (!manifest) throw new Error(`Missing MineRender manifest for published version ${selected.name}`);
|
|
1418
|
+
return { requestedVersion, version: selected.name, root: resolvedRoot, manifest };
|
|
1419
|
+
});
|
|
1420
|
+
}
|
|
1421
|
+
static async manifest(root, expectedVersion, requestRoot) {
|
|
1422
|
+
const data = await this.load("manifest", requestRoot);
|
|
1423
|
+
if (data === void 0) return void 0;
|
|
1424
|
+
if (!object(data) || data.schemaVersion !== 1 || typeof data.minecraftVersion !== "string" || !Number.isInteger(data.dataVersion) || !object(data.unavailable) || !Object.values(data.unavailable).every((value) => typeof value === "string") || !object(data.datasets) || !Object.values(data.datasets).every((entry) => {
|
|
1425
|
+
return object(entry) && typeof entry.file === "string" && /^[a-z0-9][a-z0-9._-]*\.json$/.test(entry.file) && typeof entry.sha256 === "string" && /^[0-9a-f]{64}$/.test(entry.sha256);
|
|
1426
|
+
}) || expectedVersion !== void 0 && data.minecraftVersion !== expectedVersion) {
|
|
1427
|
+
throw new Error(`Invalid MineRender manifest at ${root}/minerender-data/manifest.json`);
|
|
1428
|
+
}
|
|
1429
|
+
return data;
|
|
1430
|
+
}
|
|
1431
|
+
static load(file, root) {
|
|
1432
|
+
const key = new AssetKey("minerender-data", file, void 0, void 0, void 0, ".json", root);
|
|
1433
|
+
key.rootType = void 0;
|
|
1434
|
+
return AssetLoader.get(key, "json" /* JSON */);
|
|
1435
|
+
}
|
|
1436
|
+
static async cached(key, scope, defaultRoot, loader) {
|
|
1437
|
+
const checkSources = () => {
|
|
1438
|
+
if (AssetLoader.persistentScope !== scope || AssetLoader.ROOT !== defaultRoot) throw new Error("Asset sources changed while loading MineRender data; retry the request");
|
|
1439
|
+
};
|
|
1440
|
+
checkSources();
|
|
1441
|
+
return await Caching.mineRenderDataCache.get(scope ? `${scope}
|
|
1442
|
+
${key}` : key, async () => {
|
|
1443
|
+
const value = await loader();
|
|
1444
|
+
checkSources();
|
|
1445
|
+
return value;
|
|
1446
|
+
});
|
|
1447
|
+
}
|
|
1448
|
+
static validDataset(dataset, data) {
|
|
1449
|
+
if (!object(data)) return false;
|
|
1450
|
+
if (dataset === "potionColors") return object(data.effects) && Object.values(data.effects).every(Number.isInteger) && object(data.potions) && Object.values(data.potions).every((effects) => Array.isArray(effects) && effects.every((effect) => object(effect) && typeof effect.id === "string" && Number.isInteger(effect.amplifier)));
|
|
1451
|
+
if (dataset === "renames") return data.format === "data-fixer-renames" && Number.isInteger(data.baseDataVersion) && ["blocks", "items", "entities"].every((kind) => object(data[kind]) && Object.values(data[kind]).every((entry) => object(entry) && typeof entry.registered === "boolean" && Array.isArray(entry.ranges) && entry.ranges.length > 0 && entry.ranges.every((range) => object(range) && Number.isInteger(range.before) && typeof range.to === "string")));
|
|
1452
|
+
if (dataset === "legacyBlocks") return object(data.blocks) && Object.values(data.blocks).every((value) => typeof value === "string");
|
|
1453
|
+
if (dataset === "blockCulling") return Array.isArray(data.profiles) && data.profiles.every((profile) => object(profile) && typeof profile.type === "string") && object(data.blocks) && Object.values(data.blocks).every((index) => Number.isInteger(index) && index >= 0 && index < data.profiles.length) && (data.groups === void 0 || object(data.groups) && Object.values(data.groups).every((group) => Array.isArray(group) && group.every((type) => typeof type === "string")));
|
|
1454
|
+
return Object.values(data).every((value) => {
|
|
1455
|
+
if (!object(value)) return false;
|
|
1456
|
+
switch (dataset) {
|
|
1457
|
+
case "itemDefaults":
|
|
1458
|
+
return true;
|
|
1459
|
+
case "blockStates":
|
|
1460
|
+
return Object.values(value).every((property) => object(property) && "default" in property && Array.isArray(property.values));
|
|
1461
|
+
case "blockTints":
|
|
1462
|
+
return ["constant", "grass", "stem", "redstone"].includes(value.source) && (value.source !== "constant" || Number.isInteger(value.color)) && (value.biome === void 0 || ["grass", "foliage", "dry_foliage", "water"].includes(value.biome));
|
|
1463
|
+
case "fluids":
|
|
1464
|
+
return ["water", "lava"].includes(value.kind) && typeof value.renderModel === "boolean" && (value.levelProperty === void 0 || typeof value.levelProperty === "string");
|
|
1465
|
+
case "dyes":
|
|
1466
|
+
return [value.id, value.textureDiffuseColor, value.fireworkColor, value.textColor].every(Number.isInteger);
|
|
1467
|
+
default:
|
|
1468
|
+
return false;
|
|
1469
|
+
}
|
|
1470
|
+
});
|
|
1471
|
+
}
|
|
1472
|
+
};
|
|
1473
|
+
}
|
|
1474
|
+
});
|
|
1475
|
+
|
|
1476
|
+
// src/assets/Renames.ts
|
|
1477
|
+
var Renames;
|
|
1478
|
+
var init_Renames = __esm({
|
|
1479
|
+
"src/assets/Renames.ts"() {
|
|
1480
|
+
init_AssetKey();
|
|
1481
|
+
init_MineRenderData();
|
|
1482
|
+
Renames = class {
|
|
1483
|
+
/** Loads the selected version's rename table, or `undefined` when its published data has none. */
|
|
1484
|
+
static async get(root) {
|
|
1485
|
+
const resolved = await MineRenderData.resolve(root);
|
|
1486
|
+
return resolved.manifest.datasets.renames ? MineRenderData.get("renames", root) : void 0;
|
|
1487
|
+
}
|
|
1488
|
+
/**
|
|
1489
|
+
* Returns the ID that replaced `id`, or `undefined` when `id` keeps its meaning.
|
|
1490
|
+
* Without a data version, an ID that the selected version still registers keeps its meaning.
|
|
1491
|
+
*/
|
|
1492
|
+
static async resolve(kind, id, dataVersion, root) {
|
|
1493
|
+
const data = await this.get(root);
|
|
1494
|
+
if (!data || !Object.prototype.hasOwnProperty.call(data[kind], id)) return void 0;
|
|
1495
|
+
const entry = data[kind][id];
|
|
1496
|
+
if (dataVersion === void 0) return entry.registered ? void 0 : entry.ranges[entry.ranges.length - 1].to;
|
|
1497
|
+
if (dataVersion < data.baseDataVersion) return void 0;
|
|
1498
|
+
return entry.ranges.find((range) => dataVersion < range.before)?.to;
|
|
1499
|
+
}
|
|
1500
|
+
/** Returns a copy of `key` for the replacement ID, or `undefined` when the ID keeps its meaning. */
|
|
1501
|
+
static async key(kind, key) {
|
|
1502
|
+
const renamed = await this.resolve(kind, `${key.namespace}:${key.path}`, void 0, key.root);
|
|
1503
|
+
if (!renamed) return void 0;
|
|
1504
|
+
const [namespace, path] = renamed.split(":");
|
|
1505
|
+
return new AssetKey(namespace, path, key.assetType, key.type, key.rootType, key.extension, key.root);
|
|
1506
|
+
}
|
|
1507
|
+
};
|
|
1508
|
+
}
|
|
1509
|
+
});
|
|
1510
|
+
|
|
1511
|
+
// src/model/Model.ts
|
|
1512
|
+
function isDoubleArray(obj) {
|
|
1513
|
+
return Array.isArray(obj) && obj.length === 2;
|
|
1514
|
+
}
|
|
1515
|
+
function isTripleArray(obj) {
|
|
1516
|
+
return Array.isArray(obj) && obj.length === 3;
|
|
1517
|
+
}
|
|
1518
|
+
function isQuadArray(obj) {
|
|
1519
|
+
return Array.isArray(obj) && obj.length === 4;
|
|
1520
|
+
}
|
|
1521
|
+
var ITEM_GENERATED, BUILTIN_GENERATED, BUILTIN_ENTITY, DEFAULT_ELEMENTS;
|
|
1522
|
+
var init_Model = __esm({
|
|
1523
|
+
"src/model/Model.ts"() {
|
|
1524
|
+
ITEM_GENERATED = "item/generated";
|
|
1525
|
+
BUILTIN_GENERATED = "builtin/generated";
|
|
1526
|
+
BUILTIN_ENTITY = "builtin/entity";
|
|
1527
|
+
DEFAULT_ELEMENTS = [];
|
|
1528
|
+
}
|
|
1529
|
+
});
|
|
1530
|
+
|
|
1531
|
+
// ../../node_modules/ts-deepmerge/dist/index.js
|
|
1532
|
+
var require_dist = __commonJS({
|
|
1533
|
+
"../../node_modules/ts-deepmerge/dist/index.js"(exports) {
|
|
1534
|
+
"use strict";
|
|
1535
|
+
var __assign = exports && exports.__assign || function() {
|
|
1536
|
+
__assign = Object.assign || function(t) {
|
|
1537
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
1538
|
+
s = arguments[i];
|
|
1539
|
+
for (var p11 in s) if (Object.prototype.hasOwnProperty.call(s, p11))
|
|
1540
|
+
t[p11] = s[p11];
|
|
1541
|
+
}
|
|
1542
|
+
return t;
|
|
1543
|
+
};
|
|
1544
|
+
return __assign.apply(this, arguments);
|
|
1545
|
+
};
|
|
1546
|
+
var __read = exports && exports.__read || function(o, n) {
|
|
1547
|
+
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
1548
|
+
if (!m) return o;
|
|
1549
|
+
var i = m.call(o), r, ar = [], e;
|
|
1550
|
+
try {
|
|
1551
|
+
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
1552
|
+
} catch (error) {
|
|
1553
|
+
e = { error };
|
|
1554
|
+
} finally {
|
|
1555
|
+
try {
|
|
1556
|
+
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
1557
|
+
} finally {
|
|
1558
|
+
if (e) throw e.error;
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
return ar;
|
|
1562
|
+
};
|
|
1563
|
+
var __spreadArray = exports && exports.__spreadArray || function(to, from, pack) {
|
|
1564
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
1565
|
+
if (ar || !(i in from)) {
|
|
1566
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
1567
|
+
ar[i] = from[i];
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
1571
|
+
};
|
|
1572
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1573
|
+
var isObject = function(obj) {
|
|
1574
|
+
if (typeof obj === "object" && obj !== null) {
|
|
1575
|
+
if (typeof Object.getPrototypeOf === "function") {
|
|
1576
|
+
var prototype = Object.getPrototypeOf(obj);
|
|
1577
|
+
return prototype === Object.prototype || prototype === null;
|
|
1578
|
+
}
|
|
1579
|
+
return Object.prototype.toString.call(obj) === "[object Object]";
|
|
1427
1580
|
}
|
|
1428
|
-
|
|
1429
|
-
|
|
1581
|
+
return false;
|
|
1582
|
+
};
|
|
1583
|
+
var PROTECTED_KEYS = ["__proto__"];
|
|
1584
|
+
var merge10 = function() {
|
|
1585
|
+
var objects = [];
|
|
1586
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
1587
|
+
objects[_i] = arguments[_i];
|
|
1430
1588
|
}
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1589
|
+
return objects.reduce(function(result, current) {
|
|
1590
|
+
Object.keys(current).forEach(function(key) {
|
|
1591
|
+
if (PROTECTED_KEYS.includes(key)) {
|
|
1592
|
+
return;
|
|
1593
|
+
}
|
|
1594
|
+
if (Array.isArray(result[key]) && Array.isArray(current[key])) {
|
|
1595
|
+
result[key] = merge10.options.mergeArrays ? Array.from(new Set(result[key].concat(current[key]))) : current[key];
|
|
1596
|
+
} else if (isObject(result[key]) && isObject(current[key])) {
|
|
1597
|
+
result[key] = merge10(result[key], current[key]);
|
|
1598
|
+
} else {
|
|
1599
|
+
result[key] = current[key];
|
|
1600
|
+
}
|
|
1601
|
+
});
|
|
1602
|
+
return result;
|
|
1603
|
+
}, {});
|
|
1604
|
+
};
|
|
1605
|
+
var defaultOptions = {
|
|
1606
|
+
mergeArrays: true
|
|
1607
|
+
};
|
|
1608
|
+
merge10.options = defaultOptions;
|
|
1609
|
+
merge10.withOptions = function(options) {
|
|
1610
|
+
var objects = [];
|
|
1611
|
+
for (var _i = 1; _i < arguments.length; _i++) {
|
|
1612
|
+
objects[_i - 1] = arguments[_i];
|
|
1434
1613
|
}
|
|
1614
|
+
merge10.options = __assign({ mergeArrays: true }, options);
|
|
1615
|
+
var result = merge10.apply(void 0, __spreadArray([], __read(objects), false));
|
|
1616
|
+
merge10.options = defaultOptions;
|
|
1617
|
+
return result;
|
|
1435
1618
|
};
|
|
1619
|
+
exports.default = merge10;
|
|
1436
1620
|
}
|
|
1437
1621
|
});
|
|
1438
1622
|
|
|
@@ -1558,147 +1742,6 @@ var init_ModelMerger = __esm({
|
|
|
1558
1742
|
}
|
|
1559
1743
|
});
|
|
1560
1744
|
|
|
1561
|
-
// src/assets/source/parser/AssetParsers.ts
|
|
1562
|
-
var AssetParser;
|
|
1563
|
-
var init_AssetParsers = __esm({
|
|
1564
|
-
"src/assets/source/parser/AssetParsers.ts"() {
|
|
1565
|
-
AssetParser = /* @__PURE__ */ ((AssetParser2) => {
|
|
1566
|
-
AssetParser2["MODEL"] = "model";
|
|
1567
|
-
AssetParser2["BLOCKSTATE"] = "blockstate";
|
|
1568
|
-
AssetParser2["META"] = "meta";
|
|
1569
|
-
AssetParser2["NBT"] = "nbt";
|
|
1570
|
-
AssetParser2["IMAGE"] = "image";
|
|
1571
|
-
AssetParser2["LIST"] = "list";
|
|
1572
|
-
AssetParser2["JSON"] = "json";
|
|
1573
|
-
return AssetParser2;
|
|
1574
|
-
})(AssetParser || {});
|
|
1575
|
-
}
|
|
1576
|
-
});
|
|
1577
|
-
|
|
1578
|
-
// src/assets/MineRenderData.ts
|
|
1579
|
-
var object, release, compare, MineRenderData;
|
|
1580
|
-
var init_MineRenderData = __esm({
|
|
1581
|
-
"src/assets/MineRenderData.ts"() {
|
|
1582
|
-
init_AssetKey();
|
|
1583
|
-
init_AssetLoader();
|
|
1584
|
-
init_AssetParsers();
|
|
1585
|
-
init_Caching();
|
|
1586
|
-
object = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1587
|
-
release = (value) => /^\d+\.\d+(?:\.\d+)?$/.test(value) ? value.split(".").map(Number) : void 0;
|
|
1588
|
-
compare = (left, right) => {
|
|
1589
|
-
for (let i = 0; i < 3; i++) {
|
|
1590
|
-
const difference = (left[i] ?? 0) - (right[i] ?? 0);
|
|
1591
|
-
if (difference) return difference;
|
|
1592
|
-
}
|
|
1593
|
-
return 0;
|
|
1594
|
-
};
|
|
1595
|
-
MineRenderData = class {
|
|
1596
|
-
/** Loads one registry. Missing files and invalid data reject the request. */
|
|
1597
|
-
static async get(dataset, root) {
|
|
1598
|
-
root = root?.replace(/\/+$/, "");
|
|
1599
|
-
const scope = AssetLoader.persistentScope;
|
|
1600
|
-
const defaultRoot = AssetLoader.ROOT;
|
|
1601
|
-
const resolved = await this.resolve(root);
|
|
1602
|
-
const entry = resolved.manifest.datasets[dataset];
|
|
1603
|
-
if (!entry) throw new Error(`MineRender ${dataset} data is unavailable for ${resolved.version}: ${resolved.manifest.unavailable[dataset] ?? "not listed in the manifest"}`);
|
|
1604
|
-
const file = entry.file;
|
|
1605
|
-
const requestRoot = resolved.root === (root ?? defaultRoot).replace(/\/+$/, "") ? root : resolved.root;
|
|
1606
|
-
return await this.cached(`${requestRoot === void 0 ? "sources" : "explicit"}:${resolved.root}/${file}:${entry.sha256}`, scope, defaultRoot, async () => {
|
|
1607
|
-
const data = await this.load(file.slice(0, -5), requestRoot);
|
|
1608
|
-
if (!this.validDataset(dataset, data)) throw new Error(`Invalid MineRender ${dataset} data at ${resolved.root}/minerender-data/${file}`);
|
|
1609
|
-
return data;
|
|
1610
|
-
});
|
|
1611
|
-
}
|
|
1612
|
-
/**
|
|
1613
|
-
* Uses the exact version when published. Otherwise, selects the nearest patch in the same
|
|
1614
|
-
* release line (older on ties), then the latest earlier release or the oldest published release.
|
|
1615
|
-
* Unpublished snapshots reject.
|
|
1616
|
-
*/
|
|
1617
|
-
static async resolve(root) {
|
|
1618
|
-
const requestRoot = root?.replace(/\/+$/, "");
|
|
1619
|
-
const defaultRoot = AssetLoader.ROOT;
|
|
1620
|
-
root = (root ?? defaultRoot).replace(/\/+$/, "");
|
|
1621
|
-
const scope = AssetLoader.persistentScope;
|
|
1622
|
-
return this.cached(`resolve:${requestRoot === void 0 ? "sources" : "explicit"}:${root}`, scope, defaultRoot, async () => {
|
|
1623
|
-
const requestedVersion = root.slice(root.lastIndexOf("/") + 1);
|
|
1624
|
-
let manifest = await this.manifest(root, requestRoot !== void 0 && release(requestedVersion) ? requestedVersion : void 0, requestRoot);
|
|
1625
|
-
if (manifest) return { requestedVersion, version: manifest.minecraftVersion, root, manifest };
|
|
1626
|
-
const requested = release(requestedVersion);
|
|
1627
|
-
if (!requested) {
|
|
1628
|
-
throw new Error(`No MineRender data is published for ${requestedVersion}; automatic fallback requires a release`);
|
|
1629
|
-
}
|
|
1630
|
-
const base = root.slice(0, root.lastIndexOf("/"));
|
|
1631
|
-
const index = await this.cached(`versions:${base}`, scope, defaultRoot, async () => {
|
|
1632
|
-
const value = await this.load("versions", base);
|
|
1633
|
-
if (!object(value) || !Array.isArray(value.versions) || !value.versions.every((entry) => object(entry) && typeof entry.name === "string")) {
|
|
1634
|
-
throw new Error(`Invalid MineRender version index at ${base}/minerender-data/versions.json`);
|
|
1635
|
-
}
|
|
1636
|
-
return value.versions;
|
|
1637
|
-
});
|
|
1638
|
-
const versions = index.map((entry) => ({ name: entry.name, number: release(entry.name) })).filter((entry) => !!entry.number).sort((left, right) => compare(left.number, right.number));
|
|
1639
|
-
const sameLine = versions.filter((entry) => entry.number[0] === requested[0] && entry.number[1] === requested[1]);
|
|
1640
|
-
const selected = sameLine.sort((left, right) => Math.abs((left.number[2] ?? 0) - (requested[2] ?? 0)) - Math.abs((right.number[2] ?? 0) - (requested[2] ?? 0)) || compare(left.number, right.number))[0] ?? versions.filter((entry) => compare(entry.number, requested) <= 0).pop() ?? versions[0];
|
|
1641
|
-
if (!selected) throw new Error(`No compatible MineRender data is published for ${requestedVersion}`);
|
|
1642
|
-
const resolvedRoot = `${base}/${selected.name}`;
|
|
1643
|
-
manifest = await this.manifest(resolvedRoot, selected.name, resolvedRoot);
|
|
1644
|
-
if (!manifest) throw new Error(`Missing MineRender manifest for published version ${selected.name}`);
|
|
1645
|
-
return { requestedVersion, version: selected.name, root: resolvedRoot, manifest };
|
|
1646
|
-
});
|
|
1647
|
-
}
|
|
1648
|
-
static async manifest(root, expectedVersion, requestRoot) {
|
|
1649
|
-
const data = await this.load("manifest", requestRoot);
|
|
1650
|
-
if (data === void 0) return void 0;
|
|
1651
|
-
if (!object(data) || data.schemaVersion !== 1 || typeof data.minecraftVersion !== "string" || !Number.isInteger(data.dataVersion) || !object(data.unavailable) || !Object.values(data.unavailable).every((value) => typeof value === "string") || !object(data.datasets) || !Object.values(data.datasets).every((entry) => {
|
|
1652
|
-
return object(entry) && typeof entry.file === "string" && /^[a-z0-9][a-z0-9._-]*\.json$/.test(entry.file) && typeof entry.sha256 === "string" && /^[0-9a-f]{64}$/.test(entry.sha256);
|
|
1653
|
-
}) || expectedVersion !== void 0 && data.minecraftVersion !== expectedVersion) {
|
|
1654
|
-
throw new Error(`Invalid MineRender manifest at ${root}/minerender-data/manifest.json`);
|
|
1655
|
-
}
|
|
1656
|
-
return data;
|
|
1657
|
-
}
|
|
1658
|
-
static load(file, root) {
|
|
1659
|
-
const key = new AssetKey("minerender-data", file, void 0, void 0, void 0, ".json", root);
|
|
1660
|
-
key.rootType = void 0;
|
|
1661
|
-
return AssetLoader.get(key, "json" /* JSON */);
|
|
1662
|
-
}
|
|
1663
|
-
static async cached(key, scope, defaultRoot, loader) {
|
|
1664
|
-
const checkSources = () => {
|
|
1665
|
-
if (AssetLoader.persistentScope !== scope || AssetLoader.ROOT !== defaultRoot) throw new Error("Asset sources changed while loading MineRender data; retry the request");
|
|
1666
|
-
};
|
|
1667
|
-
checkSources();
|
|
1668
|
-
return await Caching.mineRenderDataCache.get(scope ? `${scope}
|
|
1669
|
-
${key}` : key, async () => {
|
|
1670
|
-
const value = await loader();
|
|
1671
|
-
checkSources();
|
|
1672
|
-
return value;
|
|
1673
|
-
});
|
|
1674
|
-
}
|
|
1675
|
-
static validDataset(dataset, data) {
|
|
1676
|
-
if (!object(data)) return false;
|
|
1677
|
-
if (dataset === "potionColors") return object(data.effects) && Object.values(data.effects).every(Number.isInteger) && object(data.potions) && Object.values(data.potions).every((effects) => Array.isArray(effects) && effects.every((effect) => object(effect) && typeof effect.id === "string" && Number.isInteger(effect.amplifier)));
|
|
1678
|
-
if (dataset === "legacyBlocks") return object(data.blocks) && Object.values(data.blocks).every((value) => typeof value === "string");
|
|
1679
|
-
if (dataset === "blockCulling") return Array.isArray(data.profiles) && data.profiles.every((profile) => object(profile) && typeof profile.type === "string") && object(data.blocks) && Object.values(data.blocks).every((index) => Number.isInteger(index) && index >= 0 && index < data.profiles.length) && (data.groups === void 0 || object(data.groups) && Object.values(data.groups).every((group) => Array.isArray(group) && group.every((type) => typeof type === "string")));
|
|
1680
|
-
return Object.values(data).every((value) => {
|
|
1681
|
-
if (!object(value)) return false;
|
|
1682
|
-
switch (dataset) {
|
|
1683
|
-
case "itemDefaults":
|
|
1684
|
-
return true;
|
|
1685
|
-
case "blockStates":
|
|
1686
|
-
return Object.values(value).every((property) => object(property) && "default" in property && Array.isArray(property.values));
|
|
1687
|
-
case "blockTints":
|
|
1688
|
-
return ["constant", "grass", "stem", "redstone"].includes(value.source) && (value.source !== "constant" || Number.isInteger(value.color)) && (value.biome === void 0 || ["grass", "foliage", "dry_foliage", "water"].includes(value.biome));
|
|
1689
|
-
case "fluids":
|
|
1690
|
-
return ["water", "lava"].includes(value.kind) && typeof value.renderModel === "boolean" && (value.levelProperty === void 0 || typeof value.levelProperty === "string");
|
|
1691
|
-
case "dyes":
|
|
1692
|
-
return [value.id, value.textureDiffuseColor, value.fireworkColor, value.textColor].every(Number.isInteger);
|
|
1693
|
-
default:
|
|
1694
|
-
return false;
|
|
1695
|
-
}
|
|
1696
|
-
});
|
|
1697
|
-
}
|
|
1698
|
-
};
|
|
1699
|
-
}
|
|
1700
|
-
});
|
|
1701
|
-
|
|
1702
1745
|
// src/assets/BannerPatterns.ts
|
|
1703
1746
|
var DYE_COLORS, BannerPatterns;
|
|
1704
1747
|
var init_BannerPatterns = __esm({
|
|
@@ -2021,6 +2064,7 @@ var init_ItemTints = __esm({
|
|
|
2021
2064
|
var Models;
|
|
2022
2065
|
var init_Models = __esm({
|
|
2023
2066
|
"src/assets/Models.ts"() {
|
|
2067
|
+
init_Renames();
|
|
2024
2068
|
init_Caching();
|
|
2025
2069
|
init_ModelMerger();
|
|
2026
2070
|
init_AssetLoader();
|
|
@@ -2350,6 +2394,11 @@ var init_Models = __esm({
|
|
|
2350
2394
|
key.extension = ".json";
|
|
2351
2395
|
}
|
|
2352
2396
|
const preview = this.snapshotContext(key, context);
|
|
2397
|
+
if (key.type === "item") {
|
|
2398
|
+
const renamed = await Renames.key("items", key);
|
|
2399
|
+
const replacement = renamed && await this.getMerged(renamed, preview);
|
|
2400
|
+
if (replacement) return replacement;
|
|
2401
|
+
}
|
|
2353
2402
|
const dataKey = await this.applyDefaults(key, preview);
|
|
2354
2403
|
const keyStr = AssetLoader.persistentKey(key.serialize() + (key.type === "item" ? this.contextKey(preview) + dataKey : ""));
|
|
2355
2404
|
const model = await Caching.mergedModelCache.get(keyStr, async () => {
|
|
@@ -2383,6 +2432,7 @@ var init_BlockStates = __esm({
|
|
|
2383
2432
|
init_AssetKey();
|
|
2384
2433
|
init_PersistentCache();
|
|
2385
2434
|
init_AssetParsers();
|
|
2435
|
+
init_Renames();
|
|
2386
2436
|
init_MineRenderData();
|
|
2387
2437
|
BlockStates = class {
|
|
2388
2438
|
static get PERSISTENT_CACHE() {
|
|
@@ -2423,6 +2473,9 @@ var init_BlockStates = __esm({
|
|
|
2423
2473
|
if (!key.extension) {
|
|
2424
2474
|
key.extension = ".json";
|
|
2425
2475
|
}
|
|
2476
|
+
const renamed = await Renames.key("blocks", key);
|
|
2477
|
+
const replacement = renamed && await this.get(renamed);
|
|
2478
|
+
if (replacement) return replacement;
|
|
2426
2479
|
const keyStr = key.serialize();
|
|
2427
2480
|
return Caching.blockStateCache.get(keyStr, (k) => {
|
|
2428
2481
|
return this.PERSISTENT_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
|
|
@@ -7984,6 +8037,7 @@ var Fonts = class {
|
|
|
7984
8037
|
init_MineRenderData();
|
|
7985
8038
|
init_ModelTextures();
|
|
7986
8039
|
init_Models();
|
|
8040
|
+
init_Renames();
|
|
7987
8041
|
init_source();
|
|
7988
8042
|
|
|
7989
8043
|
// src/cache/index.ts
|
|
@@ -17298,18 +17352,23 @@ var SectionModels = class {
|
|
|
17298
17352
|
return this.template(variant, choice, tints);
|
|
17299
17353
|
}));
|
|
17300
17354
|
}
|
|
17301
|
-
/** Resolves
|
|
17302
|
-
async prepareState(blockState3, properties = {}, biomeColor) {
|
|
17355
|
+
/** Resolves shared templates; dynamic biome tints are supplied on section placements instead of cached geometry. */
|
|
17356
|
+
async prepareState(blockState3, properties = {}, biomeColor, dynamicBiomeTint = false) {
|
|
17303
17357
|
const prepared = await this.state(blockState3, properties);
|
|
17304
17358
|
if (!prepared) return void 0;
|
|
17305
17359
|
let color2;
|
|
17306
17360
|
const resolveColor = typeof biomeColor === "function" ? () => color2 ?? (color2 = biomeColor()) : biomeColor;
|
|
17307
17361
|
const templates = /* @__PURE__ */ new Map();
|
|
17362
|
+
const source = dynamicBiomeTint && await BlockTints.biomeSource(blockState3.key);
|
|
17308
17363
|
await Promise.all([...prepared.choices].map(async ([variant, choice]) => {
|
|
17309
17364
|
const tints = await BlockTints.get(blockState3.key, prepared.state, choice.model, void 0, resolveColor);
|
|
17310
|
-
|
|
17365
|
+
const biomeTints = source ? await BlockTints.get(blockState3.key, prepared.state, choice.model, void 0, 16777215) : void 0;
|
|
17366
|
+
templates.set(variant, await this.template(variant, choice, tints, biomeTints));
|
|
17311
17367
|
}));
|
|
17312
|
-
return {
|
|
17368
|
+
return {
|
|
17369
|
+
pick: (position) => prepared.groups.map((group) => templates.get(BlockStateResolver.choose(group, position))),
|
|
17370
|
+
biomeTint: source ? [...templates.values()].some((template) => template.biomeTint?.some((value) => value === 1)) || void 0 : void 0
|
|
17371
|
+
};
|
|
17313
17372
|
}
|
|
17314
17373
|
async state(blockState3, properties) {
|
|
17315
17374
|
let states = this.states.get(blockState3);
|
|
@@ -17352,10 +17411,10 @@ var SectionModels = class {
|
|
|
17352
17411
|
}
|
|
17353
17412
|
return { state, groups, choices: prepared };
|
|
17354
17413
|
}
|
|
17355
|
-
async template(variant, { model, atlas, rotation }, tints) {
|
|
17414
|
+
async template(variant, { model, atlas, rotation }, tints, biomeTints) {
|
|
17356
17415
|
let models = this.models.get(model);
|
|
17357
17416
|
if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
|
|
17358
|
-
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
|
|
17417
|
+
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints, biomeTints]);
|
|
17359
17418
|
return cached(models, key, async () => {
|
|
17360
17419
|
const object4 = new SectionModel(model, {
|
|
17361
17420
|
instanceMeshes: false,
|
|
@@ -17369,11 +17428,13 @@ var SectionModels = class {
|
|
|
17369
17428
|
object4.clear();
|
|
17370
17429
|
const keep = [];
|
|
17371
17430
|
const cullFaces = [];
|
|
17431
|
+
const biomeTint = biomeTints ? [] : void 0;
|
|
17372
17432
|
for (const element of atlas.model.elements) {
|
|
17373
17433
|
for (const face of CUBE_FACES) {
|
|
17374
17434
|
const data = element.faces[face];
|
|
17375
17435
|
keep.push(data !== void 0);
|
|
17376
17436
|
if (!data) continue;
|
|
17437
|
+
biomeTint?.push(Number(data.tintindex !== void 0 && biomeTints?.[data.tintindex] !== void 0));
|
|
17377
17438
|
const local = CUBE_FACES.findIndex((name) => name === data.cullface);
|
|
17378
17439
|
if (local < 0) {
|
|
17379
17440
|
cullFaces.push(0);
|
|
@@ -17389,6 +17450,7 @@ var SectionModels = class {
|
|
|
17389
17450
|
geometry,
|
|
17390
17451
|
atlas,
|
|
17391
17452
|
cullFaces: new Uint8Array(cullFaces),
|
|
17453
|
+
biomeTint: biomeTint?.some(Boolean) ? new Uint8Array(biomeTint) : void 0,
|
|
17392
17454
|
occludes: ModelCulling.isOpaqueFullCube(atlas) && ModelCulling.toLocalMask(63, rotation) === 63,
|
|
17393
17455
|
layer: atlas.hasTranslucency ? 1 : 0,
|
|
17394
17456
|
ambientOcclusion: model.ambientocclusion !== false
|
|
@@ -17408,6 +17470,7 @@ var SectionModels = class {
|
|
|
17408
17470
|
};
|
|
17409
17471
|
|
|
17410
17472
|
// src/world/_entities/WorldEntities.ts
|
|
17473
|
+
init_Renames();
|
|
17411
17474
|
init_Entities();
|
|
17412
17475
|
init_EntityObject();
|
|
17413
17476
|
|
|
@@ -17567,8 +17630,10 @@ var WorldEntities = class {
|
|
|
17567
17630
|
try {
|
|
17568
17631
|
if (!current()) return;
|
|
17569
17632
|
const models = await Entities.getEntityList();
|
|
17570
|
-
|
|
17571
|
-
const
|
|
17633
|
+
const renamed = models.includes(placement.key.path) ? void 0 : await Renames.key("entities", placement.key);
|
|
17634
|
+
const key = renamed && models.includes(renamed.path) ? renamed : placement.key;
|
|
17635
|
+
if (!models.includes(key.path) || !current()) return;
|
|
17636
|
+
const model = await Entities.getEntity(key, placement.texture, { when: placement.when });
|
|
17572
17637
|
if (!model || !current()) return;
|
|
17573
17638
|
let tints = placement.tints;
|
|
17574
17639
|
if (placement.woolDye) {
|
|
@@ -17583,7 +17648,7 @@ var WorldEntities = class {
|
|
|
17583
17648
|
object4.rotation.y = placement.yaw;
|
|
17584
17649
|
await object4.init();
|
|
17585
17650
|
if (!current()) return;
|
|
17586
|
-
placement.owner.set(object4, { type:
|
|
17651
|
+
placement.owner.set(object4, { type: key.toNamespacedString(), position: placement.position });
|
|
17587
17652
|
this.scene.add(object4);
|
|
17588
17653
|
} catch (error) {
|
|
17589
17654
|
console.warn(`Could not draw saved entity ${placement.key.toNamespacedString()}, skipping it`, error);
|
|
@@ -17615,6 +17680,132 @@ var WorldEntities = class {
|
|
|
17615
17680
|
// src/world/MineRenderWorld.ts
|
|
17616
17681
|
init_FluidQuads();
|
|
17617
17682
|
|
|
17683
|
+
// src/world/_biomes/BiomeBlend.ts
|
|
17684
|
+
init_AssetKey();
|
|
17685
|
+
init_AssetLoader();
|
|
17686
|
+
init_Caching();
|
|
17687
|
+
var MAX_TILES = 256;
|
|
17688
|
+
function validateBiomeBlendRadius(radius) {
|
|
17689
|
+
if (!Number.isInteger(radius) || radius < 0 || radius > 7) {
|
|
17690
|
+
throw new RangeError("biomeBlendRadius must be an integer from 0 to 7");
|
|
17691
|
+
}
|
|
17692
|
+
}
|
|
17693
|
+
var BiomeBlend = class {
|
|
17694
|
+
constructor(biomeAt, radius) {
|
|
17695
|
+
this.biomeAt = biomeAt;
|
|
17696
|
+
this.tiles = /* @__PURE__ */ new Map();
|
|
17697
|
+
this.generation = Caching.generation;
|
|
17698
|
+
validateBiomeBlendRadius(radius);
|
|
17699
|
+
this._radius = radius;
|
|
17700
|
+
}
|
|
17701
|
+
get radius() {
|
|
17702
|
+
return this._radius;
|
|
17703
|
+
}
|
|
17704
|
+
clear() {
|
|
17705
|
+
this.tiles.clear();
|
|
17706
|
+
}
|
|
17707
|
+
invalidateColumn(x, z) {
|
|
17708
|
+
for (const [key, tile] of this.tiles) {
|
|
17709
|
+
if (Math.abs(tile.x - x) <= 1 && Math.abs(tile.z - z) <= 1) this.tiles.delete(key);
|
|
17710
|
+
}
|
|
17711
|
+
}
|
|
17712
|
+
async getColor(kind, origin, position) {
|
|
17713
|
+
const [x, y, z] = position;
|
|
17714
|
+
const biome = this.biomeAt(x, y, z);
|
|
17715
|
+
if (biome === void 0) return void 0;
|
|
17716
|
+
if (this.generation !== Caching.generation) {
|
|
17717
|
+
this.clear();
|
|
17718
|
+
this.generation = Caching.generation;
|
|
17719
|
+
}
|
|
17720
|
+
const tileX = Math.floor(x / 16), tileZ = Math.floor(z / 16), quartY = Math.floor(y / 4);
|
|
17721
|
+
const root = origin?.root ?? AssetLoader.ROOT;
|
|
17722
|
+
const key = JSON.stringify([root, AssetLoader.persistentScope, kind, this._radius, tileX, quartY, tileZ]);
|
|
17723
|
+
let tile = this.tiles.get(key);
|
|
17724
|
+
if (tile) {
|
|
17725
|
+
this.tiles.delete(key);
|
|
17726
|
+
this.tiles.set(key, tile);
|
|
17727
|
+
} else {
|
|
17728
|
+
const snapshot = new AssetKey("minecraft", "", void 0, void 0, "assets", ".json", root);
|
|
17729
|
+
const colors = this.buildTile(kind, snapshot, tileX, quartY, tileZ, this._radius).then((result2) => {
|
|
17730
|
+
if (!result2.complete && this.tiles.get(key)?.colors === colors) this.tiles.delete(key);
|
|
17731
|
+
return result2;
|
|
17732
|
+
}, (error) => {
|
|
17733
|
+
if (this.tiles.get(key)?.colors === colors) this.tiles.delete(key);
|
|
17734
|
+
throw error;
|
|
17735
|
+
});
|
|
17736
|
+
tile = { x: tileX, z: tileZ, colors };
|
|
17737
|
+
this.tiles.set(key, tile);
|
|
17738
|
+
if (this.tiles.size > MAX_TILES) this.tiles.delete(this.tiles.keys().next().value);
|
|
17739
|
+
}
|
|
17740
|
+
const result = await tile.colors;
|
|
17741
|
+
if (result.failures.has(biome)) throw result.failures.get(biome);
|
|
17742
|
+
const color2 = result.colors[(z - tileZ * 16) * 16 + x - tileX * 16];
|
|
17743
|
+
return color2 === -1 ? void 0 : color2;
|
|
17744
|
+
}
|
|
17745
|
+
async buildTile(kind, origin, tileX, quartY, tileZ, radius) {
|
|
17746
|
+
const size = 16 + radius * 2, stride = size + 1;
|
|
17747
|
+
const startX = tileX * 16 - radius, startZ = tileZ * 16 - radius;
|
|
17748
|
+
const quartX = Math.floor(startX / 4), quartZ = Math.floor(startZ / 4);
|
|
17749
|
+
const quartSize = Math.floor((startX + size - 1) / 4) - quartX + 1;
|
|
17750
|
+
const ids = new Array(quartSize * quartSize);
|
|
17751
|
+
const biomes = /* @__PURE__ */ new Map();
|
|
17752
|
+
for (let z = 0; z < quartSize; z++) {
|
|
17753
|
+
for (let x = 0; x < quartSize; x++) {
|
|
17754
|
+
const id = this.biomeAt((quartX + x) * 4, quartY * 4, (quartZ + z) * 4);
|
|
17755
|
+
ids[z * quartSize + x] = id;
|
|
17756
|
+
if (id !== void 0 && !biomes.has(id)) {
|
|
17757
|
+
biomes.set(id, (async () => {
|
|
17758
|
+
const swamp = kind === "grass" && (await Biomes.get(id, origin))?.effects.grass_color_modifier === "swamp";
|
|
17759
|
+
return { color: swamp ? void 0 : await Biomes.getColor(id, kind, origin), swamp };
|
|
17760
|
+
})());
|
|
17761
|
+
}
|
|
17762
|
+
}
|
|
17763
|
+
}
|
|
17764
|
+
const failures = /* @__PURE__ */ new Map();
|
|
17765
|
+
const loaded = new Map(await Promise.all([...biomes].map(async ([id, color2]) => {
|
|
17766
|
+
try {
|
|
17767
|
+
return [id, await color2];
|
|
17768
|
+
} catch (error) {
|
|
17769
|
+
failures.set(id, error);
|
|
17770
|
+
return [id, { color: void 0, swamp: false }];
|
|
17771
|
+
}
|
|
17772
|
+
})));
|
|
17773
|
+
const complete = [...loaded.values()].every((value) => value.swamp || value.color !== void 0);
|
|
17774
|
+
const samples = new Int32Array(size * size).fill(-1);
|
|
17775
|
+
const sums = Array.from({ length: 4 }, () => new Uint32Array(stride * stride));
|
|
17776
|
+
for (let z = 0; z < size; z++) {
|
|
17777
|
+
const row = [0, 0, 0, 0];
|
|
17778
|
+
for (let x = 0; x < size; x++) {
|
|
17779
|
+
const id = ids[(Math.floor((startZ + z) / 4) - quartZ) * quartSize + Math.floor((startX + x) / 4) - quartX];
|
|
17780
|
+
const value = id === void 0 ? void 0 : loaded.get(id);
|
|
17781
|
+
const color2 = value?.swamp ? swampGrassColor(startX + x, startZ + z) : value?.color;
|
|
17782
|
+
if (color2 !== void 0) {
|
|
17783
|
+
samples[z * size + x] = color2;
|
|
17784
|
+
row[0] += color2 >> 16 & 255;
|
|
17785
|
+
row[1] += color2 >> 8 & 255;
|
|
17786
|
+
row[2] += color2 & 255;
|
|
17787
|
+
row[3]++;
|
|
17788
|
+
}
|
|
17789
|
+
for (let channel = 0; channel < 4; channel++) {
|
|
17790
|
+
sums[channel][(z + 1) * stride + x + 1] = sums[channel][z * stride + x + 1] + row[channel];
|
|
17791
|
+
}
|
|
17792
|
+
}
|
|
17793
|
+
}
|
|
17794
|
+
const colors = new Int32Array(256).fill(-1), width = radius * 2 + 1;
|
|
17795
|
+
for (let z = 0; z < 16; z++) {
|
|
17796
|
+
for (let x = 0; x < 16; x++) {
|
|
17797
|
+
if (samples[(z + radius) * size + x + radius] === -1) continue;
|
|
17798
|
+
const topLeft = z * stride + x, topRight = topLeft + width;
|
|
17799
|
+
const bottomLeft = (z + width) * stride + x, bottomRight = bottomLeft + width;
|
|
17800
|
+
const sum = (values) => values[bottomRight] - values[bottomLeft] - values[topRight] + values[topLeft];
|
|
17801
|
+
const count = sum(sums[3]);
|
|
17802
|
+
if (count) colors[z * 16 + x] = Math.floor(sum(sums[0]) / count) << 16 | Math.floor(sum(sums[1]) / count) << 8 | Math.floor(sum(sums[2]) / count);
|
|
17803
|
+
}
|
|
17804
|
+
}
|
|
17805
|
+
return { colors, complete, failures };
|
|
17806
|
+
}
|
|
17807
|
+
};
|
|
17808
|
+
|
|
17618
17809
|
// src/world/WorldLight.ts
|
|
17619
17810
|
function copySectionLight(light) {
|
|
17620
17811
|
const copy = {};
|
|
@@ -17678,12 +17869,17 @@ var MineRenderWorld = class {
|
|
|
17678
17869
|
this.lightValidity = /* @__PURE__ */ new Map();
|
|
17679
17870
|
this.highestSkySection = /* @__PURE__ */ new Map();
|
|
17680
17871
|
this.ambientOcclusionPending = false;
|
|
17872
|
+
this.pendingBiomes = /* @__PURE__ */ new Map();
|
|
17681
17873
|
this.pendingCulling = /* @__PURE__ */ new Map();
|
|
17682
17874
|
if (options.savedLighting && !options.sectionMeshing) throw new TypeError("savedLighting requires sectionMeshing");
|
|
17683
17875
|
if (options.ambientOcclusion && !options.sectionMeshing) throw new TypeError("ambientOcclusion requires sectionMeshing");
|
|
17684
17876
|
if (options.ambientLight !== void 0 && (!Number.isFinite(options.ambientLight) || options.ambientLight < 0 || options.ambientLight > 1)) {
|
|
17685
17877
|
throw new RangeError("ambientLight must be a number from 0 to 1");
|
|
17686
17878
|
}
|
|
17879
|
+
this._biomeBlendRadius = options.biomeBlendRadius ?? 0;
|
|
17880
|
+
validateBiomeBlendRadius(this._biomeBlendRadius);
|
|
17881
|
+
if (this._biomeBlendRadius > 0 && !options.sectionMeshing) throw new TypeError("biomeBlendRadius requires sectionMeshing");
|
|
17882
|
+
if (this._biomeBlendRadius) this.biomeBlend = new BiomeBlend((x, y, z) => this.getBiomeAt(x, y, z), this._biomeBlendRadius);
|
|
17687
17883
|
this.scene = scene;
|
|
17688
17884
|
this.savedLighting = options.savedLighting ?? false;
|
|
17689
17885
|
this.ambientLight = options.ambientLight ?? 0;
|
|
@@ -17724,6 +17920,65 @@ var MineRenderWorld = class {
|
|
|
17724
17920
|
await this.updateCulling([]);
|
|
17725
17921
|
this.ambientOcclusionPending = false;
|
|
17726
17922
|
}
|
|
17923
|
+
/** Horizontal biome-color sampling radius, from 0 to 7 blocks. Zero disables blending. */
|
|
17924
|
+
get biomeBlendRadius() {
|
|
17925
|
+
return this._biomeBlendRadius;
|
|
17926
|
+
}
|
|
17927
|
+
/** Refreshes loaded grass, foliage, and water colors without reloading their source chunks. */
|
|
17928
|
+
async setBiomeBlendRadius(radius) {
|
|
17929
|
+
validateBiomeBlendRadius(radius);
|
|
17930
|
+
if (radius > 0 && !this.sectionModels) throw new TypeError("biomeBlendRadius requires sectionMeshing");
|
|
17931
|
+
if (radius !== this._biomeBlendRadius) {
|
|
17932
|
+
this._biomeBlendRadius = radius;
|
|
17933
|
+
this.biomeBlend?.clear();
|
|
17934
|
+
this.biomeBlend = radius ? new BiomeBlend((x, y, z) => this.getBiomeAt(x, y, z), radius) : void 0;
|
|
17935
|
+
for (const chunk of this._chunks.values()) this.pendingBiomes.set(chunk, void 0);
|
|
17936
|
+
}
|
|
17937
|
+
const failures = await this.refreshBiomes();
|
|
17938
|
+
if (failures.size) throw failures.values().next().value;
|
|
17939
|
+
}
|
|
17940
|
+
markBiomesChanged(x, z, y, includeColumn = true) {
|
|
17941
|
+
const queued = [];
|
|
17942
|
+
if (!this.biomeBlend) return queued;
|
|
17943
|
+
this.biomeBlend.invalidateColumn(x, z);
|
|
17944
|
+
for (const chunk of this._chunks.values()) {
|
|
17945
|
+
if (Math.abs(chunk.x - x) > 1 || Math.abs(chunk.z - z) > 1 || !includeColumn && chunk.x === x && chunk.z === z || y !== void 0 && chunk.y !== y && chunk.y !== y + 1) continue;
|
|
17946
|
+
queued.push(chunk);
|
|
17947
|
+
if (!this.pendingBiomes.has(chunk)) this.pendingBiomes.set(chunk, []);
|
|
17948
|
+
const columns = this.pendingBiomes.get(chunk);
|
|
17949
|
+
if (columns && !columns.some((column) => column.x === x && column.z === z)) columns.push({ x, z });
|
|
17950
|
+
}
|
|
17951
|
+
return queued;
|
|
17952
|
+
}
|
|
17953
|
+
async refreshBiomes() {
|
|
17954
|
+
if (this.biomeRefreshing) await this.biomeRefreshing;
|
|
17955
|
+
if (!this.pendingBiomes.size) return /* @__PURE__ */ new Map();
|
|
17956
|
+
return this.biomeRefreshing ?? (this.biomeRefreshing = Promise.resolve().then(async () => {
|
|
17957
|
+
const failures = /* @__PURE__ */ new Map();
|
|
17958
|
+
const batch = [...this.pendingBiomes.keys()];
|
|
17959
|
+
let sliceStart = performance.now();
|
|
17960
|
+
try {
|
|
17961
|
+
for (const chunk of batch) {
|
|
17962
|
+
const columns = this.pendingBiomes.get(chunk);
|
|
17963
|
+
this.pendingBiomes.delete(chunk);
|
|
17964
|
+
if (this._chunks.get(`${chunk.x}_${chunk.y}_${chunk.z}`) !== chunk) continue;
|
|
17965
|
+
try {
|
|
17966
|
+
await chunk.refreshBiomes(columns);
|
|
17967
|
+
} catch (error) {
|
|
17968
|
+
failures.set(chunk, error);
|
|
17969
|
+
if (this._chunks.get(`${chunk.x}_${chunk.y}_${chunk.z}`) === chunk) this.pendingBiomes.set(chunk, void 0);
|
|
17970
|
+
}
|
|
17971
|
+
if (performance.now() - sliceStart > 8) {
|
|
17972
|
+
await yieldToEventLoop();
|
|
17973
|
+
sliceStart = performance.now();
|
|
17974
|
+
}
|
|
17975
|
+
}
|
|
17976
|
+
return failures;
|
|
17977
|
+
} finally {
|
|
17978
|
+
this.biomeRefreshing = void 0;
|
|
17979
|
+
}
|
|
17980
|
+
}));
|
|
17981
|
+
}
|
|
17727
17982
|
getBlockAt(posOrX, y, z) {
|
|
17728
17983
|
if (typeof posOrX == "number") {
|
|
17729
17984
|
return this.getBlockAt(new Vector319(posOrX, y, z));
|
|
@@ -17959,6 +18214,9 @@ var MineRenderWorld = class {
|
|
|
17959
18214
|
}
|
|
17960
18215
|
await placeEntities?.();
|
|
17961
18216
|
} finally {
|
|
18217
|
+
this.markBiomesChanged(chunk.x, chunk.z, void 0, false);
|
|
18218
|
+
const failures = await this.refreshBiomes();
|
|
18219
|
+
if (failures.size) console.warn("Failed to refresh biome colors", failures.values().next().value);
|
|
17962
18220
|
await this.updateCulling([...changes.values()]);
|
|
17963
18221
|
}
|
|
17964
18222
|
}
|
|
@@ -17975,11 +18233,14 @@ var MineRenderWorld = class {
|
|
|
17975
18233
|
try {
|
|
17976
18234
|
await this.clearChunkColumn(x, z);
|
|
17977
18235
|
} finally {
|
|
18236
|
+
const failures = await this.refreshBiomes();
|
|
18237
|
+
if (failures.size) console.warn("Failed to refresh biome colors", failures.values().next().value);
|
|
17978
18238
|
await this.updateCulling([]);
|
|
17979
18239
|
}
|
|
17980
18240
|
}
|
|
17981
18241
|
async clearChunkColumn(x, z) {
|
|
17982
18242
|
this.biomes.delete(`${x}_${z}`);
|
|
18243
|
+
this.markBiomesChanged(x, z, void 0, false);
|
|
17983
18244
|
if (this.savedLighting) {
|
|
17984
18245
|
for (const y of this.light.get(`${x}_${z}`)?.keys() ?? []) this.markSectionChanged({ x, y, z });
|
|
17985
18246
|
}
|
|
@@ -17990,6 +18251,7 @@ var MineRenderWorld = class {
|
|
|
17990
18251
|
for (const [key, section] of previous) {
|
|
17991
18252
|
this._chunks.delete(key);
|
|
17992
18253
|
this.pendingCulling.delete(section);
|
|
18254
|
+
this.pendingBiomes.delete(section);
|
|
17993
18255
|
await section.clear(false);
|
|
17994
18256
|
this.markSectionChanged(section);
|
|
17995
18257
|
}
|
|
@@ -18024,6 +18286,8 @@ var MineRenderWorld = class {
|
|
|
18024
18286
|
const chunks = [...this._chunks.values()];
|
|
18025
18287
|
this._chunks.clear();
|
|
18026
18288
|
this.biomes.clear();
|
|
18289
|
+
this.biomeBlend?.clear();
|
|
18290
|
+
this.pendingBiomes.clear();
|
|
18027
18291
|
this.light.clear();
|
|
18028
18292
|
this.lightValidity.clear();
|
|
18029
18293
|
this.highestSkySection.clear();
|
|
@@ -18142,8 +18406,14 @@ var MineRenderWorld = class {
|
|
|
18142
18406
|
column?.delete(y);
|
|
18143
18407
|
if (!column?.size) this.biomes.delete(columnKey);
|
|
18144
18408
|
}
|
|
18145
|
-
|
|
18146
|
-
|
|
18409
|
+
if (this.biomeBlend) {
|
|
18410
|
+
const queued = this.markBiomesChanged(x, z, y);
|
|
18411
|
+
const failures = await this.refreshBiomes();
|
|
18412
|
+
for (const chunk2 of queued) if (failures.has(chunk2)) throw failures.get(chunk2);
|
|
18413
|
+
} else {
|
|
18414
|
+
await chunk.refreshBiomes();
|
|
18415
|
+
await this._chunks.get(`${x}_${y + 1}_${z}`)?.refreshBiomes();
|
|
18416
|
+
}
|
|
18147
18417
|
},
|
|
18148
18418
|
this.savedLighting ? (x2, y2, z2) => this.lightLevelAt(x2, y2, z2) : void 0,
|
|
18149
18419
|
async () => {
|
|
@@ -18160,7 +18430,8 @@ var MineRenderWorld = class {
|
|
|
18160
18430
|
await this.updateCulling([]);
|
|
18161
18431
|
}
|
|
18162
18432
|
},
|
|
18163
|
-
this.ambientLight
|
|
18433
|
+
this.ambientLight,
|
|
18434
|
+
() => this.biomeBlend
|
|
18164
18435
|
);
|
|
18165
18436
|
this._chunks.set(key, chunk);
|
|
18166
18437
|
}
|
|
@@ -18279,9 +18550,10 @@ var LIGHT_SAMPLE_OFFSETS = [0, 1, -1, 324, -324, 18, -18];
|
|
|
18279
18550
|
|
|
18280
18551
|
// src/world/SectionGeometry.ts
|
|
18281
18552
|
function buildSectionGeometry(input) {
|
|
18282
|
-
const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize, fluids, light, ambientLight, ambientOcclusion } = input;
|
|
18553
|
+
const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize, fluids, light, ambientLight, ambientOcclusion, biomeTints } = input;
|
|
18283
18554
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
18284
18555
|
if (count > indices.length || count > templates.length || count > cullMasks.length) throw new RangeError("Section entry count exceeds its arrays");
|
|
18556
|
+
if (biomeTints && biomeTints.length !== count * 3) throw new RangeError("Section biome tints must contain RGB for each placement");
|
|
18285
18557
|
if (fluids && fluids.cells.length !== 5832) throw new RangeError("Section fluid cells must cover an 18\xD718\xD718 neighborhood");
|
|
18286
18558
|
if (fluids?.waterTints && fluids.waterTints.length !== 4096 * 3) throw new RangeError("Section water tints must contain RGB for 4096 cells");
|
|
18287
18559
|
if (light && (light.length !== 5832 || light.some((level) => level > 15 && level !== 255))) {
|
|
@@ -18296,7 +18568,7 @@ function buildSectionGeometry(input) {
|
|
|
18296
18568
|
const cornerBrightness2 = ambientOcclusion ? new Float32Array(4) : void 0;
|
|
18297
18569
|
for (const template of templateData) {
|
|
18298
18570
|
const { quads } = template;
|
|
18299
|
-
if (template.positions.length !== quads * 12 || template.normals.length !== quads * 12 || template.uvs.length !== quads * 8 || template.uvBounds.length !== quads * 16 || template.colors.length !== quads * 12 || template.indices.length !== quads * 6 || template.cullFaces.length !== quads) {
|
|
18571
|
+
if (template.positions.length !== quads * 12 || template.normals.length !== quads * 12 || template.uvs.length !== quads * 8 || template.uvBounds.length !== quads * 16 || template.colors.length !== quads * 12 || template.indices.length !== quads * 6 || template.cullFaces.length !== quads || template.biomeTint && template.biomeTint.length !== quads) {
|
|
18300
18572
|
throw new RangeError("Section template arrays do not match its quad count");
|
|
18301
18573
|
}
|
|
18302
18574
|
}
|
|
@@ -18406,6 +18678,7 @@ function buildSectionGeometry(input) {
|
|
|
18406
18678
|
blockIndices[indexOffset / 6] = indices[entry];
|
|
18407
18679
|
if (ambientMask) quadAmbientOcclusion(template.positions, template.normals, quad, ambientMask, cornerBrightness2);
|
|
18408
18680
|
const brightness = light ? lightBrightness(light[lightIndex + LIGHT_SAMPLE_OFFSETS[quadLightIndex(template.positions, template.normals, quad)]], ambientLight) : 1;
|
|
18681
|
+
const tint = biomeTints && template.biomeTint?.[quad] && !Number.isNaN(biomeTints[entry * 3]) ? entry * 3 : void 0;
|
|
18409
18682
|
const start = vertex;
|
|
18410
18683
|
for (let corner = 0; corner < 4; corner++, vertex++) {
|
|
18411
18684
|
const source = quad * 4 + corner;
|
|
@@ -18422,9 +18695,9 @@ function buildSectionGeometry(input) {
|
|
|
18422
18695
|
uvBounds[vertex * 4 + 2] = mapU(template.uvBounds[source * 4 + 2]);
|
|
18423
18696
|
uvBounds[vertex * 4 + 3] = mapV(template.uvBounds[source * 4 + 3]);
|
|
18424
18697
|
const factor = brightness * (ambientMask ? cornerBrightness2[corner] : 1);
|
|
18425
|
-
colors[vertex * 3] = template.colors[source * 3] * factor;
|
|
18426
|
-
colors[vertex * 3 + 1] = template.colors[source * 3 + 1] * factor;
|
|
18427
|
-
colors[vertex * 3 + 2] = template.colors[source * 3 + 2] * factor;
|
|
18698
|
+
colors[vertex * 3] = (tint === void 0 ? template.colors[source * 3] : biomeTints[tint]) * factor;
|
|
18699
|
+
colors[vertex * 3 + 1] = (tint === void 0 ? template.colors[source * 3 + 1] : biomeTints[tint + 1]) * factor;
|
|
18700
|
+
colors[vertex * 3 + 2] = (tint === void 0 ? template.colors[source * 3 + 2] : biomeTints[tint + 2]) * factor;
|
|
18428
18701
|
}
|
|
18429
18702
|
for (let corner = 0; corner < 6; corner++) outputIndices[indexOffset++] = start + template.indices[quad * 6 + corner];
|
|
18430
18703
|
}
|
|
@@ -18545,7 +18818,11 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
|
18545
18818
|
...lighting
|
|
18546
18819
|
};
|
|
18547
18820
|
for (let i = 0; i < entries.length; i++) {
|
|
18548
|
-
const { index, template, cullMask } = entries[i];
|
|
18821
|
+
const { index, template, cullMask, biomeTint } = entries[i];
|
|
18822
|
+
if (biomeTint) {
|
|
18823
|
+
input.biomeTints ?? (input.biomeTints = new Float32Array(entries.length * 3).fill(NaN));
|
|
18824
|
+
input.biomeTints.set(biomeTint, i * 3);
|
|
18825
|
+
}
|
|
18549
18826
|
let id = templateIds.get(template);
|
|
18550
18827
|
if (id === void 0) {
|
|
18551
18828
|
let atlas = atlasIds.get(template.atlas);
|
|
@@ -18572,6 +18849,7 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
|
18572
18849
|
colors: new Float32Array(quads * 12),
|
|
18573
18850
|
indices: new Uint16Array(quads * 6),
|
|
18574
18851
|
cullFaces: new Uint8Array(template.cullFaces),
|
|
18852
|
+
biomeTint: template.biomeTint ? new Uint8Array(template.biomeTint) : void 0,
|
|
18575
18853
|
atlas: 0,
|
|
18576
18854
|
layer: template.layer,
|
|
18577
18855
|
ambientOcclusion: template.ambientOcclusion
|
|
@@ -18716,7 +18994,7 @@ init_CubeFace();
|
|
|
18716
18994
|
init_InstanceReference();
|
|
18717
18995
|
var WATER_TINT2 = new Color13(4159204).toArray();
|
|
18718
18996
|
var Chunk = class _Chunk {
|
|
18719
|
-
constructor(scene, x, y, z, onBlocksChanged, sectionModels, neighborCell, biomeAt, onBiomesChanged, lightAt, onLightCleared, ambientLight = 0) {
|
|
18997
|
+
constructor(scene, x, y, z, onBlocksChanged, sectionModels, neighborCell, biomeAt, onBiomesChanged, lightAt, onLightCleared, ambientLight = 0, biomeBlend) {
|
|
18720
18998
|
this.onBlocksChanged = onBlocksChanged;
|
|
18721
18999
|
this.sectionModels = sectionModels;
|
|
18722
19000
|
this.neighborCell = neighborCell;
|
|
@@ -18725,6 +19003,7 @@ var Chunk = class _Chunk {
|
|
|
18725
19003
|
this.lightAt = lightAt;
|
|
18726
19004
|
this.onLightCleared = onLightCleared;
|
|
18727
19005
|
this.ambientLight = ambientLight;
|
|
19006
|
+
this.biomeBlend = biomeBlend;
|
|
18728
19007
|
this.data = new ChunkData();
|
|
18729
19008
|
this.renderedBlocks = /* @__PURE__ */ new Map();
|
|
18730
19009
|
this.objectBlocks = /* @__PURE__ */ new Set();
|
|
@@ -18734,6 +19013,7 @@ var Chunk = class _Chunk {
|
|
|
18734
19013
|
this.blockPosition = new Vector320();
|
|
18735
19014
|
this.meshDirty = false;
|
|
18736
19015
|
this.meshGeneration = 0;
|
|
19016
|
+
this.blockChanges = /* @__PURE__ */ new Set();
|
|
18737
19017
|
this.scene = scene;
|
|
18738
19018
|
this.x = x;
|
|
18739
19019
|
this.y = y;
|
|
@@ -18832,10 +19112,20 @@ var Chunk = class _Chunk {
|
|
|
18832
19112
|
return this.refreshBiomes();
|
|
18833
19113
|
}
|
|
18834
19114
|
/** Rebuilds biome-tinted blocks while preserving hidden blocks and unrelated render objects. */
|
|
18835
|
-
|
|
19115
|
+
refreshBiomes(columns) {
|
|
19116
|
+
return this.changeBlocks(() => this.refreshBiomeBlocks(columns), true);
|
|
19117
|
+
}
|
|
19118
|
+
async refreshBiomeBlocks(columns) {
|
|
19119
|
+
const blend = columns && this.biomeBlend?.();
|
|
19120
|
+
const ownColumn = columns?.some((column) => column.x === this.x && column.z === this.z);
|
|
18836
19121
|
const affected = /* @__PURE__ */ new Map();
|
|
18837
19122
|
const blocks = [];
|
|
18838
19123
|
for (const index of this.renderedBlocks.keys()) {
|
|
19124
|
+
if (blend) {
|
|
19125
|
+
if (this.objectBlocks.has(index) && !ownColumn) continue;
|
|
19126
|
+
const [x, , z] = this.positionAt(index), radius = blend.radius;
|
|
19127
|
+
if (!columns.some((column) => x >= column.x * 16 - radius && x <= column.x * 16 + 15 + radius && z >= column.z * 16 - radius && z <= column.z * 16 + 15 + radius)) continue;
|
|
19128
|
+
}
|
|
18839
19129
|
const block = this.data.get(index);
|
|
18840
19130
|
const stateKey = ChunkData.paletteKey(block);
|
|
18841
19131
|
let matches = affected.get(stateKey);
|
|
@@ -18850,10 +19140,32 @@ var Chunk = class _Chunk {
|
|
|
18850
19140
|
}
|
|
18851
19141
|
if (await matches) blocks.push({ index, block });
|
|
18852
19142
|
}
|
|
19143
|
+
if (blend) {
|
|
19144
|
+
const groups = /* @__PURE__ */ new Map();
|
|
19145
|
+
for (const entry of blocks) {
|
|
19146
|
+
const key = ChunkData.paletteKey(entry.block);
|
|
19147
|
+
let group = groups.get(key);
|
|
19148
|
+
if (!group) groups.set(key, group = []);
|
|
19149
|
+
group.push(entry);
|
|
19150
|
+
}
|
|
19151
|
+
blocks.length = 0;
|
|
19152
|
+
const biomes = /* @__PURE__ */ new Map();
|
|
19153
|
+
for (const group of groups.values()) {
|
|
19154
|
+
const { contexts, fluid } = await this.resolveState(group[0].block, group.map((entry) => entry.index), biomes);
|
|
19155
|
+
for (const entry of group) {
|
|
19156
|
+
const context = contexts.get(entry.index);
|
|
19157
|
+
const current = this.sectionBlocks.get(entry.index)?.[0]?.biomeTint;
|
|
19158
|
+
const tint = context?.tint;
|
|
19159
|
+
const tintChanged = !!current !== !!tint || tint?.some((value, channel) => value !== current?.[channel]);
|
|
19160
|
+
const waterChanged = fluid?.kind === "water" && (context?.waterTint ?? WATER_TINT2).some((value, channel) => Math.fround(value) !== (this.waterTints?.[entry.index * 3 + channel] ?? Math.fround(WATER_TINT2[channel])));
|
|
19161
|
+
if (this.objectBlocks.has(entry.index) || tintChanged || waterChanged) blocks.push(entry);
|
|
19162
|
+
}
|
|
19163
|
+
}
|
|
19164
|
+
}
|
|
18853
19165
|
if (!blocks.length) return;
|
|
18854
19166
|
const hidden = new Set(this.hiddenBlocks);
|
|
18855
19167
|
try {
|
|
18856
|
-
await this.
|
|
19168
|
+
await this.replaceBlocks(blocks, true);
|
|
18857
19169
|
} finally {
|
|
18858
19170
|
for (const index of hidden) {
|
|
18859
19171
|
this.hiddenBlocks.add(index);
|
|
@@ -18893,6 +19205,20 @@ var Chunk = class _Chunk {
|
|
|
18893
19205
|
}
|
|
18894
19206
|
/** Places cells in input order and returns changed world positions without refreshing neighbors. */
|
|
18895
19207
|
async placeBlocks(blocks) {
|
|
19208
|
+
return this.changeBlocks(() => this.replaceBlocks(blocks));
|
|
19209
|
+
}
|
|
19210
|
+
changeBlocks(change, refreshBiomes = false) {
|
|
19211
|
+
const before = refreshBiomes ? Promise.allSettled(this.blockChanges) : this.biomeChange;
|
|
19212
|
+
const result = before ? before.catch(() => void 0).then(change) : change();
|
|
19213
|
+
const pending = result.finally(() => {
|
|
19214
|
+
this.blockChanges.delete(pending);
|
|
19215
|
+
if (this.biomeChange === pending) this.biomeChange = void 0;
|
|
19216
|
+
});
|
|
19217
|
+
this.blockChanges.add(pending);
|
|
19218
|
+
if (refreshBiomes) this.biomeChange = pending;
|
|
19219
|
+
return pending;
|
|
19220
|
+
}
|
|
19221
|
+
async replaceBlocks(blocks, preserveDataOnFailure = false) {
|
|
18896
19222
|
const placements = blocks.map(({ index, block }) => ({
|
|
18897
19223
|
index,
|
|
18898
19224
|
key: block && !ChunkData.isAir(block) ? ChunkData.paletteKey(block) : void 0,
|
|
@@ -18912,6 +19238,10 @@ var Chunk = class _Chunk {
|
|
|
18912
19238
|
const biomes = /* @__PURE__ */ new Map();
|
|
18913
19239
|
const settled = await Promise.allSettled([...states.values()].map(({ stored, indices }) => this.resolveState(stored, indices, biomes)));
|
|
18914
19240
|
const resolutions = new Map([...states.keys()].map((key, i) => [key, settled[i]]));
|
|
19241
|
+
if (preserveDataOnFailure) {
|
|
19242
|
+
const rejected = settled.find((result) => result.status === "rejected");
|
|
19243
|
+
if (rejected?.status === "rejected") throw rejected.reason;
|
|
19244
|
+
}
|
|
18915
19245
|
const positions = [];
|
|
18916
19246
|
let failed = false, failure;
|
|
18917
19247
|
for (const { index, key, block } of placements) {
|
|
@@ -18949,7 +19279,7 @@ var Chunk = class _Chunk {
|
|
|
18949
19279
|
}
|
|
18950
19280
|
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : (context?.prepared ?? prepared)?.pick(variantPosition);
|
|
18951
19281
|
if (templates) {
|
|
18952
|
-
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0 })));
|
|
19282
|
+
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0, biomeTint: context?.tint })));
|
|
18953
19283
|
this.meshDirty = true;
|
|
18954
19284
|
} else {
|
|
18955
19285
|
object4 = await this.scene.addBlock(blockState3, {
|
|
@@ -18968,7 +19298,11 @@ var Chunk = class _Chunk {
|
|
|
18968
19298
|
}, object: object4 });
|
|
18969
19299
|
if (object4) this.objectBlocks.add(index);
|
|
18970
19300
|
} catch (error) {
|
|
18971
|
-
|
|
19301
|
+
if (preserveDataOnFailure && readBlock) {
|
|
19302
|
+
this.renderedBlocks.set(index, { get block() {
|
|
19303
|
+
return readBlock();
|
|
19304
|
+
}, object: void 0 });
|
|
19305
|
+
} else this.data.assign(index, 0);
|
|
18972
19306
|
this.fluidCells[index] = 0;
|
|
18973
19307
|
object4?.removeFromScene();
|
|
18974
19308
|
if (!failed) failure = error;
|
|
@@ -18979,7 +19313,10 @@ var Chunk = class _Chunk {
|
|
|
18979
19313
|
return positions;
|
|
18980
19314
|
}
|
|
18981
19315
|
/** Replaces every cell from parsed section data. The caller refreshes neighbor culling. */
|
|
18982
|
-
|
|
19316
|
+
placeSection(data) {
|
|
19317
|
+
return this.changeBlocks(() => this.replaceSection(data));
|
|
19318
|
+
}
|
|
19319
|
+
async replaceSection(data) {
|
|
18983
19320
|
for (const info of this.renderedBlocks.values()) info.object?.removeFromScene();
|
|
18984
19321
|
this.sectionBlocks.clear();
|
|
18985
19322
|
this.fluidCells.fill(0);
|
|
@@ -19026,7 +19363,7 @@ var Chunk = class _Chunk {
|
|
|
19026
19363
|
];
|
|
19027
19364
|
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : (context?.prepared ?? prepared)?.pick(variantPosition);
|
|
19028
19365
|
if (templates) {
|
|
19029
|
-
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0 })));
|
|
19366
|
+
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0, biomeTint: context?.tint })));
|
|
19030
19367
|
this.meshDirty = true;
|
|
19031
19368
|
} else {
|
|
19032
19369
|
perBlocks.push({ index, blockState: blockState3, biome: context?.biome });
|
|
@@ -19110,12 +19447,35 @@ var Chunk = class _Chunk {
|
|
|
19110
19447
|
}
|
|
19111
19448
|
async resolveState(stored, indices, biomes) {
|
|
19112
19449
|
const blockState3 = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
|
|
19113
|
-
|
|
19450
|
+
let perBlock = !blockState3 || !this.sectionModels || !!(blockState3.key && BlockEntities.entry(await BlockEntities.getIndex(blockState3.key.root), blockState3.key.toNamespacedString()));
|
|
19114
19451
|
const fluid = await getBlockFluidState(blockState3?.key, stored.properties);
|
|
19115
19452
|
const hasBiomes = this.getBiomeAt(this.x * 16, this.y * 16, this.z * 16) !== void 0 || this.biomeAt?.(this.x * 16, this.y * 16 - 1, this.z * 16) !== void 0;
|
|
19116
19453
|
const source = hasBiomes && blockState3 ? await BlockTints.biomeSource(blockState3.key) : void 0;
|
|
19117
19454
|
const prepare = (color2) => perBlock || fluid?.renderModel === false ? void 0 : this.sectionModels.prepareState(blockState3, stored.properties, color2);
|
|
19118
19455
|
const contexts = /* @__PURE__ */ new Map();
|
|
19456
|
+
const blend = this.biomeBlend?.();
|
|
19457
|
+
if (blend && !perBlock && blockState3 && (source || fluid?.kind === "water")) {
|
|
19458
|
+
const prepared2 = fluid?.renderModel === false ? void 0 : await this.sectionModels.prepareState(blockState3, stored.properties, void 0, true);
|
|
19459
|
+
if (!prepared2 && fluid?.renderModel !== false) {
|
|
19460
|
+
perBlock = true;
|
|
19461
|
+
} else {
|
|
19462
|
+
await Promise.all(indices.map(async (index) => {
|
|
19463
|
+
const position = this.biomePosition(index, blockState3, stored);
|
|
19464
|
+
const biomeColor = (kind) => blend.getColor(kind, blockState3.key, position);
|
|
19465
|
+
const context = {};
|
|
19466
|
+
if (source && prepared2?.biomeTint) {
|
|
19467
|
+
const color2 = await biomeColor(source);
|
|
19468
|
+
if (color2 !== void 0) context.tint = new Color13(color2).toArray();
|
|
19469
|
+
}
|
|
19470
|
+
if (fluid?.kind === "water") {
|
|
19471
|
+
const color2 = await biomeColor("water");
|
|
19472
|
+
if (color2 !== void 0 && color2 !== 4159204) context.waterTint = new Color13(color2).toArray();
|
|
19473
|
+
}
|
|
19474
|
+
contexts.set(index, context);
|
|
19475
|
+
}));
|
|
19476
|
+
return { blockState: blockState3, perBlock, fluid, prepared: prepared2, contexts, stored };
|
|
19477
|
+
}
|
|
19478
|
+
}
|
|
19119
19479
|
const groups = /* @__PURE__ */ new Map();
|
|
19120
19480
|
if (blockState3 && (source || fluid?.kind === "water")) {
|
|
19121
19481
|
for (const index of indices) {
|
|
@@ -19163,7 +19523,11 @@ var Chunk = class _Chunk {
|
|
|
19163
19523
|
this.waterTints.set(tint ?? WATER_TINT2, index * 3);
|
|
19164
19524
|
}
|
|
19165
19525
|
/** Changes visibility at world block coordinates while preserving the block's data. */
|
|
19166
|
-
|
|
19526
|
+
setBlockVisibleAt(pos, visible) {
|
|
19527
|
+
const position = pos.clone();
|
|
19528
|
+
return this.changeBlocks(() => this.setBlockVisibility(position, visible));
|
|
19529
|
+
}
|
|
19530
|
+
async setBlockVisibility(pos, visible) {
|
|
19167
19531
|
const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
|
|
19168
19532
|
const block = this.renderedBlocks.get(index);
|
|
19169
19533
|
if (!block || visible === !this.hiddenBlocks.has(index)) {
|
|
@@ -19333,6 +19697,7 @@ var Chunk = class _Chunk {
|
|
|
19333
19697
|
}
|
|
19334
19698
|
/** Removes stored blocks and render objects. Set `notify` to false to skip the owning world's update. */
|
|
19335
19699
|
async clear(notify = true) {
|
|
19700
|
+
if (this.blockChanges.size) await Promise.allSettled(this.blockChanges);
|
|
19336
19701
|
const positions = notify && this.onBlocksChanged ? [...this.renderedBlocks.keys()].map((index) => this.chunkPosToWorldPos(
|
|
19337
19702
|
new Vector320(index % 16, Math.floor(index / 256), Math.floor(index / 16) % 16)
|
|
19338
19703
|
)) : [];
|
|
@@ -19646,6 +20011,7 @@ export {
|
|
|
19646
20011
|
PackMetadata,
|
|
19647
20012
|
PersistentCache,
|
|
19648
20013
|
PlayerHeadTextures,
|
|
20014
|
+
Renames,
|
|
19649
20015
|
Renderer,
|
|
19650
20016
|
RequestError,
|
|
19651
20017
|
Requests,
|