// Copyright Shacraft contributors. MIT OR Apache-2.0. // Extract factual lighting properties through the pinned runtime's public API. import java.nio.file.*; import java.util.*; import com.google.gson.*; import net.minecraft.SharedConstants; import net.minecraft.server.Bootstrap; import net.minecraft.core.Direction; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.Identifier; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.lighting.LightEngine; import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; class lighting_reference { static String canonical(BlockState state) { String id=BuiltInRegistries.BLOCK.getKey(state.getBlock()).toString(); var properties=new TreeMap(); for(var property:state.getProperties()) properties.put(property.getName(),state.getValue(property).toString().toLowerCase(Locale.ROOT)); return id+(properties.isEmpty()?"":"["+String.join(",",properties.entrySet().stream().map(e->e.getKey()+"="+e.getValue()).toList())+"]"); } static JsonArray boxes(VoxelShape shape) { var result=new JsonArray(); for(var box:shape.toAabbs()) { var coordinates=new JsonArray(); for(double value:new double[]{box.minX,box.minY,box.minZ,box.maxX,box.maxY,box.maxZ})coordinates.add(value); result.add(coordinates); } return result; } static BlockState state(String name) { return BuiltInRegistries.BLOCK.getValue(Identifier.parse("minecraft:"+name)).defaultBlockState(); } static JsonObject properties(BlockState state) { var result=new JsonObject(); result.addProperty("minecraft_id",Block.getId(state)); result.addProperty("state",canonical(state)); result.addProperty("emission",state.getLightEmission()); result.addProperty("dampening",state.getLightDampening()); result.addProperty("can_occlude",state.canOcclude()); result.addProperty("use_shape",state.useShapeForLightOcclusion()); result.addProperty("propagates_skylight_down",state.propagatesSkylightDown()); result.add("occlusion",boxes(state.canOcclude()&&state.useShapeForLightOcclusion()?state.getOcclusionShape():Shapes.empty())); return result; } static JsonObject crossing(BlockState from,BlockState to,Direction direction) { var result=new JsonObject(); result.addProperty("from",canonical(from)); result.addProperty("to",canonical(to)); result.addProperty("direction",direction.getName()); result.addProperty("block_dampening",LightEngine.getLightDampeningInto(from,to,direction,Math.max(1,to.getLightDampening()))); result.addProperty("sky_column_dampening",LightEngine.getLightDampeningInto(from,to,direction,to.getLightDampening())); return result; } public static void main(String[] args) throws Exception { SharedConstants.tryDetectVersion(); Bootstrap.bootStrap(); var root=new JsonObject(); root.addProperty("version","26.2"); var states=new JsonArray(); var shapeTable=new JsonArray(); var shapes=new HashMap(); var defaults=new JsonObject(); var blocks=new JsonObject(); var schemas=new JsonArray(); var schemaIds=new HashMap(); // Block state IDs are the same IDs already recorded by catalog generation. var byId=new TreeMap(); for(var block:BuiltInRegistries.BLOCK) { String name=BuiltInRegistries.BLOCK.getKey(block).toString(); defaults.addProperty(name,Block.getId(block.defaultBlockState())); var possible=block.getStateDefinition().getPossibleStates(); int first=possible.stream().mapToInt(Block::getId).min().orElseThrow(); var schema=new JsonArray(); for(var property:block.getStateDefinition().getProperties()) { var item=new JsonArray(); item.add(property.getName()); var values=new JsonArray(); for(var value:property.getPossibleValues())values.add(value.toString().toLowerCase(Locale.ROOT)); item.add(values); schema.add(item); } for(var state:possible) { byId.put(Block.getId(state),state); int offset=0; for(var property:block.getStateDefinition().getProperties()) { var values=new ArrayList<>(property.getPossibleValues()); offset=offset*values.size()+values.indexOf(state.getValue(property)); } if(first+offset!=Block.getId(state))throw new IllegalStateException("Non-contiguous state schema: "+canonical(state)); } String schemaKey=schema.toString(); if(!schemaIds.containsKey(schemaKey)) { schemaIds.put(schemaKey,schemas.size()); schemas.add(schema); } var definition=new JsonArray(); definition.add(first); definition.add(Block.getId(block.defaultBlockState())); definition.add(schemaIds.get(schemaKey)); blocks.add(name,definition); } for(var entry:byId.entrySet()) { var state=entry.getValue(); var occlusion=boxes(state.canOcclude()&&state.useShapeForLightOcclusion()?state.getOcclusionShape():Shapes.empty()); String key=occlusion.toString(); if(!shapes.containsKey(key)) { shapes.put(key,shapeTable.size());shapeTable.add(occlusion); } var record=new JsonArray(); record.add(entry.getKey()); record.add(state.getLightDampening()); record.add(shapes.get(key)); record.add(state.getLightEmission()); record.add(state.canOcclude()); record.add(state.useShapeForLightOcclusion()); record.add(state.propagatesSkylightDown()); states.add(record); } root.add("states",states); root.add("shapes",shapeTable); root.add("defaults",defaults); root.add("blocks",blocks); root.add("schemas",schemas); root.addProperty("state_columns","minecraft_id,dampening,occlusion_shape_id,emission,can_occlude,use_shape,propagates_skylight_down"); var examples=new JsonArray(); for(String name:List.of("air","stone","glass","tinted_glass","white_stained_glass","glass_pane","ice","packed_ice","blue_ice","water","lava","oak_leaves","azalea_leaves","oak_slab","oak_stairs","oak_trapdoor","oak_door","snow","snow_block","torch","wall_torch","redstone_torch","soul_torch","glowstone","sea_lantern","redstone_lamp","lantern","jack_o_lantern","light","sculk_sensor","copper_bulb","copper_grate")) examples.add(properties(state(name))); examples.add(properties(Blocks.REDSTONE_LAMP.defaultBlockState().setValue(BlockStateProperties.LIT,true))); examples.add(properties(Blocks.OAK_SLAB.defaultBlockState().setValue(BlockStateProperties.SLAB_TYPE,net.minecraft.world.level.block.state.properties.SlabType.TOP))); examples.add(properties(Blocks.OAK_SLAB.defaultBlockState().setValue(BlockStateProperties.SLAB_TYPE,net.minecraft.world.level.block.state.properties.SlabType.DOUBLE))); examples.add(properties(Blocks.OAK_SLAB.defaultBlockState().setValue(BlockStateProperties.WATERLOGGED,true))); root.add("examples",examples); var crossings=new JsonArray(); var air=Blocks.AIR.defaultBlockState(); for(String name:List.of("stone","glass","tinted_glass","ice","water","oak_leaves","oak_slab","oak_stairs","glowstone","sea_lantern")) for(Direction direction:Direction.values())crossings.add(crossing(air,state(name),direction)); var slab=Blocks.OAK_SLAB.defaultBlockState(); var top=slab.setValue(BlockStateProperties.SLAB_TYPE,net.minecraft.world.level.block.state.properties.SlabType.TOP); for(Direction direction:Direction.values()) { crossings.add(crossing(slab,top,direction)); crossings.add(crossing(top,slab,direction)); crossings.add(crossing(state("glowstone"),air,direction)); } root.add("crossings",crossings); root.addProperty("measurement_scope","Public original Java 26.2 block-state light properties and LightEngine.getLightDampeningInto calls. Effective shape is empty unless canOcclude and useShapeForLightOcclusion are both true. Crossings measure directional shape blocking and target attenuation, not a full running light engine or skylight source map."); Files.writeString(Path.of(args[0]),new GsonBuilder().create().toJson(root)+"\n"); System.out.println("Measured "+states.size()+" states, "+shapeTable.size()+" effective shapes, "+examples.size()+" examples and "+crossings.size()+" directional crossings."); } }