@nodecraft/rendermite 1.7.4 → 1.8.0

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.
Files changed (72) hide show
  1. package/dist/blocks/cli.js +1 -1
  2. package/dist/blocks/ender-storage.d.ts +1 -1
  3. package/dist/blocks/entities.d.ts +1 -1
  4. package/dist/blocks/entity-models.d.ts +3 -3
  5. package/dist/blocks/entity-models.js +1 -1
  6. package/dist/blocks/export.d.ts +1 -1
  7. package/dist/blocks/export.js +1 -1
  8. package/dist/blocks/geo-blocks.d.ts +1 -1
  9. package/dist/blocks/item-sprites.d.ts +1 -1
  10. package/dist/blocks/light-bytecode.d.ts +174 -0
  11. package/dist/blocks/light-bytecode.js +2 -0
  12. package/dist/blocks/light-eval.d.ts +2 -0
  13. package/dist/blocks/light-eval.js +2 -0
  14. package/dist/blocks/light.d.ts +38 -0
  15. package/dist/blocks/light.js +2 -0
  16. package/dist/blocks/pack-format.d.ts +2 -2
  17. package/dist/blocks/sprite-sheet.d.ts +1 -1
  18. package/dist/{classfile-Bo7FUrrD.js → classfile-CiIDF3vc.js} +23 -1
  19. package/dist/{classfile-BdLIk4Pe.d.ts → classfile-CsLXlh5z.d.ts} +23 -0
  20. package/dist/{cli-DBNuCnry.js → cli-Dw1Q5CPR.js} +5 -4
  21. package/dist/cli.js +5 -5
  22. package/dist/{descriptor-JyXhfd-c.js → descriptor-CmkIkDKL.js} +2 -2
  23. package/dist/entities/classfile.d.ts +1 -1
  24. package/dist/entities/classfile.js +1 -1
  25. package/dist/entities/inherit.d.ts +2 -2
  26. package/dist/entities/inherit.js +1 -1
  27. package/dist/entities/jar-classes.js +1 -1
  28. package/dist/entities/link.d.ts +2 -2
  29. package/dist/entities/link.js +1 -1
  30. package/dist/entities/mappings.d.ts +2 -2
  31. package/dist/entities/mappings.js +2 -2
  32. package/dist/entities/model-bytecode.d.ts +7 -2
  33. package/dist/entities/model-bytecode.js +2 -2
  34. package/dist/entities/model-code.d.ts +1 -1
  35. package/dist/entities/model-code.js +1 -1
  36. package/dist/{entity-models-DUKiHpn1.js → entity-models-BGiy3pey.js} +5 -5
  37. package/dist/{export-B8tPsf4K.d.ts → export-J13L9Lqu.d.ts} +3 -1
  38. package/dist/{export-Ds1EelMa.js → export-t8X_oRws.js} +22 -2
  39. package/dist/index.d.ts +1 -1
  40. package/dist/index.js +6 -6
  41. package/dist/{inherit-BYLi3Q28.js → inherit-BUn0Cve9.js} +2 -2
  42. package/dist/{jar-W2cV2ZVO.js → jar-CBYPMbw7.js} +1 -1
  43. package/dist/{jar-classes-3GVCJZT7.js → jar-classes-CSm7qq1R.js} +2 -2
  44. package/dist/light-CyRhYulM.js +582 -0
  45. package/dist/light-bytecode-Cpyuxo9b.js +1749 -0
  46. package/dist/light-eval-BsJJoSv4.js +23 -0
  47. package/dist/light-eval-D9x0ufZx.d.ts +13 -0
  48. package/dist/{link-5GmBLTzk.js → link-Cuhyrz07.js} +1 -1
  49. package/dist/{mappings-BEPVaiwE.js → mappings-Ep6ko0rg.js} +77 -9
  50. package/dist/{mappings-DxX4KoZl.d.ts → mappings-USwcjtcH.d.ts} +28 -2
  51. package/dist/{model-bytecode-B43uSqet.js → model-bytecode-VaLUQfGO.js} +2 -2
  52. package/dist/{model-code-BY44ao27.d.ts → model-code-Csc3bs3h.d.ts} +1 -1
  53. package/dist/{model-code-1Vuk-zj_.js → model-code-DbN2uVhp.js} +1 -1
  54. package/dist/mods/prepare.js +1 -1
  55. package/dist/mods/tints.js +1 -1
  56. package/dist/{pack-format-Baoc_aSZ.d.ts → pack-format-BqWf-DNc.d.ts} +23 -1
  57. package/dist/pack.d.ts +3 -2
  58. package/dist/pack.js +2 -1
  59. package/dist/{prepare-D49Gu4Lv.js → prepare-C61BcwFA.js} +2 -2
  60. package/dist/{prepare-Myd_ec4v.js → prepare-DxPEu9mo.js} +2 -2
  61. package/dist/render/pipeline.js +1 -1
  62. package/dist/resources/descriptor.js +1 -1
  63. package/dist/resources/jar.js +1 -1
  64. package/dist/resources/zip-reader.js +1 -1
  65. package/dist/{run-Ctt_1iTd.js → run-DYcem_Vv.js} +2 -2
  66. package/dist/run.js +1 -1
  67. package/dist/versions/prepare.js +1 -1
  68. package/dist/workers/render-worker.js +2 -2
  69. package/package.json +6 -6
  70. package/dist/{pipeline-BTlk9-s5.js → pipeline-CRTQGba7.js} +1 -1
  71. package/dist/{tints-BspVCz2G.js → tints-CWQz6UkM.js} +1 -1
  72. package/dist/{zip-reader-BSbJKUT1.js → zip-reader-zx3kdSq4.js} +1 -1
@@ -0,0 +1,582 @@
1
+ import { t as parseClassFile } from "./classfile-CiIDF3vc.js";
2
+ import { c as resolveMappings, s as remapDescriptor, t as MappingsUnavailableError } from "./mappings-Ep6ko0rg.js";
3
+ import { t as extractBlockLight } from "./light-bytecode-Cpyuxo9b.js";
4
+ import { r as compareVersions } from "./prepare-C61BcwFA.js";
5
+ //#region src/blocks/light.ts
6
+ /**
7
+ * Block light emission for the exported pack: `light.json`.
8
+ *
9
+ * Nothing in a jar's assets says a torch glows. Emission is set in Java, per block and often per
10
+ * blockstate — `Properties.lightLevel(state -> state.getValue(LIT) ? 7 : 0)` — so this reads the code,
11
+ * with the interpreter in `light-bytecode.ts`, for vanilla and mods alike. A mod's registry class is
12
+ * found the same way vanilla's is: it is the class whose string constants name the blocks.
13
+ *
14
+ * `VANILLA_LIGHT` is the check on that machinery, not its source. It is transcribed from `Blocks.java`
15
+ * and the wiki, and the test suite holds the extraction to it version by version: a block the
16
+ * interpreter reads differently from the table is a bug in one or the other, and either way a person
17
+ * looks. Mods have no table — their answers come from the code alone, and a block whose registration
18
+ * or light function the interpreter could not follow is recorded in `failures.json` rather than
19
+ * shipped dark.
20
+ */
21
+ const TO_INT_FUNCTION = "Ljava/util/function/ToIntFunction;";
22
+ /** Registry classes to interpret, by how many of the pack's block names they mention. */
23
+ const MAX_ROOTS = 24;
24
+ /** Classes added for reading those classes' fields, and how many reads it takes to count. */
25
+ const MAX_REFERRERS = 2;
26
+ const MIN_REFERRER_REFS = 100;
27
+ /**
28
+ * Blockstate files that describe no block. The item frames are entities whose model the game looks up
29
+ * through a blockstate, so they have no registration to find and emit nothing.
30
+ */
31
+ const NOT_BLOCKS = /* @__PURE__ */ new Set(["minecraft:item_frame", "minecraft:glow_item_frame"]);
32
+ /** Strings a class's constant pool holds, for spotting the one that registers the blocks. */
33
+ function stringConstants(classFile) {
34
+ const strings = /* @__PURE__ */ new Set();
35
+ for (const [index, constant] of classFile.constants.entries()) if (constant?.tag === "string") strings.add(classFile.utf8(index));
36
+ return strings;
37
+ }
38
+ /**
39
+ * The `Properties` builder class, in whatever the jars call it.
40
+ *
41
+ * Named by shape rather than by name, since an obfuscated jar has none: it is the class with a fluent
42
+ * method taking a `ToIntFunction` (the light function, 1.16+), and failing that the class most block
43
+ * constructors take as their last argument (1.13–1.15, where the light method takes an `int`).
44
+ */
45
+ function findPropsClasses(roots, remapClass, resolve) {
46
+ const found = /* @__PURE__ */ new Set();
47
+ const lastParams = /* @__PURE__ */ new Map();
48
+ for (const classFile of roots) for (const [index, constant] of classFile.constants.entries()) {
49
+ if (constant?.tag !== "methodref" && constant?.tag !== "interfaceMethodref") continue;
50
+ const reference = classFile.reference(index);
51
+ if (!reference) continue;
52
+ if (reference.descriptor === `(${TO_INT_FUNCTION})L${reference.owner};` && !reference.owner.startsWith("java/")) found.add(reference.owner);
53
+ if (reference.name === "<init>") {
54
+ const match = /L([^;]+);\)V$/.exec(reference.descriptor);
55
+ if (match && !match[1].startsWith("java/")) lastParams.set(match[1], (lastParams.get(match[1]) ?? 0) + 1);
56
+ }
57
+ }
58
+ for (const name of found) {
59
+ const classFile = resolve(name);
60
+ const fluent = classFile?.methods.filter((method) => method.descriptor === `()L${name};`).length ?? 0;
61
+ if (!classFile || classFile.superClass === "java/lang/Enum" || fluent < 3) found.delete(name);
62
+ }
63
+ for (const known of ["net/minecraft/world/level/block/state/BlockBehaviour$Properties", "net/minecraft/world/level/block/Block$Properties"]) {
64
+ const internal = remapClass(known);
65
+ if (resolve(internal)) {
66
+ found.add(internal);
67
+ found.add(known);
68
+ }
69
+ }
70
+ if (found.size === 0) {
71
+ const commonest = [...lastParams].toSorted((left, right) => right[1] - left[1])[0];
72
+ if (commonest && commonest[1] >= 3) found.add(commonest[0]);
73
+ }
74
+ const spelled = new Set(found);
75
+ for (const name of found) spelled.add(remapClass(name));
76
+ return spelled;
77
+ }
78
+ /**
79
+ * Classes that lean on the named ones without naming any block themselves.
80
+ *
81
+ * From the 26 line `Blocks.<clinit>` holds no block name at all: the ids live in `BlockItemIds`, and
82
+ * `Blocks` reads them as static fields. The class with the most field reads into the named classes is
83
+ * that registry.
84
+ */
85
+ function referrers(named, all, resolve) {
86
+ const targets = new Set(named.map((classFile) => classFile.thisClass));
87
+ const scored = [];
88
+ for (const internal of all) {
89
+ if (targets.has(internal)) continue;
90
+ const classFile = resolve(internal);
91
+ if (!classFile) continue;
92
+ let refs = 0;
93
+ for (const [index, constant] of classFile.constants.entries()) if (constant?.tag === "fieldref" && targets.has(classFile.reference(index)?.owner ?? "")) refs++;
94
+ if (refs >= MIN_REFERRER_REFS) scored.push({
95
+ classFile,
96
+ refs
97
+ });
98
+ }
99
+ return scored.toSorted((left, right) => right.refs - left.refs || left.classFile.thisClass.localeCompare(right.classFile.thisClass)).slice(0, MAX_REFERRERS).map((entry) => entry.classFile);
100
+ }
101
+ /** The code of a method as `[opcode, operand bytes…]` groups, for matching a trivial body by shape. */
102
+ function trivialGetterField(code) {
103
+ return code.length === 5 && code[0] === 42 && code[1] === 180 && code[4] === 172 ? { index: code[2] << 8 | code[3] } : null;
104
+ }
105
+ /**
106
+ * The methods a block class overrides to state its own light.
107
+ *
108
+ * Before 1.16 that is how a lit furnace worked: `Properties.lightValue(13)` on the block, and
109
+ * `getLightValue(state)` returning it or zero by `LIT`. An obfuscated jar names neither, so both are
110
+ * found by shape: the properties method that takes one `int`, the field it sets, the field the block's
111
+ * base constructor copies it into, and the base method that is nothing but a read of that field.
112
+ *
113
+ * From 1.16 the light is a lambda on the properties and vanilla overrides nothing, but a loader adds
114
+ * `getLightEmission(state, level, pos)` for mods, which is matched by its name — a mod jar keeps them.
115
+ */
116
+ function findLightGetters(roots, propsClasses, resolve, notes) {
117
+ const getters = [{
118
+ name: "getLightEmission",
119
+ descriptor: "",
120
+ root: null
121
+ }];
122
+ const props = [...propsClasses].map((name) => resolve(name)).find((classFile) => classFile !== null);
123
+ if (!props) {
124
+ notes.push("light getters: no properties class resolves");
125
+ return getters;
126
+ }
127
+ if (props.methods.some((method) => method.descriptor === `(${TO_INT_FUNCTION})L${props.thisClass};`)) return getters;
128
+ const setter = props.methods.find((method) => method.descriptor === `(I)L${props.thisClass};` && method.code);
129
+ const setterCode = setter?.code;
130
+ if (!setterCode || setterCode.length !== 7 || setterCode[0] !== 42 || setterCode[1] !== 27 || setterCode[2] !== 181) {
131
+ notes.push(`light getters: no int setter of the expected shape on ${props.thisClass} (${setter ? `${setter.name}: ${setterCode?.length ?? 0} bytes` : "none"})`);
132
+ return getters;
133
+ }
134
+ const propsField = props.reference(setterCode[3] << 8 | setterCode[4]);
135
+ if (!propsField) return getters;
136
+ const ctorDescriptor = `(L${props.thisClass};)V`;
137
+ const bases = /* @__PURE__ */ new Map();
138
+ for (const classFile of roots) for (const [index, constant] of classFile.constants.entries()) {
139
+ if (constant?.tag !== "methodref") continue;
140
+ const reference = classFile.reference(index);
141
+ if (reference?.name !== "<init>" || reference.descriptor !== ctorDescriptor) continue;
142
+ let at = resolve(reference.owner);
143
+ let top = null;
144
+ for (let hops = 0; at && hops < 12; hops++) {
145
+ if (at.methods.some((method) => method.name === "<init>" && method.descriptor === ctorDescriptor)) top = at.thisClass;
146
+ at = at.superClass ? resolve(at.superClass) : null;
147
+ }
148
+ if (top) bases.set(top, (bases.get(top) ?? 0) + 1);
149
+ }
150
+ const base = [...bases].toSorted((left, right) => right[1] - left[1])[0];
151
+ const baseClass = base ? resolve(base[0]) : null;
152
+ const baseCtor = baseClass?.methods.find((method) => method.name === "<init>" && method.descriptor === ctorDescriptor)?.code;
153
+ if (!baseClass || !baseCtor) {
154
+ notes.push(`light getters: no base block class found (${bases.keys().toArray().join(", ") || "no constructors"})`);
155
+ return getters;
156
+ }
157
+ const readsPropsField = (reference, viaCall) => {
158
+ if (!reference) return false;
159
+ if (!viaCall) return reference.owner === propsField.owner && reference.name === propsField.name;
160
+ const code = (resolve(reference.owner)?.methods.find((method) => method.name === reference.name && method.descriptor === reference.descriptor))?.code;
161
+ if (!code || code.length !== 5 || code[0] !== 42 || code[1] !== 180) return false;
162
+ const field = resolve(reference.owner)?.reference(code[2] << 8 | code[3]);
163
+ return field?.owner === propsField.owner && field.name === propsField.name;
164
+ };
165
+ let blockField = null;
166
+ for (let at = 0; at + 6 < baseCtor.length; at++) {
167
+ if (baseCtor[at] !== 180 && baseCtor[at] !== 184) continue;
168
+ if (readsPropsField(baseClass.reference(baseCtor[at + 1] << 8 | baseCtor[at + 2]), baseCtor[at] === 184) && baseCtor[at + 3] === 181) {
169
+ const written = baseClass.reference(baseCtor[at + 4] << 8 | baseCtor[at + 5]);
170
+ blockField = written ? `${written.owner}.${written.name}` : null;
171
+ break;
172
+ }
173
+ }
174
+ if (!blockField) {
175
+ notes.push(`light getters: ${baseClass.thisClass}.<init> does not copy ${propsField.owner}.${propsField.name}`);
176
+ return getters;
177
+ }
178
+ for (const method of baseClass.methods) {
179
+ const shape = method.code ? trivialGetterField(method.code) : null;
180
+ if (!shape || !/^\(L[^;]+;\)I$/.test(method.descriptor)) continue;
181
+ const field = baseClass.reference(shape.index);
182
+ if (field && `${field.owner}.${field.name}` === blockField) getters.push({
183
+ name: method.name,
184
+ descriptor: method.descriptor,
185
+ root: baseClass.thisClass
186
+ });
187
+ }
188
+ return getters;
189
+ }
190
+ /** Reference renaming through the mappings: a mod's original names onto an obfuscated client's. */
191
+ function remapper(mappings) {
192
+ const remapClass = (internalName) => {
193
+ const mapped = mappings.obfuscatedOf.get(internalName.replaceAll("/", "."));
194
+ return mapped ? mapped.replaceAll(".", "/") : internalName;
195
+ };
196
+ const remap = (reference, field) => {
197
+ const dotted = reference.owner.replaceAll("/", ".");
198
+ const owner = mappings.obfuscatedOf.get(dotted);
199
+ if (!owner || owner === dotted) return reference;
200
+ const member = field ? mappings.fieldOf.get(`${dotted}.${reference.name}`) : mappings.methodOf.get(`${dotted}.${reference.name}${reference.descriptor}`);
201
+ return {
202
+ owner: owner.replaceAll(".", "/"),
203
+ name: member ?? reference.name,
204
+ descriptor: remapDescriptor(reference.descriptor, mappings.obfuscatedOf)
205
+ };
206
+ };
207
+ return {
208
+ remap,
209
+ remapClass
210
+ };
211
+ }
212
+ async function collectBlockLight(options) {
213
+ const { classes, blockIds } = options;
214
+ const failures = [];
215
+ const notes = [];
216
+ const names = await classes.list();
217
+ let mappings;
218
+ try {
219
+ ({mappings} = await resolveMappings(options.version, options.cacheDir, async () => names));
220
+ } catch (err) {
221
+ if (!(err instanceof MappingsUnavailableError)) throw err;
222
+ mappings = {
223
+ obfuscatedOf: /* @__PURE__ */ new Map(),
224
+ originalOf: /* @__PURE__ */ new Map(),
225
+ fieldOf: /* @__PURE__ */ new Map(),
226
+ methodOf: /* @__PURE__ */ new Map()
227
+ };
228
+ notes.push(err.message);
229
+ }
230
+ const { remap, remapClass } = remapper(mappings);
231
+ const bytes = /* @__PURE__ */ new Map();
232
+ for (const name of names) {
233
+ const read = await classes.read(name);
234
+ if (read) bytes.set(name.replaceAll(".", "/"), read);
235
+ }
236
+ const parsed = /* @__PURE__ */ new Map();
237
+ const resolve = (name) => {
238
+ const internal = name.replaceAll(".", "/");
239
+ if (!parsed.has(internal)) {
240
+ const raw = bytes.get(internal);
241
+ let classFile;
242
+ try {
243
+ classFile = raw ? parseClassFile(raw) : null;
244
+ } catch {
245
+ classFile = null;
246
+ }
247
+ parsed.set(internal, classFile);
248
+ }
249
+ return parsed.get(internal) ?? null;
250
+ };
251
+ const wanted = /* @__PURE__ */ new Set();
252
+ for (const id of blockIds) {
253
+ wanted.add(id);
254
+ wanted.add(id.includes(":") ? id.slice(id.indexOf(":") + 1) : id);
255
+ }
256
+ const scored = [];
257
+ let scanned = 0;
258
+ for (const internal of bytes.keys()) {
259
+ options.onProgress?.(++scanned, bytes.size);
260
+ const classFile = resolve(internal);
261
+ if (!classFile) continue;
262
+ let hits = 0;
263
+ for (const text of stringConstants(classFile)) if (wanted.has(text)) hits++;
264
+ if (hits > 0) scored.push({
265
+ classFile,
266
+ hits
267
+ });
268
+ }
269
+ const named = scored.toSorted((left, right) => right.hits - left.hits || left.classFile.thisClass.localeCompare(right.classFile.thisClass)).slice(0, MAX_ROOTS).map((entry) => entry.classFile);
270
+ const roots = [...named, ...referrers(named, bytes.keys(), resolve)];
271
+ if (roots.length === 0) {
272
+ for (const namespace of new Set(blockIds.map((id) => id.includes(":") ? id.slice(0, id.indexOf(":")) : "minecraft"))) failures.push({
273
+ id: namespace,
274
+ reason: "no class in the jars names its blocks; light unknown for all of them"
275
+ });
276
+ return {
277
+ light: {},
278
+ failures,
279
+ notes
280
+ };
281
+ }
282
+ const propsClasses = findPropsClasses(roots, remapClass, resolve);
283
+ const source = {
284
+ resolve,
285
+ remap,
286
+ remapClass,
287
+ propsClasses,
288
+ lightGetters: findLightGetters(roots, propsClasses, resolve, notes),
289
+ isVanillaClass: (name) => mappings.originalOf.size === 0 || mappings.originalOf.has(name.replaceAll("/", ".")) || name.startsWith("net/minecraft/") || name.startsWith("com/mojang/")
290
+ };
291
+ notes.push(`registry classes: ${roots.map((root) => root.thisClass).join(", ")}`, `properties classes: ${[...propsClasses].join(", ") || "none found"}`, `light getters: ${source.lightGetters.map((getter) => `${getter.root ?? "*"}.${getter.name}${getter.descriptor}`).join(", ") || "none"}`);
292
+ const extraction = extractBlockLight(roots, source);
293
+ notes.push(...extraction.notes);
294
+ for (const id of options.trace ?? []) notes.push(extraction.describe(id));
295
+ const light = {};
296
+ for (const id of blockIds) {
297
+ if (NOT_BLOCKS.has(id)) continue;
298
+ const answer = extraction.lightOf(id);
299
+ if ("failure" in answer) failures.push({
300
+ id,
301
+ reason: answer.failure
302
+ });
303
+ else if (answer.light !== 0) light[id] = answer.light;
304
+ }
305
+ const modNamespaces = new Set(blockIds.map((id) => id.slice(0, id.indexOf(":"))).filter((namespace) => namespace !== "minecraft"));
306
+ if (modNamespaces.size === 1) {
307
+ const [namespace] = modNamespaces;
308
+ const listed = new Set(blockIds);
309
+ for (const [name, registration] of extraction.registrations) {
310
+ if (name.includes(":") || registration.vanilla || listed.has(`${namespace}:${name}`)) continue;
311
+ const answer = extraction.lightOf(`${namespace}:${name}`);
312
+ if ("light" in answer && answer.light !== 0) {
313
+ light[`${namespace}:${name}`] = answer.light;
314
+ notes.push(`${namespace}:${name} emits but ships no blockstate; kept from its registration alone`);
315
+ }
316
+ }
317
+ }
318
+ return {
319
+ light,
320
+ failures,
321
+ notes
322
+ };
323
+ }
324
+ /** `lit ? level : 0`, the shape a furnace, a redstone lamp or a campfire has. */
325
+ const lit = (level) => {
326
+ return {
327
+ default: 0,
328
+ cases: [{
329
+ when: { lit: "true" },
330
+ level
331
+ }]
332
+ };
333
+ };
334
+ /**
335
+ * Vanilla's emitters, transcribed from `Blocks.java` across 1.13 → 26.2 and checked against the wiki.
336
+ *
337
+ * By path, as a superset across versions: a block a version does not have never comes up in one of its
338
+ * worlds, and none of these levels has changed since the block was added. Cases are in the folded form
339
+ * the extractor emits — the commonest level as `default`, the rest as the fewest properties that
340
+ * decide them — so the two can be compared directly.
341
+ */
342
+ const VANILLA_LIGHT = {
343
+ beacon: 15,
344
+ conduit: 15,
345
+ copper_lantern: 15,
346
+ exposed_copper_lantern: 15,
347
+ weathered_copper_lantern: 15,
348
+ oxidized_copper_lantern: 15,
349
+ waxed_copper_lantern: 15,
350
+ waxed_exposed_copper_lantern: 15,
351
+ waxed_weathered_copper_lantern: 15,
352
+ waxed_oxidized_copper_lantern: 15,
353
+ end_gateway: 15,
354
+ end_portal: 15,
355
+ fire: 15,
356
+ glowstone: 15,
357
+ jack_o_lantern: 15,
358
+ lantern: 15,
359
+ lava: 15,
360
+ lava_cauldron: 15,
361
+ ochre_froglight: 15,
362
+ pearlescent_froglight: 15,
363
+ sea_lantern: 15,
364
+ shroomlight: 15,
365
+ verdant_froglight: 15,
366
+ campfire: lit(15),
367
+ redstone_lamp: lit(15),
368
+ end_rod: 14,
369
+ torch: 14,
370
+ wall_torch: 14,
371
+ copper_torch: 14,
372
+ copper_wall_torch: 14,
373
+ cave_vines: {
374
+ default: 0,
375
+ cases: [{
376
+ when: { berries: "true" },
377
+ level: 14
378
+ }]
379
+ },
380
+ cave_vines_plant: {
381
+ default: 0,
382
+ cases: [{
383
+ when: { berries: "true" },
384
+ level: 14
385
+ }]
386
+ },
387
+ blast_furnace: lit(13),
388
+ furnace: lit(13),
389
+ smoker: lit(13),
390
+ nether_portal: 11,
391
+ crying_obsidian: 10,
392
+ soul_campfire: lit(10),
393
+ soul_fire: 10,
394
+ soul_lantern: 10,
395
+ soul_torch: 10,
396
+ soul_wall_torch: 10,
397
+ deepslate_redstone_ore: lit(9),
398
+ redstone_ore: lit(9),
399
+ enchanting_table: 7,
400
+ ender_chest: 7,
401
+ glow_lichen: {
402
+ default: 7,
403
+ cases: [{
404
+ when: {
405
+ down: "false",
406
+ east: "false",
407
+ north: "false",
408
+ south: "false",
409
+ up: "false",
410
+ west: "false"
411
+ },
412
+ level: 0
413
+ }]
414
+ },
415
+ redstone_torch: lit(7),
416
+ redstone_wall_torch: lit(7),
417
+ sculk_catalyst: 6,
418
+ amethyst_cluster: 5,
419
+ large_amethyst_bud: 4,
420
+ magma_block: 3,
421
+ medium_amethyst_bud: 2,
422
+ firefly_bush: 2,
423
+ brewing_stand: 1,
424
+ brown_mushroom: 1,
425
+ calibrated_sculk_sensor: 1,
426
+ dragon_egg: 1,
427
+ end_portal_frame: 1,
428
+ sculk_sensor: 1,
429
+ small_amethyst_bud: 1,
430
+ sea_pickle: {
431
+ default: 0,
432
+ cases: [
433
+ {
434
+ when: {
435
+ pickles: "1",
436
+ waterlogged: "true"
437
+ },
438
+ level: 6
439
+ },
440
+ {
441
+ when: {
442
+ pickles: "2",
443
+ waterlogged: "true"
444
+ },
445
+ level: 9
446
+ },
447
+ {
448
+ when: {
449
+ pickles: "3",
450
+ waterlogged: "true"
451
+ },
452
+ level: 12
453
+ },
454
+ {
455
+ when: {
456
+ pickles: "4",
457
+ waterlogged: "true"
458
+ },
459
+ level: 15
460
+ }
461
+ ]
462
+ },
463
+ respawn_anchor: {
464
+ default: 0,
465
+ cases: [
466
+ {
467
+ when: { charges: "1" },
468
+ level: 3
469
+ },
470
+ {
471
+ when: { charges: "2" },
472
+ level: 7
473
+ },
474
+ {
475
+ when: { charges: "3" },
476
+ level: 11
477
+ },
478
+ {
479
+ when: { charges: "4" },
480
+ level: 15
481
+ }
482
+ ]
483
+ },
484
+ light: {
485
+ default: 0,
486
+ cases: Array.from({ length: 15 }, (_, index) => {
487
+ return {
488
+ when: { level: String(index + 1) },
489
+ level: index + 1
490
+ };
491
+ })
492
+ },
493
+ copper_bulb: lit(15),
494
+ waxed_copper_bulb: lit(15),
495
+ exposed_copper_bulb: lit(12),
496
+ waxed_exposed_copper_bulb: lit(12),
497
+ weathered_copper_bulb: lit(8),
498
+ waxed_weathered_copper_bulb: lit(8),
499
+ oxidized_copper_bulb: lit(4),
500
+ waxed_oxidized_copper_bulb: lit(4),
501
+ trial_spawner: {
502
+ default: 8,
503
+ cases: [
504
+ {
505
+ when: { trial_spawner_state: "cooldown" },
506
+ level: 0
507
+ },
508
+ {
509
+ when: { trial_spawner_state: "inactive" },
510
+ level: 0
511
+ },
512
+ {
513
+ when: { trial_spawner_state: "waiting_for_players" },
514
+ level: 4
515
+ }
516
+ ]
517
+ },
518
+ vault: {
519
+ default: 12,
520
+ cases: [{
521
+ when: { vault_state: "inactive" },
522
+ level: 6
523
+ }]
524
+ }
525
+ };
526
+ /**
527
+ * Where a block's light changed between versions: the table holds the current value, and a version at
528
+ * or below `until` gets the older one instead (`0` for a block that did not glow yet).
529
+ */
530
+ const VANILLA_LIGHT_HISTORY = [{
531
+ path: "enchanting_table",
532
+ until: "1.17.1",
533
+ light: 0
534
+ }];
535
+ /** The vanilla table as it stood at a version: `VANILLA_LIGHT` with the history applied. */
536
+ function vanillaLightAt(version) {
537
+ const table = { ...VANILLA_LIGHT };
538
+ for (const change of VANILLA_LIGHT_HISTORY) if (compareVersions(version, change.until) <= 0) {
539
+ if (change.light === 0) delete table[change.path];
540
+ else table[change.path] = change.light;
541
+ }
542
+ return table;
543
+ }
544
+ for (const name of ["candle", ...[
545
+ "white",
546
+ "orange",
547
+ "magenta",
548
+ "light_blue",
549
+ "yellow",
550
+ "lime",
551
+ "pink",
552
+ "gray",
553
+ "light_gray",
554
+ "cyan",
555
+ "purple",
556
+ "blue",
557
+ "brown",
558
+ "green",
559
+ "red",
560
+ "black"
561
+ ].map((colour) => `${colour}_candle`)]) {
562
+ VANILLA_LIGHT[name] = {
563
+ default: 0,
564
+ cases: [
565
+ 1,
566
+ 2,
567
+ 3,
568
+ 4
569
+ ].map((candles) => {
570
+ return {
571
+ when: {
572
+ candles: String(candles),
573
+ lit: "true"
574
+ },
575
+ level: candles * 3
576
+ };
577
+ })
578
+ };
579
+ VANILLA_LIGHT[`${name}_cake`] = lit(3);
580
+ }
581
+ //#endregion
582
+ export { collectBlockLight as n, vanillaLightAt as r, VANILLA_LIGHT as t };