package world import ( _ "embed" "encoding/binary" "fmt" ) // blockPropertiesBinary is generated by tools/VanillaBlockStateDump.java // directly from the 26.1.2 runtime block-state registry. None of what it holds // appears in generated/reports/blocks.json or in any tag file: light dampening, // emission, voxel face-occlusion masks, whether a state blocks motion, whether // it holds a fluid, and whether its block is a LeavesBlock all exist only once // vanilla has built the registry. // //go:embed block_properties.bin var blockPropertiesBinary []byte var ( blockOpacity [totalBlockStates]byte blockEmission [totalBlockStates]byte blockStateFlags [totalBlockStates]byte blockLightShape [totalBlockStates]uint16 lightFaceShapes [][lightShapeBytes]byte ) const ( blockPropertiesMagic = 0x52494f4c // RIOL blockPropertiesVersion = 2 lightShapeBytes = 6 * 32 // six 16x16 face masks ) // Per-state flag bits, as written by tools/VanillaBlockStateDump.java. The // first three drive lighting; the rest drive the heightmaps, and none of them // can be derived from blocks.json or from a tag file — they only exist once // vanilla's block-state registry is built. const ( flagPropagatesSkylight = 1 << iota flagCanOcclude flagShapeForOcclusion flagBlocksMotion flagFluid flagLeaves ) func init() { if err := decodeBlockProperties(blockPropertiesBinary); err != nil { panic(fmt.Sprintf("world: decode vanilla block properties: %v", err)) } } func decodeBlockProperties(data []byte) error { if len(data) < 16 { return fmt.Errorf("header is truncated") } if binary.BigEndian.Uint32(data[0:4]) != blockPropertiesMagic { return fmt.Errorf("invalid magic") } if version := binary.BigEndian.Uint32(data[4:8]); version != blockPropertiesVersion { return fmt.Errorf("unsupported version %d", version) } states := int(binary.BigEndian.Uint32(data[8:12])) shapes := int(binary.BigEndian.Uint32(data[12:16])) if states != totalBlockStates { return fmt.Errorf("state count %d, want %d", states, totalBlockStates) } want := 16 + states*5 + shapes*lightShapeBytes if len(data) != want { return fmt.Errorf("length %d, want %d", len(data), want) } offset := 16 for id := 0; id < states; id++ { blockOpacity[id] = data[offset] blockEmission[id] = data[offset+1] blockStateFlags[id] = data[offset+2] blockLightShape[id] = binary.BigEndian.Uint16(data[offset+3 : offset+5]) if int(blockLightShape[id]) >= shapes { return fmt.Errorf("state %d references shape %d of %d", id, blockLightShape[id], shapes) } offset += 5 } lightFaceShapes = make([][lightShapeBytes]byte, shapes) for i := range lightFaceShapes { copy(lightFaceShapes[i][:], data[offset:offset+lightShapeBytes]) offset += lightShapeBytes } return nil } func lightOpacity(state uint16) byte { if int(state) >= len(blockOpacity) { return 15 } return blockOpacity[state] } func lightEmission(state uint16) byte { if int(state) >= len(blockEmission) { return 0 } return blockEmission[state] } func stateFlags(state uint16) byte { if int(state) >= len(blockStateFlags) { return 0 } return blockStateFlags[state] } // blocksMotionOrFluid is Heightmap.Types.MOTION_BLOCKING's predicate: // blocksMotion() || !getFluidState().isEmpty(). Note that a waterlogged block // satisfies the fluid half, which is why the flag is per state and not per // block. func blocksMotionOrFluid(state uint16) bool { return stateFlags(state)&(flagBlocksMotion|flagFluid) != 0 } // blocksMotionNoLeaves is MOTION_BLOCKING_NO_LEAVES's predicate: the same, less // anything that is a LeavesBlock. Vanilla tests instanceof, not the // minecraft:leaves tag, and the two sets are not the same. func blocksMotionNoLeaves(state uint16) bool { f := stateFlags(state) return f&(flagBlocksMotion|flagFluid) != 0 && f&flagLeaves == 0 } // lightShapeOccludes mirrors Shapes.faceShapeOccludes for the 1/16-resolution // face masks emitted from vanilla's VoxelShape data. func lightShapeOccludes(from, into uint16, direction int) bool { if direction < 0 || direction >= 6 { return false } var fromShape, intoShape [lightShapeBytes]byte // Vanilla substitutes an empty shape unless both flags are true. Ordinary // full cubes are handled by dampening; only shape-aware blocks (slabs, // stairs, etc.) participate in face occlusion. const shapeAware = flagCanOcclude | flagShapeForOcclusion if int(from) < len(blockStateFlags) && blockStateFlags[from]&shapeAware == shapeAware { fromShape = lightFaceShapes[blockLightShape[from]] } if int(into) < len(blockStateFlags) && blockStateFlags[into]&shapeAware == shapeAware { intoShape = lightFaceShapes[blockLightShape[into]] } opposite := [...]int{1, 0, 3, 2, 5, 4} fromOffset := direction * 32 intoOffset := opposite[direction] * 32 for i := 0; i < 32; i++ { if fromShape[fromOffset+i]|intoShape[intoOffset+i] != 0xff { return false } } return true }