minerender 2.1.0-alpha.127 → 2.1.0-alpha.129
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/index.d.mts +77 -5
- package/dist/browser/index.d.ts +77 -5
- package/dist/browser/index.js +653 -287
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/index.mjs +653 -287
- package/dist/browser/index.mjs.map +1 -1
- package/dist/browser/section.worker.js +7 -5
- package/dist/browser/section.worker.js.map +1 -1
- package/dist/browser/section.worker.mjs +7 -5
- package/dist/browser/section.worker.mjs.map +1 -1
- package/dist/bundle.js +665 -293
- package/dist/bundle.js.map +1 -1
- package/dist/node/index.d.mts +77 -5
- package/dist/node/index.d.ts +77 -5
- package/dist/node/index.js +656 -287
- package/dist/node/index.js.map +1 -1
- package/dist/node/index.mjs +655 -287
- package/dist/node/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/browser/index.js
CHANGED
|
@@ -264,6 +264,10 @@ var init_Caching = __esm({
|
|
|
264
264
|
import_loading_cache = require("@inventivetalent/loading-cache");
|
|
265
265
|
import_time = require("@inventivetalent/time");
|
|
266
266
|
_Caching = class _Caching {
|
|
267
|
+
/** Changes when shared assets are cleared, so derived caches can discard their old values. */
|
|
268
|
+
static get generation() {
|
|
269
|
+
return this._generation;
|
|
270
|
+
}
|
|
267
271
|
static createAssetCache() {
|
|
268
272
|
return import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(30)).buildAsync();
|
|
269
273
|
}
|
|
@@ -299,6 +303,7 @@ var init_Caching = __esm({
|
|
|
299
303
|
}
|
|
300
304
|
/** Invalidates in-memory entries so later lookups reload them. Persisted entries are retained. */
|
|
301
305
|
static clear() {
|
|
306
|
+
this._generation++;
|
|
302
307
|
for (const cache of this.all) {
|
|
303
308
|
cache.invalidateAll();
|
|
304
309
|
}
|
|
@@ -310,6 +315,7 @@ var init_Caching = __esm({
|
|
|
310
315
|
* Use {@link shutdown} to also stop requests and the shared Ticker.
|
|
311
316
|
*/
|
|
312
317
|
static end() {
|
|
318
|
+
this._generation++;
|
|
313
319
|
for (const cache of this.all) {
|
|
314
320
|
cache.invalidateAll();
|
|
315
321
|
cache.end();
|
|
@@ -321,6 +327,7 @@ var init_Caching = __esm({
|
|
|
321
327
|
};
|
|
322
328
|
}
|
|
323
329
|
};
|
|
330
|
+
_Caching._generation = 0;
|
|
324
331
|
_Caching.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();
|
|
325
332
|
_Caching.imageDataCache = import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(10)).buildAsync();
|
|
326
333
|
_Caching.canvasImageDataCache = import_loading_cache.Caches.builder().expireAfterWrite(import_time.Time.minutes(10)).expireAfterAccess(import_time.Time.minutes(5)).expirationInterval(import_time.Time.seconds(10)).buildAsync();
|
|
@@ -1209,118 +1216,6 @@ var init_Materials = __esm({
|
|
|
1209
1216
|
}
|
|
1210
1217
|
});
|
|
1211
1218
|
|
|
1212
|
-
// src/model/Model.ts
|
|
1213
|
-
function isDoubleArray(obj) {
|
|
1214
|
-
return Array.isArray(obj) && obj.length === 2;
|
|
1215
|
-
}
|
|
1216
|
-
function isTripleArray(obj) {
|
|
1217
|
-
return Array.isArray(obj) && obj.length === 3;
|
|
1218
|
-
}
|
|
1219
|
-
function isQuadArray(obj) {
|
|
1220
|
-
return Array.isArray(obj) && obj.length === 4;
|
|
1221
|
-
}
|
|
1222
|
-
var ITEM_GENERATED, BUILTIN_GENERATED, BUILTIN_ENTITY, DEFAULT_ELEMENTS;
|
|
1223
|
-
var init_Model = __esm({
|
|
1224
|
-
"src/model/Model.ts"() {
|
|
1225
|
-
ITEM_GENERATED = "item/generated";
|
|
1226
|
-
BUILTIN_GENERATED = "builtin/generated";
|
|
1227
|
-
BUILTIN_ENTITY = "builtin/entity";
|
|
1228
|
-
DEFAULT_ELEMENTS = [];
|
|
1229
|
-
}
|
|
1230
|
-
});
|
|
1231
|
-
|
|
1232
|
-
// ../../node_modules/ts-deepmerge/dist/index.js
|
|
1233
|
-
var require_dist = __commonJS({
|
|
1234
|
-
"../../node_modules/ts-deepmerge/dist/index.js"(exports) {
|
|
1235
|
-
"use strict";
|
|
1236
|
-
var __assign = exports && exports.__assign || function() {
|
|
1237
|
-
__assign = Object.assign || function(t) {
|
|
1238
|
-
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
1239
|
-
s = arguments[i];
|
|
1240
|
-
for (var p11 in s) if (Object.prototype.hasOwnProperty.call(s, p11))
|
|
1241
|
-
t[p11] = s[p11];
|
|
1242
|
-
}
|
|
1243
|
-
return t;
|
|
1244
|
-
};
|
|
1245
|
-
return __assign.apply(this, arguments);
|
|
1246
|
-
};
|
|
1247
|
-
var __read = exports && exports.__read || function(o, n) {
|
|
1248
|
-
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
1249
|
-
if (!m) return o;
|
|
1250
|
-
var i = m.call(o), r, ar = [], e;
|
|
1251
|
-
try {
|
|
1252
|
-
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
1253
|
-
} catch (error) {
|
|
1254
|
-
e = { error };
|
|
1255
|
-
} finally {
|
|
1256
|
-
try {
|
|
1257
|
-
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
1258
|
-
} finally {
|
|
1259
|
-
if (e) throw e.error;
|
|
1260
|
-
}
|
|
1261
|
-
}
|
|
1262
|
-
return ar;
|
|
1263
|
-
};
|
|
1264
|
-
var __spreadArray = exports && exports.__spreadArray || function(to, from, pack) {
|
|
1265
|
-
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
1266
|
-
if (ar || !(i in from)) {
|
|
1267
|
-
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
1268
|
-
ar[i] = from[i];
|
|
1269
|
-
}
|
|
1270
|
-
}
|
|
1271
|
-
return to.concat(ar || Array.prototype.slice.call(from));
|
|
1272
|
-
};
|
|
1273
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1274
|
-
var isObject = function(obj) {
|
|
1275
|
-
if (typeof obj === "object" && obj !== null) {
|
|
1276
|
-
if (typeof Object.getPrototypeOf === "function") {
|
|
1277
|
-
var prototype = Object.getPrototypeOf(obj);
|
|
1278
|
-
return prototype === Object.prototype || prototype === null;
|
|
1279
|
-
}
|
|
1280
|
-
return Object.prototype.toString.call(obj) === "[object Object]";
|
|
1281
|
-
}
|
|
1282
|
-
return false;
|
|
1283
|
-
};
|
|
1284
|
-
var PROTECTED_KEYS = ["__proto__"];
|
|
1285
|
-
var merge10 = function() {
|
|
1286
|
-
var objects = [];
|
|
1287
|
-
for (var _i = 0; _i < arguments.length; _i++) {
|
|
1288
|
-
objects[_i] = arguments[_i];
|
|
1289
|
-
}
|
|
1290
|
-
return objects.reduce(function(result, current) {
|
|
1291
|
-
Object.keys(current).forEach(function(key) {
|
|
1292
|
-
if (PROTECTED_KEYS.includes(key)) {
|
|
1293
|
-
return;
|
|
1294
|
-
}
|
|
1295
|
-
if (Array.isArray(result[key]) && Array.isArray(current[key])) {
|
|
1296
|
-
result[key] = merge10.options.mergeArrays ? Array.from(new Set(result[key].concat(current[key]))) : current[key];
|
|
1297
|
-
} else if (isObject(result[key]) && isObject(current[key])) {
|
|
1298
|
-
result[key] = merge10(result[key], current[key]);
|
|
1299
|
-
} else {
|
|
1300
|
-
result[key] = current[key];
|
|
1301
|
-
}
|
|
1302
|
-
});
|
|
1303
|
-
return result;
|
|
1304
|
-
}, {});
|
|
1305
|
-
};
|
|
1306
|
-
var defaultOptions = {
|
|
1307
|
-
mergeArrays: true
|
|
1308
|
-
};
|
|
1309
|
-
merge10.options = defaultOptions;
|
|
1310
|
-
merge10.withOptions = function(options) {
|
|
1311
|
-
var objects = [];
|
|
1312
|
-
for (var _i = 1; _i < arguments.length; _i++) {
|
|
1313
|
-
objects[_i - 1] = arguments[_i];
|
|
1314
|
-
}
|
|
1315
|
-
merge10.options = __assign({ mergeArrays: true }, options);
|
|
1316
|
-
var result = merge10.apply(void 0, __spreadArray([], __read(objects), false));
|
|
1317
|
-
merge10.options = defaultOptions;
|
|
1318
|
-
return result;
|
|
1319
|
-
};
|
|
1320
|
-
exports.default = merge10;
|
|
1321
|
-
}
|
|
1322
|
-
});
|
|
1323
|
-
|
|
1324
1219
|
// src/assets/AssetKey.ts
|
|
1325
1220
|
function isResourceLocation(value) {
|
|
1326
1221
|
return typeof value === "string" && /^(?:[a-z0-9_.-]+:)?[a-z0-9_./-]+$/.test(value);
|
|
@@ -1423,17 +1318,306 @@ var init_AssetKey = __esm({
|
|
|
1423
1318
|
if (this.type) {
|
|
1424
1319
|
p11.push(this.type);
|
|
1425
1320
|
}
|
|
1426
|
-
p11.push(this.path);
|
|
1427
|
-
return p11.join("/");
|
|
1321
|
+
p11.push(this.path);
|
|
1322
|
+
return p11.join("/");
|
|
1323
|
+
}
|
|
1324
|
+
toNamespacedString() {
|
|
1325
|
+
return this.namespace + ":" + this.getFullPath();
|
|
1326
|
+
}
|
|
1327
|
+
/** Returns a cache identifier that includes the explicit root or {@link AssetLoader.ROOT}. */
|
|
1328
|
+
serialize() {
|
|
1329
|
+
return this.toString();
|
|
1330
|
+
}
|
|
1331
|
+
};
|
|
1332
|
+
}
|
|
1333
|
+
});
|
|
1334
|
+
|
|
1335
|
+
// src/assets/source/parser/AssetParsers.ts
|
|
1336
|
+
var AssetParser;
|
|
1337
|
+
var init_AssetParsers = __esm({
|
|
1338
|
+
"src/assets/source/parser/AssetParsers.ts"() {
|
|
1339
|
+
AssetParser = /* @__PURE__ */ ((AssetParser2) => {
|
|
1340
|
+
AssetParser2["MODEL"] = "model";
|
|
1341
|
+
AssetParser2["BLOCKSTATE"] = "blockstate";
|
|
1342
|
+
AssetParser2["META"] = "meta";
|
|
1343
|
+
AssetParser2["NBT"] = "nbt";
|
|
1344
|
+
AssetParser2["IMAGE"] = "image";
|
|
1345
|
+
AssetParser2["LIST"] = "list";
|
|
1346
|
+
AssetParser2["JSON"] = "json";
|
|
1347
|
+
return AssetParser2;
|
|
1348
|
+
})(AssetParser || {});
|
|
1349
|
+
}
|
|
1350
|
+
});
|
|
1351
|
+
|
|
1352
|
+
// src/assets/MineRenderData.ts
|
|
1353
|
+
var object, release, compare, MineRenderData;
|
|
1354
|
+
var init_MineRenderData = __esm({
|
|
1355
|
+
"src/assets/MineRenderData.ts"() {
|
|
1356
|
+
init_AssetKey();
|
|
1357
|
+
init_AssetLoader();
|
|
1358
|
+
init_AssetParsers();
|
|
1359
|
+
init_Caching();
|
|
1360
|
+
object = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1361
|
+
release = (value) => /^\d+\.\d+(?:\.\d+)?$/.test(value) ? value.split(".").map(Number) : void 0;
|
|
1362
|
+
compare = (left, right) => {
|
|
1363
|
+
for (let i = 0; i < 3; i++) {
|
|
1364
|
+
const difference = (left[i] ?? 0) - (right[i] ?? 0);
|
|
1365
|
+
if (difference) return difference;
|
|
1366
|
+
}
|
|
1367
|
+
return 0;
|
|
1368
|
+
};
|
|
1369
|
+
MineRenderData = class {
|
|
1370
|
+
/** Loads one registry. Missing files and invalid data reject the request. */
|
|
1371
|
+
static async get(dataset, root) {
|
|
1372
|
+
root = root?.replace(/\/+$/, "");
|
|
1373
|
+
const scope = AssetLoader.persistentScope;
|
|
1374
|
+
const defaultRoot = AssetLoader.ROOT;
|
|
1375
|
+
const resolved = await this.resolve(root);
|
|
1376
|
+
const entry = resolved.manifest.datasets[dataset];
|
|
1377
|
+
if (!entry) throw new Error(`MineRender ${dataset} data is unavailable for ${resolved.version}: ${resolved.manifest.unavailable[dataset] ?? "not listed in the manifest"}`);
|
|
1378
|
+
const file = entry.file;
|
|
1379
|
+
const requestRoot = resolved.root === (root ?? defaultRoot).replace(/\/+$/, "") ? root : resolved.root;
|
|
1380
|
+
return await this.cached(`${requestRoot === void 0 ? "sources" : "explicit"}:${resolved.root}/${file}:${entry.sha256}`, scope, defaultRoot, async () => {
|
|
1381
|
+
const data = await this.load(file.slice(0, -5), requestRoot);
|
|
1382
|
+
if (!this.validDataset(dataset, data)) throw new Error(`Invalid MineRender ${dataset} data at ${resolved.root}/minerender-data/${file}`);
|
|
1383
|
+
return data;
|
|
1384
|
+
});
|
|
1385
|
+
}
|
|
1386
|
+
/**
|
|
1387
|
+
* Uses the exact version when published. Otherwise, selects the nearest patch in the same
|
|
1388
|
+
* release line (older on ties), then the latest earlier release or the oldest published release.
|
|
1389
|
+
* Unpublished snapshots reject.
|
|
1390
|
+
*/
|
|
1391
|
+
static async resolve(root) {
|
|
1392
|
+
const requestRoot = root?.replace(/\/+$/, "");
|
|
1393
|
+
const defaultRoot = AssetLoader.ROOT;
|
|
1394
|
+
root = (root ?? defaultRoot).replace(/\/+$/, "");
|
|
1395
|
+
const scope = AssetLoader.persistentScope;
|
|
1396
|
+
return this.cached(`resolve:${requestRoot === void 0 ? "sources" : "explicit"}:${root}`, scope, defaultRoot, async () => {
|
|
1397
|
+
const requestedVersion = root.slice(root.lastIndexOf("/") + 1);
|
|
1398
|
+
let manifest = await this.manifest(root, requestRoot !== void 0 && release(requestedVersion) ? requestedVersion : void 0, requestRoot);
|
|
1399
|
+
if (manifest) return { requestedVersion, version: manifest.minecraftVersion, root, manifest };
|
|
1400
|
+
const requested = release(requestedVersion);
|
|
1401
|
+
if (!requested) {
|
|
1402
|
+
throw new Error(`No MineRender data is published for ${requestedVersion}; automatic fallback requires a release`);
|
|
1403
|
+
}
|
|
1404
|
+
const base = root.slice(0, root.lastIndexOf("/"));
|
|
1405
|
+
const index = await this.cached(`versions:${base}`, scope, defaultRoot, async () => {
|
|
1406
|
+
const value = await this.load("versions", base);
|
|
1407
|
+
if (!object(value) || !Array.isArray(value.versions) || !value.versions.every((entry) => object(entry) && typeof entry.name === "string")) {
|
|
1408
|
+
throw new Error(`Invalid MineRender version index at ${base}/minerender-data/versions.json`);
|
|
1409
|
+
}
|
|
1410
|
+
return value.versions;
|
|
1411
|
+
});
|
|
1412
|
+
const versions = index.map((entry) => ({ name: entry.name, number: release(entry.name) })).filter((entry) => !!entry.number).sort((left, right) => compare(left.number, right.number));
|
|
1413
|
+
const sameLine = versions.filter((entry) => entry.number[0] === requested[0] && entry.number[1] === requested[1]);
|
|
1414
|
+
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];
|
|
1415
|
+
if (!selected) throw new Error(`No compatible MineRender data is published for ${requestedVersion}`);
|
|
1416
|
+
const resolvedRoot = `${base}/${selected.name}`;
|
|
1417
|
+
manifest = await this.manifest(resolvedRoot, selected.name, resolvedRoot);
|
|
1418
|
+
if (!manifest) throw new Error(`Missing MineRender manifest for published version ${selected.name}`);
|
|
1419
|
+
return { requestedVersion, version: selected.name, root: resolvedRoot, manifest };
|
|
1420
|
+
});
|
|
1421
|
+
}
|
|
1422
|
+
static async manifest(root, expectedVersion, requestRoot) {
|
|
1423
|
+
const data = await this.load("manifest", requestRoot);
|
|
1424
|
+
if (data === void 0) return void 0;
|
|
1425
|
+
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) => {
|
|
1426
|
+
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);
|
|
1427
|
+
}) || expectedVersion !== void 0 && data.minecraftVersion !== expectedVersion) {
|
|
1428
|
+
throw new Error(`Invalid MineRender manifest at ${root}/minerender-data/manifest.json`);
|
|
1429
|
+
}
|
|
1430
|
+
return data;
|
|
1431
|
+
}
|
|
1432
|
+
static load(file, root) {
|
|
1433
|
+
const key = new AssetKey("minerender-data", file, void 0, void 0, void 0, ".json", root);
|
|
1434
|
+
key.rootType = void 0;
|
|
1435
|
+
return AssetLoader.get(key, "json" /* JSON */);
|
|
1436
|
+
}
|
|
1437
|
+
static async cached(key, scope, defaultRoot, loader) {
|
|
1438
|
+
const checkSources = () => {
|
|
1439
|
+
if (AssetLoader.persistentScope !== scope || AssetLoader.ROOT !== defaultRoot) throw new Error("Asset sources changed while loading MineRender data; retry the request");
|
|
1440
|
+
};
|
|
1441
|
+
checkSources();
|
|
1442
|
+
return await Caching.mineRenderDataCache.get(scope ? `${scope}
|
|
1443
|
+
${key}` : key, async () => {
|
|
1444
|
+
const value = await loader();
|
|
1445
|
+
checkSources();
|
|
1446
|
+
return value;
|
|
1447
|
+
});
|
|
1448
|
+
}
|
|
1449
|
+
static validDataset(dataset, data) {
|
|
1450
|
+
if (!object(data)) return false;
|
|
1451
|
+
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)));
|
|
1452
|
+
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")));
|
|
1453
|
+
if (dataset === "legacyBlocks") return object(data.blocks) && Object.values(data.blocks).every((value) => typeof value === "string");
|
|
1454
|
+
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")));
|
|
1455
|
+
return Object.values(data).every((value) => {
|
|
1456
|
+
if (!object(value)) return false;
|
|
1457
|
+
switch (dataset) {
|
|
1458
|
+
case "itemDefaults":
|
|
1459
|
+
return true;
|
|
1460
|
+
case "blockStates":
|
|
1461
|
+
return Object.values(value).every((property) => object(property) && "default" in property && Array.isArray(property.values));
|
|
1462
|
+
case "blockTints":
|
|
1463
|
+
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));
|
|
1464
|
+
case "fluids":
|
|
1465
|
+
return ["water", "lava"].includes(value.kind) && typeof value.renderModel === "boolean" && (value.levelProperty === void 0 || typeof value.levelProperty === "string");
|
|
1466
|
+
case "dyes":
|
|
1467
|
+
return [value.id, value.textureDiffuseColor, value.fireworkColor, value.textColor].every(Number.isInteger);
|
|
1468
|
+
default:
|
|
1469
|
+
return false;
|
|
1470
|
+
}
|
|
1471
|
+
});
|
|
1472
|
+
}
|
|
1473
|
+
};
|
|
1474
|
+
}
|
|
1475
|
+
});
|
|
1476
|
+
|
|
1477
|
+
// src/assets/Renames.ts
|
|
1478
|
+
var Renames;
|
|
1479
|
+
var init_Renames = __esm({
|
|
1480
|
+
"src/assets/Renames.ts"() {
|
|
1481
|
+
init_AssetKey();
|
|
1482
|
+
init_MineRenderData();
|
|
1483
|
+
Renames = class {
|
|
1484
|
+
/** Loads the selected version's rename table, or `undefined` when its published data has none. */
|
|
1485
|
+
static async get(root) {
|
|
1486
|
+
const resolved = await MineRenderData.resolve(root);
|
|
1487
|
+
return resolved.manifest.datasets.renames ? MineRenderData.get("renames", root) : void 0;
|
|
1488
|
+
}
|
|
1489
|
+
/**
|
|
1490
|
+
* Returns the ID that replaced `id`, or `undefined` when `id` keeps its meaning.
|
|
1491
|
+
* Without a data version, an ID that the selected version still registers keeps its meaning.
|
|
1492
|
+
*/
|
|
1493
|
+
static async resolve(kind, id, dataVersion, root) {
|
|
1494
|
+
const data = await this.get(root);
|
|
1495
|
+
if (!data || !Object.prototype.hasOwnProperty.call(data[kind], id)) return void 0;
|
|
1496
|
+
const entry = data[kind][id];
|
|
1497
|
+
if (dataVersion === void 0) return entry.registered ? void 0 : entry.ranges[entry.ranges.length - 1].to;
|
|
1498
|
+
if (dataVersion < data.baseDataVersion) return void 0;
|
|
1499
|
+
return entry.ranges.find((range) => dataVersion < range.before)?.to;
|
|
1500
|
+
}
|
|
1501
|
+
/** Returns a copy of `key` for the replacement ID, or `undefined` when the ID keeps its meaning. */
|
|
1502
|
+
static async key(kind, key) {
|
|
1503
|
+
const renamed = await this.resolve(kind, `${key.namespace}:${key.path}`, void 0, key.root);
|
|
1504
|
+
if (!renamed) return void 0;
|
|
1505
|
+
const [namespace, path] = renamed.split(":");
|
|
1506
|
+
return new AssetKey(namespace, path, key.assetType, key.type, key.rootType, key.extension, key.root);
|
|
1507
|
+
}
|
|
1508
|
+
};
|
|
1509
|
+
}
|
|
1510
|
+
});
|
|
1511
|
+
|
|
1512
|
+
// src/model/Model.ts
|
|
1513
|
+
function isDoubleArray(obj) {
|
|
1514
|
+
return Array.isArray(obj) && obj.length === 2;
|
|
1515
|
+
}
|
|
1516
|
+
function isTripleArray(obj) {
|
|
1517
|
+
return Array.isArray(obj) && obj.length === 3;
|
|
1518
|
+
}
|
|
1519
|
+
function isQuadArray(obj) {
|
|
1520
|
+
return Array.isArray(obj) && obj.length === 4;
|
|
1521
|
+
}
|
|
1522
|
+
var ITEM_GENERATED, BUILTIN_GENERATED, BUILTIN_ENTITY, DEFAULT_ELEMENTS;
|
|
1523
|
+
var init_Model = __esm({
|
|
1524
|
+
"src/model/Model.ts"() {
|
|
1525
|
+
ITEM_GENERATED = "item/generated";
|
|
1526
|
+
BUILTIN_GENERATED = "builtin/generated";
|
|
1527
|
+
BUILTIN_ENTITY = "builtin/entity";
|
|
1528
|
+
DEFAULT_ELEMENTS = [];
|
|
1529
|
+
}
|
|
1530
|
+
});
|
|
1531
|
+
|
|
1532
|
+
// ../../node_modules/ts-deepmerge/dist/index.js
|
|
1533
|
+
var require_dist = __commonJS({
|
|
1534
|
+
"../../node_modules/ts-deepmerge/dist/index.js"(exports) {
|
|
1535
|
+
"use strict";
|
|
1536
|
+
var __assign = exports && exports.__assign || function() {
|
|
1537
|
+
__assign = Object.assign || function(t) {
|
|
1538
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
1539
|
+
s = arguments[i];
|
|
1540
|
+
for (var p11 in s) if (Object.prototype.hasOwnProperty.call(s, p11))
|
|
1541
|
+
t[p11] = s[p11];
|
|
1542
|
+
}
|
|
1543
|
+
return t;
|
|
1544
|
+
};
|
|
1545
|
+
return __assign.apply(this, arguments);
|
|
1546
|
+
};
|
|
1547
|
+
var __read = exports && exports.__read || function(o, n) {
|
|
1548
|
+
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
1549
|
+
if (!m) return o;
|
|
1550
|
+
var i = m.call(o), r, ar = [], e;
|
|
1551
|
+
try {
|
|
1552
|
+
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
1553
|
+
} catch (error) {
|
|
1554
|
+
e = { error };
|
|
1555
|
+
} finally {
|
|
1556
|
+
try {
|
|
1557
|
+
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
1558
|
+
} finally {
|
|
1559
|
+
if (e) throw e.error;
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
return ar;
|
|
1563
|
+
};
|
|
1564
|
+
var __spreadArray = exports && exports.__spreadArray || function(to, from, pack) {
|
|
1565
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
1566
|
+
if (ar || !(i in from)) {
|
|
1567
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
1568
|
+
ar[i] = from[i];
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
1572
|
+
};
|
|
1573
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1574
|
+
var isObject = function(obj) {
|
|
1575
|
+
if (typeof obj === "object" && obj !== null) {
|
|
1576
|
+
if (typeof Object.getPrototypeOf === "function") {
|
|
1577
|
+
var prototype = Object.getPrototypeOf(obj);
|
|
1578
|
+
return prototype === Object.prototype || prototype === null;
|
|
1579
|
+
}
|
|
1580
|
+
return Object.prototype.toString.call(obj) === "[object Object]";
|
|
1428
1581
|
}
|
|
1429
|
-
|
|
1430
|
-
|
|
1582
|
+
return false;
|
|
1583
|
+
};
|
|
1584
|
+
var PROTECTED_KEYS = ["__proto__"];
|
|
1585
|
+
var merge10 = function() {
|
|
1586
|
+
var objects = [];
|
|
1587
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
1588
|
+
objects[_i] = arguments[_i];
|
|
1431
1589
|
}
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1590
|
+
return objects.reduce(function(result, current) {
|
|
1591
|
+
Object.keys(current).forEach(function(key) {
|
|
1592
|
+
if (PROTECTED_KEYS.includes(key)) {
|
|
1593
|
+
return;
|
|
1594
|
+
}
|
|
1595
|
+
if (Array.isArray(result[key]) && Array.isArray(current[key])) {
|
|
1596
|
+
result[key] = merge10.options.mergeArrays ? Array.from(new Set(result[key].concat(current[key]))) : current[key];
|
|
1597
|
+
} else if (isObject(result[key]) && isObject(current[key])) {
|
|
1598
|
+
result[key] = merge10(result[key], current[key]);
|
|
1599
|
+
} else {
|
|
1600
|
+
result[key] = current[key];
|
|
1601
|
+
}
|
|
1602
|
+
});
|
|
1603
|
+
return result;
|
|
1604
|
+
}, {});
|
|
1605
|
+
};
|
|
1606
|
+
var defaultOptions = {
|
|
1607
|
+
mergeArrays: true
|
|
1608
|
+
};
|
|
1609
|
+
merge10.options = defaultOptions;
|
|
1610
|
+
merge10.withOptions = function(options) {
|
|
1611
|
+
var objects = [];
|
|
1612
|
+
for (var _i = 1; _i < arguments.length; _i++) {
|
|
1613
|
+
objects[_i - 1] = arguments[_i];
|
|
1435
1614
|
}
|
|
1615
|
+
merge10.options = __assign({ mergeArrays: true }, options);
|
|
1616
|
+
var result = merge10.apply(void 0, __spreadArray([], __read(objects), false));
|
|
1617
|
+
merge10.options = defaultOptions;
|
|
1618
|
+
return result;
|
|
1436
1619
|
};
|
|
1620
|
+
exports.default = merge10;
|
|
1437
1621
|
}
|
|
1438
1622
|
});
|
|
1439
1623
|
|
|
@@ -1559,147 +1743,6 @@ var init_ModelMerger = __esm({
|
|
|
1559
1743
|
}
|
|
1560
1744
|
});
|
|
1561
1745
|
|
|
1562
|
-
// src/assets/source/parser/AssetParsers.ts
|
|
1563
|
-
var AssetParser;
|
|
1564
|
-
var init_AssetParsers = __esm({
|
|
1565
|
-
"src/assets/source/parser/AssetParsers.ts"() {
|
|
1566
|
-
AssetParser = /* @__PURE__ */ ((AssetParser2) => {
|
|
1567
|
-
AssetParser2["MODEL"] = "model";
|
|
1568
|
-
AssetParser2["BLOCKSTATE"] = "blockstate";
|
|
1569
|
-
AssetParser2["META"] = "meta";
|
|
1570
|
-
AssetParser2["NBT"] = "nbt";
|
|
1571
|
-
AssetParser2["IMAGE"] = "image";
|
|
1572
|
-
AssetParser2["LIST"] = "list";
|
|
1573
|
-
AssetParser2["JSON"] = "json";
|
|
1574
|
-
return AssetParser2;
|
|
1575
|
-
})(AssetParser || {});
|
|
1576
|
-
}
|
|
1577
|
-
});
|
|
1578
|
-
|
|
1579
|
-
// src/assets/MineRenderData.ts
|
|
1580
|
-
var object, release, compare, MineRenderData;
|
|
1581
|
-
var init_MineRenderData = __esm({
|
|
1582
|
-
"src/assets/MineRenderData.ts"() {
|
|
1583
|
-
init_AssetKey();
|
|
1584
|
-
init_AssetLoader();
|
|
1585
|
-
init_AssetParsers();
|
|
1586
|
-
init_Caching();
|
|
1587
|
-
object = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1588
|
-
release = (value) => /^\d+\.\d+(?:\.\d+)?$/.test(value) ? value.split(".").map(Number) : void 0;
|
|
1589
|
-
compare = (left, right) => {
|
|
1590
|
-
for (let i = 0; i < 3; i++) {
|
|
1591
|
-
const difference = (left[i] ?? 0) - (right[i] ?? 0);
|
|
1592
|
-
if (difference) return difference;
|
|
1593
|
-
}
|
|
1594
|
-
return 0;
|
|
1595
|
-
};
|
|
1596
|
-
MineRenderData = class {
|
|
1597
|
-
/** Loads one registry. Missing files and invalid data reject the request. */
|
|
1598
|
-
static async get(dataset, root) {
|
|
1599
|
-
root = root?.replace(/\/+$/, "");
|
|
1600
|
-
const scope = AssetLoader.persistentScope;
|
|
1601
|
-
const defaultRoot = AssetLoader.ROOT;
|
|
1602
|
-
const resolved = await this.resolve(root);
|
|
1603
|
-
const entry = resolved.manifest.datasets[dataset];
|
|
1604
|
-
if (!entry) throw new Error(`MineRender ${dataset} data is unavailable for ${resolved.version}: ${resolved.manifest.unavailable[dataset] ?? "not listed in the manifest"}`);
|
|
1605
|
-
const file = entry.file;
|
|
1606
|
-
const requestRoot = resolved.root === (root ?? defaultRoot).replace(/\/+$/, "") ? root : resolved.root;
|
|
1607
|
-
return await this.cached(`${requestRoot === void 0 ? "sources" : "explicit"}:${resolved.root}/${file}:${entry.sha256}`, scope, defaultRoot, async () => {
|
|
1608
|
-
const data = await this.load(file.slice(0, -5), requestRoot);
|
|
1609
|
-
if (!this.validDataset(dataset, data)) throw new Error(`Invalid MineRender ${dataset} data at ${resolved.root}/minerender-data/${file}`);
|
|
1610
|
-
return data;
|
|
1611
|
-
});
|
|
1612
|
-
}
|
|
1613
|
-
/**
|
|
1614
|
-
* Uses the exact version when published. Otherwise, selects the nearest patch in the same
|
|
1615
|
-
* release line (older on ties), then the latest earlier release or the oldest published release.
|
|
1616
|
-
* Unpublished snapshots reject.
|
|
1617
|
-
*/
|
|
1618
|
-
static async resolve(root) {
|
|
1619
|
-
const requestRoot = root?.replace(/\/+$/, "");
|
|
1620
|
-
const defaultRoot = AssetLoader.ROOT;
|
|
1621
|
-
root = (root ?? defaultRoot).replace(/\/+$/, "");
|
|
1622
|
-
const scope = AssetLoader.persistentScope;
|
|
1623
|
-
return this.cached(`resolve:${requestRoot === void 0 ? "sources" : "explicit"}:${root}`, scope, defaultRoot, async () => {
|
|
1624
|
-
const requestedVersion = root.slice(root.lastIndexOf("/") + 1);
|
|
1625
|
-
let manifest = await this.manifest(root, requestRoot !== void 0 && release(requestedVersion) ? requestedVersion : void 0, requestRoot);
|
|
1626
|
-
if (manifest) return { requestedVersion, version: manifest.minecraftVersion, root, manifest };
|
|
1627
|
-
const requested = release(requestedVersion);
|
|
1628
|
-
if (!requested) {
|
|
1629
|
-
throw new Error(`No MineRender data is published for ${requestedVersion}; automatic fallback requires a release`);
|
|
1630
|
-
}
|
|
1631
|
-
const base = root.slice(0, root.lastIndexOf("/"));
|
|
1632
|
-
const index = await this.cached(`versions:${base}`, scope, defaultRoot, async () => {
|
|
1633
|
-
const value = await this.load("versions", base);
|
|
1634
|
-
if (!object(value) || !Array.isArray(value.versions) || !value.versions.every((entry) => object(entry) && typeof entry.name === "string")) {
|
|
1635
|
-
throw new Error(`Invalid MineRender version index at ${base}/minerender-data/versions.json`);
|
|
1636
|
-
}
|
|
1637
|
-
return value.versions;
|
|
1638
|
-
});
|
|
1639
|
-
const versions = index.map((entry) => ({ name: entry.name, number: release(entry.name) })).filter((entry) => !!entry.number).sort((left, right) => compare(left.number, right.number));
|
|
1640
|
-
const sameLine = versions.filter((entry) => entry.number[0] === requested[0] && entry.number[1] === requested[1]);
|
|
1641
|
-
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];
|
|
1642
|
-
if (!selected) throw new Error(`No compatible MineRender data is published for ${requestedVersion}`);
|
|
1643
|
-
const resolvedRoot = `${base}/${selected.name}`;
|
|
1644
|
-
manifest = await this.manifest(resolvedRoot, selected.name, resolvedRoot);
|
|
1645
|
-
if (!manifest) throw new Error(`Missing MineRender manifest for published version ${selected.name}`);
|
|
1646
|
-
return { requestedVersion, version: selected.name, root: resolvedRoot, manifest };
|
|
1647
|
-
});
|
|
1648
|
-
}
|
|
1649
|
-
static async manifest(root, expectedVersion, requestRoot) {
|
|
1650
|
-
const data = await this.load("manifest", requestRoot);
|
|
1651
|
-
if (data === void 0) return void 0;
|
|
1652
|
-
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) => {
|
|
1653
|
-
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);
|
|
1654
|
-
}) || expectedVersion !== void 0 && data.minecraftVersion !== expectedVersion) {
|
|
1655
|
-
throw new Error(`Invalid MineRender manifest at ${root}/minerender-data/manifest.json`);
|
|
1656
|
-
}
|
|
1657
|
-
return data;
|
|
1658
|
-
}
|
|
1659
|
-
static load(file, root) {
|
|
1660
|
-
const key = new AssetKey("minerender-data", file, void 0, void 0, void 0, ".json", root);
|
|
1661
|
-
key.rootType = void 0;
|
|
1662
|
-
return AssetLoader.get(key, "json" /* JSON */);
|
|
1663
|
-
}
|
|
1664
|
-
static async cached(key, scope, defaultRoot, loader) {
|
|
1665
|
-
const checkSources = () => {
|
|
1666
|
-
if (AssetLoader.persistentScope !== scope || AssetLoader.ROOT !== defaultRoot) throw new Error("Asset sources changed while loading MineRender data; retry the request");
|
|
1667
|
-
};
|
|
1668
|
-
checkSources();
|
|
1669
|
-
return await Caching.mineRenderDataCache.get(scope ? `${scope}
|
|
1670
|
-
${key}` : key, async () => {
|
|
1671
|
-
const value = await loader();
|
|
1672
|
-
checkSources();
|
|
1673
|
-
return value;
|
|
1674
|
-
});
|
|
1675
|
-
}
|
|
1676
|
-
static validDataset(dataset, data) {
|
|
1677
|
-
if (!object(data)) return false;
|
|
1678
|
-
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)));
|
|
1679
|
-
if (dataset === "legacyBlocks") return object(data.blocks) && Object.values(data.blocks).every((value) => typeof value === "string");
|
|
1680
|
-
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")));
|
|
1681
|
-
return Object.values(data).every((value) => {
|
|
1682
|
-
if (!object(value)) return false;
|
|
1683
|
-
switch (dataset) {
|
|
1684
|
-
case "itemDefaults":
|
|
1685
|
-
return true;
|
|
1686
|
-
case "blockStates":
|
|
1687
|
-
return Object.values(value).every((property) => object(property) && "default" in property && Array.isArray(property.values));
|
|
1688
|
-
case "blockTints":
|
|
1689
|
-
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));
|
|
1690
|
-
case "fluids":
|
|
1691
|
-
return ["water", "lava"].includes(value.kind) && typeof value.renderModel === "boolean" && (value.levelProperty === void 0 || typeof value.levelProperty === "string");
|
|
1692
|
-
case "dyes":
|
|
1693
|
-
return [value.id, value.textureDiffuseColor, value.fireworkColor, value.textColor].every(Number.isInteger);
|
|
1694
|
-
default:
|
|
1695
|
-
return false;
|
|
1696
|
-
}
|
|
1697
|
-
});
|
|
1698
|
-
}
|
|
1699
|
-
};
|
|
1700
|
-
}
|
|
1701
|
-
});
|
|
1702
|
-
|
|
1703
1746
|
// src/assets/BannerPatterns.ts
|
|
1704
1747
|
var DYE_COLORS, BannerPatterns;
|
|
1705
1748
|
var init_BannerPatterns = __esm({
|
|
@@ -2022,6 +2065,7 @@ var init_ItemTints = __esm({
|
|
|
2022
2065
|
var Models;
|
|
2023
2066
|
var init_Models = __esm({
|
|
2024
2067
|
"src/assets/Models.ts"() {
|
|
2068
|
+
init_Renames();
|
|
2025
2069
|
init_Caching();
|
|
2026
2070
|
init_ModelMerger();
|
|
2027
2071
|
init_AssetLoader();
|
|
@@ -2351,6 +2395,11 @@ var init_Models = __esm({
|
|
|
2351
2395
|
key.extension = ".json";
|
|
2352
2396
|
}
|
|
2353
2397
|
const preview = this.snapshotContext(key, context);
|
|
2398
|
+
if (key.type === "item") {
|
|
2399
|
+
const renamed = await Renames.key("items", key);
|
|
2400
|
+
const replacement = renamed && await this.getMerged(renamed, preview);
|
|
2401
|
+
if (replacement) return replacement;
|
|
2402
|
+
}
|
|
2354
2403
|
const dataKey = await this.applyDefaults(key, preview);
|
|
2355
2404
|
const keyStr = AssetLoader.persistentKey(key.serialize() + (key.type === "item" ? this.contextKey(preview) + dataKey : ""));
|
|
2356
2405
|
const model = await Caching.mergedModelCache.get(keyStr, async () => {
|
|
@@ -2384,6 +2433,7 @@ var init_BlockStates = __esm({
|
|
|
2384
2433
|
init_AssetKey();
|
|
2385
2434
|
init_PersistentCache();
|
|
2386
2435
|
init_AssetParsers();
|
|
2436
|
+
init_Renames();
|
|
2387
2437
|
init_MineRenderData();
|
|
2388
2438
|
BlockStates = class {
|
|
2389
2439
|
static get PERSISTENT_CACHE() {
|
|
@@ -2424,6 +2474,9 @@ var init_BlockStates = __esm({
|
|
|
2424
2474
|
if (!key.extension) {
|
|
2425
2475
|
key.extension = ".json";
|
|
2426
2476
|
}
|
|
2477
|
+
const renamed = await Renames.key("blocks", key);
|
|
2478
|
+
const replacement = renamed && await this.get(renamed);
|
|
2479
|
+
if (replacement) return replacement;
|
|
2427
2480
|
const keyStr = key.serialize();
|
|
2428
2481
|
return Caching.blockStateCache.get(keyStr, (k) => {
|
|
2429
2482
|
return this.PERSISTENT_CACHE.getOrLoad(AssetLoader.persistentKey(keyStr), (k1) => {
|
|
@@ -7587,6 +7640,7 @@ __export(index_browser_exports, {
|
|
|
7587
7640
|
PackMetadata: () => PackMetadata,
|
|
7588
7641
|
PersistentCache: () => PersistentCache,
|
|
7589
7642
|
PlayerHeadTextures: () => PlayerHeadTextures,
|
|
7643
|
+
Renames: () => Renames,
|
|
7590
7644
|
Renderer: () => Renderer,
|
|
7591
7645
|
RequestError: () => RequestError,
|
|
7592
7646
|
Requests: () => Requests,
|
|
@@ -8196,6 +8250,7 @@ var Fonts = class {
|
|
|
8196
8250
|
init_MineRenderData();
|
|
8197
8251
|
init_ModelTextures();
|
|
8198
8252
|
init_Models();
|
|
8253
|
+
init_Renames();
|
|
8199
8254
|
init_source();
|
|
8200
8255
|
|
|
8201
8256
|
// src/cache/index.ts
|
|
@@ -17461,18 +17516,23 @@ var SectionModels = class {
|
|
|
17461
17516
|
return this.template(variant, choice, tints);
|
|
17462
17517
|
}));
|
|
17463
17518
|
}
|
|
17464
|
-
/** Resolves
|
|
17465
|
-
async prepareState(blockState3, properties = {}, biomeColor) {
|
|
17519
|
+
/** Resolves shared templates; dynamic biome tints are supplied on section placements instead of cached geometry. */
|
|
17520
|
+
async prepareState(blockState3, properties = {}, biomeColor, dynamicBiomeTint = false) {
|
|
17466
17521
|
const prepared = await this.state(blockState3, properties);
|
|
17467
17522
|
if (!prepared) return void 0;
|
|
17468
17523
|
let color2;
|
|
17469
17524
|
const resolveColor = typeof biomeColor === "function" ? () => color2 ?? (color2 = biomeColor()) : biomeColor;
|
|
17470
17525
|
const templates = /* @__PURE__ */ new Map();
|
|
17526
|
+
const source = dynamicBiomeTint && await BlockTints.biomeSource(blockState3.key);
|
|
17471
17527
|
await Promise.all([...prepared.choices].map(async ([variant, choice]) => {
|
|
17472
17528
|
const tints = await BlockTints.get(blockState3.key, prepared.state, choice.model, void 0, resolveColor);
|
|
17473
|
-
|
|
17529
|
+
const biomeTints = source ? await BlockTints.get(blockState3.key, prepared.state, choice.model, void 0, 16777215) : void 0;
|
|
17530
|
+
templates.set(variant, await this.template(variant, choice, tints, biomeTints));
|
|
17474
17531
|
}));
|
|
17475
|
-
return {
|
|
17532
|
+
return {
|
|
17533
|
+
pick: (position) => prepared.groups.map((group) => templates.get(BlockStateResolver.choose(group, position))),
|
|
17534
|
+
biomeTint: source ? [...templates.values()].some((template) => template.biomeTint?.some((value) => value === 1)) || void 0 : void 0
|
|
17535
|
+
};
|
|
17476
17536
|
}
|
|
17477
17537
|
async state(blockState3, properties) {
|
|
17478
17538
|
let states = this.states.get(blockState3);
|
|
@@ -17515,10 +17575,10 @@ var SectionModels = class {
|
|
|
17515
17575
|
}
|
|
17516
17576
|
return { state, groups, choices: prepared };
|
|
17517
17577
|
}
|
|
17518
|
-
async template(variant, { model, atlas, rotation }, tints) {
|
|
17578
|
+
async template(variant, { model, atlas, rotation }, tints, biomeTints) {
|
|
17519
17579
|
let models = this.models.get(model);
|
|
17520
17580
|
if (!models) this.models.set(model, models = /* @__PURE__ */ new Map());
|
|
17521
|
-
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints]);
|
|
17581
|
+
const key = JSON.stringify([rotation.x, rotation.y, rotation.z, variant.uvlock, tints, biomeTints]);
|
|
17522
17582
|
return cached(models, key, async () => {
|
|
17523
17583
|
const object4 = new SectionModel(model, {
|
|
17524
17584
|
instanceMeshes: false,
|
|
@@ -17532,11 +17592,13 @@ var SectionModels = class {
|
|
|
17532
17592
|
object4.clear();
|
|
17533
17593
|
const keep = [];
|
|
17534
17594
|
const cullFaces = [];
|
|
17595
|
+
const biomeTint = biomeTints ? [] : void 0;
|
|
17535
17596
|
for (const element of atlas.model.elements) {
|
|
17536
17597
|
for (const face of CUBE_FACES) {
|
|
17537
17598
|
const data = element.faces[face];
|
|
17538
17599
|
keep.push(data !== void 0);
|
|
17539
17600
|
if (!data) continue;
|
|
17601
|
+
biomeTint?.push(Number(data.tintindex !== void 0 && biomeTints?.[data.tintindex] !== void 0));
|
|
17540
17602
|
const local = CUBE_FACES.findIndex((name) => name === data.cullface);
|
|
17541
17603
|
if (local < 0) {
|
|
17542
17604
|
cullFaces.push(0);
|
|
@@ -17552,6 +17614,7 @@ var SectionModels = class {
|
|
|
17552
17614
|
geometry,
|
|
17553
17615
|
atlas,
|
|
17554
17616
|
cullFaces: new Uint8Array(cullFaces),
|
|
17617
|
+
biomeTint: biomeTint?.some(Boolean) ? new Uint8Array(biomeTint) : void 0,
|
|
17555
17618
|
occludes: ModelCulling.isOpaqueFullCube(atlas) && ModelCulling.toLocalMask(63, rotation) === 63,
|
|
17556
17619
|
layer: atlas.hasTranslucency ? 1 : 0,
|
|
17557
17620
|
ambientOcclusion: model.ambientocclusion !== false
|
|
@@ -17571,6 +17634,7 @@ var SectionModels = class {
|
|
|
17571
17634
|
};
|
|
17572
17635
|
|
|
17573
17636
|
// src/world/_entities/WorldEntities.ts
|
|
17637
|
+
init_Renames();
|
|
17574
17638
|
init_Entities();
|
|
17575
17639
|
init_EntityObject();
|
|
17576
17640
|
|
|
@@ -17730,8 +17794,10 @@ var WorldEntities = class {
|
|
|
17730
17794
|
try {
|
|
17731
17795
|
if (!current()) return;
|
|
17732
17796
|
const models = await Entities.getEntityList();
|
|
17733
|
-
|
|
17734
|
-
const
|
|
17797
|
+
const renamed = models.includes(placement.key.path) ? void 0 : await Renames.key("entities", placement.key);
|
|
17798
|
+
const key = renamed && models.includes(renamed.path) ? renamed : placement.key;
|
|
17799
|
+
if (!models.includes(key.path) || !current()) return;
|
|
17800
|
+
const model = await Entities.getEntity(key, placement.texture, { when: placement.when });
|
|
17735
17801
|
if (!model || !current()) return;
|
|
17736
17802
|
let tints = placement.tints;
|
|
17737
17803
|
if (placement.woolDye) {
|
|
@@ -17746,7 +17812,7 @@ var WorldEntities = class {
|
|
|
17746
17812
|
object4.rotation.y = placement.yaw;
|
|
17747
17813
|
await object4.init();
|
|
17748
17814
|
if (!current()) return;
|
|
17749
|
-
placement.owner.set(object4, { type:
|
|
17815
|
+
placement.owner.set(object4, { type: key.toNamespacedString(), position: placement.position });
|
|
17750
17816
|
this.scene.add(object4);
|
|
17751
17817
|
} catch (error) {
|
|
17752
17818
|
console.warn(`Could not draw saved entity ${placement.key.toNamespacedString()}, skipping it`, error);
|
|
@@ -17778,6 +17844,132 @@ var WorldEntities = class {
|
|
|
17778
17844
|
// src/world/MineRenderWorld.ts
|
|
17779
17845
|
init_FluidQuads();
|
|
17780
17846
|
|
|
17847
|
+
// src/world/_biomes/BiomeBlend.ts
|
|
17848
|
+
init_AssetKey();
|
|
17849
|
+
init_AssetLoader();
|
|
17850
|
+
init_Caching();
|
|
17851
|
+
var MAX_TILES = 256;
|
|
17852
|
+
function validateBiomeBlendRadius(radius) {
|
|
17853
|
+
if (!Number.isInteger(radius) || radius < 0 || radius > 7) {
|
|
17854
|
+
throw new RangeError("biomeBlendRadius must be an integer from 0 to 7");
|
|
17855
|
+
}
|
|
17856
|
+
}
|
|
17857
|
+
var BiomeBlend = class {
|
|
17858
|
+
constructor(biomeAt, radius) {
|
|
17859
|
+
this.biomeAt = biomeAt;
|
|
17860
|
+
this.tiles = /* @__PURE__ */ new Map();
|
|
17861
|
+
this.generation = Caching.generation;
|
|
17862
|
+
validateBiomeBlendRadius(radius);
|
|
17863
|
+
this._radius = radius;
|
|
17864
|
+
}
|
|
17865
|
+
get radius() {
|
|
17866
|
+
return this._radius;
|
|
17867
|
+
}
|
|
17868
|
+
clear() {
|
|
17869
|
+
this.tiles.clear();
|
|
17870
|
+
}
|
|
17871
|
+
invalidateColumn(x, z) {
|
|
17872
|
+
for (const [key, tile] of this.tiles) {
|
|
17873
|
+
if (Math.abs(tile.x - x) <= 1 && Math.abs(tile.z - z) <= 1) this.tiles.delete(key);
|
|
17874
|
+
}
|
|
17875
|
+
}
|
|
17876
|
+
async getColor(kind, origin, position) {
|
|
17877
|
+
const [x, y, z] = position;
|
|
17878
|
+
const biome = this.biomeAt(x, y, z);
|
|
17879
|
+
if (biome === void 0) return void 0;
|
|
17880
|
+
if (this.generation !== Caching.generation) {
|
|
17881
|
+
this.clear();
|
|
17882
|
+
this.generation = Caching.generation;
|
|
17883
|
+
}
|
|
17884
|
+
const tileX = Math.floor(x / 16), tileZ = Math.floor(z / 16), quartY = Math.floor(y / 4);
|
|
17885
|
+
const root = origin?.root ?? AssetLoader.ROOT;
|
|
17886
|
+
const key = JSON.stringify([root, AssetLoader.persistentScope, kind, this._radius, tileX, quartY, tileZ]);
|
|
17887
|
+
let tile = this.tiles.get(key);
|
|
17888
|
+
if (tile) {
|
|
17889
|
+
this.tiles.delete(key);
|
|
17890
|
+
this.tiles.set(key, tile);
|
|
17891
|
+
} else {
|
|
17892
|
+
const snapshot = new AssetKey("minecraft", "", void 0, void 0, "assets", ".json", root);
|
|
17893
|
+
const colors = this.buildTile(kind, snapshot, tileX, quartY, tileZ, this._radius).then((result2) => {
|
|
17894
|
+
if (!result2.complete && this.tiles.get(key)?.colors === colors) this.tiles.delete(key);
|
|
17895
|
+
return result2;
|
|
17896
|
+
}, (error) => {
|
|
17897
|
+
if (this.tiles.get(key)?.colors === colors) this.tiles.delete(key);
|
|
17898
|
+
throw error;
|
|
17899
|
+
});
|
|
17900
|
+
tile = { x: tileX, z: tileZ, colors };
|
|
17901
|
+
this.tiles.set(key, tile);
|
|
17902
|
+
if (this.tiles.size > MAX_TILES) this.tiles.delete(this.tiles.keys().next().value);
|
|
17903
|
+
}
|
|
17904
|
+
const result = await tile.colors;
|
|
17905
|
+
if (result.failures.has(biome)) throw result.failures.get(biome);
|
|
17906
|
+
const color2 = result.colors[(z - tileZ * 16) * 16 + x - tileX * 16];
|
|
17907
|
+
return color2 === -1 ? void 0 : color2;
|
|
17908
|
+
}
|
|
17909
|
+
async buildTile(kind, origin, tileX, quartY, tileZ, radius) {
|
|
17910
|
+
const size = 16 + radius * 2, stride = size + 1;
|
|
17911
|
+
const startX = tileX * 16 - radius, startZ = tileZ * 16 - radius;
|
|
17912
|
+
const quartX = Math.floor(startX / 4), quartZ = Math.floor(startZ / 4);
|
|
17913
|
+
const quartSize = Math.floor((startX + size - 1) / 4) - quartX + 1;
|
|
17914
|
+
const ids = new Array(quartSize * quartSize);
|
|
17915
|
+
const biomes = /* @__PURE__ */ new Map();
|
|
17916
|
+
for (let z = 0; z < quartSize; z++) {
|
|
17917
|
+
for (let x = 0; x < quartSize; x++) {
|
|
17918
|
+
const id = this.biomeAt((quartX + x) * 4, quartY * 4, (quartZ + z) * 4);
|
|
17919
|
+
ids[z * quartSize + x] = id;
|
|
17920
|
+
if (id !== void 0 && !biomes.has(id)) {
|
|
17921
|
+
biomes.set(id, (async () => {
|
|
17922
|
+
const swamp = kind === "grass" && (await Biomes.get(id, origin))?.effects.grass_color_modifier === "swamp";
|
|
17923
|
+
return { color: swamp ? void 0 : await Biomes.getColor(id, kind, origin), swamp };
|
|
17924
|
+
})());
|
|
17925
|
+
}
|
|
17926
|
+
}
|
|
17927
|
+
}
|
|
17928
|
+
const failures = /* @__PURE__ */ new Map();
|
|
17929
|
+
const loaded = new Map(await Promise.all([...biomes].map(async ([id, color2]) => {
|
|
17930
|
+
try {
|
|
17931
|
+
return [id, await color2];
|
|
17932
|
+
} catch (error) {
|
|
17933
|
+
failures.set(id, error);
|
|
17934
|
+
return [id, { color: void 0, swamp: false }];
|
|
17935
|
+
}
|
|
17936
|
+
})));
|
|
17937
|
+
const complete = [...loaded.values()].every((value) => value.swamp || value.color !== void 0);
|
|
17938
|
+
const samples = new Int32Array(size * size).fill(-1);
|
|
17939
|
+
const sums = Array.from({ length: 4 }, () => new Uint32Array(stride * stride));
|
|
17940
|
+
for (let z = 0; z < size; z++) {
|
|
17941
|
+
const row = [0, 0, 0, 0];
|
|
17942
|
+
for (let x = 0; x < size; x++) {
|
|
17943
|
+
const id = ids[(Math.floor((startZ + z) / 4) - quartZ) * quartSize + Math.floor((startX + x) / 4) - quartX];
|
|
17944
|
+
const value = id === void 0 ? void 0 : loaded.get(id);
|
|
17945
|
+
const color2 = value?.swamp ? swampGrassColor(startX + x, startZ + z) : value?.color;
|
|
17946
|
+
if (color2 !== void 0) {
|
|
17947
|
+
samples[z * size + x] = color2;
|
|
17948
|
+
row[0] += color2 >> 16 & 255;
|
|
17949
|
+
row[1] += color2 >> 8 & 255;
|
|
17950
|
+
row[2] += color2 & 255;
|
|
17951
|
+
row[3]++;
|
|
17952
|
+
}
|
|
17953
|
+
for (let channel = 0; channel < 4; channel++) {
|
|
17954
|
+
sums[channel][(z + 1) * stride + x + 1] = sums[channel][z * stride + x + 1] + row[channel];
|
|
17955
|
+
}
|
|
17956
|
+
}
|
|
17957
|
+
}
|
|
17958
|
+
const colors = new Int32Array(256).fill(-1), width = radius * 2 + 1;
|
|
17959
|
+
for (let z = 0; z < 16; z++) {
|
|
17960
|
+
for (let x = 0; x < 16; x++) {
|
|
17961
|
+
if (samples[(z + radius) * size + x + radius] === -1) continue;
|
|
17962
|
+
const topLeft = z * stride + x, topRight = topLeft + width;
|
|
17963
|
+
const bottomLeft = (z + width) * stride + x, bottomRight = bottomLeft + width;
|
|
17964
|
+
const sum = (values) => values[bottomRight] - values[bottomLeft] - values[topRight] + values[topLeft];
|
|
17965
|
+
const count = sum(sums[3]);
|
|
17966
|
+
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);
|
|
17967
|
+
}
|
|
17968
|
+
}
|
|
17969
|
+
return { colors, complete, failures };
|
|
17970
|
+
}
|
|
17971
|
+
};
|
|
17972
|
+
|
|
17781
17973
|
// src/world/WorldLight.ts
|
|
17782
17974
|
function copySectionLight(light) {
|
|
17783
17975
|
const copy = {};
|
|
@@ -17841,12 +18033,17 @@ var MineRenderWorld = class {
|
|
|
17841
18033
|
this.lightValidity = /* @__PURE__ */ new Map();
|
|
17842
18034
|
this.highestSkySection = /* @__PURE__ */ new Map();
|
|
17843
18035
|
this.ambientOcclusionPending = false;
|
|
18036
|
+
this.pendingBiomes = /* @__PURE__ */ new Map();
|
|
17844
18037
|
this.pendingCulling = /* @__PURE__ */ new Map();
|
|
17845
18038
|
if (options.savedLighting && !options.sectionMeshing) throw new TypeError("savedLighting requires sectionMeshing");
|
|
17846
18039
|
if (options.ambientOcclusion && !options.sectionMeshing) throw new TypeError("ambientOcclusion requires sectionMeshing");
|
|
17847
18040
|
if (options.ambientLight !== void 0 && (!Number.isFinite(options.ambientLight) || options.ambientLight < 0 || options.ambientLight > 1)) {
|
|
17848
18041
|
throw new RangeError("ambientLight must be a number from 0 to 1");
|
|
17849
18042
|
}
|
|
18043
|
+
this._biomeBlendRadius = options.biomeBlendRadius ?? 0;
|
|
18044
|
+
validateBiomeBlendRadius(this._biomeBlendRadius);
|
|
18045
|
+
if (this._biomeBlendRadius > 0 && !options.sectionMeshing) throw new TypeError("biomeBlendRadius requires sectionMeshing");
|
|
18046
|
+
if (this._biomeBlendRadius) this.biomeBlend = new BiomeBlend((x, y, z) => this.getBiomeAt(x, y, z), this._biomeBlendRadius);
|
|
17850
18047
|
this.scene = scene;
|
|
17851
18048
|
this.savedLighting = options.savedLighting ?? false;
|
|
17852
18049
|
this.ambientLight = options.ambientLight ?? 0;
|
|
@@ -17887,6 +18084,65 @@ var MineRenderWorld = class {
|
|
|
17887
18084
|
await this.updateCulling([]);
|
|
17888
18085
|
this.ambientOcclusionPending = false;
|
|
17889
18086
|
}
|
|
18087
|
+
/** Horizontal biome-color sampling radius, from 0 to 7 blocks. Zero disables blending. */
|
|
18088
|
+
get biomeBlendRadius() {
|
|
18089
|
+
return this._biomeBlendRadius;
|
|
18090
|
+
}
|
|
18091
|
+
/** Refreshes loaded grass, foliage, and water colors without reloading their source chunks. */
|
|
18092
|
+
async setBiomeBlendRadius(radius) {
|
|
18093
|
+
validateBiomeBlendRadius(radius);
|
|
18094
|
+
if (radius > 0 && !this.sectionModels) throw new TypeError("biomeBlendRadius requires sectionMeshing");
|
|
18095
|
+
if (radius !== this._biomeBlendRadius) {
|
|
18096
|
+
this._biomeBlendRadius = radius;
|
|
18097
|
+
this.biomeBlend?.clear();
|
|
18098
|
+
this.biomeBlend = radius ? new BiomeBlend((x, y, z) => this.getBiomeAt(x, y, z), radius) : void 0;
|
|
18099
|
+
for (const chunk of this._chunks.values()) this.pendingBiomes.set(chunk, void 0);
|
|
18100
|
+
}
|
|
18101
|
+
const failures = await this.refreshBiomes();
|
|
18102
|
+
if (failures.size) throw failures.values().next().value;
|
|
18103
|
+
}
|
|
18104
|
+
markBiomesChanged(x, z, y, includeColumn = true) {
|
|
18105
|
+
const queued = [];
|
|
18106
|
+
if (!this.biomeBlend) return queued;
|
|
18107
|
+
this.biomeBlend.invalidateColumn(x, z);
|
|
18108
|
+
for (const chunk of this._chunks.values()) {
|
|
18109
|
+
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;
|
|
18110
|
+
queued.push(chunk);
|
|
18111
|
+
if (!this.pendingBiomes.has(chunk)) this.pendingBiomes.set(chunk, []);
|
|
18112
|
+
const columns = this.pendingBiomes.get(chunk);
|
|
18113
|
+
if (columns && !columns.some((column) => column.x === x && column.z === z)) columns.push({ x, z });
|
|
18114
|
+
}
|
|
18115
|
+
return queued;
|
|
18116
|
+
}
|
|
18117
|
+
async refreshBiomes() {
|
|
18118
|
+
if (this.biomeRefreshing) await this.biomeRefreshing;
|
|
18119
|
+
if (!this.pendingBiomes.size) return /* @__PURE__ */ new Map();
|
|
18120
|
+
return this.biomeRefreshing ?? (this.biomeRefreshing = Promise.resolve().then(async () => {
|
|
18121
|
+
const failures = /* @__PURE__ */ new Map();
|
|
18122
|
+
const batch = [...this.pendingBiomes.keys()];
|
|
18123
|
+
let sliceStart = performance.now();
|
|
18124
|
+
try {
|
|
18125
|
+
for (const chunk of batch) {
|
|
18126
|
+
const columns = this.pendingBiomes.get(chunk);
|
|
18127
|
+
this.pendingBiomes.delete(chunk);
|
|
18128
|
+
if (this._chunks.get(`${chunk.x}_${chunk.y}_${chunk.z}`) !== chunk) continue;
|
|
18129
|
+
try {
|
|
18130
|
+
await chunk.refreshBiomes(columns);
|
|
18131
|
+
} catch (error) {
|
|
18132
|
+
failures.set(chunk, error);
|
|
18133
|
+
if (this._chunks.get(`${chunk.x}_${chunk.y}_${chunk.z}`) === chunk) this.pendingBiomes.set(chunk, void 0);
|
|
18134
|
+
}
|
|
18135
|
+
if (performance.now() - sliceStart > 8) {
|
|
18136
|
+
await yieldToEventLoop();
|
|
18137
|
+
sliceStart = performance.now();
|
|
18138
|
+
}
|
|
18139
|
+
}
|
|
18140
|
+
return failures;
|
|
18141
|
+
} finally {
|
|
18142
|
+
this.biomeRefreshing = void 0;
|
|
18143
|
+
}
|
|
18144
|
+
}));
|
|
18145
|
+
}
|
|
17890
18146
|
getBlockAt(posOrX, y, z) {
|
|
17891
18147
|
if (typeof posOrX == "number") {
|
|
17892
18148
|
return this.getBlockAt(new import_three45.Vector3(posOrX, y, z));
|
|
@@ -18122,6 +18378,9 @@ var MineRenderWorld = class {
|
|
|
18122
18378
|
}
|
|
18123
18379
|
await placeEntities?.();
|
|
18124
18380
|
} finally {
|
|
18381
|
+
this.markBiomesChanged(chunk.x, chunk.z, void 0, false);
|
|
18382
|
+
const failures = await this.refreshBiomes();
|
|
18383
|
+
if (failures.size) console.warn("Failed to refresh biome colors", failures.values().next().value);
|
|
18125
18384
|
await this.updateCulling([...changes.values()]);
|
|
18126
18385
|
}
|
|
18127
18386
|
}
|
|
@@ -18138,11 +18397,14 @@ var MineRenderWorld = class {
|
|
|
18138
18397
|
try {
|
|
18139
18398
|
await this.clearChunkColumn(x, z);
|
|
18140
18399
|
} finally {
|
|
18400
|
+
const failures = await this.refreshBiomes();
|
|
18401
|
+
if (failures.size) console.warn("Failed to refresh biome colors", failures.values().next().value);
|
|
18141
18402
|
await this.updateCulling([]);
|
|
18142
18403
|
}
|
|
18143
18404
|
}
|
|
18144
18405
|
async clearChunkColumn(x, z) {
|
|
18145
18406
|
this.biomes.delete(`${x}_${z}`);
|
|
18407
|
+
this.markBiomesChanged(x, z, void 0, false);
|
|
18146
18408
|
if (this.savedLighting) {
|
|
18147
18409
|
for (const y of this.light.get(`${x}_${z}`)?.keys() ?? []) this.markSectionChanged({ x, y, z });
|
|
18148
18410
|
}
|
|
@@ -18153,6 +18415,7 @@ var MineRenderWorld = class {
|
|
|
18153
18415
|
for (const [key, section] of previous) {
|
|
18154
18416
|
this._chunks.delete(key);
|
|
18155
18417
|
this.pendingCulling.delete(section);
|
|
18418
|
+
this.pendingBiomes.delete(section);
|
|
18156
18419
|
await section.clear(false);
|
|
18157
18420
|
this.markSectionChanged(section);
|
|
18158
18421
|
}
|
|
@@ -18187,6 +18450,8 @@ var MineRenderWorld = class {
|
|
|
18187
18450
|
const chunks = [...this._chunks.values()];
|
|
18188
18451
|
this._chunks.clear();
|
|
18189
18452
|
this.biomes.clear();
|
|
18453
|
+
this.biomeBlend?.clear();
|
|
18454
|
+
this.pendingBiomes.clear();
|
|
18190
18455
|
this.light.clear();
|
|
18191
18456
|
this.lightValidity.clear();
|
|
18192
18457
|
this.highestSkySection.clear();
|
|
@@ -18305,8 +18570,14 @@ var MineRenderWorld = class {
|
|
|
18305
18570
|
column?.delete(y);
|
|
18306
18571
|
if (!column?.size) this.biomes.delete(columnKey);
|
|
18307
18572
|
}
|
|
18308
|
-
|
|
18309
|
-
|
|
18573
|
+
if (this.biomeBlend) {
|
|
18574
|
+
const queued = this.markBiomesChanged(x, z, y);
|
|
18575
|
+
const failures = await this.refreshBiomes();
|
|
18576
|
+
for (const chunk2 of queued) if (failures.has(chunk2)) throw failures.get(chunk2);
|
|
18577
|
+
} else {
|
|
18578
|
+
await chunk.refreshBiomes();
|
|
18579
|
+
await this._chunks.get(`${x}_${y + 1}_${z}`)?.refreshBiomes();
|
|
18580
|
+
}
|
|
18310
18581
|
},
|
|
18311
18582
|
this.savedLighting ? (x2, y2, z2) => this.lightLevelAt(x2, y2, z2) : void 0,
|
|
18312
18583
|
async () => {
|
|
@@ -18323,7 +18594,8 @@ var MineRenderWorld = class {
|
|
|
18323
18594
|
await this.updateCulling([]);
|
|
18324
18595
|
}
|
|
18325
18596
|
},
|
|
18326
|
-
this.ambientLight
|
|
18597
|
+
this.ambientLight,
|
|
18598
|
+
() => this.biomeBlend
|
|
18327
18599
|
);
|
|
18328
18600
|
this._chunks.set(key, chunk);
|
|
18329
18601
|
}
|
|
@@ -18442,9 +18714,10 @@ var LIGHT_SAMPLE_OFFSETS = [0, 1, -1, 324, -324, 18, -18];
|
|
|
18442
18714
|
|
|
18443
18715
|
// src/world/SectionGeometry.ts
|
|
18444
18716
|
function buildSectionGeometry(input) {
|
|
18445
|
-
const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize, fluids, light, ambientLight, ambientOcclusion } = input;
|
|
18717
|
+
const { count, indices, templates, cullMasks, templateData, atlases, maxAtlasSize, fluids, light, ambientLight, ambientOcclusion, biomeTints } = input;
|
|
18446
18718
|
if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError("Section atlas size must be a positive integer");
|
|
18447
18719
|
if (count > indices.length || count > templates.length || count > cullMasks.length) throw new RangeError("Section entry count exceeds its arrays");
|
|
18720
|
+
if (biomeTints && biomeTints.length !== count * 3) throw new RangeError("Section biome tints must contain RGB for each placement");
|
|
18448
18721
|
if (fluids && fluids.cells.length !== 5832) throw new RangeError("Section fluid cells must cover an 18\xD718\xD718 neighborhood");
|
|
18449
18722
|
if (fluids?.waterTints && fluids.waterTints.length !== 4096 * 3) throw new RangeError("Section water tints must contain RGB for 4096 cells");
|
|
18450
18723
|
if (light && (light.length !== 5832 || light.some((level) => level > 15 && level !== 255))) {
|
|
@@ -18459,7 +18732,7 @@ function buildSectionGeometry(input) {
|
|
|
18459
18732
|
const cornerBrightness2 = ambientOcclusion ? new Float32Array(4) : void 0;
|
|
18460
18733
|
for (const template of templateData) {
|
|
18461
18734
|
const { quads } = template;
|
|
18462
|
-
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) {
|
|
18735
|
+
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) {
|
|
18463
18736
|
throw new RangeError("Section template arrays do not match its quad count");
|
|
18464
18737
|
}
|
|
18465
18738
|
}
|
|
@@ -18569,6 +18842,7 @@ function buildSectionGeometry(input) {
|
|
|
18569
18842
|
blockIndices[indexOffset / 6] = indices[entry];
|
|
18570
18843
|
if (ambientMask) quadAmbientOcclusion(template.positions, template.normals, quad, ambientMask, cornerBrightness2);
|
|
18571
18844
|
const brightness = light ? lightBrightness(light[lightIndex + LIGHT_SAMPLE_OFFSETS[quadLightIndex(template.positions, template.normals, quad)]], ambientLight) : 1;
|
|
18845
|
+
const tint = biomeTints && template.biomeTint?.[quad] && !Number.isNaN(biomeTints[entry * 3]) ? entry * 3 : void 0;
|
|
18572
18846
|
const start = vertex;
|
|
18573
18847
|
for (let corner = 0; corner < 4; corner++, vertex++) {
|
|
18574
18848
|
const source = quad * 4 + corner;
|
|
@@ -18585,9 +18859,9 @@ function buildSectionGeometry(input) {
|
|
|
18585
18859
|
uvBounds[vertex * 4 + 2] = mapU(template.uvBounds[source * 4 + 2]);
|
|
18586
18860
|
uvBounds[vertex * 4 + 3] = mapV(template.uvBounds[source * 4 + 3]);
|
|
18587
18861
|
const factor = brightness * (ambientMask ? cornerBrightness2[corner] : 1);
|
|
18588
|
-
colors[vertex * 3] = template.colors[source * 3] * factor;
|
|
18589
|
-
colors[vertex * 3 + 1] = template.colors[source * 3 + 1] * factor;
|
|
18590
|
-
colors[vertex * 3 + 2] = template.colors[source * 3 + 2] * factor;
|
|
18862
|
+
colors[vertex * 3] = (tint === void 0 ? template.colors[source * 3] : biomeTints[tint]) * factor;
|
|
18863
|
+
colors[vertex * 3 + 1] = (tint === void 0 ? template.colors[source * 3 + 1] : biomeTints[tint + 1]) * factor;
|
|
18864
|
+
colors[vertex * 3 + 2] = (tint === void 0 ? template.colors[source * 3 + 2] : biomeTints[tint + 2]) * factor;
|
|
18591
18865
|
}
|
|
18592
18866
|
for (let corner = 0; corner < 6; corner++) outputIndices[indexOffset++] = start + template.indices[quad * 6 + corner];
|
|
18593
18867
|
}
|
|
@@ -18708,7 +18982,11 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
|
18708
18982
|
...lighting
|
|
18709
18983
|
};
|
|
18710
18984
|
for (let i = 0; i < entries.length; i++) {
|
|
18711
|
-
const { index, template, cullMask } = entries[i];
|
|
18985
|
+
const { index, template, cullMask, biomeTint } = entries[i];
|
|
18986
|
+
if (biomeTint) {
|
|
18987
|
+
input.biomeTints ?? (input.biomeTints = new Float32Array(entries.length * 3).fill(NaN));
|
|
18988
|
+
input.biomeTints.set(biomeTint, i * 3);
|
|
18989
|
+
}
|
|
18712
18990
|
let id = templateIds.get(template);
|
|
18713
18991
|
if (id === void 0) {
|
|
18714
18992
|
let atlas = atlasIds.get(template.atlas);
|
|
@@ -18735,6 +19013,7 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
|
18735
19013
|
colors: new Float32Array(quads * 12),
|
|
18736
19014
|
indices: new Uint16Array(quads * 6),
|
|
18737
19015
|
cullFaces: new Uint8Array(template.cullFaces),
|
|
19016
|
+
biomeTint: template.biomeTint ? new Uint8Array(template.biomeTint) : void 0,
|
|
18738
19017
|
atlas: 0,
|
|
18739
19018
|
layer: template.layer,
|
|
18740
19019
|
ambientOcclusion: template.ambientOcclusion
|
|
@@ -18879,7 +19158,7 @@ init_CubeFace();
|
|
|
18879
19158
|
init_InstanceReference();
|
|
18880
19159
|
var WATER_TINT2 = new import_three47.Color(4159204).toArray();
|
|
18881
19160
|
var Chunk = class _Chunk {
|
|
18882
|
-
constructor(scene, x, y, z, onBlocksChanged, sectionModels, neighborCell, biomeAt, onBiomesChanged, lightAt, onLightCleared, ambientLight = 0) {
|
|
19161
|
+
constructor(scene, x, y, z, onBlocksChanged, sectionModels, neighborCell, biomeAt, onBiomesChanged, lightAt, onLightCleared, ambientLight = 0, biomeBlend) {
|
|
18883
19162
|
this.onBlocksChanged = onBlocksChanged;
|
|
18884
19163
|
this.sectionModels = sectionModels;
|
|
18885
19164
|
this.neighborCell = neighborCell;
|
|
@@ -18888,6 +19167,7 @@ var Chunk = class _Chunk {
|
|
|
18888
19167
|
this.lightAt = lightAt;
|
|
18889
19168
|
this.onLightCleared = onLightCleared;
|
|
18890
19169
|
this.ambientLight = ambientLight;
|
|
19170
|
+
this.biomeBlend = biomeBlend;
|
|
18891
19171
|
this.data = new ChunkData();
|
|
18892
19172
|
this.renderedBlocks = /* @__PURE__ */ new Map();
|
|
18893
19173
|
this.objectBlocks = /* @__PURE__ */ new Set();
|
|
@@ -18897,6 +19177,7 @@ var Chunk = class _Chunk {
|
|
|
18897
19177
|
this.blockPosition = new import_three47.Vector3();
|
|
18898
19178
|
this.meshDirty = false;
|
|
18899
19179
|
this.meshGeneration = 0;
|
|
19180
|
+
this.blockChanges = /* @__PURE__ */ new Set();
|
|
18900
19181
|
this.scene = scene;
|
|
18901
19182
|
this.x = x;
|
|
18902
19183
|
this.y = y;
|
|
@@ -18995,10 +19276,20 @@ var Chunk = class _Chunk {
|
|
|
18995
19276
|
return this.refreshBiomes();
|
|
18996
19277
|
}
|
|
18997
19278
|
/** Rebuilds biome-tinted blocks while preserving hidden blocks and unrelated render objects. */
|
|
18998
|
-
|
|
19279
|
+
refreshBiomes(columns) {
|
|
19280
|
+
return this.changeBlocks(() => this.refreshBiomeBlocks(columns), true);
|
|
19281
|
+
}
|
|
19282
|
+
async refreshBiomeBlocks(columns) {
|
|
19283
|
+
const blend = columns && this.biomeBlend?.();
|
|
19284
|
+
const ownColumn = columns?.some((column) => column.x === this.x && column.z === this.z);
|
|
18999
19285
|
const affected = /* @__PURE__ */ new Map();
|
|
19000
19286
|
const blocks = [];
|
|
19001
19287
|
for (const index of this.renderedBlocks.keys()) {
|
|
19288
|
+
if (blend) {
|
|
19289
|
+
if (this.objectBlocks.has(index) && !ownColumn) continue;
|
|
19290
|
+
const [x, , z] = this.positionAt(index), radius = blend.radius;
|
|
19291
|
+
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;
|
|
19292
|
+
}
|
|
19002
19293
|
const block = this.data.get(index);
|
|
19003
19294
|
const stateKey = ChunkData.paletteKey(block);
|
|
19004
19295
|
let matches = affected.get(stateKey);
|
|
@@ -19013,10 +19304,32 @@ var Chunk = class _Chunk {
|
|
|
19013
19304
|
}
|
|
19014
19305
|
if (await matches) blocks.push({ index, block });
|
|
19015
19306
|
}
|
|
19307
|
+
if (blend) {
|
|
19308
|
+
const groups = /* @__PURE__ */ new Map();
|
|
19309
|
+
for (const entry of blocks) {
|
|
19310
|
+
const key = ChunkData.paletteKey(entry.block);
|
|
19311
|
+
let group = groups.get(key);
|
|
19312
|
+
if (!group) groups.set(key, group = []);
|
|
19313
|
+
group.push(entry);
|
|
19314
|
+
}
|
|
19315
|
+
blocks.length = 0;
|
|
19316
|
+
const biomes = /* @__PURE__ */ new Map();
|
|
19317
|
+
for (const group of groups.values()) {
|
|
19318
|
+
const { contexts, fluid } = await this.resolveState(group[0].block, group.map((entry) => entry.index), biomes);
|
|
19319
|
+
for (const entry of group) {
|
|
19320
|
+
const context = contexts.get(entry.index);
|
|
19321
|
+
const current = this.sectionBlocks.get(entry.index)?.[0]?.biomeTint;
|
|
19322
|
+
const tint = context?.tint;
|
|
19323
|
+
const tintChanged = !!current !== !!tint || tint?.some((value, channel) => value !== current?.[channel]);
|
|
19324
|
+
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])));
|
|
19325
|
+
if (this.objectBlocks.has(entry.index) || tintChanged || waterChanged) blocks.push(entry);
|
|
19326
|
+
}
|
|
19327
|
+
}
|
|
19328
|
+
}
|
|
19016
19329
|
if (!blocks.length) return;
|
|
19017
19330
|
const hidden = new Set(this.hiddenBlocks);
|
|
19018
19331
|
try {
|
|
19019
|
-
await this.
|
|
19332
|
+
await this.replaceBlocks(blocks, true);
|
|
19020
19333
|
} finally {
|
|
19021
19334
|
for (const index of hidden) {
|
|
19022
19335
|
this.hiddenBlocks.add(index);
|
|
@@ -19056,6 +19369,20 @@ var Chunk = class _Chunk {
|
|
|
19056
19369
|
}
|
|
19057
19370
|
/** Places cells in input order and returns changed world positions without refreshing neighbors. */
|
|
19058
19371
|
async placeBlocks(blocks) {
|
|
19372
|
+
return this.changeBlocks(() => this.replaceBlocks(blocks));
|
|
19373
|
+
}
|
|
19374
|
+
changeBlocks(change, refreshBiomes = false) {
|
|
19375
|
+
const before = refreshBiomes ? Promise.allSettled(this.blockChanges) : this.biomeChange;
|
|
19376
|
+
const result = before ? before.catch(() => void 0).then(change) : change();
|
|
19377
|
+
const pending = result.finally(() => {
|
|
19378
|
+
this.blockChanges.delete(pending);
|
|
19379
|
+
if (this.biomeChange === pending) this.biomeChange = void 0;
|
|
19380
|
+
});
|
|
19381
|
+
this.blockChanges.add(pending);
|
|
19382
|
+
if (refreshBiomes) this.biomeChange = pending;
|
|
19383
|
+
return pending;
|
|
19384
|
+
}
|
|
19385
|
+
async replaceBlocks(blocks, preserveDataOnFailure = false) {
|
|
19059
19386
|
const placements = blocks.map(({ index, block }) => ({
|
|
19060
19387
|
index,
|
|
19061
19388
|
key: block && !ChunkData.isAir(block) ? ChunkData.paletteKey(block) : void 0,
|
|
@@ -19075,6 +19402,10 @@ var Chunk = class _Chunk {
|
|
|
19075
19402
|
const biomes = /* @__PURE__ */ new Map();
|
|
19076
19403
|
const settled = await Promise.allSettled([...states.values()].map(({ stored, indices }) => this.resolveState(stored, indices, biomes)));
|
|
19077
19404
|
const resolutions = new Map([...states.keys()].map((key, i) => [key, settled[i]]));
|
|
19405
|
+
if (preserveDataOnFailure) {
|
|
19406
|
+
const rejected = settled.find((result) => result.status === "rejected");
|
|
19407
|
+
if (rejected?.status === "rejected") throw rejected.reason;
|
|
19408
|
+
}
|
|
19078
19409
|
const positions = [];
|
|
19079
19410
|
let failed = false, failure;
|
|
19080
19411
|
for (const { index, key, block } of placements) {
|
|
@@ -19112,7 +19443,7 @@ var Chunk = class _Chunk {
|
|
|
19112
19443
|
}
|
|
19113
19444
|
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : (context?.prepared ?? prepared)?.pick(variantPosition);
|
|
19114
19445
|
if (templates) {
|
|
19115
|
-
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0 })));
|
|
19446
|
+
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0, biomeTint: context?.tint })));
|
|
19116
19447
|
this.meshDirty = true;
|
|
19117
19448
|
} else {
|
|
19118
19449
|
object4 = await this.scene.addBlock(blockState3, {
|
|
@@ -19131,7 +19462,11 @@ var Chunk = class _Chunk {
|
|
|
19131
19462
|
}, object: object4 });
|
|
19132
19463
|
if (object4) this.objectBlocks.add(index);
|
|
19133
19464
|
} catch (error) {
|
|
19134
|
-
|
|
19465
|
+
if (preserveDataOnFailure && readBlock) {
|
|
19466
|
+
this.renderedBlocks.set(index, { get block() {
|
|
19467
|
+
return readBlock();
|
|
19468
|
+
}, object: void 0 });
|
|
19469
|
+
} else this.data.assign(index, 0);
|
|
19135
19470
|
this.fluidCells[index] = 0;
|
|
19136
19471
|
object4?.removeFromScene();
|
|
19137
19472
|
if (!failed) failure = error;
|
|
@@ -19142,7 +19477,10 @@ var Chunk = class _Chunk {
|
|
|
19142
19477
|
return positions;
|
|
19143
19478
|
}
|
|
19144
19479
|
/** Replaces every cell from parsed section data. The caller refreshes neighbor culling. */
|
|
19145
|
-
|
|
19480
|
+
placeSection(data) {
|
|
19481
|
+
return this.changeBlocks(() => this.replaceSection(data));
|
|
19482
|
+
}
|
|
19483
|
+
async replaceSection(data) {
|
|
19146
19484
|
for (const info of this.renderedBlocks.values()) info.object?.removeFromScene();
|
|
19147
19485
|
this.sectionBlocks.clear();
|
|
19148
19486
|
this.fluidCells.fill(0);
|
|
@@ -19189,7 +19527,7 @@ var Chunk = class _Chunk {
|
|
|
19189
19527
|
];
|
|
19190
19528
|
const templates = perBlock ? void 0 : fluid?.renderModel === false ? [] : (context?.prepared ?? prepared)?.pick(variantPosition);
|
|
19191
19529
|
if (templates) {
|
|
19192
|
-
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0 })));
|
|
19530
|
+
this.sectionBlocks.set(index, templates.map((template) => ({ index, template, cullMask: 0, biomeTint: context?.tint })));
|
|
19193
19531
|
this.meshDirty = true;
|
|
19194
19532
|
} else {
|
|
19195
19533
|
perBlocks.push({ index, blockState: blockState3, biome: context?.biome });
|
|
@@ -19273,12 +19611,35 @@ var Chunk = class _Chunk {
|
|
|
19273
19611
|
}
|
|
19274
19612
|
async resolveState(stored, indices, biomes) {
|
|
19275
19613
|
const blockState3 = await BlockStates.get(AssetKey.parse("blockstates", stored.type));
|
|
19276
|
-
|
|
19614
|
+
let perBlock = !blockState3 || !this.sectionModels || !!(blockState3.key && BlockEntities.entry(await BlockEntities.getIndex(blockState3.key.root), blockState3.key.toNamespacedString()));
|
|
19277
19615
|
const fluid = await getBlockFluidState(blockState3?.key, stored.properties);
|
|
19278
19616
|
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;
|
|
19279
19617
|
const source = hasBiomes && blockState3 ? await BlockTints.biomeSource(blockState3.key) : void 0;
|
|
19280
19618
|
const prepare = (color2) => perBlock || fluid?.renderModel === false ? void 0 : this.sectionModels.prepareState(blockState3, stored.properties, color2);
|
|
19281
19619
|
const contexts = /* @__PURE__ */ new Map();
|
|
19620
|
+
const blend = this.biomeBlend?.();
|
|
19621
|
+
if (blend && !perBlock && blockState3 && (source || fluid?.kind === "water")) {
|
|
19622
|
+
const prepared2 = fluid?.renderModel === false ? void 0 : await this.sectionModels.prepareState(blockState3, stored.properties, void 0, true);
|
|
19623
|
+
if (!prepared2 && fluid?.renderModel !== false) {
|
|
19624
|
+
perBlock = true;
|
|
19625
|
+
} else {
|
|
19626
|
+
await Promise.all(indices.map(async (index) => {
|
|
19627
|
+
const position = this.biomePosition(index, blockState3, stored);
|
|
19628
|
+
const biomeColor = (kind) => blend.getColor(kind, blockState3.key, position);
|
|
19629
|
+
const context = {};
|
|
19630
|
+
if (source && prepared2?.biomeTint) {
|
|
19631
|
+
const color2 = await biomeColor(source);
|
|
19632
|
+
if (color2 !== void 0) context.tint = new import_three47.Color(color2).toArray();
|
|
19633
|
+
}
|
|
19634
|
+
if (fluid?.kind === "water") {
|
|
19635
|
+
const color2 = await biomeColor("water");
|
|
19636
|
+
if (color2 !== void 0 && color2 !== 4159204) context.waterTint = new import_three47.Color(color2).toArray();
|
|
19637
|
+
}
|
|
19638
|
+
contexts.set(index, context);
|
|
19639
|
+
}));
|
|
19640
|
+
return { blockState: blockState3, perBlock, fluid, prepared: prepared2, contexts, stored };
|
|
19641
|
+
}
|
|
19642
|
+
}
|
|
19282
19643
|
const groups = /* @__PURE__ */ new Map();
|
|
19283
19644
|
if (blockState3 && (source || fluid?.kind === "water")) {
|
|
19284
19645
|
for (const index of indices) {
|
|
@@ -19326,7 +19687,11 @@ var Chunk = class _Chunk {
|
|
|
19326
19687
|
this.waterTints.set(tint ?? WATER_TINT2, index * 3);
|
|
19327
19688
|
}
|
|
19328
19689
|
/** Changes visibility at world block coordinates while preserving the block's data. */
|
|
19329
|
-
|
|
19690
|
+
setBlockVisibleAt(pos, visible) {
|
|
19691
|
+
const position = pos.clone();
|
|
19692
|
+
return this.changeBlocks(() => this.setBlockVisibility(position, visible));
|
|
19693
|
+
}
|
|
19694
|
+
async setBlockVisibility(pos, visible) {
|
|
19330
19695
|
const index = _Chunk.chunkPosToBlockIndex(this.worldPosToChunkPos(pos));
|
|
19331
19696
|
const block = this.renderedBlocks.get(index);
|
|
19332
19697
|
if (!block || visible === !this.hiddenBlocks.has(index)) {
|
|
@@ -19496,6 +19861,7 @@ var Chunk = class _Chunk {
|
|
|
19496
19861
|
}
|
|
19497
19862
|
/** Removes stored blocks and render objects. Set `notify` to false to skip the owning world's update. */
|
|
19498
19863
|
async clear(notify = true) {
|
|
19864
|
+
if (this.blockChanges.size) await Promise.allSettled(this.blockChanges);
|
|
19499
19865
|
const positions = notify && this.onBlocksChanged ? [...this.renderedBlocks.keys()].map((index) => this.chunkPosToWorldPos(
|
|
19500
19866
|
new import_three47.Vector3(index % 16, Math.floor(index / 256), Math.floor(index / 16) % 16)
|
|
19501
19867
|
)) : [];
|