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.js
CHANGED
|
@@ -252,6 +252,13 @@ var init_Caching = __esm({
|
|
|
252
252
|
import_loading_cache = require("@inventivetalent/loading-cache");
|
|
253
253
|
import_time = require("@inventivetalent/time");
|
|
254
254
|
Caching = class _Caching {
|
|
255
|
+
static {
|
|
256
|
+
this._generation = 0;
|
|
257
|
+
}
|
|
258
|
+
/** Changes when shared assets are cleared, so derived caches can discard their old values. */
|
|
259
|
+
static get generation() {
|
|
260
|
+
return this._generation;
|
|
261
|
+
}
|
|
255
262
|
static {
|
|
256
263
|
this.rawImageCache = import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(5)).buildAsync();
|
|
257
264
|
}
|
|
@@ -362,6 +369,7 @@ var init_Caching = __esm({
|
|
|
362
369
|
}
|
|
363
370
|
/** Invalidates in-memory entries so later lookups reload them. Persisted entries are retained. */
|
|
364
371
|
static clear() {
|
|
372
|
+
this._generation++;
|
|
365
373
|
for (const cache of this.all) {
|
|
366
374
|
cache.invalidateAll();
|
|
367
375
|
}
|
|
@@ -373,6 +381,7 @@ var init_Caching = __esm({
|
|
|
373
381
|
* Use {@link shutdown} to also stop requests and the shared Ticker.
|
|
374
382
|
*/
|
|
375
383
|
static end() {
|
|
384
|
+
this._generation++;
|
|
376
385
|
for (const cache of this.all) {
|
|
377
386
|
cache.invalidateAll();
|
|
378
387
|
cache.end();
|
|
@@ -1265,118 +1274,6 @@ var init_Materials = __esm({
|
|
|
1265
1274
|
}
|
|
1266
1275
|
});
|
|
1267
1276
|
|
|
1268
|
-
// src/model/Model.ts
|
|
1269
|
-
function isDoubleArray(obj) {
|
|
1270
|
-
return Array.isArray(obj) && obj.length === 2;
|
|
1271
|
-
}
|
|
1272
|
-
function isTripleArray(obj) {
|
|
1273
|
-
return Array.isArray(obj) && obj.length === 3;
|
|
1274
|
-
}
|
|
1275
|
-
function isQuadArray(obj) {
|
|
1276
|
-
return Array.isArray(obj) && obj.length === 4;
|
|
1277
|
-
}
|
|
1278
|
-
var ITEM_GENERATED, BUILTIN_GENERATED, BUILTIN_ENTITY, DEFAULT_ELEMENTS;
|
|
1279
|
-
var init_Model = __esm({
|
|
1280
|
-
"src/model/Model.ts"() {
|
|
1281
|
-
ITEM_GENERATED = "item/generated";
|
|
1282
|
-
BUILTIN_GENERATED = "builtin/generated";
|
|
1283
|
-
BUILTIN_ENTITY = "builtin/entity";
|
|
1284
|
-
DEFAULT_ELEMENTS = [];
|
|
1285
|
-
}
|
|
1286
|
-
});
|
|
1287
|
-
|
|
1288
|
-
// ../../node_modules/ts-deepmerge/dist/index.js
|
|
1289
|
-
var require_dist = __commonJS({
|
|
1290
|
-
"../../node_modules/ts-deepmerge/dist/index.js"(exports2) {
|
|
1291
|
-
"use strict";
|
|
1292
|
-
var __assign = exports2 && exports2.__assign || function() {
|
|
1293
|
-
__assign = Object.assign || function(t) {
|
|
1294
|
-
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
1295
|
-
s = arguments[i];
|
|
1296
|
-
for (var p10 in s) if (Object.prototype.hasOwnProperty.call(s, p10))
|
|
1297
|
-
t[p10] = s[p10];
|
|
1298
|
-
}
|
|
1299
|
-
return t;
|
|
1300
|
-
};
|
|
1301
|
-
return __assign.apply(this, arguments);
|
|
1302
|
-
};
|
|
1303
|
-
var __read = exports2 && exports2.__read || function(o, n) {
|
|
1304
|
-
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
1305
|
-
if (!m) return o;
|
|
1306
|
-
var i = m.call(o), r, ar = [], e;
|
|
1307
|
-
try {
|
|
1308
|
-
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
1309
|
-
} catch (error) {
|
|
1310
|
-
e = { error };
|
|
1311
|
-
} finally {
|
|
1312
|
-
try {
|
|
1313
|
-
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
1314
|
-
} finally {
|
|
1315
|
-
if (e) throw e.error;
|
|
1316
|
-
}
|
|
1317
|
-
}
|
|
1318
|
-
return ar;
|
|
1319
|
-
};
|
|
1320
|
-
var __spreadArray = exports2 && exports2.__spreadArray || function(to, from, pack) {
|
|
1321
|
-
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
1322
|
-
if (ar || !(i in from)) {
|
|
1323
|
-
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
1324
|
-
ar[i] = from[i];
|
|
1325
|
-
}
|
|
1326
|
-
}
|
|
1327
|
-
return to.concat(ar || Array.prototype.slice.call(from));
|
|
1328
|
-
};
|
|
1329
|
-
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
1330
|
-
var isObject = function(obj) {
|
|
1331
|
-
if (typeof obj === "object" && obj !== null) {
|
|
1332
|
-
if (typeof Object.getPrototypeOf === "function") {
|
|
1333
|
-
var prototype = Object.getPrototypeOf(obj);
|
|
1334
|
-
return prototype === Object.prototype || prototype === null;
|
|
1335
|
-
}
|
|
1336
|
-
return Object.prototype.toString.call(obj) === "[object Object]";
|
|
1337
|
-
}
|
|
1338
|
-
return false;
|
|
1339
|
-
};
|
|
1340
|
-
var PROTECTED_KEYS = ["__proto__"];
|
|
1341
|
-
var merge10 = function() {
|
|
1342
|
-
var objects = [];
|
|
1343
|
-
for (var _i = 0; _i < arguments.length; _i++) {
|
|
1344
|
-
objects[_i] = arguments[_i];
|
|
1345
|
-
}
|
|
1346
|
-
return objects.reduce(function(result, current) {
|
|
1347
|
-
Object.keys(current).forEach(function(key) {
|
|
1348
|
-
if (PROTECTED_KEYS.includes(key)) {
|
|
1349
|
-
return;
|
|
1350
|
-
}
|
|
1351
|
-
if (Array.isArray(result[key]) && Array.isArray(current[key])) {
|
|
1352
|
-
result[key] = merge10.options.mergeArrays ? Array.from(new Set(result[key].concat(current[key]))) : current[key];
|
|
1353
|
-
} else if (isObject(result[key]) && isObject(current[key])) {
|
|
1354
|
-
result[key] = merge10(result[key], current[key]);
|
|
1355
|
-
} else {
|
|
1356
|
-
result[key] = current[key];
|
|
1357
|
-
}
|
|
1358
|
-
});
|
|
1359
|
-
return result;
|
|
1360
|
-
}, {});
|
|
1361
|
-
};
|
|
1362
|
-
var defaultOptions = {
|
|
1363
|
-
mergeArrays: true
|
|
1364
|
-
};
|
|
1365
|
-
merge10.options = defaultOptions;
|
|
1366
|
-
merge10.withOptions = function(options) {
|
|
1367
|
-
var objects = [];
|
|
1368
|
-
for (var _i = 1; _i < arguments.length; _i++) {
|
|
1369
|
-
objects[_i - 1] = arguments[_i];
|
|
1370
|
-
}
|
|
1371
|
-
merge10.options = __assign({ mergeArrays: true }, options);
|
|
1372
|
-
var result = merge10.apply(void 0, __spreadArray([], __read(objects), false));
|
|
1373
|
-
merge10.options = defaultOptions;
|
|
1374
|
-
return result;
|
|
1375
|
-
};
|
|
1376
|
-
exports2.default = merge10;
|
|
1377
|
-
}
|
|
1378
|
-
});
|
|
1379
|
-
|
|
1380
1277
|
// src/assets/AssetKey.ts
|
|
1381
1278
|
function isResourceLocation(value) {
|
|
1382
1279
|
return typeof value === "string" && /^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(value);
|
|
@@ -1479,17 +1376,306 @@ var init_AssetKey = __esm({
|
|
|
1479
1376
|
if (this.type) {
|
|
1480
1377
|
p10.push(this.type);
|
|
1481
1378
|
}
|
|
1482
|
-
p10.push(this.path);
|
|
1483
|
-
return p10.join("/");
|
|
1379
|
+
p10.push(this.path);
|
|
1380
|
+
return p10.join("/");
|
|
1381
|
+
}
|
|
1382
|
+
toNamespacedString() {
|
|
1383
|
+
return this.namespace + ":" + this.getFullPath();
|
|
1384
|
+
}
|
|
1385
|
+
/** Returns a cache identifier that includes the explicit root or {@link AssetLoader.ROOT}. */
|
|
1386
|
+
serialize() {
|
|
1387
|
+
return this.toString();
|
|
1388
|
+
}
|
|
1389
|
+
};
|
|
1390
|
+
}
|
|
1391
|
+
});
|
|
1392
|
+
|
|
1393
|
+
// src/assets/source/parser/AssetParsers.ts
|
|
1394
|
+
var AssetParser;
|
|
1395
|
+
var init_AssetParsers = __esm({
|
|
1396
|
+
"src/assets/source/parser/AssetParsers.ts"() {
|
|
1397
|
+
AssetParser = /* @__PURE__ */ ((AssetParser2) => {
|
|
1398
|
+
AssetParser2["MODEL"] = "model";
|
|
1399
|
+
AssetParser2["BLOCKSTATE"] = "blockstate";
|
|
1400
|
+
AssetParser2["META"] = "meta";
|
|
1401
|
+
AssetParser2["NBT"] = "nbt";
|
|
1402
|
+
AssetParser2["IMAGE"] = "image";
|
|
1403
|
+
AssetParser2["LIST"] = "list";
|
|
1404
|
+
AssetParser2["JSON"] = "json";
|
|
1405
|
+
return AssetParser2;
|
|
1406
|
+
})(AssetParser || {});
|
|
1407
|
+
}
|
|
1408
|
+
});
|
|
1409
|
+
|
|
1410
|
+
// src/assets/MineRenderData.ts
|
|
1411
|
+
var object, release, compare, MineRenderData;
|
|
1412
|
+
var init_MineRenderData = __esm({
|
|
1413
|
+
"src/assets/MineRenderData.ts"() {
|
|
1414
|
+
init_AssetKey();
|
|
1415
|
+
init_AssetLoader();
|
|
1416
|
+
init_AssetParsers();
|
|
1417
|
+
init_Caching();
|
|
1418
|
+
object = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1419
|
+
release = (value) => /^\d+\.\d+(?:\.\d+)?$/.test(value) ? value.split(".").map(Number) : void 0;
|
|
1420
|
+
compare = (left, right) => {
|
|
1421
|
+
for (let i = 0; i < 3; i++) {
|
|
1422
|
+
const difference = (left[i] ?? 0) - (right[i] ?? 0);
|
|
1423
|
+
if (difference) return difference;
|
|
1424
|
+
}
|
|
1425
|
+
return 0;
|
|
1426
|
+
};
|
|
1427
|
+
MineRenderData = class {
|
|
1428
|
+
/** Loads one registry. Missing files and invalid data reject the request. */
|
|
1429
|
+
static async get(dataset, root) {
|
|
1430
|
+
root = root?.replace(/\/+$/, "");
|
|
1431
|
+
const scope = AssetLoader.persistentScope;
|
|
1432
|
+
const defaultRoot = AssetLoader.ROOT;
|
|
1433
|
+
const resolved = await this.resolve(root);
|
|
1434
|
+
const entry = resolved.manifest.datasets[dataset];
|
|
1435
|
+
if (!entry) throw new Error(`MineRender ${dataset} data is unavailable for ${resolved.version}: ${resolved.manifest.unavailable[dataset] ?? "not listed in the manifest"}`);
|
|
1436
|
+
const file = entry.file;
|
|
1437
|
+
const requestRoot = resolved.root === (root ?? defaultRoot).replace(/\/+$/, "") ? root : resolved.root;
|
|
1438
|
+
return await this.cached(`${requestRoot === void 0 ? "sources" : "explicit"}:${resolved.root}/${file}:${entry.sha256}`, scope, defaultRoot, async () => {
|
|
1439
|
+
const data = await this.load(file.slice(0, -5), requestRoot);
|
|
1440
|
+
if (!this.validDataset(dataset, data)) throw new Error(`Invalid MineRender ${dataset} data at ${resolved.root}/minerender-data/${file}`);
|
|
1441
|
+
return data;
|
|
1442
|
+
});
|
|
1443
|
+
}
|
|
1444
|
+
/**
|
|
1445
|
+
* Uses the exact version when published. Otherwise, selects the nearest patch in the same
|
|
1446
|
+
* release line (older on ties), then the latest earlier release or the oldest published release.
|
|
1447
|
+
* Unpublished snapshots reject.
|
|
1448
|
+
*/
|
|
1449
|
+
static async resolve(root) {
|
|
1450
|
+
const requestRoot = root?.replace(/\/+$/, "");
|
|
1451
|
+
const defaultRoot = AssetLoader.ROOT;
|
|
1452
|
+
root = (root ?? defaultRoot).replace(/\/+$/, "");
|
|
1453
|
+
const scope = AssetLoader.persistentScope;
|
|
1454
|
+
return this.cached(`resolve:${requestRoot === void 0 ? "sources" : "explicit"}:${root}`, scope, defaultRoot, async () => {
|
|
1455
|
+
const requestedVersion = root.slice(root.lastIndexOf("/") + 1);
|
|
1456
|
+
let manifest = await this.manifest(root, requestRoot !== void 0 && release(requestedVersion) ? requestedVersion : void 0, requestRoot);
|
|
1457
|
+
if (manifest) return { requestedVersion, version: manifest.minecraftVersion, root, manifest };
|
|
1458
|
+
const requested = release(requestedVersion);
|
|
1459
|
+
if (!requested) {
|
|
1460
|
+
throw new Error(`No MineRender data is published for ${requestedVersion}; automatic fallback requires a release`);
|
|
1461
|
+
}
|
|
1462
|
+
const base = root.slice(0, root.lastIndexOf("/"));
|
|
1463
|
+
const index = await this.cached(`versions:${base}`, scope, defaultRoot, async () => {
|
|
1464
|
+
const value = await this.load("versions", base);
|
|
1465
|
+
if (!object(value) || !Array.isArray(value.versions) || !value.versions.every((entry) => object(entry) && typeof entry.name === "string")) {
|
|
1466
|
+
throw new Error(`Invalid MineRender version index at ${base}/minerender-data/versions.json`);
|
|
1467
|
+
}
|
|
1468
|
+
return value.versions;
|
|
1469
|
+
});
|
|
1470
|
+
const versions = index.map((entry) => ({ name: entry.name, number: release(entry.name) })).filter((entry) => !!entry.number).sort((left, right) => compare(left.number, right.number));
|
|
1471
|
+
const sameLine = versions.filter((entry) => entry.number[0] === requested[0] && entry.number[1] === requested[1]);
|
|
1472
|
+
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];
|
|
1473
|
+
if (!selected) throw new Error(`No compatible MineRender data is published for ${requestedVersion}`);
|
|
1474
|
+
const resolvedRoot = `${base}/${selected.name}`;
|
|
1475
|
+
manifest = await this.manifest(resolvedRoot, selected.name, resolvedRoot);
|
|
1476
|
+
if (!manifest) throw new Error(`Missing MineRender manifest for published version ${selected.name}`);
|
|
1477
|
+
return { requestedVersion, version: selected.name, root: resolvedRoot, manifest };
|
|
1478
|
+
});
|
|
1479
|
+
}
|
|
1480
|
+
static async manifest(root, expectedVersion, requestRoot) {
|
|
1481
|
+
const data = await this.load("manifest", requestRoot);
|
|
1482
|
+
if (data === void 0) return void 0;
|
|
1483
|
+
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) => {
|
|
1484
|
+
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);
|
|
1485
|
+
}) || expectedVersion !== void 0 && data.minecraftVersion !== expectedVersion) {
|
|
1486
|
+
throw new Error(`Invalid MineRender manifest at ${root}/minerender-data/manifest.json`);
|
|
1487
|
+
}
|
|
1488
|
+
return data;
|
|
1489
|
+
}
|
|
1490
|
+
static load(file, root) {
|
|
1491
|
+
const key = new AssetKey("minerender-data", file, void 0, void 0, void 0, ".json", root);
|
|
1492
|
+
key.rootType = void 0;
|
|
1493
|
+
return AssetLoader.get(key, "json" /* JSON */);
|
|
1494
|
+
}
|
|
1495
|
+
static async cached(key, scope, defaultRoot, loader) {
|
|
1496
|
+
const checkSources = () => {
|
|
1497
|
+
if (AssetLoader.persistentScope !== scope || AssetLoader.ROOT !== defaultRoot) throw new Error("Asset sources changed while loading MineRender data; retry the request");
|
|
1498
|
+
};
|
|
1499
|
+
checkSources();
|
|
1500
|
+
return await Caching.mineRenderDataCache.get(scope ? `${scope}
|
|
1501
|
+
${key}` : key, async () => {
|
|
1502
|
+
const value = await loader();
|
|
1503
|
+
checkSources();
|
|
1504
|
+
return value;
|
|
1505
|
+
});
|
|
1506
|
+
}
|
|
1507
|
+
static validDataset(dataset, data) {
|
|
1508
|
+
if (!object(data)) return false;
|
|
1509
|
+
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)));
|
|
1510
|
+
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")));
|
|
1511
|
+
if (dataset === "legacyBlocks") return object(data.blocks) && Object.values(data.blocks).every((value) => typeof value === "string");
|
|
1512
|
+
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")));
|
|
1513
|
+
return Object.values(data).every((value) => {
|
|
1514
|
+
if (!object(value)) return false;
|
|
1515
|
+
switch (dataset) {
|
|
1516
|
+
case "itemDefaults":
|
|
1517
|
+
return true;
|
|
1518
|
+
case "blockStates":
|
|
1519
|
+
return Object.values(value).every((property) => object(property) && "default" in property && Array.isArray(property.values));
|
|
1520
|
+
case "blockTints":
|
|
1521
|
+
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));
|
|
1522
|
+
case "fluids":
|
|
1523
|
+
return ["water", "lava"].includes(value.kind) && typeof value.renderModel === "boolean" && (value.levelProperty === void 0 || typeof value.levelProperty === "string");
|
|
1524
|
+
case "dyes":
|
|
1525
|
+
return [value.id, value.textureDiffuseColor, value.fireworkColor, value.textColor].every(Number.isInteger);
|
|
1526
|
+
default:
|
|
1527
|
+
return false;
|
|
1528
|
+
}
|
|
1529
|
+
});
|
|
1530
|
+
}
|
|
1531
|
+
};
|
|
1532
|
+
}
|
|
1533
|
+
});
|
|
1534
|
+
|
|
1535
|
+
// src/assets/Renames.ts
|
|
1536
|
+
var Renames;
|
|
1537
|
+
var init_Renames = __esm({
|
|
1538
|
+
"src/assets/Renames.ts"() {
|
|
1539
|
+
init_AssetKey();
|
|
1540
|
+
init_MineRenderData();
|
|
1541
|
+
Renames = class {
|
|
1542
|
+
/** Loads the selected version's rename table, or `undefined` when its published data has none. */
|
|
1543
|
+
static async get(root) {
|
|
1544
|
+
const resolved = await MineRenderData.resolve(root);
|
|
1545
|
+
return resolved.manifest.datasets.renames ? MineRenderData.get("renames", root) : void 0;
|
|
1546
|
+
}
|
|
1547
|
+
/**
|
|
1548
|
+
* Returns the ID that replaced `id`, or `undefined` when `id` keeps its meaning.
|
|
1549
|
+
* Without a data version, an ID that the selected version still registers keeps its meaning.
|
|
1550
|
+
*/
|
|
1551
|
+
static async resolve(kind, id, dataVersion, root) {
|
|
1552
|
+
const data = await this.get(root);
|
|
1553
|
+
if (!data || !Object.prototype.hasOwnProperty.call(data[kind], id)) return void 0;
|
|
1554
|
+
const entry = data[kind][id];
|
|
1555
|
+
if (dataVersion === void 0) return entry.registered ? void 0 : entry.ranges[entry.ranges.length - 1].to;
|
|
1556
|
+
if (dataVersion < data.baseDataVersion) return void 0;
|
|
1557
|
+
return entry.ranges.find((range) => dataVersion < range.before)?.to;
|
|
1558
|
+
}
|
|
1559
|
+
/** Returns a copy of `key` for the replacement ID, or `undefined` when the ID keeps its meaning. */
|
|
1560
|
+
static async key(kind, key) {
|
|
1561
|
+
const renamed = await this.resolve(kind, `${key.namespace}:${key.path}`, void 0, key.root);
|
|
1562
|
+
if (!renamed) return void 0;
|
|
1563
|
+
const [namespace, path] = renamed.split(":");
|
|
1564
|
+
return new AssetKey(namespace, path, key.assetType, key.type, key.rootType, key.extension, key.root);
|
|
1565
|
+
}
|
|
1566
|
+
};
|
|
1567
|
+
}
|
|
1568
|
+
});
|
|
1569
|
+
|
|
1570
|
+
// src/model/Model.ts
|
|
1571
|
+
function isDoubleArray(obj) {
|
|
1572
|
+
return Array.isArray(obj) && obj.length === 2;
|
|
1573
|
+
}
|
|
1574
|
+
function isTripleArray(obj) {
|
|
1575
|
+
return Array.isArray(obj) && obj.length === 3;
|
|
1576
|
+
}
|
|
1577
|
+
function isQuadArray(obj) {
|
|
1578
|
+
return Array.isArray(obj) && obj.length === 4;
|
|
1579
|
+
}
|
|
1580
|
+
var ITEM_GENERATED, BUILTIN_GENERATED, BUILTIN_ENTITY, DEFAULT_ELEMENTS;
|
|
1581
|
+
var init_Model = __esm({
|
|
1582
|
+
"src/model/Model.ts"() {
|
|
1583
|
+
ITEM_GENERATED = "item/generated";
|
|
1584
|
+
BUILTIN_GENERATED = "builtin/generated";
|
|
1585
|
+
BUILTIN_ENTITY = "builtin/entity";
|
|
1586
|
+
DEFAULT_ELEMENTS = [];
|
|
1587
|
+
}
|
|
1588
|
+
});
|
|
1589
|
+
|
|
1590
|
+
// ../../node_modules/ts-deepmerge/dist/index.js
|
|
1591
|
+
var require_dist = __commonJS({
|
|
1592
|
+
"../../node_modules/ts-deepmerge/dist/index.js"(exports2) {
|
|
1593
|
+
"use strict";
|
|
1594
|
+
var __assign = exports2 && exports2.__assign || function() {
|
|
1595
|
+
__assign = Object.assign || function(t) {
|
|
1596
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
1597
|
+
s = arguments[i];
|
|
1598
|
+
for (var p10 in s) if (Object.prototype.hasOwnProperty.call(s, p10))
|
|
1599
|
+
t[p10] = s[p10];
|
|
1600
|
+
}
|
|
1601
|
+
return t;
|
|
1602
|
+
};
|
|
1603
|
+
return __assign.apply(this, arguments);
|
|
1604
|
+
};
|
|
1605
|
+
var __read = exports2 && exports2.__read || function(o, n) {
|
|
1606
|
+
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
1607
|
+
if (!m) return o;
|
|
1608
|
+
var i = m.call(o), r, ar = [], e;
|
|
1609
|
+
try {
|
|
1610
|
+
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
1611
|
+
} catch (error) {
|
|
1612
|
+
e = { error };
|
|
1613
|
+
} finally {
|
|
1614
|
+
try {
|
|
1615
|
+
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
1616
|
+
} finally {
|
|
1617
|
+
if (e) throw e.error;
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
return ar;
|
|
1621
|
+
};
|
|
1622
|
+
var __spreadArray = exports2 && exports2.__spreadArray || function(to, from, pack) {
|
|
1623
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
1624
|
+
if (ar || !(i in from)) {
|
|
1625
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
1626
|
+
ar[i] = from[i];
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
1630
|
+
};
|
|
1631
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
1632
|
+
var isObject = function(obj) {
|
|
1633
|
+
if (typeof obj === "object" && obj !== null) {
|
|
1634
|
+
if (typeof Object.getPrototypeOf === "function") {
|
|
1635
|
+
var prototype = Object.getPrototypeOf(obj);
|
|
1636
|
+
return prototype === Object.prototype || prototype === null;
|
|
1637
|
+
}
|
|
1638
|
+
return Object.prototype.toString.call(obj) === "[object Object]";
|
|
1484
1639
|
}
|
|
1485
|
-
|
|
1486
|
-
|
|
1640
|
+
return false;
|
|
1641
|
+
};
|
|
1642
|
+
var PROTECTED_KEYS = ["__proto__"];
|
|
1643
|
+
var merge10 = function() {
|
|
1644
|
+
var objects = [];
|
|
1645
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
1646
|
+
objects[_i] = arguments[_i];
|
|
1487
1647
|
}
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1648
|
+
return objects.reduce(function(result, current) {
|
|
1649
|
+
Object.keys(current).forEach(function(key) {
|
|
1650
|
+
if (PROTECTED_KEYS.includes(key)) {
|
|
1651
|
+
return;
|
|
1652
|
+
}
|
|
1653
|
+
if (Array.isArray(result[key]) && Array.isArray(current[key])) {
|
|
1654
|
+
result[key] = merge10.options.mergeArrays ? Array.from(new Set(result[key].concat(current[key]))) : current[key];
|
|
1655
|
+
} else if (isObject(result[key]) && isObject(current[key])) {
|
|
1656
|
+
result[key] = merge10(result[key], current[key]);
|
|
1657
|
+
} else {
|
|
1658
|
+
result[key] = current[key];
|
|
1659
|
+
}
|
|
1660
|
+
});
|
|
1661
|
+
return result;
|
|
1662
|
+
}, {});
|
|
1663
|
+
};
|
|
1664
|
+
var defaultOptions = {
|
|
1665
|
+
mergeArrays: true
|
|
1666
|
+
};
|
|
1667
|
+
merge10.options = defaultOptions;
|
|
1668
|
+
merge10.withOptions = function(options) {
|
|
1669
|
+
var objects = [];
|
|
1670
|
+
for (var _i = 1; _i < arguments.length; _i++) {
|
|
1671
|
+
objects[_i - 1] = arguments[_i];
|
|
1491
1672
|
}
|
|
1673
|
+
merge10.options = __assign({ mergeArrays: true }, options);
|
|
1674
|
+
var result = merge10.apply(void 0, __spreadArray([], __read(objects), false));
|
|
1675
|
+
merge10.options = defaultOptions;
|
|
1676
|
+
return result;
|
|
1492
1677
|
};
|
|
1678
|
+
exports2.default = merge10;
|
|
1493
1679
|
}
|
|
1494
1680
|
});
|
|
1495
1681
|
|
|
@@ -1615,147 +1801,6 @@ var init_ModelMerger = __esm({
|
|
|
1615
1801
|
}
|
|
1616
1802
|
});
|
|
1617
1803
|
|
|
1618
|
-
// src/assets/source/parser/AssetParsers.ts
|
|
1619
|
-
var AssetParser;
|
|
1620
|
-
var init_AssetParsers = __esm({
|
|
1621
|
-
"src/assets/source/parser/AssetParsers.ts"() {
|
|
1622
|
-
AssetParser = /* @__PURE__ */ ((AssetParser2) => {
|
|
1623
|
-
AssetParser2["MODEL"] = "model";
|
|
1624
|
-
AssetParser2["BLOCKSTATE"] = "blockstate";
|
|
1625
|
-
AssetParser2["META"] = "meta";
|
|
1626
|
-
AssetParser2["NBT"] = "nbt";
|
|
1627
|
-
AssetParser2["IMAGE"] = "image";
|
|
1628
|
-
AssetParser2["LIST"] = "list";
|
|
1629
|
-
AssetParser2["JSON"] = "json";
|
|
1630
|
-
return AssetParser2;
|
|
1631
|
-
})(AssetParser || {});
|
|
1632
|
-
}
|
|
1633
|
-
});
|
|
1634
|
-
|
|
1635
|
-
// src/assets/MineRenderData.ts
|
|
1636
|
-
var object, release, compare, MineRenderData;
|
|
1637
|
-
var init_MineRenderData = __esm({
|
|
1638
|
-
"src/assets/MineRenderData.ts"() {
|
|
1639
|
-
init_AssetKey();
|
|
1640
|
-
init_AssetLoader();
|
|
1641
|
-
init_AssetParsers();
|
|
1642
|
-
init_Caching();
|
|
1643
|
-
object = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1644
|
-
release = (value) => /^\d+\.\d+(?:\.\d+)?$/.test(value) ? value.split(".").map(Number) : void 0;
|
|
1645
|
-
compare = (left, right) => {
|
|
1646
|
-
for (let i = 0; i < 3; i++) {
|
|
1647
|
-
const difference = (left[i] ?? 0) - (right[i] ?? 0);
|
|
1648
|
-
if (difference) return difference;
|
|
1649
|
-
}
|
|
1650
|
-
return 0;
|
|
1651
|
-
};
|
|
1652
|
-
MineRenderData = class {
|
|
1653
|
-
/** Loads one registry. Missing files and invalid data reject the request. */
|
|
1654
|
-
static async get(dataset, root) {
|
|
1655
|
-
root = root?.replace(/\/+$/, "");
|
|
1656
|
-
const scope = AssetLoader.persistentScope;
|
|
1657
|
-
const defaultRoot = AssetLoader.ROOT;
|
|
1658
|
-
const resolved = await this.resolve(root);
|
|
1659
|
-
const entry = resolved.manifest.datasets[dataset];
|
|
1660
|
-
if (!entry) throw new Error(`MineRender ${dataset} data is unavailable for ${resolved.version}: ${resolved.manifest.unavailable[dataset] ?? "not listed in the manifest"}`);
|
|
1661
|
-
const file = entry.file;
|
|
1662
|
-
const requestRoot = resolved.root === (root ?? defaultRoot).replace(/\/+$/, "") ? root : resolved.root;
|
|
1663
|
-
return await this.cached(`${requestRoot === void 0 ? "sources" : "explicit"}:${resolved.root}/${file}:${entry.sha256}`, scope, defaultRoot, async () => {
|
|
1664
|
-
const data = await this.load(file.slice(0, -5), requestRoot);
|
|
1665
|
-
if (!this.validDataset(dataset, data)) throw new Error(`Invalid MineRender ${dataset} data at ${resolved.root}/minerender-data/${file}`);
|
|
1666
|
-
return data;
|
|
1667
|
-
});
|
|
1668
|
-
}
|
|
1669
|
-
/**
|
|
1670
|
-
* Uses the exact version when published. Otherwise, selects the nearest patch in the same
|
|
1671
|
-
* release line (older on ties), then the latest earlier release or the oldest published release.
|
|
1672
|
-
* Unpublished snapshots reject.
|
|
1673
|
-
*/
|
|
1674
|
-
static async resolve(root) {
|
|
1675
|
-
const requestRoot = root?.replace(/\/+$/, "");
|
|
1676
|
-
const defaultRoot = AssetLoader.ROOT;
|
|
1677
|
-
root = (root ?? defaultRoot).replace(/\/+$/, "");
|
|
1678
|
-
const scope = AssetLoader.persistentScope;
|
|
1679
|
-
return this.cached(`resolve:${requestRoot === void 0 ? "sources" : "explicit"}:${root}`, scope, defaultRoot, async () => {
|
|
1680
|
-
const requestedVersion = root.slice(root.lastIndexOf("/") + 1);
|
|
1681
|
-
let manifest = await this.manifest(root, requestRoot !== void 0 && release(requestedVersion) ? requestedVersion : void 0, requestRoot);
|
|
1682
|
-
if (manifest) return { requestedVersion, version: manifest.minecraftVersion, root, manifest };
|
|
1683
|
-
const requested = release(requestedVersion);
|
|
1684
|
-
if (!requested) {
|
|
1685
|
-
throw new Error(`No MineRender data is published for ${requestedVersion}; automatic fallback requires a release`);
|
|
1686
|
-
}
|
|
1687
|
-
const base = root.slice(0, root.lastIndexOf("/"));
|
|
1688
|
-
const index = await this.cached(`versions:${base}`, scope, defaultRoot, async () => {
|
|
1689
|
-
const value = await this.load("versions", base);
|
|
1690
|
-
if (!object(value) || !Array.isArray(value.versions) || !value.versions.every((entry) => object(entry) && typeof entry.name === "string")) {
|
|
1691
|
-
throw new Error(`Invalid MineRender version index at ${base}/minerender-data/versions.json`);
|
|
1692
|
-
}
|
|
1693
|
-
return value.versions;
|
|
1694
|
-
});
|
|
1695
|
-
const versions = index.map((entry) => ({ name: entry.name, number: release(entry.name) })).filter((entry) => !!entry.number).sort((left, right) => compare(left.number, right.number));
|
|
1696
|
-
const sameLine = versions.filter((entry) => entry.number[0] === requested[0] && entry.number[1] === requested[1]);
|
|
1697
|
-
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];
|
|
1698
|
-
if (!selected) throw new Error(`No compatible MineRender data is published for ${requestedVersion}`);
|
|
1699
|
-
const resolvedRoot = `${base}/${selected.name}`;
|
|
1700
|
-
manifest = await this.manifest(resolvedRoot, selected.name, resolvedRoot);
|
|
1701
|
-
if (!manifest) throw new Error(`Missing MineRender manifest for published version ${selected.name}`);
|
|
1702
|
-
return { requestedVersion, version: selected.name, root: resolvedRoot, manifest };
|
|
1703
|
-
});
|
|
1704
|
-
}
|
|
1705
|
-
static async manifest(root, expectedVersion, requestRoot) {
|
|
1706
|
-
const data = await this.load("manifest", requestRoot);
|
|
1707
|
-
if (data === void 0) return void 0;
|
|
1708
|
-
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) => {
|
|
1709
|
-
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);
|
|
1710
|
-
}) || expectedVersion !== void 0 && data.minecraftVersion !== expectedVersion) {
|
|
1711
|
-
throw new Error(`Invalid MineRender manifest at ${root}/minerender-data/manifest.json`);
|
|
1712
|
-
}
|
|
1713
|
-
return data;
|
|
1714
|
-
}
|
|
1715
|
-
static load(file, root) {
|
|
1716
|
-
const key = new AssetKey("minerender-data", file, void 0, void 0, void 0, ".json", root);
|
|
1717
|
-
key.rootType = void 0;
|
|
1718
|
-
return AssetLoader.get(key, "json" /* JSON */);
|
|
1719
|
-
}
|
|
1720
|
-
static async cached(key, scope, defaultRoot, loader) {
|
|
1721
|
-
const checkSources = () => {
|
|
1722
|
-
if (AssetLoader.persistentScope !== scope || AssetLoader.ROOT !== defaultRoot) throw new Error("Asset sources changed while loading MineRender data; retry the request");
|
|
1723
|
-
};
|
|
1724
|
-
checkSources();
|
|
1725
|
-
return await Caching.mineRenderDataCache.get(scope ? `${scope}
|
|
1726
|
-
${key}` : key, async () => {
|
|
1727
|
-
const value = await loader();
|
|
1728
|
-
checkSources();
|
|
1729
|
-
return value;
|
|
1730
|
-
});
|
|
1731
|
-
}
|
|
1732
|
-
static validDataset(dataset, data) {
|
|
1733
|
-
if (!object(data)) return false;
|
|
1734
|
-
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)));
|
|
1735
|
-
if (dataset === "legacyBlocks") return object(data.blocks) && Object.values(data.blocks).every((value) => typeof value === "string");
|
|
1736
|
-
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")));
|
|
1737
|
-
return Object.values(data).every((value) => {
|
|
1738
|
-
if (!object(value)) return false;
|
|
1739
|
-
switch (dataset) {
|
|
1740
|
-
case "itemDefaults":
|
|
1741
|
-
return true;
|
|
1742
|
-
case "blockStates":
|
|
1743
|
-
return Object.values(value).every((property) => object(property) && "default" in property && Array.isArray(property.values));
|
|
1744
|
-
case "blockTints":
|
|
1745
|
-
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));
|
|
1746
|
-
case "fluids":
|
|
1747
|
-
return ["water", "lava"].includes(value.kind) && typeof value.renderModel === "boolean" && (value.levelProperty === void 0 || typeof value.levelProperty === "string");
|
|
1748
|
-
case "dyes":
|
|
1749
|
-
return [value.id, value.textureDiffuseColor, value.fireworkColor, value.textColor].every(Number.isInteger);
|
|
1750
|
-
default:
|
|
1751
|
-
return false;
|
|
1752
|
-
}
|
|
1753
|
-
});
|
|
1754
|
-
}
|
|
1755
|
-
};
|
|
1756
|
-
}
|
|
1757
|
-
});
|
|
1758
|
-
|
|
1759
1804
|
// src/assets/BannerPatterns.ts
|
|
1760
1805
|
var DYE_COLORS, BannerPatterns;
|
|
1761
1806
|
var init_BannerPatterns = __esm({
|
|
@@ -2078,6 +2123,7 @@ var init_ItemTints = __esm({
|
|
|
2078
2123
|
var Models;
|
|
2079
2124
|
var init_Models = __esm({
|
|
2080
2125
|
"src/assets/Models.ts"() {
|
|
2126
|
+
init_Renames();
|
|
2081
2127
|
init_Caching();
|
|
2082
2128
|
init_ModelMerger();
|
|
2083
2129
|
init_AssetLoader();
|
|
@@ -2411,6 +2457,11 @@ var init_Models = __esm({
|
|
|
2411
2457
|
key.extension = ".json";
|
|
2412
2458
|
}
|
|
2413
2459
|
const preview = this.snapshotContext(key, context);
|
|
2460
|
+
if (key.type === "item") {
|
|
2461
|
+
const renamed = await Renames.key("items", key);
|
|
2462
|
+
const replacement = renamed && await this.getMerged(renamed, preview);
|
|
2463
|
+
if (replacement) return replacement;
|
|
2464
|
+
}
|
|
2414
2465
|
const dataKey = await this.applyDefaults(key, preview);
|
|
2415
2466
|
const keyStr = AssetLoader.persistentKey(key.serialize() + (key.type === "item" ? this.contextKey(preview) + dataKey : ""));
|
|
2416
2467
|
const model = await Caching.mergedModelCache.get(keyStr, async () => {
|
|
@@ -2442,6 +2493,7 @@ var init_BlockStates = __esm({
|
|
|
2442
2493
|
init_AssetKey();
|
|
2443
2494
|
init_PersistentCache();
|
|
2444
2495
|
init_AssetParsers();
|
|
2496
|
+
init_Renames();
|
|
2445
2497
|
init_MineRenderData();
|
|
2446
2498
|
BlockStates = class {
|
|
2447
2499
|
static get PERSISTENT_CACHE() {
|
|
@@ -2482,6 +2534,9 @@ var init_BlockStates = __esm({
|
|
|
2482
2534
|
if (!key.extension) {
|
|
2483
2535
|
key.extension = ".json";
|
|
2484
2536
|
}
|
|
2537
|
+
const renamed = await Renames.key("blocks", key);
|
|
2538
|
+
const replacement = renamed && await this.get(renamed);
|
|
2539
|
+
if (replacement) return replacement;
|
|
2485
2540
|
const keyStr = key.serialize();
|
|
2486
2541
|
return Caching.blockStateCache.get(keyStr, (k) => {
|
|
2487
2542
|
return this.PERSISTENT_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
|
|
@@ -7688,6 +7743,7 @@ __export(index_node_exports, {
|
|
|
7688
7743
|
PackMetadata: () => PackMetadata,
|
|
7689
7744
|
PersistentCache: () => PersistentCache,
|
|
7690
7745
|
PlayerHeadTextures: () => PlayerHeadTextures,
|
|
7746
|
+
Renames: () => Renames,
|
|
7691
7747
|
Renderer: () => Renderer,
|
|
7692
7748
|
RequestError: () => RequestError,
|
|
7693
7749
|
Requests: () => Requests,
|
|
@@ -8228,6 +8284,7 @@ var Fonts = class {
|
|
|
8228
8284
|
init_MineRenderData();
|
|
8229
8285
|
init_ModelTextures();
|
|
8230
8286
|
init_Models();
|
|
8287
|
+
init_Renames();
|
|
8231
8288
|
init_source();
|
|
8232
8289
|
|
|
8233
8290
|
// src/cache/index.ts
|
|
@@ -17496,18 +17553,23 @@ var SectionModels = class {
|
|
|
17496
17553
|
return this.template(variant, choice, tints);
|
|
17497
17554
|
}));
|
|
17498
17555
|
}
|
|
17499
|
-
/** Resolves
|
|
17500
|
-
async prepareState(blockState3, properties = {}, biomeColor) {
|
|
17556
|
+
/** Resolves shared templates; dynamic biome tints are supplied on section placements instead of cached geometry. */
|
|
17557
|
+
async prepareState(blockState3, properties = {}, biomeColor, dynamicBiomeTint = false) {
|
|
17501
17558
|
const prepared = await this.state(blockState3, properties);
|
|
17502
17559
|
if (!prepared) return void 0;
|
|
17503
17560
|
let color2;
|
|
17504
17561
|
const resolveColor = typeof biomeColor === "function" ? () => color2 ??= biomeColor() : biomeColor;
|
|
17505
17562
|
const templates = /* @__PURE__ */ new Map();
|
|
17563
|
+
const source = dynamicBiomeTint && await BlockTints.biomeSource(blockState3.key);
|
|
17506
17564
|
await Promise.all([...prepared.choices].map(async ([variant, choice]) => {
|
|
17507
17565
|
const tints = await BlockTints.get(blockState3.key, prepared.state, choice.model, void 0, resolveColor);
|
|
17508
|
-
|
|
17566
|
+
const biomeTints = source ? await BlockTints.get(blockState3.key, prepared.state, choice.model, void 0, 16777215) : void 0;
|
|
17567
|
+
templates.set(variant, await this.template(variant, choice, tints, biomeTints));
|
|
17509
17568
|
}));
|
|
17510
|
-
return {
|
|
17569
|
+
return {
|
|
17570
|
+
pick: (position) => prepared.groups.map((group) => templates.get(BlockStateResolver.choose(group, position))),
|
|
17571
|
+
biomeTint: source ? [...templates.values()].some((template) => template.biomeTint?.some((value) => value === 1)) || void 0 : void 0
|
|
17572
|
+
};
|
|
17511
17573
|
}
|
|
17512
17574
|
async state(blockState3, properties) {
|
|
17513
17575
|
let states = this.states.get(blockState3);
|
|
@@ -17550,10 +17612,10 @@ var SectionModels = class {
|
|
|
17550
17612
|
}
|
|
17551
17613
|
return { state, groups, choices: prepared };
|
|
17552
17614
|
}
|
|
17553
|
-
async template(variant, { model, atlas, rotation }, tints) {
|
|
17615
|
+
async template(variant, { model, atlas, rotation }, tints, biomeTints) {
|
|
17554
17616
|
let models = this.models.get(model);
|
|
17555
17617
|
if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
|
|
17556
|
-
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
|
|
17618
|
+
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints, biomeTints]);
|
|
17557
17619
|
return cached(models, key, async () => {
|
|
17558
17620
|
const object4 = new SectionModel(model, {
|
|
17559
17621
|
instanceMeshes: false,
|
|
@@ -17567,11 +17629,13 @@ var SectionModels = class {
|
|
|
17567
17629
|
object4.clear();
|
|
17568
17630
|
const keep = [];
|
|
17569
17631
|
const cullFaces = [];
|
|
17632
|
+
const biomeTint = biomeTints ? [] : void 0;
|
|
17570
17633
|
for (const element of atlas.model.elements) {
|
|
17571
17634
|
for (const face of CUBE_FACES) {
|
|
17572
17635
|
const data = element.faces[face];
|
|
17573
17636
|
keep.push(data !== void 0);
|
|
17574
17637
|
if (!data) continue;
|
|
17638
|
+
biomeTint?.push(Number(data.tintindex !== void 0 && biomeTints?.[data.tintindex] !== void 0));
|
|
17575
17639
|
const local = CUBE_FACES.findIndex((name) => name === data.cullface);
|
|
17576
17640
|
if (local < 0) {
|
|
17577
17641
|
cullFaces.push(0);
|
|
@@ -17587,6 +17651,7 @@ var SectionModels = class {
|
|
|
17587
17651
|
geometry,
|
|
17588
17652
|
atlas,
|
|
17589
17653
|
cullFaces: new Uint8Array(cullFaces),
|
|
17654
|
+
biomeTint: biomeTint?.some(Boolean) ? new Uint8Array(biomeTint) : void 0,
|
|
17590
17655
|
occludes: ModelCulling.isOpaqueFullCube(atlas) && ModelCulling.toLocalMask(63, rotation) === 63,
|
|
17591
17656
|
layer: atlas.hasTranslucency ? 1 : 0,
|
|
17592
17657
|
ambientOcclusion: model.ambientocclusion !== false
|
|
@@ -17606,6 +17671,7 @@ var SectionModels = class {
|
|
|
17606
17671
|
};
|
|
17607
17672
|
|
|
17608
17673
|
// src/world/_entities/WorldEntities.ts
|
|
17674
|
+
init_Renames();
|
|
17609
17675
|
init_Entities();
|
|
17610
17676
|
init_EntityObject();
|
|
17611
17677
|
|
|
@@ -17765,8 +17831,10 @@ var WorldEntities = class {
|
|
|
17765
17831
|
try {
|
|
17766
17832
|
if (!current()) return;
|
|
17767
17833
|
const models = await Entities.getEntityList();
|
|
17768
|
-
|
|
17769
|
-
const
|
|
17834
|
+
const renamed = models.includes(placement.key.path) ? void 0 : await Renames.key("entities", placement.key);
|
|
17835
|
+
const key = renamed && models.includes(renamed.path) ? renamed : placement.key;
|
|
17836
|
+
if (!models.includes(key.path) || !current()) return;
|
|
17837
|
+
const model = await Entities.getEntity(key, placement.texture, { when: placement.when });
|
|
17770
17838
|
if (!model || !current()) return;
|
|
17771
17839
|
let tints = placement.tints;
|
|
17772
17840
|
if (placement.woolDye) {
|
|
@@ -17781,7 +17849,7 @@ var WorldEntities = class {
|
|
|
17781
17849
|
object4.rotation.y = placement.yaw;
|
|
17782
17850
|
await object4.init();
|
|
17783
17851
|
if (!current()) return;
|
|
17784
|
-
placement.owner.set(object4, { type:
|
|
17852
|
+
placement.owner.set(object4, { type: key.toNamespacedString(), position: placement.position });
|
|
17785
17853
|
this.scene.add(object4);
|
|
17786
17854
|
} catch (error) {
|
|
17787
17855
|
console.warn(`Could not draw saved entity ${placement.key.toNamespacedString()}, skipping it`, error);
|
|
@@ -17813,6 +17881,132 @@ var WorldEntities = class {
|
|
|
17813
17881
|
// src/world/MineRenderWorld.ts
|
|
17814
17882
|
init_FluidQuads();
|
|
17815
17883
|
|
|
17884
|
+
// src/world/_biomes/BiomeBlend.ts
|
|
17885
|
+
init_AssetKey();
|
|
17886
|
+
init_AssetLoader();
|
|
17887
|
+
init_Caching();
|
|
17888
|
+
var MAX_TILES = 256;
|
|
17889
|
+
function validateBiomeBlendRadius(radius) {
|
|
17890
|
+
if (!Number.isInteger(radius) || radius < 0 || radius > 7) {
|
|
17891
|
+
throw new RangeError("biomeBlendRadius must be an integer from 0 to 7");
|
|
17892
|
+
}
|
|
17893
|
+
}
|
|
17894
|
+
var BiomeBlend = class {
|
|
17895
|
+
constructor(biomeAt, radius) {
|
|
17896
|
+
this.biomeAt = biomeAt;
|
|
17897
|
+
this.tiles = /* @__PURE__ */ new Map();
|
|
17898
|
+
this.generation = Caching.generation;
|
|
17899
|
+
validateBiomeBlendRadius(radius);
|
|
17900
|
+
this._radius = radius;
|
|
17901
|
+
}
|
|
17902
|
+
get radius() {
|
|
17903
|
+
return this._radius;
|
|
17904
|
+
}
|
|
17905
|
+
clear() {
|
|
17906
|
+
this.tiles.clear();
|
|
17907
|
+
}
|
|
17908
|
+
invalidateColumn(x, z) {
|
|
17909
|
+
for (const [key, tile] of this.tiles) {
|
|
17910
|
+
if (Math.abs(tile.x - x) <= 1 && Math.abs(tile.z - z) <= 1) this.tiles.delete(key);
|
|
17911
|
+
}
|
|
17912
|
+
}
|
|
17913
|
+
async getColor(kind, origin, position) {
|
|
17914
|
+
const [x, y, z] = position;
|
|
17915
|
+
const biome = this.biomeAt(x, y, z);
|
|
17916
|
+
if (biome === void 0) return void 0;
|
|
17917
|
+
if (this.generation !== Caching.generation) {
|
|
17918
|
+
this.clear();
|
|
17919
|
+
this.generation = Caching.generation;
|
|
17920
|
+
}
|
|
17921
|
+
const tileX = Math.floor(x / 16), tileZ = Math.floor(z / 16), quartY = Math.floor(y / 4);
|
|
17922
|
+
const root = origin?.root ?? AssetLoader.ROOT;
|
|
17923
|
+
const key = JSON.stringify([root, AssetLoader.persistentScope, kind, this._radius, tileX, quartY, tileZ]);
|
|
17924
|
+
let tile = this.tiles.get(key);
|
|
17925
|
+
if (tile) {
|
|
17926
|
+
this.tiles.delete(key);
|
|
17927
|
+
this.tiles.set(key, tile);
|
|
17928
|
+
} else {
|
|
17929
|
+
const snapshot = new AssetKey("minecraft", "", void 0, void 0, "assets", ".json", root);
|
|
17930
|
+
const colors = this.buildTile(kind, snapshot, tileX, quartY, tileZ, this._radius).then((result2) => {
|
|
17931
|
+
if (!result2.complete && this.tiles.get(key)?.colors === colors) this.tiles.delete(key);
|
|
17932
|
+
return result2;
|
|
17933
|
+
}, (error) => {
|
|
17934
|
+
if (this.tiles.get(key)?.colors === colors) this.tiles.delete(key);
|
|
17935
|
+
throw error;
|
|
17936
|
+
});
|
|
17937
|
+
tile = { x: tileX, z: tileZ, colors };
|
|
17938
|
+
this.tiles.set(key, tile);
|
|
17939
|
+
if (this.tiles.size > MAX_TILES) this.tiles.delete(this.tiles.keys().next().value);
|
|
17940
|
+
}
|
|
17941
|
+
const result = await tile.colors;
|
|
17942
|
+
if (result.failures.has(biome)) throw result.failures.get(biome);
|
|
17943
|
+
const color2 = result.colors[(z - tileZ * 16) * 16 + x - tileX * 16];
|
|
17944
|
+
return color2 === -1 ? void 0 : color2;
|
|
17945
|
+
}
|
|
17946
|
+
async buildTile(kind, origin, tileX, quartY, tileZ, radius) {
|
|
17947
|
+
const size = 16 + radius * 2, stride = size + 1;
|
|
17948
|
+
const startX = tileX * 16 - radius, startZ = tileZ * 16 - radius;
|
|
17949
|
+
const quartX = Math.floor(startX / 4), quartZ = Math.floor(startZ / 4);
|
|
17950
|
+
const quartSize = Math.floor((startX + size - 1) / 4) - quartX + 1;
|
|
17951
|
+
const ids = new Array(quartSize * quartSize);
|
|
17952
|
+
const biomes = /* @__PURE__ */ new Map();
|
|
17953
|
+
for (let z = 0; z < quartSize; z++) {
|
|
17954
|
+
for (let x = 0; x < quartSize; x++) {
|
|
17955
|
+
const id = this.biomeAt((quartX + x) * 4, quartY * 4, (quartZ + z) * 4);
|
|
17956
|
+
ids[z * quartSize + x] = id;
|
|
17957
|
+
if (id !== void 0 && !biomes.has(id)) {
|
|
17958
|
+
biomes.set(id, (async () => {
|
|
17959
|
+
const swamp = kind === "grass" && (await Biomes.get(id, origin))?.effects.grass_color_modifier === "swamp";
|
|
17960
|
+
return { color: swamp ? void 0 : await Biomes.getColor(id, kind, origin), swamp };
|
|
17961
|
+
})());
|
|
17962
|
+
}
|
|
17963
|
+
}
|
|
17964
|
+
}
|
|
17965
|
+
const failures = /* @__PURE__ */ new Map();
|
|
17966
|
+
const loaded = new Map(await Promise.all([...biomes].map(async ([id, color2]) => {
|
|
17967
|
+
try {
|
|
17968
|
+
return [id, await color2];
|
|
17969
|
+
} catch (error) {
|
|
17970
|
+
failures.set(id, error);
|
|
17971
|
+
return [id, { color: void 0, swamp: false }];
|
|
17972
|
+
}
|
|
17973
|
+
})));
|
|
17974
|
+
const complete = [...loaded.values()].every((value) => value.swamp || value.color !== void 0);
|
|
17975
|
+
const samples = new Int32Array(size * size).fill(-1);
|
|
17976
|
+
const sums = Array.from({ length: 4 }, () => new Uint32Array(stride * stride));
|
|
17977
|
+
for (let z = 0; z < size; z++) {
|
|
17978
|
+
const row = [0, 0, 0, 0];
|
|
17979
|
+
for (let x = 0; x < size; x++) {
|
|
17980
|
+
const id = ids[(Math.floor((startZ + z) / 4) - quartZ) * quartSize + Math.floor((startX + x) / 4) - quartX];
|
|
17981
|
+
const value = id === void 0 ? void 0 : loaded.get(id);
|
|
17982
|
+
const color2 = value?.swamp ? swampGrassColor(startX + x, startZ + z) : value?.color;
|
|
17983
|
+
if (color2 !== void 0) {
|
|
17984
|
+
samples[z * size + x] = color2;
|
|
17985
|
+
row[0] += color2 >> 16 & 255;
|
|
17986
|
+
row[1] += color2 >> 8 & 255;
|
|
17987
|
+
row[2] += color2 & 255;
|
|
17988
|
+
row[3]++;
|
|
17989
|
+
}
|
|
17990
|
+
for (let channel = 0; channel < 4; channel++) {
|
|
17991
|
+
sums[channel][(z + 1) * stride + x + 1] = sums[channel][z * stride + x + 1] + row[channel];
|
|
17992
|
+
}
|
|
17993
|
+
}
|
|
17994
|
+
}
|
|
17995
|
+
const colors = new Int32Array(256).fill(-1), width = radius * 2 + 1;
|
|
17996
|
+
for (let z = 0; z < 16; z++) {
|
|
17997
|
+
for (let x = 0; x < 16; x++) {
|
|
17998
|
+
if (samples[(z + radius) * size + x + radius] === -1) continue;
|
|
17999
|
+
const topLeft = z * stride + x, topRight = topLeft + width;
|
|
18000
|
+
const bottomLeft = (z + width) * stride + x, bottomRight = bottomLeft + width;
|
|
18001
|
+
const sum = (values) => values[bottomRight] - values[bottomLeft] - values[topRight] + values[topLeft];
|
|
18002
|
+
const count = sum(sums[3]);
|
|
18003
|
+
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);
|
|
18004
|
+
}
|
|
18005
|
+
}
|
|
18006
|
+
return { colors, complete, failures };
|
|
18007
|
+
}
|
|
18008
|
+
};
|
|
18009
|
+
|
|
17816
18010
|
// src/world/WorldLight.ts
|
|
17817
18011
|
function copySectionLight(light) {
|
|
17818
18012
|
const copy = {};
|
|
@@ -17876,12 +18070,17 @@ var MineRenderWorld = class {
|
|
|
17876
18070
|
this.lightValidity = /* @__PURE__ */ new Map();
|
|
17877
18071
|
this.highestSkySection = /* @__PURE__ */ new Map();
|
|
17878
18072
|
this.ambientOcclusionPending = false;
|
|
18073
|
+
this.pendingBiomes = /* @__PURE__ */ new Map();
|
|
17879
18074
|
this.pendingCulling = /* @__PURE__ */ new Map();
|
|
17880
18075
|
if (options.savedLighting && !options.sectionMeshing) throw new TypeError("savedLighting requires sectionMeshing");
|
|
17881
18076
|
if (options.ambientOcclusion && !options.sectionMeshing) throw new TypeError("ambientOcclusion requires sectionMeshing");
|
|
17882
18077
|
if (options.ambientLight !== void 0 && (!Number.isFinite(options.ambientLight) || options.ambientLight < 0 || options.ambientLight > 1)) {
|
|
17883
18078
|
throw new RangeError("ambientLight must be a number from 0 to 1");
|
|
17884
18079
|
}
|
|
18080
|
+
this._biomeBlendRadius = options.biomeBlendRadius ?? 0;
|
|
18081
|
+
validateBiomeBlendRadius(this._biomeBlendRadius);
|
|
18082
|
+
if (this._biomeBlendRadius > 0 && !options.sectionMeshing) throw new TypeError("biomeBlendRadius requires sectionMeshing");
|
|
18083
|
+
if (this._biomeBlendRadius) this.biomeBlend = new BiomeBlend((x, y, z) => this.getBiomeAt(x, y, z), this._biomeBlendRadius);
|
|
17885
18084
|
this.scene = scene;
|
|
17886
18085
|
this.savedLighting = options.savedLighting ?? false;
|
|
17887
18086
|
this.ambientLight = options.ambientLight ?? 0;
|
|
@@ -17922,6 +18121,65 @@ var MineRenderWorld = class {
|
|
|
17922
18121
|
await this.updateCulling([]);
|
|
17923
18122
|
this.ambientOcclusionPending = false;
|
|
17924
18123
|
}
|
|
18124
|
+
/** Horizontal biome-color sampling radius, from 0 to 7 blocks. Zero disables blending. */
|
|
18125
|
+
get biomeBlendRadius() {
|
|
18126
|
+
return this._biomeBlendRadius;
|
|
18127
|
+
}
|
|
18128
|
+
/** Refreshes loaded grass, foliage, and water colors without reloading their source chunks. */
|
|
18129
|
+
async setBiomeBlendRadius(radius) {
|
|
18130
|
+
validateBiomeBlendRadius(radius);
|
|
18131
|
+
if (radius > 0 && !this.sectionModels) throw new TypeError("biomeBlendRadius requires sectionMeshing");
|
|
18132
|
+
if (radius !== this._biomeBlendRadius) {
|
|
18133
|
+
this._biomeBlendRadius = radius;
|
|
18134
|
+
this.biomeBlend?.clear();
|
|
18135
|
+
this.biomeBlend = radius ? new BiomeBlend((x, y, z) => this.getBiomeAt(x, y, z), radius) : void 0;
|
|
18136
|
+
for (const chunk of this._chunks.values()) this.pendingBiomes.set(chunk, void 0);
|
|
18137
|
+
}
|
|
18138
|
+
const failures = await this.refreshBiomes();
|
|
18139
|
+
if (failures.size) throw failures.values().next().value;
|
|
18140
|
+
}
|
|
18141
|
+
markBiomesChanged(x, z, y, includeColumn = true) {
|
|
18142
|
+
const queued = [];
|
|
18143
|
+
if (!this.biomeBlend) return queued;
|
|
18144
|
+
this.biomeBlend.invalidateColumn(x, z);
|
|
18145
|
+
for (const chunk of this._chunks.values()) {
|
|
18146
|
+
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;
|
|
18147
|
+
queued.push(chunk);
|
|
18148
|
+
if (!this.pendingBiomes.has(chunk)) this.pendingBiomes.set(chunk, []);
|
|
18149
|
+
const columns = this.pendingBiomes.get(chunk);
|
|
18150
|
+
if (columns && !columns.some((column) => column.x === x && column.z === z)) columns.push({ x, z });
|
|
18151
|
+
}
|
|
18152
|
+
return queued;
|
|
18153
|
+
}
|
|
18154
|
+
async refreshBiomes() {
|
|
18155
|
+
if (this.biomeRefreshing) await this.biomeRefreshing;
|
|
18156
|
+
if (!this.pendingBiomes.size) return /* @__PURE__ */ new Map();
|
|
18157
|
+
return this.biomeRefreshing ??= Promise.resolve().then(async () => {
|
|
18158
|
+
const failures = /* @__PURE__ */ new Map();
|
|
18159
|
+
const batch = [...this.pendingBiomes.keys()];
|
|
18160
|
+
let sliceStart = performance.now();
|
|
18161
|
+
try {
|
|
18162
|
+
for (const chunk of batch) {
|
|
18163
|
+
const columns = this.pendingBiomes.get(chunk);
|
|
18164
|
+
this.pendingBiomes.delete(chunk);
|
|
18165
|
+
if (this._chunks.get(`${chunk.x}_${chunk.y}_${chunk.z}`) !== chunk) continue;
|
|
18166
|
+
try {
|
|
18167
|
+
await chunk.refreshBiomes(columns);
|
|
18168
|
+
} catch (error) {
|
|
18169
|
+
failures.set(chunk, error);
|
|
18170
|
+
if (this._chunks.get(`${chunk.x}_${chunk.y}_${chunk.z}`) === chunk) this.pendingBiomes.set(chunk, void 0);
|
|
18171
|
+
}
|
|
18172
|
+
if (performance.now() - sliceStart > 8) {
|
|
18173
|
+
await yieldToEventLoop();
|
|
18174
|
+
sliceStart = performance.now();
|
|
18175
|
+
}
|
|
18176
|
+
}
|
|
18177
|
+
return failures;
|
|
18178
|
+
} finally {
|
|
18179
|
+
this.biomeRefreshing = void 0;
|
|
18180
|
+
}
|
|
18181
|
+
});
|
|
18182
|
+
}
|
|
17925
18183
|
getBlockAt(posOrX, y, z) {
|
|
17926
18184
|
if (typeof posOrX == "number") {
|
|
17927
18185
|
return this.getBlockAt(new import_three45.Vector3(posOrX, y, z));
|
|
@@ -18157,6 +18415,9 @@ var MineRenderWorld = class {
|
|
|
18157
18415
|
}
|
|
18158
18416
|
await placeEntities?.();
|
|
18159
18417
|
} finally {
|
|
18418
|
+
this.markBiomesChanged(chunk.x, chunk.z, void 0, false);
|
|
18419
|
+
const failures = await this.refreshBiomes();
|
|
18420
|
+
if (failures.size) console.warn("Failed to refresh biome colors", failures.values().next().value);
|
|
18160
18421
|
await this.updateCulling([...changes.values()]);
|
|
18161
18422
|
}
|
|
18162
18423
|
}
|
|
@@ -18173,11 +18434,14 @@ var MineRenderWorld = class {
|
|
|
18173
18434
|
try {
|
|
18174
18435
|
await this.clearChunkColumn(x, z);
|
|
18175
18436
|
} finally {
|
|
18437
|
+
const failures = await this.refreshBiomes();
|
|
18438
|
+
if (failures.size) console.warn("Failed to refresh biome colors", failures.values().next().value);
|
|
18176
18439
|
await this.updateCulling([]);
|
|
18177
18440
|
}
|
|
18178
18441
|
}
|
|
18179
18442
|
async clearChunkColumn(x, z) {
|
|
18180
18443
|
this.biomes.delete(`${x}_${z}`);
|
|
18444
|
+
this.markBiomesChanged(x, z, void 0, false);
|
|
18181
18445
|
if (this.savedLighting) {
|
|
18182
18446
|
for (const y of this.light.get(`${x}_${z}`)?.keys() ?? []) this.markSectionChanged({ x, y, z });
|
|
18183
18447
|
}
|
|
@@ -18188,6 +18452,7 @@ var MineRenderWorld = class {
|
|
|
18188
18452
|
for (const [key, section] of previous) {
|
|
18189
18453
|
this._chunks.delete(key);
|
|
18190
18454
|
this.pendingCulling.delete(section);
|
|
18455
|
+
this.pendingBiomes.delete(section);
|
|
18191
18456
|
await section.clear(false);
|
|
18192
18457
|
this.markSectionChanged(section);
|
|
18193
18458
|
}
|
|
@@ -18222,6 +18487,8 @@ var MineRenderWorld = class {
|
|
|
18222
18487
|
const chunks = [...this._chunks.values()];
|
|
18223
18488
|
this._chunks.clear();
|
|
18224
18489
|
this.biomes.clear();
|
|
18490
|
+
this.biomeBlend?.clear();
|
|
18491
|
+
this.pendingBiomes.clear();
|
|
18225
18492
|
this.light.clear();
|
|
18226
18493
|
this.lightValidity.clear();
|
|
18227
18494
|
this.highestSkySection.clear();
|
|
@@ -18340,8 +18607,14 @@ var MineRenderWorld = class {
|
|
|
18340
18607
|
column?.delete(y);
|
|
18341
18608
|
if (!column?.size) this.biomes.delete(columnKey);
|
|
18342
18609
|
}
|
|
18343
|
-
|
|
18344
|
-
|
|
18610
|
+
if (this.biomeBlend) {
|
|
18611
|
+
const queued = this.markBiomesChanged(x, z, y);
|
|
18612
|
+
const failures = await this.refreshBiomes();
|
|
18613
|
+
for (const chunk2 of queued) if (failures.has(chunk2)) throw failures.get(chunk2);
|
|
18614
|
+
} else {
|
|
18615
|
+
await chunk.refreshBiomes();
|
|
18616
|
+
await this._chunks.get(`${x}_${y + 1}_${z}`)?.refreshBiomes();
|
|
18617
|
+
}
|
|
18345
18618
|
},
|
|
18346
18619
|
this.savedLighting ? (x2, y2, z2) => this.lightLevelAt(x2, y2, z2) : void 0,
|
|
18347
18620
|
async () => {
|
|
@@ -18358,7 +18631,8 @@ var MineRenderWorld = class {
|
|
|
18358
18631
|
await this.updateCulling([]);
|
|
18359
18632
|
}
|
|
18360
18633
|
},
|
|
18361
|
-
this.ambientLight
|
|
18634
|
+
this.ambientLight,
|
|
18635
|
+
() => this.biomeBlend
|
|
18362
18636
|
);
|
|
18363
18637
|
this._chunks.set(key, chunk);
|
|
18364
18638
|
}
|
|
@@ -18477,9 +18751,10 @@ var LIGHT_SAMPLE_OFFSETS = [0, 1, -1, 324, -324, 18, -18];
|
|
|
18477
18751
|
|
|
18478
18752
|
// src/world/SectionGeometry.ts
|
|
18479
18753
|
function buildSectionGeometry(input) {
|
|
18480
|
-
const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize, fluids, light, ambientLight, ambientOcclusion } = input;
|
|
18754
|
+
const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize, fluids, light, ambientLight, ambientOcclusion, biomeTints } = input;
|
|
18481
18755
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
18482
18756
|
if (count > indices.length || count > templates.length || count > cullMasks.length) throw new RangeError("Section entry count exceeds its arrays");
|
|
18757
|
+
if (biomeTints && biomeTints.length !== count * 3) throw new RangeError("Section biome tints must contain RGB for each placement");
|
|
18483
18758
|
if (fluids && fluids.cells.length !== 5832) throw new RangeError("Section fluid cells must cover an 18\xD718\xD718 neighborhood");
|
|
18484
18759
|
if (fluids?.waterTints && fluids.waterTints.length !== 4096 * 3) throw new RangeError("Section water tints must contain RGB for 4096 cells");
|
|
18485
18760
|
if (light && (light.length !== 5832 || light.some((level) => level > 15 && level !== 255))) {
|
|
@@ -18494,7 +18769,7 @@ function buildSectionGeometry(input) {
|
|
|
18494
18769
|
const cornerBrightness2 = ambientOcclusion ? new Float32Array(4) : void 0;
|
|
18495
18770
|
for (const template of templateData) {
|
|
18496
18771
|
const { quads } = template;
|
|
18497
|
-
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) {
|
|
18772
|
+
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) {
|
|
18498
18773
|
throw new RangeError("Section template arrays do not match its quad count");
|
|
18499
18774
|
}
|
|
18500
18775
|
}
|
|
@@ -18604,6 +18879,7 @@ function buildSectionGeometry(input) {
|
|
|
18604
18879
|
blockIndices[indexOffset / 6] = indices[entry];
|
|
18605
18880
|
if (ambientMask) quadAmbientOcclusion(template.positions, template.normals, quad, ambientMask, cornerBrightness2);
|
|
18606
18881
|
const brightness = light ? lightBrightness(light[lightIndex + LIGHT_SAMPLE_OFFSETS[quadLightIndex(template.positions, template.normals, quad)]], ambientLight) : 1;
|
|
18882
|
+
const tint = biomeTints && template.biomeTint?.[quad] && !Number.isNaN(biomeTints[entry * 3]) ? entry * 3 : void 0;
|
|
18607
18883
|
const start = vertex;
|
|
18608
18884
|
for (let corner = 0; corner < 4; corner++, vertex++) {
|
|
18609
18885
|
const source = quad * 4 + corner;
|
|
@@ -18620,9 +18896,9 @@ function buildSectionGeometry(input) {
|
|
|
18620
18896
|
uvBounds[vertex * 4 + 2] = mapU(template.uvBounds[source * 4 + 2]);
|
|
18621
18897
|
uvBounds[vertex * 4 + 3] = mapV(template.uvBounds[source * 4 + 3]);
|
|
18622
18898
|
const factor = brightness * (ambientMask ? cornerBrightness2[corner] : 1);
|
|
18623
|
-
colors[vertex * 3] = template.colors[source * 3] * factor;
|
|
18624
|
-
colors[vertex * 3 + 1] = template.colors[source * 3 + 1] * factor;
|
|
18625
|
-
colors[vertex * 3 + 2] = template.colors[source * 3 + 2] * factor;
|
|
18899
|
+
colors[vertex * 3] = (tint === void 0 ? template.colors[source * 3] : biomeTints[tint]) * factor;
|
|
18900
|
+
colors[vertex * 3 + 1] = (tint === void 0 ? template.colors[source * 3 + 1] : biomeTints[tint + 1]) * factor;
|
|
18901
|
+
colors[vertex * 3 + 2] = (tint === void 0 ? template.colors[source * 3 + 2] : biomeTints[tint + 2]) * factor;
|
|
18626
18902
|
}
|
|
18627
18903
|
for (let corner = 0; corner < 6; corner++) outputIndices[indexOffset++] = start + template.indices[quad * 6 + corner];
|
|
18628
18904
|
}
|
|
@@ -18744,7 +19020,11 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
|
18744
19020
|
...lighting
|
|
18745
19021
|
};
|
|
18746
19022
|
for (let i = 0; i < entries.length; i++) {
|
|
18747
|
-
const { index, template, cullMask } = entries[i];
|
|
19023
|
+
const { index, template, cullMask, biomeTint } = entries[i];
|
|
19024
|
+
if (biomeTint) {
|
|
19025
|
+
input.biomeTints ??= new Float32Array(entries.length * 3).fill(NaN);
|
|
19026
|
+
input.biomeTints.set(biomeTint, i * 3);
|
|
19027
|
+
}
|
|
18748
19028
|
let id = templateIds.get(template);
|
|
18749
19029
|
if (id === void 0) {
|
|
18750
19030
|
let atlas = atlasIds.get(template.atlas);
|
|
@@ -18771,6 +19051,7 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
|
18771
19051
|
colors: new Float32Array(quads * 12),
|
|
18772
19052
|
indices: new Uint16Array(quads * 6),
|
|
18773
19053
|
cullFaces: new Uint8Array(template.cullFaces),
|
|
19054
|
+
biomeTint: template.biomeTint ? new Uint8Array(template.biomeTint) : void 0,
|
|
18774
19055
|
atlas: 0,
|
|
18775
19056
|
layer: template.layer,
|
|
18776
19057
|
ambientOcclusion: template.ambientOcclusion
|
|
@@ -18915,7 +19196,7 @@ init_CubeFace();
|
|
|
18915
19196
|
init_InstanceReference();
|
|
18916
19197
|
var WATER_TINT2 = new import_three47.Color(4159204).toArray();
|
|
18917
19198
|
var Chunk = class _Chunk {
|
|
18918
|
-
constructor(scene, x, y, z, onBlocksChanged, sectionModels, neighborCell, biomeAt, onBiomesChanged, lightAt, onLightCleared, ambientLight = 0) {
|
|
19199
|
+
constructor(scene, x, y, z, onBlocksChanged, sectionModels, neighborCell, biomeAt, onBiomesChanged, lightAt, onLightCleared, ambientLight = 0, biomeBlend) {
|
|
18919
19200
|
this.onBlocksChanged = onBlocksChanged;
|
|
18920
19201
|
this.sectionModels = sectionModels;
|
|
18921
19202
|
this.neighborCell = neighborCell;
|
|
@@ -18924,6 +19205,7 @@ var Chunk = class _Chunk {
|
|
|
18924
19205
|
this.lightAt = lightAt;
|
|
18925
19206
|
this.onLightCleared = onLightCleared;
|
|
18926
19207
|
this.ambientLight = ambientLight;
|
|
19208
|
+
this.biomeBlend = biomeBlend;
|
|
18927
19209
|
this.data = new ChunkData();
|
|
18928
19210
|
this.renderedBlocks = /* @__PURE__ */ new Map();
|
|
18929
19211
|
this.objectBlocks = /* @__PURE__ */ new Set();
|
|
@@ -18933,6 +19215,7 @@ var Chunk = class _Chunk {
|
|
|
18933
19215
|
this.blockPosition = new import_three47.Vector3();
|
|
18934
19216
|
this.meshDirty = false;
|
|
18935
19217
|
this.meshGeneration = 0;
|
|
19218
|
+
this.blockChanges = /* @__PURE__ */ new Set();
|
|
18936
19219
|
this.scene = scene;
|
|
18937
19220
|
this.x = x;
|
|
18938
19221
|
this.y = y;
|
|
@@ -19031,10 +19314,20 @@ var Chunk = class _Chunk {
|
|
|
19031
19314
|
return this.refreshBiomes();
|
|
19032
19315
|
}
|
|
19033
19316
|
/** Rebuilds biome-tinted blocks while preserving hidden blocks and unrelated render objects. */
|
|
19034
|
-
|
|
19317
|
+
refreshBiomes(columns) {
|
|
19318
|
+
return this.changeBlocks(() => this.refreshBiomeBlocks(columns), true);
|
|
19319
|
+
}
|
|
19320
|
+
async refreshBiomeBlocks(columns) {
|
|
19321
|
+
const blend = columns && this.biomeBlend?.();
|
|
19322
|
+
const ownColumn = columns?.some((column) => column.x === this.x && column.z === this.z);
|
|
19035
19323
|
const affected = /* @__PURE__ */ new Map();
|
|
19036
19324
|
const blocks = [];
|
|
19037
19325
|
for (const index of this.renderedBlocks.keys()) {
|
|
19326
|
+
if (blend) {
|
|
19327
|
+
if (this.objectBlocks.has(index) && !ownColumn) continue;
|
|
19328
|
+
const [x, , z] = this.positionAt(index), radius = blend.radius;
|
|
19329
|
+
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;
|
|
19330
|
+
}
|
|
19038
19331
|
const block = this.data.get(index);
|
|
19039
19332
|
const stateKey = ChunkData.paletteKey(block);
|
|
19040
19333
|
let matches = affected.get(stateKey);
|
|
@@ -19049,10 +19342,32 @@ var Chunk = class _Chunk {
|
|
|
19049
19342
|
}
|
|
19050
19343
|
if (await matches) blocks.push({ index, block });
|
|
19051
19344
|
}
|
|
19345
|
+
if (blend) {
|
|
19346
|
+
const groups = /* @__PURE__ */ new Map();
|
|
19347
|
+
for (const entry of blocks) {
|
|
19348
|
+
const key = ChunkData.paletteKey(entry.block);
|
|
19349
|
+
let group = groups.get(key);
|
|
19350
|
+
if (!group) groups.set(key, group = []);
|
|
19351
|
+
group.push(entry);
|
|
19352
|
+
}
|
|
19353
|
+
blocks.length = 0;
|
|
19354
|
+
const biomes = /* @__PURE__ */ new Map();
|
|
19355
|
+
for (const group of groups.values()) {
|
|
19356
|
+
const { contexts, fluid } = await this.resolveState(group[0].block, group.map((entry) => entry.index), biomes);
|
|
19357
|
+
for (const entry of group) {
|
|
19358
|
+
const context = contexts.get(entry.index);
|
|
19359
|
+
const current = this.sectionBlocks.get(entry.index)?.[0]?.biomeTint;
|
|
19360
|
+
const tint = context?.tint;
|
|
19361
|
+
const tintChanged = !!current !== !!tint || tint?.some((value, channel) => value !== current?.[channel]);
|
|
19362
|
+
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])));
|
|
19363
|
+
if (this.objectBlocks.has(entry.index) || tintChanged || waterChanged) blocks.push(entry);
|
|
19364
|
+
}
|
|
19365
|
+
}
|
|
19366
|
+
}
|
|
19052
19367
|
if (!blocks.length) return;
|
|
19053
19368
|
const hidden = new Set(this.hiddenBlocks);
|
|
19054
19369
|
try {
|
|
19055
|
-
await this.
|
|
19370
|
+
await this.replaceBlocks(blocks, true);
|
|
19056
19371
|
} finally {
|
|
19057
19372
|
for (const index of hidden) {
|
|
19058
19373
|
this.hiddenBlocks.add(index);
|
|
@@ -19092,6 +19407,20 @@ var Chunk = class _Chunk {
|
|
|
19092
19407
|
}
|
|
19093
19408
|
/** Places cells in input order and returns changed world positions without refreshing neighbors. */
|
|
19094
19409
|
async placeBlocks(blocks) {
|
|
19410
|
+
return this.changeBlocks(() => this.replaceBlocks(blocks));
|
|
19411
|
+
}
|
|
19412
|
+
changeBlocks(change, refreshBiomes = false) {
|
|
19413
|
+
const before = refreshBiomes ? Promise.allSettled(this.blockChanges) : this.biomeChange;
|
|
19414
|
+
const result = before ? before.catch(() => void 0).then(change) : change();
|
|
19415
|
+
const pending = result.finally(() => {
|
|
19416
|
+
this.blockChanges.delete(pending);
|
|
19417
|
+
if (this.biomeChange === pending) this.biomeChange = void 0;
|
|
19418
|
+
});
|
|
19419
|
+
this.blockChanges.add(pending);
|
|
19420
|
+
if (refreshBiomes) this.biomeChange = pending;
|
|
19421
|
+
return pending;
|
|
19422
|
+
}
|
|
19423
|
+
async replaceBlocks(blocks, preserveDataOnFailure = false) {
|
|
19095
19424
|
const placements = blocks.map(({ index, block }) => ({
|
|
19096
19425
|
index,
|
|
19097
19426
|
key: block && !ChunkData.isAir(block) ? ChunkData.paletteKey(block) : void 0,
|
|
@@ -19111,6 +19440,10 @@ var Chunk = class _Chunk {
|
|
|
19111
19440
|
const biomes = /* @__PURE__ */ new Map();
|
|
19112
19441
|
const settled = await Promise.allSettled([...states.values()].map(({ stored, indices }) => this.resolveState(stored, indices, biomes)));
|
|
19113
19442
|
const resolutions = new Map([...states.keys()].map((key, i) => [key, settled[i]]));
|
|
19443
|
+
if (preserveDataOnFailure) {
|
|
19444
|
+
const rejected = settled.find((result) => result.status === "rejected");
|
|
19445
|
+
if (rejected?.status === "rejected") throw rejected.reason;
|
|
19446
|
+
}
|
|
19114
19447
|
const positions = [];
|
|
19115
19448
|
let failed = false, failure;
|
|
19116
19449
|
for (const { index, key, block } of placements) {
|
|
@@ -19148,7 +19481,7 @@ var Chunk = class _Chunk {
|
|
|
19148
19481
|
}
|
|
19149
19482
|
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : (context?.prepared ?? prepared)?.pick(variantPosition);
|
|
19150
19483
|
if (templates) {
|
|
19151
|
-
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0 })));
|
|
19484
|
+
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0, biomeTint: context?.tint })));
|
|
19152
19485
|
this.meshDirty = true;
|
|
19153
19486
|
} else {
|
|
19154
19487
|
object4 = await this.scene.addBlock(blockState3, {
|
|
@@ -19167,7 +19500,11 @@ var Chunk = class _Chunk {
|
|
|
19167
19500
|
}, object: object4 });
|
|
19168
19501
|
if (object4) this.objectBlocks.add(index);
|
|
19169
19502
|
} catch (error) {
|
|
19170
|
-
|
|
19503
|
+
if (preserveDataOnFailure && readBlock) {
|
|
19504
|
+
this.renderedBlocks.set(index, { get block() {
|
|
19505
|
+
return readBlock();
|
|
19506
|
+
}, object: void 0 });
|
|
19507
|
+
} else this.data.assign(index, 0);
|
|
19171
19508
|
this.fluidCells[index] = 0;
|
|
19172
19509
|
object4?.removeFromScene();
|
|
19173
19510
|
if (!failed) failure = error;
|
|
@@ -19178,7 +19515,10 @@ var Chunk = class _Chunk {
|
|
|
19178
19515
|
return positions;
|
|
19179
19516
|
}
|
|
19180
19517
|
/** Replaces every cell from parsed section data. The caller refreshes neighbor culling. */
|
|
19181
|
-
|
|
19518
|
+
placeSection(data) {
|
|
19519
|
+
return this.changeBlocks(() => this.replaceSection(data));
|
|
19520
|
+
}
|
|
19521
|
+
async replaceSection(data) {
|
|
19182
19522
|
for (const info of this.renderedBlocks.values()) info.object?.removeFromScene();
|
|
19183
19523
|
this.sectionBlocks.clear();
|
|
19184
19524
|
this.fluidCells.fill(0);
|
|
@@ -19225,7 +19565,7 @@ var Chunk = class _Chunk {
|
|
|
19225
19565
|
];
|
|
19226
19566
|
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : (context?.prepared ?? prepared)?.pick(variantPosition);
|
|
19227
19567
|
if (templates) {
|
|
19228
|
-
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0 })));
|
|
19568
|
+
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0, biomeTint: context?.tint })));
|
|
19229
19569
|
this.meshDirty = true;
|
|
19230
19570
|
} else {
|
|
19231
19571
|
perBlocks.push({ index, blockState: blockState3, biome: context?.biome });
|
|
@@ -19309,12 +19649,35 @@ var Chunk = class _Chunk {
|
|
|
19309
19649
|
}
|
|
19310
19650
|
async resolveState(stored, indices, biomes) {
|
|
19311
19651
|
const blockState3 = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
|
|
19312
|
-
|
|
19652
|
+
let perBlock = !blockState3 || !this.sectionModels || !!(blockState3.key && BlockEntities.entry(await BlockEntities.getIndex(blockState3.key.root), blockState3.key.toNamespacedString()));
|
|
19313
19653
|
const fluid = await getBlockFluidState(blockState3?.key, stored.properties);
|
|
19314
19654
|
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;
|
|
19315
19655
|
const source = hasBiomes && blockState3 ? await BlockTints.biomeSource(blockState3.key) : void 0;
|
|
19316
19656
|
const prepare = (color2) => perBlock || fluid?.renderModel === false ? void 0 : this.sectionModels.prepareState(blockState3, stored.properties, color2);
|
|
19317
19657
|
const contexts = /* @__PURE__ */ new Map();
|
|
19658
|
+
const blend = this.biomeBlend?.();
|
|
19659
|
+
if (blend && !perBlock && blockState3 && (source || fluid?.kind === "water")) {
|
|
19660
|
+
const prepared2 = fluid?.renderModel === false ? void 0 : await this.sectionModels.prepareState(blockState3, stored.properties, void 0, true);
|
|
19661
|
+
if (!prepared2 && fluid?.renderModel !== false) {
|
|
19662
|
+
perBlock = true;
|
|
19663
|
+
} else {
|
|
19664
|
+
await Promise.all(indices.map(async (index) => {
|
|
19665
|
+
const position = this.biomePosition(index, blockState3, stored);
|
|
19666
|
+
const biomeColor = (kind) => blend.getColor(kind, blockState3.key, position);
|
|
19667
|
+
const context = {};
|
|
19668
|
+
if (source && prepared2?.biomeTint) {
|
|
19669
|
+
const color2 = await biomeColor(source);
|
|
19670
|
+
if (color2 !== void 0) context.tint = new import_three47.Color(color2).toArray();
|
|
19671
|
+
}
|
|
19672
|
+
if (fluid?.kind === "water") {
|
|
19673
|
+
const color2 = await biomeColor("water");
|
|
19674
|
+
if (color2 !== void 0 && color2 !== 4159204) context.waterTint = new import_three47.Color(color2).toArray();
|
|
19675
|
+
}
|
|
19676
|
+
contexts.set(index, context);
|
|
19677
|
+
}));
|
|
19678
|
+
return { blockState: blockState3, perBlock, fluid, prepared: prepared2, contexts, stored };
|
|
19679
|
+
}
|
|
19680
|
+
}
|
|
19318
19681
|
const groups = /* @__PURE__ */ new Map();
|
|
19319
19682
|
if (blockState3 && (source || fluid?.kind === "water")) {
|
|
19320
19683
|
for (const index of indices) {
|
|
@@ -19362,7 +19725,11 @@ var Chunk = class _Chunk {
|
|
|
19362
19725
|
this.waterTints.set(tint ?? WATER_TINT2, index * 3);
|
|
19363
19726
|
}
|
|
19364
19727
|
/** Changes visibility at world block coordinates while preserving the block's data. */
|
|
19365
|
-
|
|
19728
|
+
setBlockVisibleAt(pos, visible) {
|
|
19729
|
+
const position = pos.clone();
|
|
19730
|
+
return this.changeBlocks(() => this.setBlockVisibility(position, visible));
|
|
19731
|
+
}
|
|
19732
|
+
async setBlockVisibility(pos, visible) {
|
|
19366
19733
|
const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
|
|
19367
19734
|
const block = this.renderedBlocks.get(index);
|
|
19368
19735
|
if (!block || visible === !this.hiddenBlocks.has(index)) {
|
|
@@ -19532,6 +19899,7 @@ var Chunk = class _Chunk {
|
|
|
19532
19899
|
}
|
|
19533
19900
|
/** Removes stored blocks and render objects. Set `notify` to false to skip the owning world's update. */
|
|
19534
19901
|
async clear(notify = true) {
|
|
19902
|
+
if (this.blockChanges.size) await Promise.allSettled(this.blockChanges);
|
|
19535
19903
|
const positions = notify && this.onBlocksChanged ? [...this.renderedBlocks.keys()].map((index) => this.chunkPosToWorldPos(
|
|
19536
19904
|
new import_three47.Vector3(index % 16, Math.floor(index / 256), Math.floor(index / 16) % 16)
|
|
19537
19905
|
)) : [];
|
|
@@ -20011,6 +20379,7 @@ var NodeRenderer = class _NodeRenderer extends Renderer {
|
|
|
20011
20379
|
PackMetadata,
|
|
20012
20380
|
PersistentCache,
|
|
20013
20381
|
PlayerHeadTextures,
|
|
20382
|
+
Renames,
|
|
20014
20383
|
Renderer,
|
|
20015
20384
|
RequestError,
|
|
20016
20385
|
Requests,
|