@nodecraft/rendermite 1.6.0 → 1.6.2
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/{block-entities-xv3bQpEX.js → block-entities-XAynAVUE.js} +1 -1
- package/dist/blocks/block-entities.js +1 -1
- package/dist/blocks/cli.js +1 -1
- package/dist/blocks/entities.js +1 -1
- package/dist/blocks/entity-models.d.ts +45 -2
- package/dist/blocks/entity-models.js +1 -1
- package/dist/blocks/export.d.ts +1 -1
- package/dist/blocks/export.js +1 -1
- package/dist/{classfile-BwaQjnUM.d.ts → classfile-BdLIk4Pe.d.ts} +16 -0
- package/dist/{classfile-CAhS5qNf.js → classfile-Bo7FUrrD.js} +42 -10
- package/dist/{cli-BSKnXBPr.js → cli-Br0mY0LV.js} +6 -2
- package/dist/cli.js +2 -2
- package/dist/entities/classfile.d.ts +1 -1
- package/dist/entities/classfile.js +1 -1
- package/dist/entities/inherit.d.ts +2 -2
- package/dist/entities/inherit.js +1 -1
- package/dist/entities/jar-classes.d.ts +2 -26
- package/dist/entities/jar-classes.js +2 -2
- package/dist/entities/link.d.ts +2 -2
- package/dist/entities/link.js +1 -1
- package/dist/entities/merge.d.ts +16 -1
- package/dist/entities/merge.js +2 -2
- package/dist/entities/model-bytecode.d.ts +1 -1
- package/dist/entities/model-bytecode.js +1 -1
- package/dist/entities/model-code.d.ts +1 -1
- package/dist/entities/model-code.js +1 -1
- package/dist/{entities-DvmA9fEV.js → entities-DubtdM11.js} +6 -2
- package/dist/{entity-fallback-Bth0fa7m.js → entity-fallback-BcJWh91-.js} +1 -1
- package/dist/{entity-models-B6-0r2gR.js → entity-models-D_QGok9j.js} +9 -5
- package/dist/{entity-textures-DFRIVdF1.js → entity-textures-4XXT8jVI.js} +25 -2
- package/dist/{export-D8fLC1wS.d.ts → export-B8tPsf4K.d.ts} +1 -1
- package/dist/{export-DT_drDoI.js → export-CTKEwro6.js} +3 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/{inherit-KkDsM2N_.js → inherit-C5Ifg3SM.js} +4 -4
- package/dist/{jar-classes-Bj15hjdr.js → jar-classes-3GVCJZT7.js} +38 -1
- package/dist/jar-classes-CSRrPAUy.d.ts +41 -0
- package/dist/{link-Bm5oQYh6.js → link-5GmBLTzk.js} +25 -1
- package/dist/merge-DeHruviG.js +109 -0
- package/dist/{model-bytecode-C1nw2G8O.js → model-bytecode-BQKCLPze.js} +151 -39
- package/dist/{model-code-BKMEMXxF.js → model-code-1Vuk-zj_.js} +1 -1
- package/dist/{model-code-B7jSFQqd.d.ts → model-code-BY44ao27.d.ts} +1 -1
- package/dist/{pipeline-BUIMzMzg.js → pipeline-7rvL6vx1.js} +1 -1
- package/dist/render/entity-fallback.js +1 -1
- package/dist/render/pipeline.js +1 -1
- package/dist/resources/entity-textures.d.ts +13 -1
- package/dist/resources/entity-textures.js +2 -2
- package/dist/{run-BQJkqc5f.js → run-BXAqZ5qY.js} +1 -1
- package/dist/run.js +1 -1
- package/dist/workers/render-worker.js +1 -1
- package/package.json +1 -1
- package/dist/entity-models-c2HpfdWV.d.ts +0 -49
- package/dist/merge-3HkeQ_6A.js +0 -46
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
//#region src/entities/jar-classes.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Reading `.class` entries out of a jar.
|
|
4
|
+
*
|
|
5
|
+
* `JarResourceManager` deliberately indexes only `assets/` and `data/` — it is a *resource* manager,
|
|
6
|
+
* and a class file is not a resource. But entity geometry lives in the code (see
|
|
7
|
+
* `docs/entity-geometry.md`), so the extractor needs the other half of the archive, and it needs it
|
|
8
|
+
* by internal class name rather than by resource locator.
|
|
9
|
+
*
|
|
10
|
+
* Same shape as the resource manager for the same reason: index the central directory once, then read
|
|
11
|
+
* entries on demand. A client jar holds around eight thousand classes and the extractor touches a
|
|
12
|
+
* hundred of them.
|
|
13
|
+
*/
|
|
14
|
+
/** What the extractor reads code out of: one jar, or several layered into one view. */
|
|
15
|
+
interface ClassSource {
|
|
16
|
+
list: () => Promise<string[]>;
|
|
17
|
+
read: (internalName: string) => Promise<Uint8Array | null>;
|
|
18
|
+
close: () => Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Several jars as one class view, first match winning — the same layering the resource managers use.
|
|
22
|
+
*
|
|
23
|
+
* A modded export needs this, and needed it before cross-jar linking did: the class source used to be
|
|
24
|
+
* the client jar alone, so a mod's own model classes were never read and its mobs came out as boxes
|
|
25
|
+
* however good the interpreter was. Layered, one view holds both, which is also what lets a mod entity
|
|
26
|
+
* link to a *vanilla* model — `ars_nouveau:summon_horse` is the game's own horse.
|
|
27
|
+
*/
|
|
28
|
+
declare function layeredClasses(sources: readonly ClassSource[]): ClassSource;
|
|
29
|
+
/** Reads class files out of one jar, by internal name. */
|
|
30
|
+
declare class JarClassReader {
|
|
31
|
+
private readonly jarPath;
|
|
32
|
+
private index;
|
|
33
|
+
constructor(jarPath: string);
|
|
34
|
+
private getIndex;
|
|
35
|
+
/** Every class in the jar, for a sweep that does not know what it is looking for. */
|
|
36
|
+
list(): Promise<string[]>;
|
|
37
|
+
read(internalName: string): Promise<Uint8Array | null>;
|
|
38
|
+
close(): Promise<void>;
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
41
|
+
export { JarClassReader as n, layeredClasses as r, ClassSource as t };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as parseClassFile } from "./classfile-
|
|
1
|
+
import { t as parseClassFile } from "./classfile-Bo7FUrrD.js";
|
|
2
2
|
//#region src/entities/link.ts
|
|
3
3
|
/**
|
|
4
4
|
* Which class holds a given entity's geometry.
|
|
@@ -257,6 +257,7 @@ function buildModelNameIndex(internalNames) {
|
|
|
257
257
|
*/
|
|
258
258
|
async function linkEntityModel(entityId, context) {
|
|
259
259
|
const name = entityId.includes(":") ? entityId.slice(entityId.indexOf(":") + 1) : entityId;
|
|
260
|
+
const modded = entityId.includes(":") && !entityId.startsWith("minecraft:");
|
|
260
261
|
const { mappings } = context;
|
|
261
262
|
const accept = async (className, route) => {
|
|
262
263
|
const obfuscated = mappings.obfuscatedOf.get(className);
|
|
@@ -309,8 +310,31 @@ async function linkEntityModel(entityId, context) {
|
|
|
309
310
|
route: "mod-name"
|
|
310
311
|
};
|
|
311
312
|
}
|
|
313
|
+
if (modded) for (const shorter of withTokensDropped(name)) {
|
|
314
|
+
const found = await linkEntityModel(shorter, context);
|
|
315
|
+
if (found) return {
|
|
316
|
+
...found,
|
|
317
|
+
route: "vanilla-name"
|
|
318
|
+
};
|
|
319
|
+
}
|
|
312
320
|
return null;
|
|
313
321
|
}
|
|
322
|
+
/**
|
|
323
|
+
* The name with some of its tokens dropped, longest first: `summon_horse` gives `summon` and `horse`.
|
|
324
|
+
*
|
|
325
|
+
* Capped at five tokens, because the subsets double with each one and no id is longer than that for a
|
|
326
|
+
* reason this would help with.
|
|
327
|
+
*/
|
|
328
|
+
function withTokensDropped(name) {
|
|
329
|
+
const tokens = name.split("_").filter((token) => token.length > 0);
|
|
330
|
+
if (tokens.length < 2 || tokens.length > 5) return [];
|
|
331
|
+
const found = [];
|
|
332
|
+
for (let size = tokens.length - 1; size >= 1; size--) for (let mask = 0; mask < 2 ** tokens.length; mask++) {
|
|
333
|
+
const kept = tokens.filter((token, index) => (mask & 1 << index) !== 0);
|
|
334
|
+
if (kept.length === size) found.push(kept.join("_"));
|
|
335
|
+
}
|
|
336
|
+
return [...new Set(found)];
|
|
337
|
+
}
|
|
314
338
|
/** Reads and caches classes out of an extracted jar directory or a jar reader. */
|
|
315
339
|
function classReader(load) {
|
|
316
340
|
const cache = /* @__PURE__ */ new Map();
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
//#region src/entities/merge.ts
|
|
2
|
+
/** A root the interpreter had to invent, because a `MeshDefinition`'s root part is anonymous. */
|
|
3
|
+
const SYNTHETIC_ROOT = /^part\d+$/;
|
|
4
|
+
/**
|
|
5
|
+
* Where a piece's parts sit: at the top, or inside the one anonymous root a `MeshDefinition` makes.
|
|
6
|
+
* Both merge into the same list of named slots and only one of them is nested.
|
|
7
|
+
*/
|
|
8
|
+
function slotsOf(parts) {
|
|
9
|
+
const [only] = parts;
|
|
10
|
+
if (parts.length === 1 && only.cubes.length === 0 && SYNTHETIC_ROOT.test(only.name)) return {
|
|
11
|
+
container: only,
|
|
12
|
+
slots: only.children
|
|
13
|
+
};
|
|
14
|
+
return {
|
|
15
|
+
container: null,
|
|
16
|
+
slots: parts
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/** True when this piece built its own mesh rather than adding to one it was handed. */
|
|
20
|
+
function buildsOwnMesh(model) {
|
|
21
|
+
return slotsOf(model.parts).container !== null;
|
|
22
|
+
}
|
|
23
|
+
/** `base` plus the parts of `extra` that `base` does not name. */
|
|
24
|
+
function mergeModels(base, extra) {
|
|
25
|
+
const target = slotsOf(base.parts);
|
|
26
|
+
const source = slotsOf(extra.parts);
|
|
27
|
+
const named = new Set(target.slots.map((part) => part.name));
|
|
28
|
+
const missing = source.slots.filter((part) => !named.has(part.name));
|
|
29
|
+
if (missing.length === 0) return base;
|
|
30
|
+
const parts = target.container ? [{
|
|
31
|
+
...target.container,
|
|
32
|
+
children: [...target.container.children, ...missing]
|
|
33
|
+
}] : [...target.slots, ...missing];
|
|
34
|
+
const notes = [.../* @__PURE__ */ new Set([...base.notes, ...extra.notes])].filter((note) => parts.length > 1 || !note.includes("disconnected roots"));
|
|
35
|
+
return {
|
|
36
|
+
textureSize: base.textureSize[0] === 64 && base.textureSize[1] === 32 ? extra.textureSize : base.textureSize,
|
|
37
|
+
parts,
|
|
38
|
+
notes
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Drops boxes that land exactly where one already is.
|
|
43
|
+
*
|
|
44
|
+
* A model builder's branches are all taken, because they turn on a constructor argument this cannot
|
|
45
|
+
* know — a `CubeDeformation`, a baby-variant flag — so a model that builds one shape *or* another
|
|
46
|
+
* emits both. Where the two arms differ, that is the honest answer and the model says so in a note.
|
|
47
|
+
* Where they agree, it is four legs drawn twice in the same place: `HorseModel` builds
|
|
48
|
+
* `left_hind_leg` and `left_hind_baby_leg` at identical coordinates, and two coplanar boxes are
|
|
49
|
+
* exactly what z-fighting is.
|
|
50
|
+
*
|
|
51
|
+
* Keyed on the accumulated pivot and rotation rather than on evaluated corners, since duplicates come
|
|
52
|
+
* from one builder running twice and land on identical frames. The first is kept: a builder states the
|
|
53
|
+
* ordinary shape before the variant.
|
|
54
|
+
*/
|
|
55
|
+
function dedupeCubes(model) {
|
|
56
|
+
const seen = /* @__PURE__ */ new Set();
|
|
57
|
+
const round = (value) => value.toFixed(4);
|
|
58
|
+
const walk = (part, pivot, rotation) => {
|
|
59
|
+
const at = [
|
|
60
|
+
pivot[0] + part.pivot[0],
|
|
61
|
+
pivot[1] + part.pivot[1],
|
|
62
|
+
pivot[2] + part.pivot[2]
|
|
63
|
+
];
|
|
64
|
+
const spun = [
|
|
65
|
+
rotation[0] + part.rotation[0],
|
|
66
|
+
rotation[1] + part.rotation[1],
|
|
67
|
+
rotation[2] + part.rotation[2]
|
|
68
|
+
];
|
|
69
|
+
const cubes = part.cubes.filter((cube) => {
|
|
70
|
+
const key = [
|
|
71
|
+
...at.map(round),
|
|
72
|
+
...spun.map(round),
|
|
73
|
+
...cube.origin.map(round),
|
|
74
|
+
...cube.size.map(round),
|
|
75
|
+
...cube.uv.map(round),
|
|
76
|
+
round(cube.inflate)
|
|
77
|
+
].join("|");
|
|
78
|
+
if (seen.has(key)) return false;
|
|
79
|
+
seen.add(key);
|
|
80
|
+
return true;
|
|
81
|
+
});
|
|
82
|
+
const children = part.children.map((child) => walk(child, at, spun)).filter((child) => child.cubes.length > 0 || child.children.length > 0);
|
|
83
|
+
return {
|
|
84
|
+
...part,
|
|
85
|
+
cubes,
|
|
86
|
+
children
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
return {
|
|
90
|
+
...model,
|
|
91
|
+
parts: model.parts.map((part) => walk(part, [
|
|
92
|
+
0,
|
|
93
|
+
0,
|
|
94
|
+
0
|
|
95
|
+
], [
|
|
96
|
+
0,
|
|
97
|
+
0,
|
|
98
|
+
0
|
|
99
|
+
])).filter((part) => part.cubes.length > 0 || part.children.length > 0)
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function countCubes(part) {
|
|
103
|
+
return part.cubes.length + part.children.reduce((total, child) => total + countCubes(child), 0);
|
|
104
|
+
}
|
|
105
|
+
function cubesOf(model) {
|
|
106
|
+
return model.parts.reduce((total, part) => total + countCubes(part), 0);
|
|
107
|
+
}
|
|
108
|
+
//#endregion
|
|
109
|
+
export { mergeModels as a, dedupeCubes as i, countCubes as n, slotsOf as o, cubesOf as r, buildsOwnMesh as t };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as parseClassFile } from "./classfile-
|
|
2
|
-
import {
|
|
1
|
+
import { t as parseClassFile } from "./classfile-Bo7FUrrD.js";
|
|
2
|
+
import { a as mergeModels, r as cubesOf, t as buildsOwnMesh } from "./merge-DeHruviG.js";
|
|
3
3
|
//#region src/entities/model-bytecode.ts
|
|
4
4
|
/**
|
|
5
5
|
* Abstract interpretation of a boxy-model builder, enough to recover the geometry.
|
|
@@ -43,6 +43,28 @@ const NUMERIC = /* @__PURE__ */ new Set([
|
|
|
43
43
|
"B",
|
|
44
44
|
"C"
|
|
45
45
|
]);
|
|
46
|
+
/**
|
|
47
|
+
* Whether a conditional jump is taken, for the opcodes that compare numbers.
|
|
48
|
+
*
|
|
49
|
+
* The single-operand forms (`ifeq` … `ifle`) test against zero and the two-operand forms (`if_icmpeq`
|
|
50
|
+
* …) against each other, so both reduce to the sign of `left - right`.
|
|
51
|
+
*/
|
|
52
|
+
function compare(op, left, right) {
|
|
53
|
+
const difference = left - right;
|
|
54
|
+
switch (op) {
|
|
55
|
+
case 153:
|
|
56
|
+
case 159: return difference === 0;
|
|
57
|
+
case 154:
|
|
58
|
+
case 160: return difference !== 0;
|
|
59
|
+
case 155:
|
|
60
|
+
case 161: return difference < 0;
|
|
61
|
+
case 156:
|
|
62
|
+
case 162: return difference >= 0;
|
|
63
|
+
case 157:
|
|
64
|
+
case 163: return difference > 0;
|
|
65
|
+
default: return difference <= 0;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
46
68
|
/** Every entity sheet is a power of two, at least 16 in each direction. A texture offset rarely is. */
|
|
47
69
|
function isSheetSize(width, height) {
|
|
48
70
|
const power = (value) => value >= 16 && (value & value - 1) === 0;
|
|
@@ -109,6 +131,18 @@ const TEXTURE_HEIGHT_FIELDS = /* @__PURE__ */ new Set(["texHeight", "textureHeig
|
|
|
109
131
|
const MAX_INLINE_DEPTH = 5;
|
|
110
132
|
/** Frames per method. A model spends a handful; the cap is there so a strange jar cannot spend all day. */
|
|
111
133
|
const INLINE_BUDGET = 96;
|
|
134
|
+
/**
|
|
135
|
+
* Instructions per extraction, shared across every frame.
|
|
136
|
+
*
|
|
137
|
+
* A model builder is a few hundred instructions and the longest vanilla loop runs a few thousand, so
|
|
138
|
+
* this is two orders of magnitude of headroom — and it is what makes following a backward goto safe: a
|
|
139
|
+
* loop whose bound cannot be worked out stops here instead of spinning.
|
|
140
|
+
*/
|
|
141
|
+
const STEP_BUDGET = 2e5;
|
|
142
|
+
/** Elements tracked per array. A table of segment sizes is a handful; nothing here needs thousands. */
|
|
143
|
+
const MAX_ARRAY = 1024;
|
|
144
|
+
/** Times round a loop. Vanilla's longest builds sixteen segments, so this is generous by four times. */
|
|
145
|
+
const MAX_ITERATIONS = 64;
|
|
112
146
|
/** How far up a hierarchy to look for an inherited method, which is as deep as a model hierarchy goes. */
|
|
113
147
|
const MAX_METHOD_HOPS = 6;
|
|
114
148
|
function newShared(resolve) {
|
|
@@ -120,7 +154,10 @@ function newShared(resolve) {
|
|
|
120
154
|
branched: false,
|
|
121
155
|
nextHandle: 1,
|
|
122
156
|
resolve,
|
|
123
|
-
budget: INLINE_BUDGET
|
|
157
|
+
budget: INLINE_BUDGET,
|
|
158
|
+
statics: /* @__PURE__ */ new Map(),
|
|
159
|
+
initialised: /* @__PURE__ */ new Set(),
|
|
160
|
+
steps: STEP_BUDGET
|
|
124
161
|
};
|
|
125
162
|
}
|
|
126
163
|
/** The method a call names, looked up where the JVM would look: the owner, then up its ancestors. */
|
|
@@ -143,6 +180,8 @@ var Interpreter = class Interpreter {
|
|
|
143
180
|
locals = /* @__PURE__ */ new Map();
|
|
144
181
|
shared;
|
|
145
182
|
depth;
|
|
183
|
+
/** How many times each backward jump has been taken, so an undecidable loop cannot spin. */
|
|
184
|
+
loops = /* @__PURE__ */ new Map();
|
|
146
185
|
/** What this frame returned, for the caller that inlined it. */
|
|
147
186
|
returned = null;
|
|
148
187
|
constructor(shared, depth = 0) {
|
|
@@ -171,6 +210,19 @@ var Interpreter = class Interpreter {
|
|
|
171
210
|
}
|
|
172
211
|
}
|
|
173
212
|
/**
|
|
213
|
+
* Runs a class's `<clinit>` once, so its static tables can be read.
|
|
214
|
+
*
|
|
215
|
+
* Only for model code, and only once per class per extraction: the point is a table of numbers, and
|
|
216
|
+
* anything else a static initialiser does is of no interest here.
|
|
217
|
+
*/
|
|
218
|
+
initialise(owner) {
|
|
219
|
+
if (this.shared.initialised.has(owner) || this.depth >= MAX_INLINE_DEPTH) return;
|
|
220
|
+
this.shared.initialised.add(owner);
|
|
221
|
+
const target = findMethod(this.shared.resolve, owner, "<clinit>", "()V");
|
|
222
|
+
if (!target) return;
|
|
223
|
+
new Interpreter(this.shared, this.depth + 1).run(target.code, target.classFile);
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
174
226
|
* Interprets a call into the model's own code, with the arguments it was actually given.
|
|
175
227
|
*
|
|
176
228
|
* This is what makes an inherited part come out right. `CowModel` calls `super(12, 0)` and the legs
|
|
@@ -189,6 +241,10 @@ var Interpreter = class Interpreter {
|
|
|
189
241
|
frame.seed(receiver, descriptor, args);
|
|
190
242
|
return frame.run(target.code, target.classFile) ? frame.returned : null;
|
|
191
243
|
}
|
|
244
|
+
/** Records a note once, since every candidate method would otherwise repeat it. */
|
|
245
|
+
note(message) {
|
|
246
|
+
if (!this.shared.notes.includes(message)) this.shared.notes.push(message);
|
|
247
|
+
}
|
|
192
248
|
pop() {
|
|
193
249
|
return this.stack.pop() ?? { kind: "unknown" };
|
|
194
250
|
}
|
|
@@ -218,6 +274,11 @@ var Interpreter = class Interpreter {
|
|
|
218
274
|
const first = named ? 1 : 0;
|
|
219
275
|
if (target && (named || floats >= 6 && args.length >= 6 && NUMERIC.has(args[0]) && NUMERIC.has(args[5]))) {
|
|
220
276
|
const extra = args.slice(first + 6);
|
|
277
|
+
const floatAt = extra.findIndex((arg) => arg === "F" || arg === "D");
|
|
278
|
+
const deformed = taken.find((value) => value.kind === "object" && value.handle.deformation !== void 0);
|
|
279
|
+
let inflate = target.pendingInflate;
|
|
280
|
+
if (floatAt !== -1) inflate = this.number(taken[first + 6 + floatAt]);
|
|
281
|
+
else if (deformed?.kind === "object") inflate = deformed.handle.deformation ?? 0;
|
|
221
282
|
target.cubes.push({
|
|
222
283
|
origin: [
|
|
223
284
|
numbers[first],
|
|
@@ -230,7 +291,7 @@ var Interpreter = class Interpreter {
|
|
|
230
291
|
numbers[first + 5]
|
|
231
292
|
],
|
|
232
293
|
uv: [...target.pendingUv],
|
|
233
|
-
inflate
|
|
294
|
+
inflate,
|
|
234
295
|
mirror: target.pendingMirror || extra.includes("Z") && this.number(taken[args.length - 1]) === 1
|
|
235
296
|
});
|
|
236
297
|
this.push(returns === "V" ? null : receiver);
|
|
@@ -308,6 +369,12 @@ var Interpreter = class Interpreter {
|
|
|
308
369
|
});
|
|
309
370
|
return;
|
|
310
371
|
}
|
|
372
|
+
const lambda = taken.find((value) => value.kind === "lambda");
|
|
373
|
+
if (lambda?.kind === "lambda" && receiver?.kind === "object" && taken.length === 1) {
|
|
374
|
+
const applied = this.inline(lambda.owner, lambda.name, lambda.descriptor, null, [receiver]);
|
|
375
|
+
this.push(returns === "V" ? null : applied ?? receiver);
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
311
378
|
if (args.length === 1 && args[0] === "Ljava/lang/String;" && returns.startsWith("L") && target) {
|
|
312
379
|
const name_ = taken[0].kind === "string" ? taken[0].value : "";
|
|
313
380
|
const found = target.children.find((child) => child.name === name_);
|
|
@@ -342,6 +409,11 @@ var Interpreter = class Interpreter {
|
|
|
342
409
|
this.push(returns === "V" ? null : receiver);
|
|
343
410
|
return;
|
|
344
411
|
}
|
|
412
|
+
if (args.length === 0 && target && returns === `L${owner};`) {
|
|
413
|
+
target.pendingMirror = true;
|
|
414
|
+
this.push(receiver);
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
345
417
|
if (args.length === 0 && returns.startsWith("L")) {
|
|
346
418
|
if (target) {
|
|
347
419
|
target.isRoot = true;
|
|
@@ -365,6 +437,10 @@ var Interpreter = class Interpreter {
|
|
|
365
437
|
run(code, constants) {
|
|
366
438
|
let at = 0;
|
|
367
439
|
while (at < code.length) {
|
|
440
|
+
if (this.shared.steps-- <= 0) {
|
|
441
|
+
this.note("ran past the step budget, so the shape is only as far as it got");
|
|
442
|
+
return false;
|
|
443
|
+
}
|
|
368
444
|
const op = code[at++];
|
|
369
445
|
switch (true) {
|
|
370
446
|
case op === 1:
|
|
@@ -437,9 +513,10 @@ var Interpreter = class Interpreter {
|
|
|
437
513
|
this.locals.set((op - 59) % 4, this.pop());
|
|
438
514
|
break;
|
|
439
515
|
case ARRAY_LOADS.has(op): {
|
|
440
|
-
this.pop();
|
|
516
|
+
const index = this.pop();
|
|
441
517
|
const array = this.pop();
|
|
442
|
-
|
|
518
|
+
const at = index.kind === "number" ? index.value : 0;
|
|
519
|
+
this.push(array.kind === "array" ? array.items[at] ?? { kind: "unknown" } : { kind: "unknown" });
|
|
443
520
|
break;
|
|
444
521
|
}
|
|
445
522
|
case op >= 120 && op <= 131:
|
|
@@ -459,18 +536,19 @@ var Interpreter = class Interpreter {
|
|
|
459
536
|
});
|
|
460
537
|
break;
|
|
461
538
|
case op === 191: return true;
|
|
462
|
-
case op === 190:
|
|
463
|
-
this.pop();
|
|
464
|
-
this.push({
|
|
539
|
+
case op === 190: {
|
|
540
|
+
const array = this.pop();
|
|
541
|
+
this.push(array.kind === "array" ? {
|
|
465
542
|
kind: "number",
|
|
466
|
-
value:
|
|
467
|
-
});
|
|
543
|
+
value: array.items.length
|
|
544
|
+
} : { kind: "unknown" });
|
|
468
545
|
break;
|
|
546
|
+
}
|
|
469
547
|
case ARRAY_STORES.has(op): {
|
|
470
548
|
const value = this.pop();
|
|
471
|
-
this.pop();
|
|
549
|
+
const index = this.pop();
|
|
472
550
|
const array = this.pop();
|
|
473
|
-
if (array.kind === "array") array.items.
|
|
551
|
+
if (array.kind === "array") array.items[index.kind === "number" ? index.value : array.items.length] = value;
|
|
474
552
|
break;
|
|
475
553
|
}
|
|
476
554
|
case op === 87:
|
|
@@ -525,20 +603,36 @@ var Interpreter = class Interpreter {
|
|
|
525
603
|
});
|
|
526
604
|
break;
|
|
527
605
|
case op >= 133 && op <= 147: break;
|
|
528
|
-
case op === 132:
|
|
606
|
+
case op === 132: {
|
|
607
|
+
const slot = code[at];
|
|
608
|
+
const step = code[at + 1] << 24 >> 24;
|
|
529
609
|
at += 2;
|
|
610
|
+
this.locals.set(slot, {
|
|
611
|
+
kind: "number",
|
|
612
|
+
value: this.number(this.locals.get(slot) ?? { kind: "unknown" }) + step
|
|
613
|
+
});
|
|
530
614
|
break;
|
|
615
|
+
}
|
|
531
616
|
case RETURNS.has(op):
|
|
532
617
|
if (op !== 177) this.returned = this.pop();
|
|
533
618
|
return true;
|
|
534
|
-
case op === 178:
|
|
619
|
+
case op === 178: {
|
|
620
|
+
const index = code[at] << 8 | code[at + 1];
|
|
535
621
|
at += 2;
|
|
536
|
-
|
|
622
|
+
const reference = constants.reference(index);
|
|
623
|
+
const key = reference ? `${reference.owner}.${reference.name}` : "";
|
|
624
|
+
if (reference && !this.shared.statics.has(key)) this.initialise(reference.owner);
|
|
625
|
+
this.push(this.shared.statics.get(key) ?? { kind: "unknown" });
|
|
537
626
|
break;
|
|
538
|
-
|
|
627
|
+
}
|
|
628
|
+
case op === 179: {
|
|
629
|
+
const index = code[at] << 8 | code[at + 1];
|
|
539
630
|
at += 2;
|
|
540
|
-
this.pop();
|
|
631
|
+
const value = this.pop();
|
|
632
|
+
const reference = constants.reference(index);
|
|
633
|
+
if (reference) this.shared.statics.set(`${reference.owner}.${reference.name}`, value);
|
|
541
634
|
break;
|
|
635
|
+
}
|
|
542
636
|
case op === 180: {
|
|
543
637
|
const index = code[at] << 8 | code[at + 1];
|
|
544
638
|
at += 2;
|
|
@@ -574,6 +668,7 @@ var Interpreter = class Interpreter {
|
|
|
574
668
|
const taken = [];
|
|
575
669
|
for (let count = 0; count < args.length; count++) taken.unshift(this.pop());
|
|
576
670
|
const objectRef = this.pop();
|
|
671
|
+
if (objectRef.kind === "object" && args.length > 0 && args.length <= 3 && args.every((arg) => arg === "F")) objectRef.handle.deformation = Math.max(...taken.map((value) => this.number(value)));
|
|
577
672
|
if (objectRef.kind === "object" && args.length >= 2 && args.at(-1) === "I" && args.at(-2) === "I") {
|
|
578
673
|
objectRef.handle.pendingUv = [this.number(taken[args.length - 2]), this.number(taken[args.length - 1])];
|
|
579
674
|
objectRef.handle.uvFromConstructor = true;
|
|
@@ -591,22 +686,18 @@ var Interpreter = class Interpreter {
|
|
|
591
686
|
handle: this.handle()
|
|
592
687
|
});
|
|
593
688
|
break;
|
|
594
|
-
case op === 188:
|
|
595
|
-
at += 1;
|
|
596
|
-
this.pop();
|
|
689
|
+
case op === 188 || op === 189: {
|
|
690
|
+
at += op === 188 ? 1 : 2;
|
|
691
|
+
const length = this.pop();
|
|
692
|
+
const size = length.kind === "number" ? Math.min(Math.max(length.value, 0), MAX_ARRAY) : 0;
|
|
597
693
|
this.push({
|
|
598
694
|
kind: "array",
|
|
599
|
-
items:
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
case op === 189:
|
|
603
|
-
at += 2;
|
|
604
|
-
this.pop();
|
|
605
|
-
this.push({
|
|
606
|
-
kind: "array",
|
|
607
|
-
items: []
|
|
695
|
+
items: Array.from({ length: size }, () => {
|
|
696
|
+
return { kind: "unknown" };
|
|
697
|
+
})
|
|
608
698
|
});
|
|
609
699
|
break;
|
|
700
|
+
}
|
|
610
701
|
case op === 192:
|
|
611
702
|
at += 2;
|
|
612
703
|
break;
|
|
@@ -619,14 +710,28 @@ var Interpreter = class Interpreter {
|
|
|
619
710
|
returns: "V"
|
|
620
711
|
};
|
|
621
712
|
for (let count = 0; count < args.length; count++) this.pop();
|
|
622
|
-
|
|
713
|
+
const target = constants.lambdaTarget(index);
|
|
714
|
+
this.push(returns === "V" ? null : target ? {
|
|
715
|
+
kind: "lambda",
|
|
716
|
+
...target
|
|
717
|
+
} : { kind: "unknown" });
|
|
623
718
|
break;
|
|
624
719
|
}
|
|
625
720
|
case op >= 153 && op <= 166: {
|
|
626
|
-
const
|
|
627
|
-
|
|
721
|
+
const twoOperands = op >= 159;
|
|
722
|
+
const right = this.pop();
|
|
723
|
+
const left = twoOperands ? this.pop() : {
|
|
724
|
+
kind: "number",
|
|
725
|
+
value: 0
|
|
726
|
+
};
|
|
727
|
+
const offset = (code[at] << 8 | code[at + 1]) << 16 >> 16;
|
|
628
728
|
at += 2;
|
|
629
|
-
|
|
729
|
+
const decided = left.kind === "number" && right.kind === "number" && op <= 164 ? compare(op, twoOperands ? left.value : right.value, twoOperands ? right.value : 0) : null;
|
|
730
|
+
if (decided === null) {
|
|
731
|
+
this.shared.branched = true;
|
|
732
|
+
break;
|
|
733
|
+
}
|
|
734
|
+
if (decided) at += offset - 3;
|
|
630
735
|
break;
|
|
631
736
|
}
|
|
632
737
|
case op === 198 || op === 199:
|
|
@@ -636,12 +741,19 @@ var Interpreter = class Interpreter {
|
|
|
636
741
|
break;
|
|
637
742
|
case op === 167: {
|
|
638
743
|
const offset = (code[at] << 8 | code[at + 1]) << 16 >> 16;
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
744
|
+
at += 2;
|
|
745
|
+
if (offset > 0) {
|
|
746
|
+
at += offset - 3;
|
|
747
|
+
break;
|
|
642
748
|
}
|
|
643
|
-
|
|
644
|
-
this.
|
|
749
|
+
const site = at - 3;
|
|
750
|
+
const round = (this.loops.get(site) ?? 0) + 1;
|
|
751
|
+
this.loops.set(site, round);
|
|
752
|
+
if (round > MAX_ITERATIONS) {
|
|
753
|
+
this.note(`a loop at ${site} went round more than ${MAX_ITERATIONS} times, so the shape is only as far as it got`);
|
|
754
|
+
break;
|
|
755
|
+
}
|
|
756
|
+
at += offset - 3;
|
|
645
757
|
break;
|
|
646
758
|
}
|
|
647
759
|
case op >= 148 && op <= 152:
|
|
@@ -11,7 +11,7 @@ import { n as LayeredResourceManager, t as FolderResourceManager } from "./manag
|
|
|
11
11
|
import { n as TextureLoader } from "./loader-C18GpurU.js";
|
|
12
12
|
import { t as encodeAnimatedIcon } from "./encode-LR9wxigC.js";
|
|
13
13
|
import { a as builtinDynamicResources } from "./generators-BCZVlWig.js";
|
|
14
|
-
import { a as spawnEggTints, i as renderEntityFallback, n as dyedVariantTint, r as renderDyedVariantFallback, t as dyeVariantName } from "./entity-fallback-
|
|
14
|
+
import { a as spawnEggTints, i as renderEntityFallback, n as dyedVariantTint, r as renderDyedVariantFallback, t as dyeVariantName } from "./entity-fallback-BcJWh91-.js";
|
|
15
15
|
import { t as legacyTintsForChain } from "./legacy-tints-l-541wgI.js";
|
|
16
16
|
import { n as defaultBlockStateModel, t as blockStateTransform } from "./blockstate-DVb8Mhxc.js";
|
|
17
17
|
import path from "node:path";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as spawnEggTints, i as renderEntityFallback, n as dyedVariantTint, r as renderDyedVariantFallback, t as dyeVariantName } from "../entity-fallback-
|
|
1
|
+
import { a as spawnEggTints, i as renderEntityFallback, n as dyedVariantTint, r as renderDyedVariantFallback, t as dyeVariantName } from "../entity-fallback-BcJWh91-.js";
|
|
2
2
|
export { dyeVariantName, dyedVariantTint, renderDyedVariantFallback, renderEntityFallback, spawnEggTints };
|
package/dist/render/pipeline.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as ItemPipeline } from "../pipeline-
|
|
1
|
+
import { t as ItemPipeline } from "../pipeline-7rvL6vx1.js";
|
|
2
2
|
export { ItemPipeline };
|
|
@@ -12,6 +12,18 @@ declare const HEAD_LAYOUTS: Record<string, string>;
|
|
|
12
12
|
* live at entity/<mob>.png or entity/<mob>/<mob>.png depending on the mod, and a
|
|
13
13
|
* head only ever wants the base skin — never a blink or armour overlay.
|
|
14
14
|
*/
|
|
15
|
+
/**
|
|
16
|
+
* Directories that hold an *overlay* rather than a mob.
|
|
17
|
+
*
|
|
18
|
+
* Matched as whole path segments rather than as tokens, because a token would take `chest_boat` out
|
|
19
|
+
* along with `chest`. A trader llama is the case that needed it: its sheet is filed as
|
|
20
|
+
* `entity/llama/decor/trader_llama`, whose leaf is exactly the mob's name — so a search by name found
|
|
21
|
+
* the carpet it wears, which is a few opaque pixels on a transparent sheet, and the llama underneath
|
|
22
|
+
* came out invisible on most of its faces.
|
|
23
|
+
*/
|
|
24
|
+
declare const OVERLAY_DIRECTORIES: Set<string>;
|
|
25
|
+
/** Whether a texture is filed inside one of those, whatever the file itself is called. */
|
|
26
|
+
declare function isOverlayPath(path: string): boolean;
|
|
15
27
|
declare function findMobTexture(resources: ResourceManager, namespace: string, mob: string): Promise<ResourceLocator | null>;
|
|
16
28
|
/**
|
|
17
29
|
* Find the mod's own chest sheet for a chest-like item by token overlap over
|
|
@@ -48,4 +60,4 @@ declare function findFluidStill(resources: ResourceManager, namespace: string, b
|
|
|
48
60
|
*/
|
|
49
61
|
declare function findFluidSibling(resources: ResourceManager, still: ResourceLocator, suffixes: string[]): Promise<ResourceLocator | null>;
|
|
50
62
|
//#endregion
|
|
51
|
-
export { DYE_COLORS, HEAD_LAYOUTS, coversSheet, findBedTexture, findChestTexture, findEntityTexture, findFluidSibling, findFluidStill, findMobTexture, tokens };
|
|
63
|
+
export { DYE_COLORS, HEAD_LAYOUTS, OVERLAY_DIRECTORIES, coversSheet, findBedTexture, findChestTexture, findEntityTexture, findFluidSibling, findFluidStill, findMobTexture, isOverlayPath, tokens };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export { DYE_COLORS, HEAD_LAYOUTS, coversSheet, findBedTexture, findChestTexture, findEntityTexture, findFluidSibling, findFluidStill, findMobTexture, tokens };
|
|
1
|
+
import { a as findBedTexture, c as findFluidSibling, d as isOverlayPath, f as tokens, i as coversSheet, l as findFluidStill, n as HEAD_LAYOUTS, o as findChestTexture, r as OVERLAY_DIRECTORIES, s as findEntityTexture, t as DYE_COLORS, u as findMobTexture } from "../entity-textures-4XXT8jVI.js";
|
|
2
|
+
export { DYE_COLORS, HEAD_LAYOUTS, OVERLAY_DIRECTORIES, coversSheet, findBedTexture, findChestTexture, findEntityTexture, findFluidSibling, findFluidStill, findMobTexture, isOverlayPath, tokens };
|
|
@@ -6,7 +6,7 @@ import { r as encodeRasterAsset } from "./encode-LR9wxigC.js";
|
|
|
6
6
|
import { t as classifyItemStyle } from "./item-styles-DjYRdyHi.js";
|
|
7
7
|
import { t as minifyStylesheet } from "./minify-bjMRmr5J.js";
|
|
8
8
|
import { t as generateStylesheet } from "./stylesheet-CovT3B2S.js";
|
|
9
|
-
import { t as ItemPipeline } from "./pipeline-
|
|
9
|
+
import { t as ItemPipeline } from "./pipeline-7rvL6vx1.js";
|
|
10
10
|
import { t as describeResources } from "./descriptor-JyXhfd-c.js";
|
|
11
11
|
import { t as WorkerRenderPool } from "./pool-CKGp1qup.js";
|
|
12
12
|
import path from "node:path";
|
package/dist/run.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as ItemPipeline } from "../pipeline-
|
|
1
|
+
import { t as ItemPipeline } from "../pipeline-7rvL6vx1.js";
|
|
2
2
|
import { n as restoreResources } from "../descriptor-JyXhfd-c.js";
|
|
3
3
|
import sharp from "sharp";
|
|
4
4
|
import { parentPort, workerData } from "node:worker_threads";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nodecraft/rendermite",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.2",
|
|
4
4
|
"description": "Minecraft item icon renderer: renders inventory-identical item icons from vanilla and modded assets across game versions, outputting optimized PNG/WebP/AVIF plus CSS and a JSON item library.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|