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/node/index.mjs
CHANGED
|
@@ -251,6 +251,13 @@ var Caching;
|
|
|
251
251
|
var init_Caching = __esm({
|
|
252
252
|
"src/cache/Caching.ts"() {
|
|
253
253
|
Caching = class _Caching {
|
|
254
|
+
static {
|
|
255
|
+
this._generation = 0;
|
|
256
|
+
}
|
|
257
|
+
/** Changes when shared assets are cleared, so derived caches can discard their old values. */
|
|
258
|
+
static get generation() {
|
|
259
|
+
return this._generation;
|
|
260
|
+
}
|
|
254
261
|
static {
|
|
255
262
|
this.rawImageCache = Caches.builder().expireAfterWrite(Time.minutes(10)).expireAfterAccess(Time.minutes(5)).expirationInterval(Time.seconds(5)).buildAsync();
|
|
256
263
|
}
|
|
@@ -361,6 +368,7 @@ var init_Caching = __esm({
|
|
|
361
368
|
}
|
|
362
369
|
/** Invalidates in-memory entries so later lookups reload them. Persisted entries are retained. */
|
|
363
370
|
static clear() {
|
|
371
|
+
this._generation++;
|
|
364
372
|
for (const cache of this.all) {
|
|
365
373
|
cache.invalidateAll();
|
|
366
374
|
}
|
|
@@ -372,6 +380,7 @@ var init_Caching = __esm({
|
|
|
372
380
|
* Use {@link shutdown} to also stop requests and the shared Ticker.
|
|
373
381
|
*/
|
|
374
382
|
static end() {
|
|
383
|
+
this._generation++;
|
|
375
384
|
for (const cache of this.all) {
|
|
376
385
|
cache.invalidateAll();
|
|
377
386
|
cache.end();
|
|
@@ -1264,118 +1273,6 @@ var init_Materials = __esm({
|
|
|
1264
1273
|
}
|
|
1265
1274
|
});
|
|
1266
1275
|
|
|
1267
|
-
// src/model/Model.ts
|
|
1268
|
-
function isDoubleArray(obj) {
|
|
1269
|
-
return Array.isArray(obj) && obj.length === 2;
|
|
1270
|
-
}
|
|
1271
|
-
function isTripleArray(obj) {
|
|
1272
|
-
return Array.isArray(obj) && obj.length === 3;
|
|
1273
|
-
}
|
|
1274
|
-
function isQuadArray(obj) {
|
|
1275
|
-
return Array.isArray(obj) && obj.length === 4;
|
|
1276
|
-
}
|
|
1277
|
-
var ITEM_GENERATED, BUILTIN_GENERATED, BUILTIN_ENTITY, DEFAULT_ELEMENTS;
|
|
1278
|
-
var init_Model = __esm({
|
|
1279
|
-
"src/model/Model.ts"() {
|
|
1280
|
-
ITEM_GENERATED = "item/generated";
|
|
1281
|
-
BUILTIN_GENERATED = "builtin/generated";
|
|
1282
|
-
BUILTIN_ENTITY = "builtin/entity";
|
|
1283
|
-
DEFAULT_ELEMENTS = [];
|
|
1284
|
-
}
|
|
1285
|
-
});
|
|
1286
|
-
|
|
1287
|
-
// ../../node_modules/ts-deepmerge/dist/index.js
|
|
1288
|
-
var require_dist = __commonJS({
|
|
1289
|
-
"../../node_modules/ts-deepmerge/dist/index.js"(exports) {
|
|
1290
|
-
"use strict";
|
|
1291
|
-
var __assign = exports && exports.__assign || function() {
|
|
1292
|
-
__assign = Object.assign || function(t) {
|
|
1293
|
-
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
1294
|
-
s = arguments[i];
|
|
1295
|
-
for (var p10 in s) if (Object.prototype.hasOwnProperty.call(s, p10))
|
|
1296
|
-
t[p10] = s[p10];
|
|
1297
|
-
}
|
|
1298
|
-
return t;
|
|
1299
|
-
};
|
|
1300
|
-
return __assign.apply(this, arguments);
|
|
1301
|
-
};
|
|
1302
|
-
var __read = exports && exports.__read || function(o, n) {
|
|
1303
|
-
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
1304
|
-
if (!m) return o;
|
|
1305
|
-
var i = m.call(o), r, ar = [], e;
|
|
1306
|
-
try {
|
|
1307
|
-
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
1308
|
-
} catch (error) {
|
|
1309
|
-
e = { error };
|
|
1310
|
-
} finally {
|
|
1311
|
-
try {
|
|
1312
|
-
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
1313
|
-
} finally {
|
|
1314
|
-
if (e) throw e.error;
|
|
1315
|
-
}
|
|
1316
|
-
}
|
|
1317
|
-
return ar;
|
|
1318
|
-
};
|
|
1319
|
-
var __spreadArray = exports && exports.__spreadArray || function(to, from, pack) {
|
|
1320
|
-
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
1321
|
-
if (ar || !(i in from)) {
|
|
1322
|
-
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
1323
|
-
ar[i] = from[i];
|
|
1324
|
-
}
|
|
1325
|
-
}
|
|
1326
|
-
return to.concat(ar || Array.prototype.slice.call(from));
|
|
1327
|
-
};
|
|
1328
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1329
|
-
var isObject = function(obj) {
|
|
1330
|
-
if (typeof obj === "object" && obj !== null) {
|
|
1331
|
-
if (typeof Object.getPrototypeOf === "function") {
|
|
1332
|
-
var prototype = Object.getPrototypeOf(obj);
|
|
1333
|
-
return prototype === Object.prototype || prototype === null;
|
|
1334
|
-
}
|
|
1335
|
-
return Object.prototype.toString.call(obj) === "[object Object]";
|
|
1336
|
-
}
|
|
1337
|
-
return false;
|
|
1338
|
-
};
|
|
1339
|
-
var PROTECTED_KEYS = ["__proto__"];
|
|
1340
|
-
var merge10 = function() {
|
|
1341
|
-
var objects = [];
|
|
1342
|
-
for (var _i = 0; _i < arguments.length; _i++) {
|
|
1343
|
-
objects[_i] = arguments[_i];
|
|
1344
|
-
}
|
|
1345
|
-
return objects.reduce(function(result, current) {
|
|
1346
|
-
Object.keys(current).forEach(function(key) {
|
|
1347
|
-
if (PROTECTED_KEYS.includes(key)) {
|
|
1348
|
-
return;
|
|
1349
|
-
}
|
|
1350
|
-
if (Array.isArray(result[key]) && Array.isArray(current[key])) {
|
|
1351
|
-
result[key] = merge10.options.mergeArrays ? Array.from(new Set(result[key].concat(current[key]))) : current[key];
|
|
1352
|
-
} else if (isObject(result[key]) && isObject(current[key])) {
|
|
1353
|
-
result[key] = merge10(result[key], current[key]);
|
|
1354
|
-
} else {
|
|
1355
|
-
result[key] = current[key];
|
|
1356
|
-
}
|
|
1357
|
-
});
|
|
1358
|
-
return result;
|
|
1359
|
-
}, {});
|
|
1360
|
-
};
|
|
1361
|
-
var defaultOptions = {
|
|
1362
|
-
mergeArrays: true
|
|
1363
|
-
};
|
|
1364
|
-
merge10.options = defaultOptions;
|
|
1365
|
-
merge10.withOptions = function(options) {
|
|
1366
|
-
var objects = [];
|
|
1367
|
-
for (var _i = 1; _i < arguments.length; _i++) {
|
|
1368
|
-
objects[_i - 1] = arguments[_i];
|
|
1369
|
-
}
|
|
1370
|
-
merge10.options = __assign({ mergeArrays: true }, options);
|
|
1371
|
-
var result = merge10.apply(void 0, __spreadArray([], __read(objects), false));
|
|
1372
|
-
merge10.options = defaultOptions;
|
|
1373
|
-
return result;
|
|
1374
|
-
};
|
|
1375
|
-
exports.default = merge10;
|
|
1376
|
-
}
|
|
1377
|
-
});
|
|
1378
|
-
|
|
1379
1276
|
// src/assets/AssetKey.ts
|
|
1380
1277
|
function isResourceLocation(value) {
|
|
1381
1278
|
return typeof value === "string" && /^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(value);
|
|
@@ -1478,17 +1375,306 @@ var init_AssetKey = __esm({
|
|
|
1478
1375
|
if (this.type) {
|
|
1479
1376
|
p10.push(this.type);
|
|
1480
1377
|
}
|
|
1481
|
-
p10.push(this.path);
|
|
1482
|
-
return p10.join("/");
|
|
1378
|
+
p10.push(this.path);
|
|
1379
|
+
return p10.join("/");
|
|
1380
|
+
}
|
|
1381
|
+
toNamespacedString() {
|
|
1382
|
+
return this.namespace + ":" + this.getFullPath();
|
|
1383
|
+
}
|
|
1384
|
+
/** Returns a cache identifier that includes the explicit root or {@link AssetLoader.ROOT}. */
|
|
1385
|
+
serialize() {
|
|
1386
|
+
return this.toString();
|
|
1387
|
+
}
|
|
1388
|
+
};
|
|
1389
|
+
}
|
|
1390
|
+
});
|
|
1391
|
+
|
|
1392
|
+
// src/assets/source/parser/AssetParsers.ts
|
|
1393
|
+
var AssetParser;
|
|
1394
|
+
var init_AssetParsers = __esm({
|
|
1395
|
+
"src/assets/source/parser/AssetParsers.ts"() {
|
|
1396
|
+
AssetParser = /* @__PURE__ */ ((AssetParser2) => {
|
|
1397
|
+
AssetParser2["MODEL"] = "model";
|
|
1398
|
+
AssetParser2["BLOCKSTATE"] = "blockstate";
|
|
1399
|
+
AssetParser2["META"] = "meta";
|
|
1400
|
+
AssetParser2["NBT"] = "nbt";
|
|
1401
|
+
AssetParser2["IMAGE"] = "image";
|
|
1402
|
+
AssetParser2["LIST"] = "list";
|
|
1403
|
+
AssetParser2["JSON"] = "json";
|
|
1404
|
+
return AssetParser2;
|
|
1405
|
+
})(AssetParser || {});
|
|
1406
|
+
}
|
|
1407
|
+
});
|
|
1408
|
+
|
|
1409
|
+
// src/assets/MineRenderData.ts
|
|
1410
|
+
var object, release, compare, MineRenderData;
|
|
1411
|
+
var init_MineRenderData = __esm({
|
|
1412
|
+
"src/assets/MineRenderData.ts"() {
|
|
1413
|
+
init_AssetKey();
|
|
1414
|
+
init_AssetLoader();
|
|
1415
|
+
init_AssetParsers();
|
|
1416
|
+
init_Caching();
|
|
1417
|
+
object = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1418
|
+
release = (value) => /^\d+\.\d+(?:\.\d+)?$/.test(value) ? value.split(".").map(Number) : void 0;
|
|
1419
|
+
compare = (left, right) => {
|
|
1420
|
+
for (let i = 0; i < 3; i++) {
|
|
1421
|
+
const difference = (left[i] ?? 0) - (right[i] ?? 0);
|
|
1422
|
+
if (difference) return difference;
|
|
1423
|
+
}
|
|
1424
|
+
return 0;
|
|
1425
|
+
};
|
|
1426
|
+
MineRenderData = class {
|
|
1427
|
+
/** Loads one registry. Missing files and invalid data reject the request. */
|
|
1428
|
+
static async get(dataset, root) {
|
|
1429
|
+
root = root?.replace(/\/+$/, "");
|
|
1430
|
+
const scope = AssetLoader.persistentScope;
|
|
1431
|
+
const defaultRoot = AssetLoader.ROOT;
|
|
1432
|
+
const resolved = await this.resolve(root);
|
|
1433
|
+
const entry = resolved.manifest.datasets[dataset];
|
|
1434
|
+
if (!entry) throw new Error(`MineRender ${dataset} data is unavailable for ${resolved.version}: ${resolved.manifest.unavailable[dataset] ?? "not listed in the manifest"}`);
|
|
1435
|
+
const file = entry.file;
|
|
1436
|
+
const requestRoot = resolved.root === (root ?? defaultRoot).replace(/\/+$/, "") ? root : resolved.root;
|
|
1437
|
+
return await this.cached(`${requestRoot === void 0 ? "sources" : "explicit"}:${resolved.root}/${file}:${entry.sha256}`, scope, defaultRoot, async () => {
|
|
1438
|
+
const data = await this.load(file.slice(0, -5), requestRoot);
|
|
1439
|
+
if (!this.validDataset(dataset, data)) throw new Error(`Invalid MineRender ${dataset} data at ${resolved.root}/minerender-data/${file}`);
|
|
1440
|
+
return data;
|
|
1441
|
+
});
|
|
1442
|
+
}
|
|
1443
|
+
/**
|
|
1444
|
+
* Uses the exact version when published. Otherwise, selects the nearest patch in the same
|
|
1445
|
+
* release line (older on ties), then the latest earlier release or the oldest published release.
|
|
1446
|
+
* Unpublished snapshots reject.
|
|
1447
|
+
*/
|
|
1448
|
+
static async resolve(root) {
|
|
1449
|
+
const requestRoot = root?.replace(/\/+$/, "");
|
|
1450
|
+
const defaultRoot = AssetLoader.ROOT;
|
|
1451
|
+
root = (root ?? defaultRoot).replace(/\/+$/, "");
|
|
1452
|
+
const scope = AssetLoader.persistentScope;
|
|
1453
|
+
return this.cached(`resolve:${requestRoot === void 0 ? "sources" : "explicit"}:${root}`, scope, defaultRoot, async () => {
|
|
1454
|
+
const requestedVersion = root.slice(root.lastIndexOf("/") + 1);
|
|
1455
|
+
let manifest = await this.manifest(root, requestRoot !== void 0 && release(requestedVersion) ? requestedVersion : void 0, requestRoot);
|
|
1456
|
+
if (manifest) return { requestedVersion, version: manifest.minecraftVersion, root, manifest };
|
|
1457
|
+
const requested = release(requestedVersion);
|
|
1458
|
+
if (!requested) {
|
|
1459
|
+
throw new Error(`No MineRender data is published for ${requestedVersion}; automatic fallback requires a release`);
|
|
1460
|
+
}
|
|
1461
|
+
const base = root.slice(0, root.lastIndexOf("/"));
|
|
1462
|
+
const index = await this.cached(`versions:${base}`, scope, defaultRoot, async () => {
|
|
1463
|
+
const value = await this.load("versions", base);
|
|
1464
|
+
if (!object(value) || !Array.isArray(value.versions) || !value.versions.every((entry) => object(entry) && typeof entry.name === "string")) {
|
|
1465
|
+
throw new Error(`Invalid MineRender version index at ${base}/minerender-data/versions.json`);
|
|
1466
|
+
}
|
|
1467
|
+
return value.versions;
|
|
1468
|
+
});
|
|
1469
|
+
const versions = index.map((entry) => ({ name: entry.name, number: release(entry.name) })).filter((entry) => !!entry.number).sort((left, right) => compare(left.number, right.number));
|
|
1470
|
+
const sameLine = versions.filter((entry) => entry.number[0] === requested[0] && entry.number[1] === requested[1]);
|
|
1471
|
+
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];
|
|
1472
|
+
if (!selected) throw new Error(`No compatible MineRender data is published for ${requestedVersion}`);
|
|
1473
|
+
const resolvedRoot = `${base}/${selected.name}`;
|
|
1474
|
+
manifest = await this.manifest(resolvedRoot, selected.name, resolvedRoot);
|
|
1475
|
+
if (!manifest) throw new Error(`Missing MineRender manifest for published version ${selected.name}`);
|
|
1476
|
+
return { requestedVersion, version: selected.name, root: resolvedRoot, manifest };
|
|
1477
|
+
});
|
|
1478
|
+
}
|
|
1479
|
+
static async manifest(root, expectedVersion, requestRoot) {
|
|
1480
|
+
const data = await this.load("manifest", requestRoot);
|
|
1481
|
+
if (data === void 0) return void 0;
|
|
1482
|
+
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) => {
|
|
1483
|
+
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);
|
|
1484
|
+
}) || expectedVersion !== void 0 && data.minecraftVersion !== expectedVersion) {
|
|
1485
|
+
throw new Error(`Invalid MineRender manifest at ${root}/minerender-data/manifest.json`);
|
|
1486
|
+
}
|
|
1487
|
+
return data;
|
|
1488
|
+
}
|
|
1489
|
+
static load(file, root) {
|
|
1490
|
+
const key = new AssetKey("minerender-data", file, void 0, void 0, void 0, ".json", root);
|
|
1491
|
+
key.rootType = void 0;
|
|
1492
|
+
return AssetLoader.get(key, "json" /* JSON */);
|
|
1493
|
+
}
|
|
1494
|
+
static async cached(key, scope, defaultRoot, loader) {
|
|
1495
|
+
const checkSources = () => {
|
|
1496
|
+
if (AssetLoader.persistentScope !== scope || AssetLoader.ROOT !== defaultRoot) throw new Error("Asset sources changed while loading MineRender data; retry the request");
|
|
1497
|
+
};
|
|
1498
|
+
checkSources();
|
|
1499
|
+
return await Caching.mineRenderDataCache.get(scope ? `${scope}
|
|
1500
|
+
${key}` : key, async () => {
|
|
1501
|
+
const value = await loader();
|
|
1502
|
+
checkSources();
|
|
1503
|
+
return value;
|
|
1504
|
+
});
|
|
1505
|
+
}
|
|
1506
|
+
static validDataset(dataset, data) {
|
|
1507
|
+
if (!object(data)) return false;
|
|
1508
|
+
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)));
|
|
1509
|
+
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")));
|
|
1510
|
+
if (dataset === "legacyBlocks") return object(data.blocks) && Object.values(data.blocks).every((value) => typeof value === "string");
|
|
1511
|
+
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")));
|
|
1512
|
+
return Object.values(data).every((value) => {
|
|
1513
|
+
if (!object(value)) return false;
|
|
1514
|
+
switch (dataset) {
|
|
1515
|
+
case "itemDefaults":
|
|
1516
|
+
return true;
|
|
1517
|
+
case "blockStates":
|
|
1518
|
+
return Object.values(value).every((property) => object(property) && "default" in property && Array.isArray(property.values));
|
|
1519
|
+
case "blockTints":
|
|
1520
|
+
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));
|
|
1521
|
+
case "fluids":
|
|
1522
|
+
return ["water", "lava"].includes(value.kind) && typeof value.renderModel === "boolean" && (value.levelProperty === void 0 || typeof value.levelProperty === "string");
|
|
1523
|
+
case "dyes":
|
|
1524
|
+
return [value.id, value.textureDiffuseColor, value.fireworkColor, value.textColor].every(Number.isInteger);
|
|
1525
|
+
default:
|
|
1526
|
+
return false;
|
|
1527
|
+
}
|
|
1528
|
+
});
|
|
1529
|
+
}
|
|
1530
|
+
};
|
|
1531
|
+
}
|
|
1532
|
+
});
|
|
1533
|
+
|
|
1534
|
+
// src/assets/Renames.ts
|
|
1535
|
+
var Renames;
|
|
1536
|
+
var init_Renames = __esm({
|
|
1537
|
+
"src/assets/Renames.ts"() {
|
|
1538
|
+
init_AssetKey();
|
|
1539
|
+
init_MineRenderData();
|
|
1540
|
+
Renames = class {
|
|
1541
|
+
/** Loads the selected version's rename table, or `undefined` when its published data has none. */
|
|
1542
|
+
static async get(root) {
|
|
1543
|
+
const resolved = await MineRenderData.resolve(root);
|
|
1544
|
+
return resolved.manifest.datasets.renames ? MineRenderData.get("renames", root) : void 0;
|
|
1545
|
+
}
|
|
1546
|
+
/**
|
|
1547
|
+
* Returns the ID that replaced `id`, or `undefined` when `id` keeps its meaning.
|
|
1548
|
+
* Without a data version, an ID that the selected version still registers keeps its meaning.
|
|
1549
|
+
*/
|
|
1550
|
+
static async resolve(kind, id, dataVersion, root) {
|
|
1551
|
+
const data = await this.get(root);
|
|
1552
|
+
if (!data || !Object.prototype.hasOwnProperty.call(data[kind], id)) return void 0;
|
|
1553
|
+
const entry = data[kind][id];
|
|
1554
|
+
if (dataVersion === void 0) return entry.registered ? void 0 : entry.ranges[entry.ranges.length - 1].to;
|
|
1555
|
+
if (dataVersion < data.baseDataVersion) return void 0;
|
|
1556
|
+
return entry.ranges.find((range) => dataVersion < range.before)?.to;
|
|
1557
|
+
}
|
|
1558
|
+
/** Returns a copy of `key` for the replacement ID, or `undefined` when the ID keeps its meaning. */
|
|
1559
|
+
static async key(kind, key) {
|
|
1560
|
+
const renamed = await this.resolve(kind, `${key.namespace}:${key.path}`, void 0, key.root);
|
|
1561
|
+
if (!renamed) return void 0;
|
|
1562
|
+
const [namespace, path] = renamed.split(":");
|
|
1563
|
+
return new AssetKey(namespace, path, key.assetType, key.type, key.rootType, key.extension, key.root);
|
|
1564
|
+
}
|
|
1565
|
+
};
|
|
1566
|
+
}
|
|
1567
|
+
});
|
|
1568
|
+
|
|
1569
|
+
// src/model/Model.ts
|
|
1570
|
+
function isDoubleArray(obj) {
|
|
1571
|
+
return Array.isArray(obj) && obj.length === 2;
|
|
1572
|
+
}
|
|
1573
|
+
function isTripleArray(obj) {
|
|
1574
|
+
return Array.isArray(obj) && obj.length === 3;
|
|
1575
|
+
}
|
|
1576
|
+
function isQuadArray(obj) {
|
|
1577
|
+
return Array.isArray(obj) && obj.length === 4;
|
|
1578
|
+
}
|
|
1579
|
+
var ITEM_GENERATED, BUILTIN_GENERATED, BUILTIN_ENTITY, DEFAULT_ELEMENTS;
|
|
1580
|
+
var init_Model = __esm({
|
|
1581
|
+
"src/model/Model.ts"() {
|
|
1582
|
+
ITEM_GENERATED = "item/generated";
|
|
1583
|
+
BUILTIN_GENERATED = "builtin/generated";
|
|
1584
|
+
BUILTIN_ENTITY = "builtin/entity";
|
|
1585
|
+
DEFAULT_ELEMENTS = [];
|
|
1586
|
+
}
|
|
1587
|
+
});
|
|
1588
|
+
|
|
1589
|
+
// ../../node_modules/ts-deepmerge/dist/index.js
|
|
1590
|
+
var require_dist = __commonJS({
|
|
1591
|
+
"../../node_modules/ts-deepmerge/dist/index.js"(exports) {
|
|
1592
|
+
"use strict";
|
|
1593
|
+
var __assign = exports && exports.__assign || function() {
|
|
1594
|
+
__assign = Object.assign || function(t) {
|
|
1595
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
1596
|
+
s = arguments[i];
|
|
1597
|
+
for (var p10 in s) if (Object.prototype.hasOwnProperty.call(s, p10))
|
|
1598
|
+
t[p10] = s[p10];
|
|
1599
|
+
}
|
|
1600
|
+
return t;
|
|
1601
|
+
};
|
|
1602
|
+
return __assign.apply(this, arguments);
|
|
1603
|
+
};
|
|
1604
|
+
var __read = exports && exports.__read || function(o, n) {
|
|
1605
|
+
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
1606
|
+
if (!m) return o;
|
|
1607
|
+
var i = m.call(o), r, ar = [], e;
|
|
1608
|
+
try {
|
|
1609
|
+
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
1610
|
+
} catch (error) {
|
|
1611
|
+
e = { error };
|
|
1612
|
+
} finally {
|
|
1613
|
+
try {
|
|
1614
|
+
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
1615
|
+
} finally {
|
|
1616
|
+
if (e) throw e.error;
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
return ar;
|
|
1620
|
+
};
|
|
1621
|
+
var __spreadArray = exports && exports.__spreadArray || function(to, from, pack) {
|
|
1622
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
1623
|
+
if (ar || !(i in from)) {
|
|
1624
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
1625
|
+
ar[i] = from[i];
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
1629
|
+
};
|
|
1630
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1631
|
+
var isObject = function(obj) {
|
|
1632
|
+
if (typeof obj === "object" && obj !== null) {
|
|
1633
|
+
if (typeof Object.getPrototypeOf === "function") {
|
|
1634
|
+
var prototype = Object.getPrototypeOf(obj);
|
|
1635
|
+
return prototype === Object.prototype || prototype === null;
|
|
1636
|
+
}
|
|
1637
|
+
return Object.prototype.toString.call(obj) === "[object Object]";
|
|
1483
1638
|
}
|
|
1484
|
-
|
|
1485
|
-
|
|
1639
|
+
return false;
|
|
1640
|
+
};
|
|
1641
|
+
var PROTECTED_KEYS = ["__proto__"];
|
|
1642
|
+
var merge10 = function() {
|
|
1643
|
+
var objects = [];
|
|
1644
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
1645
|
+
objects[_i] = arguments[_i];
|
|
1486
1646
|
}
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1647
|
+
return objects.reduce(function(result, current) {
|
|
1648
|
+
Object.keys(current).forEach(function(key) {
|
|
1649
|
+
if (PROTECTED_KEYS.includes(key)) {
|
|
1650
|
+
return;
|
|
1651
|
+
}
|
|
1652
|
+
if (Array.isArray(result[key]) && Array.isArray(current[key])) {
|
|
1653
|
+
result[key] = merge10.options.mergeArrays ? Array.from(new Set(result[key].concat(current[key]))) : current[key];
|
|
1654
|
+
} else if (isObject(result[key]) && isObject(current[key])) {
|
|
1655
|
+
result[key] = merge10(result[key], current[key]);
|
|
1656
|
+
} else {
|
|
1657
|
+
result[key] = current[key];
|
|
1658
|
+
}
|
|
1659
|
+
});
|
|
1660
|
+
return result;
|
|
1661
|
+
}, {});
|
|
1662
|
+
};
|
|
1663
|
+
var defaultOptions = {
|
|
1664
|
+
mergeArrays: true
|
|
1665
|
+
};
|
|
1666
|
+
merge10.options = defaultOptions;
|
|
1667
|
+
merge10.withOptions = function(options) {
|
|
1668
|
+
var objects = [];
|
|
1669
|
+
for (var _i = 1; _i < arguments.length; _i++) {
|
|
1670
|
+
objects[_i - 1] = arguments[_i];
|
|
1490
1671
|
}
|
|
1672
|
+
merge10.options = __assign({ mergeArrays: true }, options);
|
|
1673
|
+
var result = merge10.apply(void 0, __spreadArray([], __read(objects), false));
|
|
1674
|
+
merge10.options = defaultOptions;
|
|
1675
|
+
return result;
|
|
1491
1676
|
};
|
|
1677
|
+
exports.default = merge10;
|
|
1492
1678
|
}
|
|
1493
1679
|
});
|
|
1494
1680
|
|
|
@@ -1614,147 +1800,6 @@ var init_ModelMerger = __esm({
|
|
|
1614
1800
|
}
|
|
1615
1801
|
});
|
|
1616
1802
|
|
|
1617
|
-
// src/assets/source/parser/AssetParsers.ts
|
|
1618
|
-
var AssetParser;
|
|
1619
|
-
var init_AssetParsers = __esm({
|
|
1620
|
-
"src/assets/source/parser/AssetParsers.ts"() {
|
|
1621
|
-
AssetParser = /* @__PURE__ */ ((AssetParser2) => {
|
|
1622
|
-
AssetParser2["MODEL"] = "model";
|
|
1623
|
-
AssetParser2["BLOCKSTATE"] = "blockstate";
|
|
1624
|
-
AssetParser2["META"] = "meta";
|
|
1625
|
-
AssetParser2["NBT"] = "nbt";
|
|
1626
|
-
AssetParser2["IMAGE"] = "image";
|
|
1627
|
-
AssetParser2["LIST"] = "list";
|
|
1628
|
-
AssetParser2["JSON"] = "json";
|
|
1629
|
-
return AssetParser2;
|
|
1630
|
-
})(AssetParser || {});
|
|
1631
|
-
}
|
|
1632
|
-
});
|
|
1633
|
-
|
|
1634
|
-
// src/assets/MineRenderData.ts
|
|
1635
|
-
var object, release, compare, MineRenderData;
|
|
1636
|
-
var init_MineRenderData = __esm({
|
|
1637
|
-
"src/assets/MineRenderData.ts"() {
|
|
1638
|
-
init_AssetKey();
|
|
1639
|
-
init_AssetLoader();
|
|
1640
|
-
init_AssetParsers();
|
|
1641
|
-
init_Caching();
|
|
1642
|
-
object = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1643
|
-
release = (value) => /^\d+\.\d+(?:\.\d+)?$/.test(value) ? value.split(".").map(Number) : void 0;
|
|
1644
|
-
compare = (left, right) => {
|
|
1645
|
-
for (let i = 0; i < 3; i++) {
|
|
1646
|
-
const difference = (left[i] ?? 0) - (right[i] ?? 0);
|
|
1647
|
-
if (difference) return difference;
|
|
1648
|
-
}
|
|
1649
|
-
return 0;
|
|
1650
|
-
};
|
|
1651
|
-
MineRenderData = class {
|
|
1652
|
-
/** Loads one registry. Missing files and invalid data reject the request. */
|
|
1653
|
-
static async get(dataset, root) {
|
|
1654
|
-
root = root?.replace(/\/+$/, "");
|
|
1655
|
-
const scope = AssetLoader.persistentScope;
|
|
1656
|
-
const defaultRoot = AssetLoader.ROOT;
|
|
1657
|
-
const resolved = await this.resolve(root);
|
|
1658
|
-
const entry = resolved.manifest.datasets[dataset];
|
|
1659
|
-
if (!entry) throw new Error(`MineRender ${dataset} data is unavailable for ${resolved.version}: ${resolved.manifest.unavailable[dataset] ?? "not listed in the manifest"}`);
|
|
1660
|
-
const file = entry.file;
|
|
1661
|
-
const requestRoot = resolved.root === (root ?? defaultRoot).replace(/\/+$/, "") ? root : resolved.root;
|
|
1662
|
-
return await this.cached(`${requestRoot === void 0 ? "sources" : "explicit"}:${resolved.root}/${file}:${entry.sha256}`, scope, defaultRoot, async () => {
|
|
1663
|
-
const data = await this.load(file.slice(0, -5), requestRoot);
|
|
1664
|
-
if (!this.validDataset(dataset, data)) throw new Error(`Invalid MineRender ${dataset} data at ${resolved.root}/minerender-data/${file}`);
|
|
1665
|
-
return data;
|
|
1666
|
-
});
|
|
1667
|
-
}
|
|
1668
|
-
/**
|
|
1669
|
-
* Uses the exact version when published. Otherwise, selects the nearest patch in the same
|
|
1670
|
-
* release line (older on ties), then the latest earlier release or the oldest published release.
|
|
1671
|
-
* Unpublished snapshots reject.
|
|
1672
|
-
*/
|
|
1673
|
-
static async resolve(root) {
|
|
1674
|
-
const requestRoot = root?.replace(/\/+$/, "");
|
|
1675
|
-
const defaultRoot = AssetLoader.ROOT;
|
|
1676
|
-
root = (root ?? defaultRoot).replace(/\/+$/, "");
|
|
1677
|
-
const scope = AssetLoader.persistentScope;
|
|
1678
|
-
return this.cached(`resolve:${requestRoot === void 0 ? "sources" : "explicit"}:${root}`, scope, defaultRoot, async () => {
|
|
1679
|
-
const requestedVersion = root.slice(root.lastIndexOf("/") + 1);
|
|
1680
|
-
let manifest = await this.manifest(root, requestRoot !== void 0 && release(requestedVersion) ? requestedVersion : void 0, requestRoot);
|
|
1681
|
-
if (manifest) return { requestedVersion, version: manifest.minecraftVersion, root, manifest };
|
|
1682
|
-
const requested = release(requestedVersion);
|
|
1683
|
-
if (!requested) {
|
|
1684
|
-
throw new Error(`No MineRender data is published for ${requestedVersion}; automatic fallback requires a release`);
|
|
1685
|
-
}
|
|
1686
|
-
const base = root.slice(0, root.lastIndexOf("/"));
|
|
1687
|
-
const index = await this.cached(`versions:${base}`, scope, defaultRoot, async () => {
|
|
1688
|
-
const value = await this.load("versions", base);
|
|
1689
|
-
if (!object(value) || !Array.isArray(value.versions) || !value.versions.every((entry) => object(entry) && typeof entry.name === "string")) {
|
|
1690
|
-
throw new Error(`Invalid MineRender version index at ${base}/minerender-data/versions.json`);
|
|
1691
|
-
}
|
|
1692
|
-
return value.versions;
|
|
1693
|
-
});
|
|
1694
|
-
const versions = index.map((entry) => ({ name: entry.name, number: release(entry.name) })).filter((entry) => !!entry.number).sort((left, right) => compare(left.number, right.number));
|
|
1695
|
-
const sameLine = versions.filter((entry) => entry.number[0] === requested[0] && entry.number[1] === requested[1]);
|
|
1696
|
-
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];
|
|
1697
|
-
if (!selected) throw new Error(`No compatible MineRender data is published for ${requestedVersion}`);
|
|
1698
|
-
const resolvedRoot = `${base}/${selected.name}`;
|
|
1699
|
-
manifest = await this.manifest(resolvedRoot, selected.name, resolvedRoot);
|
|
1700
|
-
if (!manifest) throw new Error(`Missing MineRender manifest for published version ${selected.name}`);
|
|
1701
|
-
return { requestedVersion, version: selected.name, root: resolvedRoot, manifest };
|
|
1702
|
-
});
|
|
1703
|
-
}
|
|
1704
|
-
static async manifest(root, expectedVersion, requestRoot) {
|
|
1705
|
-
const data = await this.load("manifest", requestRoot);
|
|
1706
|
-
if (data === void 0) return void 0;
|
|
1707
|
-
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) => {
|
|
1708
|
-
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);
|
|
1709
|
-
}) || expectedVersion !== void 0 && data.minecraftVersion !== expectedVersion) {
|
|
1710
|
-
throw new Error(`Invalid MineRender manifest at ${root}/minerender-data/manifest.json`);
|
|
1711
|
-
}
|
|
1712
|
-
return data;
|
|
1713
|
-
}
|
|
1714
|
-
static load(file, root) {
|
|
1715
|
-
const key = new AssetKey("minerender-data", file, void 0, void 0, void 0, ".json", root);
|
|
1716
|
-
key.rootType = void 0;
|
|
1717
|
-
return AssetLoader.get(key, "json" /* JSON */);
|
|
1718
|
-
}
|
|
1719
|
-
static async cached(key, scope, defaultRoot, loader) {
|
|
1720
|
-
const checkSources = () => {
|
|
1721
|
-
if (AssetLoader.persistentScope !== scope || AssetLoader.ROOT !== defaultRoot) throw new Error("Asset sources changed while loading MineRender data; retry the request");
|
|
1722
|
-
};
|
|
1723
|
-
checkSources();
|
|
1724
|
-
return await Caching.mineRenderDataCache.get(scope ? `${scope}
|
|
1725
|
-
${key}` : key, async () => {
|
|
1726
|
-
const value = await loader();
|
|
1727
|
-
checkSources();
|
|
1728
|
-
return value;
|
|
1729
|
-
});
|
|
1730
|
-
}
|
|
1731
|
-
static validDataset(dataset, data) {
|
|
1732
|
-
if (!object(data)) return false;
|
|
1733
|
-
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)));
|
|
1734
|
-
if (dataset === "legacyBlocks") return object(data.blocks) && Object.values(data.blocks).every((value) => typeof value === "string");
|
|
1735
|
-
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")));
|
|
1736
|
-
return Object.values(data).every((value) => {
|
|
1737
|
-
if (!object(value)) return false;
|
|
1738
|
-
switch (dataset) {
|
|
1739
|
-
case "itemDefaults":
|
|
1740
|
-
return true;
|
|
1741
|
-
case "blockStates":
|
|
1742
|
-
return Object.values(value).every((property) => object(property) && "default" in property && Array.isArray(property.values));
|
|
1743
|
-
case "blockTints":
|
|
1744
|
-
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));
|
|
1745
|
-
case "fluids":
|
|
1746
|
-
return ["water", "lava"].includes(value.kind) && typeof value.renderModel === "boolean" && (value.levelProperty === void 0 || typeof value.levelProperty === "string");
|
|
1747
|
-
case "dyes":
|
|
1748
|
-
return [value.id, value.textureDiffuseColor, value.fireworkColor, value.textColor].every(Number.isInteger);
|
|
1749
|
-
default:
|
|
1750
|
-
return false;
|
|
1751
|
-
}
|
|
1752
|
-
});
|
|
1753
|
-
}
|
|
1754
|
-
};
|
|
1755
|
-
}
|
|
1756
|
-
});
|
|
1757
|
-
|
|
1758
1803
|
// src/assets/BannerPatterns.ts
|
|
1759
1804
|
var DYE_COLORS, BannerPatterns;
|
|
1760
1805
|
var init_BannerPatterns = __esm({
|
|
@@ -2077,6 +2122,7 @@ var init_ItemTints = __esm({
|
|
|
2077
2122
|
var Models;
|
|
2078
2123
|
var init_Models = __esm({
|
|
2079
2124
|
"src/assets/Models.ts"() {
|
|
2125
|
+
init_Renames();
|
|
2080
2126
|
init_Caching();
|
|
2081
2127
|
init_ModelMerger();
|
|
2082
2128
|
init_AssetLoader();
|
|
@@ -2410,6 +2456,11 @@ var init_Models = __esm({
|
|
|
2410
2456
|
key.extension = ".json";
|
|
2411
2457
|
}
|
|
2412
2458
|
const preview = this.snapshotContext(key, context);
|
|
2459
|
+
if (key.type === "item") {
|
|
2460
|
+
const renamed = await Renames.key("items", key);
|
|
2461
|
+
const replacement = renamed && await this.getMerged(renamed, preview);
|
|
2462
|
+
if (replacement) return replacement;
|
|
2463
|
+
}
|
|
2413
2464
|
const dataKey = await this.applyDefaults(key, preview);
|
|
2414
2465
|
const keyStr = AssetLoader.persistentKey(key.serialize() + (key.type === "item" ? this.contextKey(preview) + dataKey : ""));
|
|
2415
2466
|
const model = await Caching.mergedModelCache.get(keyStr, async () => {
|
|
@@ -2441,6 +2492,7 @@ var init_BlockStates = __esm({
|
|
|
2441
2492
|
init_AssetKey();
|
|
2442
2493
|
init_PersistentCache();
|
|
2443
2494
|
init_AssetParsers();
|
|
2495
|
+
init_Renames();
|
|
2444
2496
|
init_MineRenderData();
|
|
2445
2497
|
BlockStates = class {
|
|
2446
2498
|
static get PERSISTENT_CACHE() {
|
|
@@ -2481,6 +2533,9 @@ var init_BlockStates = __esm({
|
|
|
2481
2533
|
if (!key.extension) {
|
|
2482
2534
|
key.extension = ".json";
|
|
2483
2535
|
}
|
|
2536
|
+
const renamed = await Renames.key("blocks", key);
|
|
2537
|
+
const replacement = renamed && await this.get(renamed);
|
|
2538
|
+
if (replacement) return replacement;
|
|
2484
2539
|
const keyStr = key.serialize();
|
|
2485
2540
|
return Caching.blockStateCache.get(keyStr, (k) => {
|
|
2486
2541
|
return this.PERSISTENT_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
|
|
@@ -8017,6 +8072,7 @@ var Fonts = class {
|
|
|
8017
8072
|
init_MineRenderData();
|
|
8018
8073
|
init_ModelTextures();
|
|
8019
8074
|
init_Models();
|
|
8075
|
+
init_Renames();
|
|
8020
8076
|
init_source();
|
|
8021
8077
|
|
|
8022
8078
|
// src/cache/index.ts
|
|
@@ -17334,18 +17390,23 @@ var SectionModels = class {
|
|
|
17334
17390
|
return this.template(variant, choice, tints);
|
|
17335
17391
|
}));
|
|
17336
17392
|
}
|
|
17337
|
-
/** Resolves
|
|
17338
|
-
async prepareState(blockState3, properties = {}, biomeColor) {
|
|
17393
|
+
/** Resolves shared templates; dynamic biome tints are supplied on section placements instead of cached geometry. */
|
|
17394
|
+
async prepareState(blockState3, properties = {}, biomeColor, dynamicBiomeTint = false) {
|
|
17339
17395
|
const prepared = await this.state(blockState3, properties);
|
|
17340
17396
|
if (!prepared) return void 0;
|
|
17341
17397
|
let color2;
|
|
17342
17398
|
const resolveColor = typeof biomeColor === "function" ? () => color2 ??= biomeColor() : biomeColor;
|
|
17343
17399
|
const templates = /* @__PURE__ */ new Map();
|
|
17400
|
+
const source = dynamicBiomeTint && await BlockTints.biomeSource(blockState3.key);
|
|
17344
17401
|
await Promise.all([...prepared.choices].map(async ([variant, choice]) => {
|
|
17345
17402
|
const tints = await BlockTints.get(blockState3.key, prepared.state, choice.model, void 0, resolveColor);
|
|
17346
|
-
|
|
17403
|
+
const biomeTints = source ? await BlockTints.get(blockState3.key, prepared.state, choice.model, void 0, 16777215) : void 0;
|
|
17404
|
+
templates.set(variant, await this.template(variant, choice, tints, biomeTints));
|
|
17347
17405
|
}));
|
|
17348
|
-
return {
|
|
17406
|
+
return {
|
|
17407
|
+
pick: (position) => prepared.groups.map((group) => templates.get(BlockStateResolver.choose(group, position))),
|
|
17408
|
+
biomeTint: source ? [...templates.values()].some((template) => template.biomeTint?.some((value) => value === 1)) || void 0 : void 0
|
|
17409
|
+
};
|
|
17349
17410
|
}
|
|
17350
17411
|
async state(blockState3, properties) {
|
|
17351
17412
|
let states = this.states.get(blockState3);
|
|
@@ -17388,10 +17449,10 @@ var SectionModels = class {
|
|
|
17388
17449
|
}
|
|
17389
17450
|
return { state, groups, choices: prepared };
|
|
17390
17451
|
}
|
|
17391
|
-
async template(variant, { model, atlas, rotation }, tints) {
|
|
17452
|
+
async template(variant, { model, atlas, rotation }, tints, biomeTints) {
|
|
17392
17453
|
let models = this.models.get(model);
|
|
17393
17454
|
if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
|
|
17394
|
-
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
|
|
17455
|
+
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints, biomeTints]);
|
|
17395
17456
|
return cached(models, key, async () => {
|
|
17396
17457
|
const object4 = new SectionModel(model, {
|
|
17397
17458
|
instanceMeshes: false,
|
|
@@ -17405,11 +17466,13 @@ var SectionModels = class {
|
|
|
17405
17466
|
object4.clear();
|
|
17406
17467
|
const keep = [];
|
|
17407
17468
|
const cullFaces = [];
|
|
17469
|
+
const biomeTint = biomeTints ? [] : void 0;
|
|
17408
17470
|
for (const element of atlas.model.elements) {
|
|
17409
17471
|
for (const face of CUBE_FACES) {
|
|
17410
17472
|
const data = element.faces[face];
|
|
17411
17473
|
keep.push(data !== void 0);
|
|
17412
17474
|
if (!data) continue;
|
|
17475
|
+
biomeTint?.push(Number(data.tintindex !== void 0 && biomeTints?.[data.tintindex] !== void 0));
|
|
17413
17476
|
const local = CUBE_FACES.findIndex((name) => name === data.cullface);
|
|
17414
17477
|
if (local < 0) {
|
|
17415
17478
|
cullFaces.push(0);
|
|
@@ -17425,6 +17488,7 @@ var SectionModels = class {
|
|
|
17425
17488
|
geometry,
|
|
17426
17489
|
atlas,
|
|
17427
17490
|
cullFaces: new Uint8Array(cullFaces),
|
|
17491
|
+
biomeTint: biomeTint?.some(Boolean) ? new Uint8Array(biomeTint) : void 0,
|
|
17428
17492
|
occludes: ModelCulling.isOpaqueFullCube(atlas) && ModelCulling.toLocalMask(63, rotation) === 63,
|
|
17429
17493
|
layer: atlas.hasTranslucency ? 1 : 0,
|
|
17430
17494
|
ambientOcclusion: model.ambientocclusion !== false
|
|
@@ -17444,6 +17508,7 @@ var SectionModels = class {
|
|
|
17444
17508
|
};
|
|
17445
17509
|
|
|
17446
17510
|
// src/world/_entities/WorldEntities.ts
|
|
17511
|
+
init_Renames();
|
|
17447
17512
|
init_Entities();
|
|
17448
17513
|
init_EntityObject();
|
|
17449
17514
|
|
|
@@ -17603,8 +17668,10 @@ var WorldEntities = class {
|
|
|
17603
17668
|
try {
|
|
17604
17669
|
if (!current()) return;
|
|
17605
17670
|
const models = await Entities.getEntityList();
|
|
17606
|
-
|
|
17607
|
-
const
|
|
17671
|
+
const renamed = models.includes(placement.key.path) ? void 0 : await Renames.key("entities", placement.key);
|
|
17672
|
+
const key = renamed && models.includes(renamed.path) ? renamed : placement.key;
|
|
17673
|
+
if (!models.includes(key.path) || !current()) return;
|
|
17674
|
+
const model = await Entities.getEntity(key, placement.texture, { when: placement.when });
|
|
17608
17675
|
if (!model || !current()) return;
|
|
17609
17676
|
let tints = placement.tints;
|
|
17610
17677
|
if (placement.woolDye) {
|
|
@@ -17619,7 +17686,7 @@ var WorldEntities = class {
|
|
|
17619
17686
|
object4.rotation.y = placement.yaw;
|
|
17620
17687
|
await object4.init();
|
|
17621
17688
|
if (!current()) return;
|
|
17622
|
-
placement.owner.set(object4, { type:
|
|
17689
|
+
placement.owner.set(object4, { type: key.toNamespacedString(), position: placement.position });
|
|
17623
17690
|
this.scene.add(object4);
|
|
17624
17691
|
} catch (error) {
|
|
17625
17692
|
console.warn(`Could not draw saved entity ${placement.key.toNamespacedString()}, skipping it`, error);
|
|
@@ -17651,6 +17718,132 @@ var WorldEntities = class {
|
|
|
17651
17718
|
// src/world/MineRenderWorld.ts
|
|
17652
17719
|
init_FluidQuads();
|
|
17653
17720
|
|
|
17721
|
+
// src/world/_biomes/BiomeBlend.ts
|
|
17722
|
+
init_AssetKey();
|
|
17723
|
+
init_AssetLoader();
|
|
17724
|
+
init_Caching();
|
|
17725
|
+
var MAX_TILES = 256;
|
|
17726
|
+
function validateBiomeBlendRadius(radius) {
|
|
17727
|
+
if (!Number.isInteger(radius) || radius < 0 || radius > 7) {
|
|
17728
|
+
throw new RangeError("biomeBlendRadius must be an integer from 0 to 7");
|
|
17729
|
+
}
|
|
17730
|
+
}
|
|
17731
|
+
var BiomeBlend = class {
|
|
17732
|
+
constructor(biomeAt, radius) {
|
|
17733
|
+
this.biomeAt = biomeAt;
|
|
17734
|
+
this.tiles = /* @__PURE__ */ new Map();
|
|
17735
|
+
this.generation = Caching.generation;
|
|
17736
|
+
validateBiomeBlendRadius(radius);
|
|
17737
|
+
this._radius = radius;
|
|
17738
|
+
}
|
|
17739
|
+
get radius() {
|
|
17740
|
+
return this._radius;
|
|
17741
|
+
}
|
|
17742
|
+
clear() {
|
|
17743
|
+
this.tiles.clear();
|
|
17744
|
+
}
|
|
17745
|
+
invalidateColumn(x, z) {
|
|
17746
|
+
for (const [key, tile] of this.tiles) {
|
|
17747
|
+
if (Math.abs(tile.x - x) <= 1 && Math.abs(tile.z - z) <= 1) this.tiles.delete(key);
|
|
17748
|
+
}
|
|
17749
|
+
}
|
|
17750
|
+
async getColor(kind, origin, position) {
|
|
17751
|
+
const [x, y, z] = position;
|
|
17752
|
+
const biome = this.biomeAt(x, y, z);
|
|
17753
|
+
if (biome === void 0) return void 0;
|
|
17754
|
+
if (this.generation !== Caching.generation) {
|
|
17755
|
+
this.clear();
|
|
17756
|
+
this.generation = Caching.generation;
|
|
17757
|
+
}
|
|
17758
|
+
const tileX = Math.floor(x / 16), tileZ = Math.floor(z / 16), quartY = Math.floor(y / 4);
|
|
17759
|
+
const root = origin?.root ?? AssetLoader.ROOT;
|
|
17760
|
+
const key = JSON.stringify([root, AssetLoader.persistentScope, kind, this._radius, tileX, quartY, tileZ]);
|
|
17761
|
+
let tile = this.tiles.get(key);
|
|
17762
|
+
if (tile) {
|
|
17763
|
+
this.tiles.delete(key);
|
|
17764
|
+
this.tiles.set(key, tile);
|
|
17765
|
+
} else {
|
|
17766
|
+
const snapshot = new AssetKey("minecraft", "", void 0, void 0, "assets", ".json", root);
|
|
17767
|
+
const colors = this.buildTile(kind, snapshot, tileX, quartY, tileZ, this._radius).then((result2) => {
|
|
17768
|
+
if (!result2.complete && this.tiles.get(key)?.colors === colors) this.tiles.delete(key);
|
|
17769
|
+
return result2;
|
|
17770
|
+
}, (error) => {
|
|
17771
|
+
if (this.tiles.get(key)?.colors === colors) this.tiles.delete(key);
|
|
17772
|
+
throw error;
|
|
17773
|
+
});
|
|
17774
|
+
tile = { x: tileX, z: tileZ, colors };
|
|
17775
|
+
this.tiles.set(key, tile);
|
|
17776
|
+
if (this.tiles.size > MAX_TILES) this.tiles.delete(this.tiles.keys().next().value);
|
|
17777
|
+
}
|
|
17778
|
+
const result = await tile.colors;
|
|
17779
|
+
if (result.failures.has(biome)) throw result.failures.get(biome);
|
|
17780
|
+
const color2 = result.colors[(z - tileZ * 16) * 16 + x - tileX * 16];
|
|
17781
|
+
return color2 === -1 ? void 0 : color2;
|
|
17782
|
+
}
|
|
17783
|
+
async buildTile(kind, origin, tileX, quartY, tileZ, radius) {
|
|
17784
|
+
const size = 16 + radius * 2, stride = size + 1;
|
|
17785
|
+
const startX = tileX * 16 - radius, startZ = tileZ * 16 - radius;
|
|
17786
|
+
const quartX = Math.floor(startX / 4), quartZ = Math.floor(startZ / 4);
|
|
17787
|
+
const quartSize = Math.floor((startX + size - 1) / 4) - quartX + 1;
|
|
17788
|
+
const ids = new Array(quartSize * quartSize);
|
|
17789
|
+
const biomes = /* @__PURE__ */ new Map();
|
|
17790
|
+
for (let z = 0; z < quartSize; z++) {
|
|
17791
|
+
for (let x = 0; x < quartSize; x++) {
|
|
17792
|
+
const id = this.biomeAt((quartX + x) * 4, quartY * 4, (quartZ + z) * 4);
|
|
17793
|
+
ids[z * quartSize + x] = id;
|
|
17794
|
+
if (id !== void 0 && !biomes.has(id)) {
|
|
17795
|
+
biomes.set(id, (async () => {
|
|
17796
|
+
const swamp = kind === "grass" && (await Biomes.get(id, origin))?.effects.grass_color_modifier === "swamp";
|
|
17797
|
+
return { color: swamp ? void 0 : await Biomes.getColor(id, kind, origin), swamp };
|
|
17798
|
+
})());
|
|
17799
|
+
}
|
|
17800
|
+
}
|
|
17801
|
+
}
|
|
17802
|
+
const failures = /* @__PURE__ */ new Map();
|
|
17803
|
+
const loaded = new Map(await Promise.all([...biomes].map(async ([id, color2]) => {
|
|
17804
|
+
try {
|
|
17805
|
+
return [id, await color2];
|
|
17806
|
+
} catch (error) {
|
|
17807
|
+
failures.set(id, error);
|
|
17808
|
+
return [id, { color: void 0, swamp: false }];
|
|
17809
|
+
}
|
|
17810
|
+
})));
|
|
17811
|
+
const complete = [...loaded.values()].every((value) => value.swamp || value.color !== void 0);
|
|
17812
|
+
const samples = new Int32Array(size * size).fill(-1);
|
|
17813
|
+
const sums = Array.from({ length: 4 }, () => new Uint32Array(stride * stride));
|
|
17814
|
+
for (let z = 0; z < size; z++) {
|
|
17815
|
+
const row = [0, 0, 0, 0];
|
|
17816
|
+
for (let x = 0; x < size; x++) {
|
|
17817
|
+
const id = ids[(Math.floor((startZ + z) / 4) - quartZ) * quartSize + Math.floor((startX + x) / 4) - quartX];
|
|
17818
|
+
const value = id === void 0 ? void 0 : loaded.get(id);
|
|
17819
|
+
const color2 = value?.swamp ? swampGrassColor(startX + x, startZ + z) : value?.color;
|
|
17820
|
+
if (color2 !== void 0) {
|
|
17821
|
+
samples[z * size + x] = color2;
|
|
17822
|
+
row[0] += color2 >> 16 & 255;
|
|
17823
|
+
row[1] += color2 >> 8 & 255;
|
|
17824
|
+
row[2] += color2 & 255;
|
|
17825
|
+
row[3]++;
|
|
17826
|
+
}
|
|
17827
|
+
for (let channel = 0; channel < 4; channel++) {
|
|
17828
|
+
sums[channel][(z + 1) * stride + x + 1] = sums[channel][z * stride + x + 1] + row[channel];
|
|
17829
|
+
}
|
|
17830
|
+
}
|
|
17831
|
+
}
|
|
17832
|
+
const colors = new Int32Array(256).fill(-1), width = radius * 2 + 1;
|
|
17833
|
+
for (let z = 0; z < 16; z++) {
|
|
17834
|
+
for (let x = 0; x < 16; x++) {
|
|
17835
|
+
if (samples[(z + radius) * size + x + radius] === -1) continue;
|
|
17836
|
+
const topLeft = z * stride + x, topRight = topLeft + width;
|
|
17837
|
+
const bottomLeft = (z + width) * stride + x, bottomRight = bottomLeft + width;
|
|
17838
|
+
const sum = (values) => values[bottomRight] - values[bottomLeft] - values[topRight] + values[topLeft];
|
|
17839
|
+
const count = sum(sums[3]);
|
|
17840
|
+
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);
|
|
17841
|
+
}
|
|
17842
|
+
}
|
|
17843
|
+
return { colors, complete, failures };
|
|
17844
|
+
}
|
|
17845
|
+
};
|
|
17846
|
+
|
|
17654
17847
|
// src/world/WorldLight.ts
|
|
17655
17848
|
function copySectionLight(light) {
|
|
17656
17849
|
const copy = {};
|
|
@@ -17714,12 +17907,17 @@ var MineRenderWorld = class {
|
|
|
17714
17907
|
this.lightValidity = /* @__PURE__ */ new Map();
|
|
17715
17908
|
this.highestSkySection = /* @__PURE__ */ new Map();
|
|
17716
17909
|
this.ambientOcclusionPending = false;
|
|
17910
|
+
this.pendingBiomes = /* @__PURE__ */ new Map();
|
|
17717
17911
|
this.pendingCulling = /* @__PURE__ */ new Map();
|
|
17718
17912
|
if (options.savedLighting && !options.sectionMeshing) throw new TypeError("savedLighting requires sectionMeshing");
|
|
17719
17913
|
if (options.ambientOcclusion && !options.sectionMeshing) throw new TypeError("ambientOcclusion requires sectionMeshing");
|
|
17720
17914
|
if (options.ambientLight !== void 0 && (!Number.isFinite(options.ambientLight) || options.ambientLight < 0 || options.ambientLight > 1)) {
|
|
17721
17915
|
throw new RangeError("ambientLight must be a number from 0 to 1");
|
|
17722
17916
|
}
|
|
17917
|
+
this._biomeBlendRadius = options.biomeBlendRadius ?? 0;
|
|
17918
|
+
validateBiomeBlendRadius(this._biomeBlendRadius);
|
|
17919
|
+
if (this._biomeBlendRadius > 0 && !options.sectionMeshing) throw new TypeError("biomeBlendRadius requires sectionMeshing");
|
|
17920
|
+
if (this._biomeBlendRadius) this.biomeBlend = new BiomeBlend((x, y, z) => this.getBiomeAt(x, y, z), this._biomeBlendRadius);
|
|
17723
17921
|
this.scene = scene;
|
|
17724
17922
|
this.savedLighting = options.savedLighting ?? false;
|
|
17725
17923
|
this.ambientLight = options.ambientLight ?? 0;
|
|
@@ -17760,6 +17958,65 @@ var MineRenderWorld = class {
|
|
|
17760
17958
|
await this.updateCulling([]);
|
|
17761
17959
|
this.ambientOcclusionPending = false;
|
|
17762
17960
|
}
|
|
17961
|
+
/** Horizontal biome-color sampling radius, from 0 to 7 blocks. Zero disables blending. */
|
|
17962
|
+
get biomeBlendRadius() {
|
|
17963
|
+
return this._biomeBlendRadius;
|
|
17964
|
+
}
|
|
17965
|
+
/** Refreshes loaded grass, foliage, and water colors without reloading their source chunks. */
|
|
17966
|
+
async setBiomeBlendRadius(radius) {
|
|
17967
|
+
validateBiomeBlendRadius(radius);
|
|
17968
|
+
if (radius > 0 && !this.sectionModels) throw new TypeError("biomeBlendRadius requires sectionMeshing");
|
|
17969
|
+
if (radius !== this._biomeBlendRadius) {
|
|
17970
|
+
this._biomeBlendRadius = radius;
|
|
17971
|
+
this.biomeBlend?.clear();
|
|
17972
|
+
this.biomeBlend = radius ? new BiomeBlend((x, y, z) => this.getBiomeAt(x, y, z), radius) : void 0;
|
|
17973
|
+
for (const chunk of this._chunks.values()) this.pendingBiomes.set(chunk, void 0);
|
|
17974
|
+
}
|
|
17975
|
+
const failures = await this.refreshBiomes();
|
|
17976
|
+
if (failures.size) throw failures.values().next().value;
|
|
17977
|
+
}
|
|
17978
|
+
markBiomesChanged(x, z, y, includeColumn = true) {
|
|
17979
|
+
const queued = [];
|
|
17980
|
+
if (!this.biomeBlend) return queued;
|
|
17981
|
+
this.biomeBlend.invalidateColumn(x, z);
|
|
17982
|
+
for (const chunk of this._chunks.values()) {
|
|
17983
|
+
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;
|
|
17984
|
+
queued.push(chunk);
|
|
17985
|
+
if (!this.pendingBiomes.has(chunk)) this.pendingBiomes.set(chunk, []);
|
|
17986
|
+
const columns = this.pendingBiomes.get(chunk);
|
|
17987
|
+
if (columns && !columns.some((column) => column.x === x && column.z === z)) columns.push({ x, z });
|
|
17988
|
+
}
|
|
17989
|
+
return queued;
|
|
17990
|
+
}
|
|
17991
|
+
async refreshBiomes() {
|
|
17992
|
+
if (this.biomeRefreshing) await this.biomeRefreshing;
|
|
17993
|
+
if (!this.pendingBiomes.size) return /* @__PURE__ */ new Map();
|
|
17994
|
+
return this.biomeRefreshing ??= Promise.resolve().then(async () => {
|
|
17995
|
+
const failures = /* @__PURE__ */ new Map();
|
|
17996
|
+
const batch = [...this.pendingBiomes.keys()];
|
|
17997
|
+
let sliceStart = performance.now();
|
|
17998
|
+
try {
|
|
17999
|
+
for (const chunk of batch) {
|
|
18000
|
+
const columns = this.pendingBiomes.get(chunk);
|
|
18001
|
+
this.pendingBiomes.delete(chunk);
|
|
18002
|
+
if (this._chunks.get(`${chunk.x}_${chunk.y}_${chunk.z}`) !== chunk) continue;
|
|
18003
|
+
try {
|
|
18004
|
+
await chunk.refreshBiomes(columns);
|
|
18005
|
+
} catch (error) {
|
|
18006
|
+
failures.set(chunk, error);
|
|
18007
|
+
if (this._chunks.get(`${chunk.x}_${chunk.y}_${chunk.z}`) === chunk) this.pendingBiomes.set(chunk, void 0);
|
|
18008
|
+
}
|
|
18009
|
+
if (performance.now() - sliceStart > 8) {
|
|
18010
|
+
await yieldToEventLoop();
|
|
18011
|
+
sliceStart = performance.now();
|
|
18012
|
+
}
|
|
18013
|
+
}
|
|
18014
|
+
return failures;
|
|
18015
|
+
} finally {
|
|
18016
|
+
this.biomeRefreshing = void 0;
|
|
18017
|
+
}
|
|
18018
|
+
});
|
|
18019
|
+
}
|
|
17763
18020
|
getBlockAt(posOrX, y, z) {
|
|
17764
18021
|
if (typeof posOrX == "number") {
|
|
17765
18022
|
return this.getBlockAt(new Vector319(posOrX, y, z));
|
|
@@ -17995,6 +18252,9 @@ var MineRenderWorld = class {
|
|
|
17995
18252
|
}
|
|
17996
18253
|
await placeEntities?.();
|
|
17997
18254
|
} finally {
|
|
18255
|
+
this.markBiomesChanged(chunk.x, chunk.z, void 0, false);
|
|
18256
|
+
const failures = await this.refreshBiomes();
|
|
18257
|
+
if (failures.size) console.warn("Failed to refresh biome colors", failures.values().next().value);
|
|
17998
18258
|
await this.updateCulling([...changes.values()]);
|
|
17999
18259
|
}
|
|
18000
18260
|
}
|
|
@@ -18011,11 +18271,14 @@ var MineRenderWorld = class {
|
|
|
18011
18271
|
try {
|
|
18012
18272
|
await this.clearChunkColumn(x, z);
|
|
18013
18273
|
} finally {
|
|
18274
|
+
const failures = await this.refreshBiomes();
|
|
18275
|
+
if (failures.size) console.warn("Failed to refresh biome colors", failures.values().next().value);
|
|
18014
18276
|
await this.updateCulling([]);
|
|
18015
18277
|
}
|
|
18016
18278
|
}
|
|
18017
18279
|
async clearChunkColumn(x, z) {
|
|
18018
18280
|
this.biomes.delete(`${x}_${z}`);
|
|
18281
|
+
this.markBiomesChanged(x, z, void 0, false);
|
|
18019
18282
|
if (this.savedLighting) {
|
|
18020
18283
|
for (const y of this.light.get(`${x}_${z}`)?.keys() ?? []) this.markSectionChanged({ x, y, z });
|
|
18021
18284
|
}
|
|
@@ -18026,6 +18289,7 @@ var MineRenderWorld = class {
|
|
|
18026
18289
|
for (const [key, section] of previous) {
|
|
18027
18290
|
this._chunks.delete(key);
|
|
18028
18291
|
this.pendingCulling.delete(section);
|
|
18292
|
+
this.pendingBiomes.delete(section);
|
|
18029
18293
|
await section.clear(false);
|
|
18030
18294
|
this.markSectionChanged(section);
|
|
18031
18295
|
}
|
|
@@ -18060,6 +18324,8 @@ var MineRenderWorld = class {
|
|
|
18060
18324
|
const chunks = [...this._chunks.values()];
|
|
18061
18325
|
this._chunks.clear();
|
|
18062
18326
|
this.biomes.clear();
|
|
18327
|
+
this.biomeBlend?.clear();
|
|
18328
|
+
this.pendingBiomes.clear();
|
|
18063
18329
|
this.light.clear();
|
|
18064
18330
|
this.lightValidity.clear();
|
|
18065
18331
|
this.highestSkySection.clear();
|
|
@@ -18178,8 +18444,14 @@ var MineRenderWorld = class {
|
|
|
18178
18444
|
column?.delete(y);
|
|
18179
18445
|
if (!column?.size) this.biomes.delete(columnKey);
|
|
18180
18446
|
}
|
|
18181
|
-
|
|
18182
|
-
|
|
18447
|
+
if (this.biomeBlend) {
|
|
18448
|
+
const queued = this.markBiomesChanged(x, z, y);
|
|
18449
|
+
const failures = await this.refreshBiomes();
|
|
18450
|
+
for (const chunk2 of queued) if (failures.has(chunk2)) throw failures.get(chunk2);
|
|
18451
|
+
} else {
|
|
18452
|
+
await chunk.refreshBiomes();
|
|
18453
|
+
await this._chunks.get(`${x}_${y + 1}_${z}`)?.refreshBiomes();
|
|
18454
|
+
}
|
|
18183
18455
|
},
|
|
18184
18456
|
this.savedLighting ? (x2, y2, z2) => this.lightLevelAt(x2, y2, z2) : void 0,
|
|
18185
18457
|
async () => {
|
|
@@ -18196,7 +18468,8 @@ var MineRenderWorld = class {
|
|
|
18196
18468
|
await this.updateCulling([]);
|
|
18197
18469
|
}
|
|
18198
18470
|
},
|
|
18199
|
-
this.ambientLight
|
|
18471
|
+
this.ambientLight,
|
|
18472
|
+
() => this.biomeBlend
|
|
18200
18473
|
);
|
|
18201
18474
|
this._chunks.set(key, chunk);
|
|
18202
18475
|
}
|
|
@@ -18315,9 +18588,10 @@ var LIGHT_SAMPLE_OFFSETS = [0, 1, -1, 324, -324, 18, -18];
|
|
|
18315
18588
|
|
|
18316
18589
|
// src/world/SectionGeometry.ts
|
|
18317
18590
|
function buildSectionGeometry(input) {
|
|
18318
|
-
const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize, fluids, light, ambientLight, ambientOcclusion } = input;
|
|
18591
|
+
const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize, fluids, light, ambientLight, ambientOcclusion, biomeTints } = input;
|
|
18319
18592
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
18320
18593
|
if (count > indices.length || count > templates.length || count > cullMasks.length) throw new RangeError("Section entry count exceeds its arrays");
|
|
18594
|
+
if (biomeTints && biomeTints.length !== count * 3) throw new RangeError("Section biome tints must contain RGB for each placement");
|
|
18321
18595
|
if (fluids && fluids.cells.length !== 5832) throw new RangeError("Section fluid cells must cover an 18\xD718\xD718 neighborhood");
|
|
18322
18596
|
if (fluids?.waterTints && fluids.waterTints.length !== 4096 * 3) throw new RangeError("Section water tints must contain RGB for 4096 cells");
|
|
18323
18597
|
if (light && (light.length !== 5832 || light.some((level) => level > 15 && level !== 255))) {
|
|
@@ -18332,7 +18606,7 @@ function buildSectionGeometry(input) {
|
|
|
18332
18606
|
const cornerBrightness2 = ambientOcclusion ? new Float32Array(4) : void 0;
|
|
18333
18607
|
for (const template of templateData) {
|
|
18334
18608
|
const { quads } = template;
|
|
18335
|
-
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) {
|
|
18609
|
+
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) {
|
|
18336
18610
|
throw new RangeError("Section template arrays do not match its quad count");
|
|
18337
18611
|
}
|
|
18338
18612
|
}
|
|
@@ -18442,6 +18716,7 @@ function buildSectionGeometry(input) {
|
|
|
18442
18716
|
blockIndices[indexOffset / 6] = indices[entry];
|
|
18443
18717
|
if (ambientMask) quadAmbientOcclusion(template.positions, template.normals, quad, ambientMask, cornerBrightness2);
|
|
18444
18718
|
const brightness = light ? lightBrightness(light[lightIndex + LIGHT_SAMPLE_OFFSETS[quadLightIndex(template.positions, template.normals, quad)]], ambientLight) : 1;
|
|
18719
|
+
const tint = biomeTints && template.biomeTint?.[quad] && !Number.isNaN(biomeTints[entry * 3]) ? entry * 3 : void 0;
|
|
18445
18720
|
const start = vertex;
|
|
18446
18721
|
for (let corner = 0; corner < 4; corner++, vertex++) {
|
|
18447
18722
|
const source = quad * 4 + corner;
|
|
@@ -18458,9 +18733,9 @@ function buildSectionGeometry(input) {
|
|
|
18458
18733
|
uvBounds[vertex * 4 + 2] = mapU(template.uvBounds[source * 4 + 2]);
|
|
18459
18734
|
uvBounds[vertex * 4 + 3] = mapV(template.uvBounds[source * 4 + 3]);
|
|
18460
18735
|
const factor = brightness * (ambientMask ? cornerBrightness2[corner] : 1);
|
|
18461
|
-
colors[vertex * 3] = template.colors[source * 3] * factor;
|
|
18462
|
-
colors[vertex * 3 + 1] = template.colors[source * 3 + 1] * factor;
|
|
18463
|
-
colors[vertex * 3 + 2] = template.colors[source * 3 + 2] * factor;
|
|
18736
|
+
colors[vertex * 3] = (tint === void 0 ? template.colors[source * 3] : biomeTints[tint]) * factor;
|
|
18737
|
+
colors[vertex * 3 + 1] = (tint === void 0 ? template.colors[source * 3 + 1] : biomeTints[tint + 1]) * factor;
|
|
18738
|
+
colors[vertex * 3 + 2] = (tint === void 0 ? template.colors[source * 3 + 2] : biomeTints[tint + 2]) * factor;
|
|
18464
18739
|
}
|
|
18465
18740
|
for (let corner = 0; corner < 6; corner++) outputIndices[indexOffset++] = start + template.indices[quad * 6 + corner];
|
|
18466
18741
|
}
|
|
@@ -18582,7 +18857,11 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
|
18582
18857
|
...lighting
|
|
18583
18858
|
};
|
|
18584
18859
|
for (let i = 0; i < entries.length; i++) {
|
|
18585
|
-
const { index, template, cullMask } = entries[i];
|
|
18860
|
+
const { index, template, cullMask, biomeTint } = entries[i];
|
|
18861
|
+
if (biomeTint) {
|
|
18862
|
+
input.biomeTints ??= new Float32Array(entries.length * 3).fill(NaN);
|
|
18863
|
+
input.biomeTints.set(biomeTint, i * 3);
|
|
18864
|
+
}
|
|
18586
18865
|
let id = templateIds.get(template);
|
|
18587
18866
|
if (id === void 0) {
|
|
18588
18867
|
let atlas = atlasIds.get(template.atlas);
|
|
@@ -18609,6 +18888,7 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
|
18609
18888
|
colors: new Float32Array(quads * 12),
|
|
18610
18889
|
indices: new Uint16Array(quads * 6),
|
|
18611
18890
|
cullFaces: new Uint8Array(template.cullFaces),
|
|
18891
|
+
biomeTint: template.biomeTint ? new Uint8Array(template.biomeTint) : void 0,
|
|
18612
18892
|
atlas: 0,
|
|
18613
18893
|
layer: template.layer,
|
|
18614
18894
|
ambientOcclusion: template.ambientOcclusion
|
|
@@ -18753,7 +19033,7 @@ init_CubeFace();
|
|
|
18753
19033
|
init_InstanceReference();
|
|
18754
19034
|
var WATER_TINT2 = new Color13(4159204).toArray();
|
|
18755
19035
|
var Chunk = class _Chunk {
|
|
18756
|
-
constructor(scene, x, y, z, onBlocksChanged, sectionModels, neighborCell, biomeAt, onBiomesChanged, lightAt, onLightCleared, ambientLight = 0) {
|
|
19036
|
+
constructor(scene, x, y, z, onBlocksChanged, sectionModels, neighborCell, biomeAt, onBiomesChanged, lightAt, onLightCleared, ambientLight = 0, biomeBlend) {
|
|
18757
19037
|
this.onBlocksChanged = onBlocksChanged;
|
|
18758
19038
|
this.sectionModels = sectionModels;
|
|
18759
19039
|
this.neighborCell = neighborCell;
|
|
@@ -18762,6 +19042,7 @@ var Chunk = class _Chunk {
|
|
|
18762
19042
|
this.lightAt = lightAt;
|
|
18763
19043
|
this.onLightCleared = onLightCleared;
|
|
18764
19044
|
this.ambientLight = ambientLight;
|
|
19045
|
+
this.biomeBlend = biomeBlend;
|
|
18765
19046
|
this.data = new ChunkData();
|
|
18766
19047
|
this.renderedBlocks = /* @__PURE__ */ new Map();
|
|
18767
19048
|
this.objectBlocks = /* @__PURE__ */ new Set();
|
|
@@ -18771,6 +19052,7 @@ var Chunk = class _Chunk {
|
|
|
18771
19052
|
this.blockPosition = new Vector320();
|
|
18772
19053
|
this.meshDirty = false;
|
|
18773
19054
|
this.meshGeneration = 0;
|
|
19055
|
+
this.blockChanges = /* @__PURE__ */ new Set();
|
|
18774
19056
|
this.scene = scene;
|
|
18775
19057
|
this.x = x;
|
|
18776
19058
|
this.y = y;
|
|
@@ -18869,10 +19151,20 @@ var Chunk = class _Chunk {
|
|
|
18869
19151
|
return this.refreshBiomes();
|
|
18870
19152
|
}
|
|
18871
19153
|
/** Rebuilds biome-tinted blocks while preserving hidden blocks and unrelated render objects. */
|
|
18872
|
-
|
|
19154
|
+
refreshBiomes(columns) {
|
|
19155
|
+
return this.changeBlocks(() => this.refreshBiomeBlocks(columns), true);
|
|
19156
|
+
}
|
|
19157
|
+
async refreshBiomeBlocks(columns) {
|
|
19158
|
+
const blend = columns && this.biomeBlend?.();
|
|
19159
|
+
const ownColumn = columns?.some((column) => column.x === this.x && column.z === this.z);
|
|
18873
19160
|
const affected = /* @__PURE__ */ new Map();
|
|
18874
19161
|
const blocks = [];
|
|
18875
19162
|
for (const index of this.renderedBlocks.keys()) {
|
|
19163
|
+
if (blend) {
|
|
19164
|
+
if (this.objectBlocks.has(index) && !ownColumn) continue;
|
|
19165
|
+
const [x, , z] = this.positionAt(index), radius = blend.radius;
|
|
19166
|
+
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;
|
|
19167
|
+
}
|
|
18876
19168
|
const block = this.data.get(index);
|
|
18877
19169
|
const stateKey = ChunkData.paletteKey(block);
|
|
18878
19170
|
let matches = affected.get(stateKey);
|
|
@@ -18887,10 +19179,32 @@ var Chunk = class _Chunk {
|
|
|
18887
19179
|
}
|
|
18888
19180
|
if (await matches) blocks.push({ index, block });
|
|
18889
19181
|
}
|
|
19182
|
+
if (blend) {
|
|
19183
|
+
const groups = /* @__PURE__ */ new Map();
|
|
19184
|
+
for (const entry of blocks) {
|
|
19185
|
+
const key = ChunkData.paletteKey(entry.block);
|
|
19186
|
+
let group = groups.get(key);
|
|
19187
|
+
if (!group) groups.set(key, group = []);
|
|
19188
|
+
group.push(entry);
|
|
19189
|
+
}
|
|
19190
|
+
blocks.length = 0;
|
|
19191
|
+
const biomes = /* @__PURE__ */ new Map();
|
|
19192
|
+
for (const group of groups.values()) {
|
|
19193
|
+
const { contexts, fluid } = await this.resolveState(group[0].block, group.map((entry) => entry.index), biomes);
|
|
19194
|
+
for (const entry of group) {
|
|
19195
|
+
const context = contexts.get(entry.index);
|
|
19196
|
+
const current = this.sectionBlocks.get(entry.index)?.[0]?.biomeTint;
|
|
19197
|
+
const tint = context?.tint;
|
|
19198
|
+
const tintChanged = !!current !== !!tint || tint?.some((value, channel) => value !== current?.[channel]);
|
|
19199
|
+
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])));
|
|
19200
|
+
if (this.objectBlocks.has(entry.index) || tintChanged || waterChanged) blocks.push(entry);
|
|
19201
|
+
}
|
|
19202
|
+
}
|
|
19203
|
+
}
|
|
18890
19204
|
if (!blocks.length) return;
|
|
18891
19205
|
const hidden = new Set(this.hiddenBlocks);
|
|
18892
19206
|
try {
|
|
18893
|
-
await this.
|
|
19207
|
+
await this.replaceBlocks(blocks, true);
|
|
18894
19208
|
} finally {
|
|
18895
19209
|
for (const index of hidden) {
|
|
18896
19210
|
this.hiddenBlocks.add(index);
|
|
@@ -18930,6 +19244,20 @@ var Chunk = class _Chunk {
|
|
|
18930
19244
|
}
|
|
18931
19245
|
/** Places cells in input order and returns changed world positions without refreshing neighbors. */
|
|
18932
19246
|
async placeBlocks(blocks) {
|
|
19247
|
+
return this.changeBlocks(() => this.replaceBlocks(blocks));
|
|
19248
|
+
}
|
|
19249
|
+
changeBlocks(change, refreshBiomes = false) {
|
|
19250
|
+
const before = refreshBiomes ? Promise.allSettled(this.blockChanges) : this.biomeChange;
|
|
19251
|
+
const result = before ? before.catch(() => void 0).then(change) : change();
|
|
19252
|
+
const pending = result.finally(() => {
|
|
19253
|
+
this.blockChanges.delete(pending);
|
|
19254
|
+
if (this.biomeChange === pending) this.biomeChange = void 0;
|
|
19255
|
+
});
|
|
19256
|
+
this.blockChanges.add(pending);
|
|
19257
|
+
if (refreshBiomes) this.biomeChange = pending;
|
|
19258
|
+
return pending;
|
|
19259
|
+
}
|
|
19260
|
+
async replaceBlocks(blocks, preserveDataOnFailure = false) {
|
|
18933
19261
|
const placements = blocks.map(({ index, block }) => ({
|
|
18934
19262
|
index,
|
|
18935
19263
|
key: block && !ChunkData.isAir(block) ? ChunkData.paletteKey(block) : void 0,
|
|
@@ -18949,6 +19277,10 @@ var Chunk = class _Chunk {
|
|
|
18949
19277
|
const biomes = /* @__PURE__ */ new Map();
|
|
18950
19278
|
const settled = await Promise.allSettled([...states.values()].map(({ stored, indices }) => this.resolveState(stored, indices, biomes)));
|
|
18951
19279
|
const resolutions = new Map([...states.keys()].map((key, i) => [key, settled[i]]));
|
|
19280
|
+
if (preserveDataOnFailure) {
|
|
19281
|
+
const rejected = settled.find((result) => result.status === "rejected");
|
|
19282
|
+
if (rejected?.status === "rejected") throw rejected.reason;
|
|
19283
|
+
}
|
|
18952
19284
|
const positions = [];
|
|
18953
19285
|
let failed = false, failure;
|
|
18954
19286
|
for (const { index, key, block } of placements) {
|
|
@@ -18986,7 +19318,7 @@ var Chunk = class _Chunk {
|
|
|
18986
19318
|
}
|
|
18987
19319
|
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : (context?.prepared ?? prepared)?.pick(variantPosition);
|
|
18988
19320
|
if (templates) {
|
|
18989
|
-
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0 })));
|
|
19321
|
+
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0, biomeTint: context?.tint })));
|
|
18990
19322
|
this.meshDirty = true;
|
|
18991
19323
|
} else {
|
|
18992
19324
|
object4 = await this.scene.addBlock(blockState3, {
|
|
@@ -19005,7 +19337,11 @@ var Chunk = class _Chunk {
|
|
|
19005
19337
|
}, object: object4 });
|
|
19006
19338
|
if (object4) this.objectBlocks.add(index);
|
|
19007
19339
|
} catch (error) {
|
|
19008
|
-
|
|
19340
|
+
if (preserveDataOnFailure && readBlock) {
|
|
19341
|
+
this.renderedBlocks.set(index, { get block() {
|
|
19342
|
+
return readBlock();
|
|
19343
|
+
}, object: void 0 });
|
|
19344
|
+
} else this.data.assign(index, 0);
|
|
19009
19345
|
this.fluidCells[index] = 0;
|
|
19010
19346
|
object4?.removeFromScene();
|
|
19011
19347
|
if (!failed) failure = error;
|
|
@@ -19016,7 +19352,10 @@ var Chunk = class _Chunk {
|
|
|
19016
19352
|
return positions;
|
|
19017
19353
|
}
|
|
19018
19354
|
/** Replaces every cell from parsed section data. The caller refreshes neighbor culling. */
|
|
19019
|
-
|
|
19355
|
+
placeSection(data) {
|
|
19356
|
+
return this.changeBlocks(() => this.replaceSection(data));
|
|
19357
|
+
}
|
|
19358
|
+
async replaceSection(data) {
|
|
19020
19359
|
for (const info of this.renderedBlocks.values()) info.object?.removeFromScene();
|
|
19021
19360
|
this.sectionBlocks.clear();
|
|
19022
19361
|
this.fluidCells.fill(0);
|
|
@@ -19063,7 +19402,7 @@ var Chunk = class _Chunk {
|
|
|
19063
19402
|
];
|
|
19064
19403
|
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : (context?.prepared ?? prepared)?.pick(variantPosition);
|
|
19065
19404
|
if (templates) {
|
|
19066
|
-
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0 })));
|
|
19405
|
+
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0, biomeTint: context?.tint })));
|
|
19067
19406
|
this.meshDirty = true;
|
|
19068
19407
|
} else {
|
|
19069
19408
|
perBlocks.push({ index, blockState: blockState3, biome: context?.biome });
|
|
@@ -19147,12 +19486,35 @@ var Chunk = class _Chunk {
|
|
|
19147
19486
|
}
|
|
19148
19487
|
async resolveState(stored, indices, biomes) {
|
|
19149
19488
|
const blockState3 = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
|
|
19150
|
-
|
|
19489
|
+
let perBlock = !blockState3 || !this.sectionModels || !!(blockState3.key && BlockEntities.entry(await BlockEntities.getIndex(blockState3.key.root), blockState3.key.toNamespacedString()));
|
|
19151
19490
|
const fluid = await getBlockFluidState(blockState3?.key, stored.properties);
|
|
19152
19491
|
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;
|
|
19153
19492
|
const source = hasBiomes && blockState3 ? await BlockTints.biomeSource(blockState3.key) : void 0;
|
|
19154
19493
|
const prepare = (color2) => perBlock || fluid?.renderModel === false ? void 0 : this.sectionModels.prepareState(blockState3, stored.properties, color2);
|
|
19155
19494
|
const contexts = /* @__PURE__ */ new Map();
|
|
19495
|
+
const blend = this.biomeBlend?.();
|
|
19496
|
+
if (blend && !perBlock && blockState3 && (source || fluid?.kind === "water")) {
|
|
19497
|
+
const prepared2 = fluid?.renderModel === false ? void 0 : await this.sectionModels.prepareState(blockState3, stored.properties, void 0, true);
|
|
19498
|
+
if (!prepared2 && fluid?.renderModel !== false) {
|
|
19499
|
+
perBlock = true;
|
|
19500
|
+
} else {
|
|
19501
|
+
await Promise.all(indices.map(async (index) => {
|
|
19502
|
+
const position = this.biomePosition(index, blockState3, stored);
|
|
19503
|
+
const biomeColor = (kind) => blend.getColor(kind, blockState3.key, position);
|
|
19504
|
+
const context = {};
|
|
19505
|
+
if (source && prepared2?.biomeTint) {
|
|
19506
|
+
const color2 = await biomeColor(source);
|
|
19507
|
+
if (color2 !== void 0) context.tint = new Color13(color2).toArray();
|
|
19508
|
+
}
|
|
19509
|
+
if (fluid?.kind === "water") {
|
|
19510
|
+
const color2 = await biomeColor("water");
|
|
19511
|
+
if (color2 !== void 0 && color2 !== 4159204) context.waterTint = new Color13(color2).toArray();
|
|
19512
|
+
}
|
|
19513
|
+
contexts.set(index, context);
|
|
19514
|
+
}));
|
|
19515
|
+
return { blockState: blockState3, perBlock, fluid, prepared: prepared2, contexts, stored };
|
|
19516
|
+
}
|
|
19517
|
+
}
|
|
19156
19518
|
const groups = /* @__PURE__ */ new Map();
|
|
19157
19519
|
if (blockState3 && (source || fluid?.kind === "water")) {
|
|
19158
19520
|
for (const index of indices) {
|
|
@@ -19200,7 +19562,11 @@ var Chunk = class _Chunk {
|
|
|
19200
19562
|
this.waterTints.set(tint ?? WATER_TINT2, index * 3);
|
|
19201
19563
|
}
|
|
19202
19564
|
/** Changes visibility at world block coordinates while preserving the block's data. */
|
|
19203
|
-
|
|
19565
|
+
setBlockVisibleAt(pos, visible) {
|
|
19566
|
+
const position = pos.clone();
|
|
19567
|
+
return this.changeBlocks(() => this.setBlockVisibility(position, visible));
|
|
19568
|
+
}
|
|
19569
|
+
async setBlockVisibility(pos, visible) {
|
|
19204
19570
|
const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
|
|
19205
19571
|
const block = this.renderedBlocks.get(index);
|
|
19206
19572
|
if (!block || visible === !this.hiddenBlocks.has(index)) {
|
|
@@ -19370,6 +19736,7 @@ var Chunk = class _Chunk {
|
|
|
19370
19736
|
}
|
|
19371
19737
|
/** Removes stored blocks and render objects. Set `notify` to false to skip the owning world's update. */
|
|
19372
19738
|
async clear(notify = true) {
|
|
19739
|
+
if (this.blockChanges.size) await Promise.allSettled(this.blockChanges);
|
|
19373
19740
|
const positions = notify && this.onBlocksChanged ? [...this.renderedBlocks.keys()].map((index) => this.chunkPosToWorldPos(
|
|
19374
19741
|
new Vector320(index % 16, Math.floor(index / 256), Math.floor(index / 16) % 16)
|
|
19375
19742
|
)) : [];
|
|
@@ -19848,6 +20215,7 @@ export {
|
|
|
19848
20215
|
PackMetadata,
|
|
19849
20216
|
PersistentCache,
|
|
19850
20217
|
PlayerHeadTextures,
|
|
20218
|
+
Renames,
|
|
19851
20219
|
Renderer,
|
|
19852
20220
|
RequestError,
|
|
19853
20221
|
Requests,
|