parsing refactor
This commit is contained in:
151
blockstate.js
151
blockstate.js
@@ -1,3 +1,20 @@
|
||||
export class BlockstateHandler {
|
||||
async process(cache, id, url) {
|
||||
if (id === ':missing') {
|
||||
return new Blockstate({variants: {"": {model: ":missing"}}});
|
||||
}
|
||||
try {
|
||||
const res = await fetch(url + '.json');
|
||||
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
||||
const json = await res.json();
|
||||
return new Blockstate(json);
|
||||
} catch (e) {
|
||||
console.warn(`Blockstate missing/broken (${id}), falling back to :missing`);
|
||||
return new Blockstate({variants: {"": {model: ":missing"}}});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function evaluateWhen(properties, condition) {
|
||||
if (!condition) return true;
|
||||
if (condition.OR) return condition.OR.some(sub => evaluateWhen(properties, sub));
|
||||
@@ -15,43 +32,119 @@ function evaluateWhen(properties, condition) {
|
||||
return true;
|
||||
}
|
||||
|
||||
export function resolveBlock(state, properties) {
|
||||
const parts = [];
|
||||
export class Blockstate {
|
||||
constructor(json) {
|
||||
this.variants = json.variants;
|
||||
this.multipart = json.multipart;
|
||||
|
||||
if (state.variants) {
|
||||
// Handle standard variants
|
||||
const propStr = Object.keys(properties).sort().map(k => `${k}=${properties[k]}`).join(',');
|
||||
let variantDef = state.variants[propStr] || state.variants[""] || state.variants["normal"];
|
||||
// Memoize evaluated property combinations to prevent console spam
|
||||
// and speed up Sweep 2 recalculations.
|
||||
this._resolutionCache = new Map();
|
||||
}
|
||||
|
||||
// --- NEW ERROR REPORTING ---
|
||||
if (!variantDef) {
|
||||
console.warn("Available states:", Object.keys(state.variants));
|
||||
throw new Error(
|
||||
`Failed to resolve block variant.\nRequested: "${propStr}"\nCheck the console for available states.`
|
||||
);
|
||||
}
|
||||
_getVariantsSummary() {
|
||||
const reqs = {};
|
||||
|
||||
if (Array.isArray(variantDef)) variantDef = variantDef[0]; // Take first random model
|
||||
parts.push(variantDef);
|
||||
} else if (state.multipart) {
|
||||
// Handle multipart
|
||||
for (const part of state.multipart) {
|
||||
if (evaluateWhen(properties, part.when)) {
|
||||
let applyDef = part.apply;
|
||||
if (Array.isArray(applyDef)) applyDef = applyDef[0];
|
||||
parts.push(applyDef);
|
||||
for (const key of Object.keys(this.variants)) {
|
||||
if (key === "" || key === "normal") continue;
|
||||
|
||||
const pairs = key.split(',');
|
||||
for (const pair of pairs) {
|
||||
const [prop, val] = pair.split('=');
|
||||
if (prop && val) {
|
||||
if (!reqs[prop]) reqs[prop] = new Set();
|
||||
reqs[prop].add(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (parts.length === 0) {
|
||||
console.warn("Possibly malformed multipart block with no parts.")
|
||||
const summary = {};
|
||||
for (const [k, v] of Object.entries(reqs)) {
|
||||
summary[k] = Array.from(v);
|
||||
}
|
||||
return summary;
|
||||
}
|
||||
|
||||
return parts;
|
||||
}
|
||||
_getMultipartSummary() {
|
||||
const reqs = {};
|
||||
|
||||
export function getVariantHash(part) {
|
||||
// Uniquely identifies a baked geometry variant so we can pool instances
|
||||
return `${part.model}#y=${part.y || 0},x=${part.x || 0},uvlock=${!!part.uvlock}`;
|
||||
const traverse = (cond) => {
|
||||
if (!cond) return;
|
||||
if (cond.OR) cond.OR.forEach(traverse);
|
||||
if (cond.AND) cond.AND.forEach(traverse);
|
||||
|
||||
for (const [key, val] of Object.entries(cond)) {
|
||||
if (key === 'OR' || key === 'AND') continue;
|
||||
if (!reqs[key]) reqs[key] = new Set();
|
||||
reqs[key].add(val);
|
||||
}
|
||||
};
|
||||
|
||||
if (this.multipart) {
|
||||
for (const part of this.multipart) {
|
||||
traverse(part.when);
|
||||
}
|
||||
}
|
||||
|
||||
// Convert sets to arrays for readable console logging
|
||||
const summary = {};
|
||||
for (const [k, v] of Object.entries(reqs)) {
|
||||
summary[k] = Array.from(v);
|
||||
}
|
||||
return summary;
|
||||
}
|
||||
|
||||
resolveParts(properties) {
|
||||
// Create a deterministic cache key from the properties object
|
||||
const cacheKey = Object.keys(properties).sort().map(k => `${k}=${properties[k]}`).join(',');
|
||||
|
||||
if (this._resolutionCache.has(cacheKey)) {
|
||||
return this._resolutionCache.get(cacheKey);
|
||||
}
|
||||
|
||||
const parts = [];
|
||||
|
||||
if (this.variants) {
|
||||
let variantDef = this.variants[cacheKey] || this.variants[""] || this.variants["normal"];
|
||||
|
||||
if (!variantDef) {
|
||||
console.warn(
|
||||
`Failed to resolve block variant: "${cacheKey}". Falling back to :missing.\n` +
|
||||
`Properties evaluated by this blockstate:`,
|
||||
this._getVariantsSummary()
|
||||
);
|
||||
const missingFallback = [{model: ":missing"}];
|
||||
this._resolutionCache.set(cacheKey, missingFallback);
|
||||
return missingFallback;
|
||||
}
|
||||
|
||||
if (Array.isArray(variantDef)) variantDef = variantDef[0];
|
||||
parts.push(variantDef);
|
||||
} else if (this.multipart) {
|
||||
for (const part of this.multipart) {
|
||||
if (evaluateWhen(properties, part.when)) {
|
||||
let applyDef = part.apply;
|
||||
if (Array.isArray(applyDef)) applyDef = applyDef[0];
|
||||
parts.push(applyDef);
|
||||
}
|
||||
}
|
||||
if (parts.length === 0) {
|
||||
console.warn(
|
||||
`Multipart block ("${cacheKey}") resolved to no parts. Falling back to :missing.\n` +
|
||||
`Properties evaluated by this blockstate:`,
|
||||
this._getMultipartSummary()
|
||||
);
|
||||
const missingFallback = [{model: ":missing"}];
|
||||
this._resolutionCache.set(cacheKey, missingFallback);
|
||||
return missingFallback;
|
||||
}
|
||||
}
|
||||
|
||||
this._resolutionCache.set(cacheKey, parts);
|
||||
return parts;
|
||||
}
|
||||
|
||||
static getVariantHash(part) {
|
||||
return `${part.model}#y=${part.y || 0},x=${part.x || 0},uvlock=${!!part.uvlock}`;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user