Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,332 changes: 1,166 additions & 1,166 deletions pomme-client/src/world/block/data/state-1.21.10.json

Large diffs are not rendered by default.

2,332 changes: 1,166 additions & 1,166 deletions pomme-client/src/world/block/data/state-1.21.11.json

Large diffs are not rendered by default.

2,336 changes: 1,168 additions & 1,168 deletions pomme-client/src/world/block/data/state-26.1.json

Large diffs are not rendered by default.

2,392 changes: 1,196 additions & 1,196 deletions pomme-client/src/world/block/data/state-26.2.json

Large diffs are not rendered by default.

261 changes: 17 additions & 244 deletions pomme-client/src/world/block/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ struct BlockData {
/// Collision shape; see `block_shape::partial_shape` for the encoding.
shape: Option<Box<[LocalBox]>>,
is_air: bool,
/// Vanilla `hasCollision`; precomputed so physics avoids name lookups.
/// Vanilla `hasCollision` (`BlockBehaviour.Properties.noCollision()`).
collides: bool,
fluid: Fluid,
light: LightProps,
Expand Down Expand Up @@ -166,7 +166,7 @@ struct StateFile {
blocks: Vec<StateEntry>,
}

/// Per-block light fields; each is a scalar when uniform across the block's
/// Per-block fields; each is a scalar when uniform across the block's
/// states, else one value per state. `f` is 6 mask-dictionary indices —
/// a single tuple when uniform, else a per-state list of tuple-or-null.
#[derive(serde::Deserialize)]
Expand All @@ -177,6 +177,8 @@ struct StateEntry {
p: ScalarOrPerState,
o: ScalarOrPerState,
u: ScalarOrPerState,
/// `hasCollision`; block-level in vanilla, so always a scalar.
c: u8,
#[serde(default)]
f: Option<serde_json::Value>,
}
Expand Down Expand Up @@ -229,247 +231,6 @@ static ACTIVE_TABLE: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicU
/// Vanilla getFluidState overrides on blocks without a `waterlogged` property.
const IMPLICIT_WATER: [&str; 4] = ["seagrass", "tall_seagrass", "kelp", "kelp_plant"];

/// Blocks built with vanilla `noCollission()` (plus fluids/portals whose shape
/// is empty); consulted once per block at [`init`].
const NO_COLLISION: &[&str] = &[
"acacia_hanging_sign",
"acacia_pressure_plate",
"acacia_sapling",
"acacia_sign",
"acacia_wall_hanging_sign",
"acacia_wall_sign",
"activator_rail",
"air",
"allium",
"azure_bluet",
"bamboo_hanging_sign",
"bamboo_pressure_plate",
"bamboo_sapling",
"bamboo_sign",
"bamboo_wall_hanging_sign",
"bamboo_wall_sign",
"beetroots",
"big_dripleaf_stem",
"birch_hanging_sign",
"birch_pressure_plate",
"birch_sapling",
"birch_sign",
"birch_wall_hanging_sign",
"birch_wall_sign",
"black_banner",
"black_wall_banner",
"blue_banner",
"blue_orchid",
"blue_wall_banner",
"brain_coral",
"brain_coral_fan",
"brain_coral_wall_fan",
"brown_banner",
"brown_mushroom",
"brown_wall_banner",
"bubble_column",
"bubble_coral",
"bubble_coral_fan",
"bubble_coral_wall_fan",
"bush",
"cactus_flower",
"carrots",
"cave_air",
"cave_vines",
"cave_vines_plant",
"cherry_hanging_sign",
"cherry_pressure_plate",
"cherry_sapling",
"cherry_sign",
"cherry_wall_hanging_sign",
"cherry_wall_sign",
"closed_eyeblossom",
"cobweb",
"copper_torch",
"copper_wall_torch",
"cornflower",
"crimson_fungus",
"crimson_hanging_sign",
"crimson_pressure_plate",
"crimson_roots",
"crimson_sign",
"crimson_wall_hanging_sign",
"crimson_wall_sign",
"cyan_banner",
"cyan_wall_banner",
"dandelion",
"dark_oak_hanging_sign",
"dark_oak_pressure_plate",
"dark_oak_sapling",
"dark_oak_sign",
"dark_oak_wall_hanging_sign",
"dark_oak_wall_sign",
"dead_brain_coral",
"dead_brain_coral_fan",
"dead_brain_coral_wall_fan",
"dead_bubble_coral",
"dead_bubble_coral_fan",
"dead_bubble_coral_wall_fan",
"dead_bush",
"dead_fire_coral",
"dead_fire_coral_fan",
"dead_fire_coral_wall_fan",
"dead_horn_coral",
"dead_horn_coral_fan",
"dead_horn_coral_wall_fan",
"dead_tube_coral",
"dead_tube_coral_fan",
"dead_tube_coral_wall_fan",
"detector_rail",
"end_gateway",
"end_portal",
"fern",
"fire",
"fire_coral",
"fire_coral_fan",
"fire_coral_wall_fan",
"firefly_bush",
"frogspawn",
"glow_lichen",
"gray_banner",
"gray_wall_banner",
"green_banner",
"green_wall_banner",
"hanging_roots",
"heavy_weighted_pressure_plate",
"horn_coral",
"horn_coral_fan",
"horn_coral_wall_fan",
"jungle_hanging_sign",
"jungle_pressure_plate",
"jungle_sapling",
"jungle_sign",
"jungle_wall_hanging_sign",
"jungle_wall_sign",
"kelp",
"kelp_plant",
"large_fern",
"lava",
"leaf_litter",
"lever",
"light_blue_banner",
"light_blue_wall_banner",
"light_gray_banner",
"light_gray_wall_banner",
"light_weighted_pressure_plate",
"lilac",
"lily_of_the_valley",
"lime_banner",
"lime_wall_banner",
"magenta_banner",
"magenta_wall_banner",
"mangrove_hanging_sign",
"mangrove_pressure_plate",
"mangrove_propagule",
"mangrove_sign",
"mangrove_wall_hanging_sign",
"mangrove_wall_sign",
"nether_portal",
"nether_sprouts",
"nether_wart",
"oak_hanging_sign",
"oak_pressure_plate",
"oak_sapling",
"oak_sign",
"oak_wall_hanging_sign",
"oak_wall_sign",
"open_eyeblossom",
"orange_banner",
"orange_tulip",
"orange_wall_banner",
"oxeye_daisy",
"pale_hanging_moss",
"pale_oak_hanging_sign",
"pale_oak_pressure_plate",
"pale_oak_sapling",
"pale_oak_sign",
"pale_oak_wall_hanging_sign",
"pale_oak_wall_sign",
"peony",
"pink_banner",
"pink_petals",
"pink_tulip",
"pink_wall_banner",
"pitcher_crop",
"pitcher_plant",
"polished_blackstone_pressure_plate",
"poppy",
"potatoes",
"powered_rail",
"purple_banner",
"purple_wall_banner",
"rail",
"red_banner",
"red_mushroom",
"red_tulip",
"red_wall_banner",
"redstone_torch",
"redstone_wall_torch",
"redstone_wire",
"resin_clump",
"rose_bush",
"scaffolding",
"sculk_vein",
"seagrass",
"short_dry_grass",
"short_grass",
"small_dripleaf",
"soul_fire",
"soul_torch",
"soul_wall_torch",
"spore_blossom",
"spruce_hanging_sign",
"spruce_pressure_plate",
"spruce_sapling",
"spruce_sign",
"spruce_wall_hanging_sign",
"spruce_wall_sign",
"stone_pressure_plate",
"structure_void",
"sugar_cane",
"sunflower",
"sweet_berry_bush",
"tall_dry_grass",
"tall_grass",
"tall_seagrass",
"torch",
"torchflower",
"torchflower_crop",
"tripwire",
"tripwire_hook",
"tube_coral",
"tube_coral_fan",
"tube_coral_wall_fan",
"twisting_vines",
"twisting_vines_plant",
"vine",
"void_air",
"wall_torch",
"warped_fungus",
"warped_hanging_sign",
"warped_pressure_plate",
"warped_roots",
"warped_sign",
"warped_wall_hanging_sign",
"warped_wall_sign",
"water",
"weeping_vines",
"weeping_vines_plant",
"wheat",
"white_banner",
"white_tulip",
"white_wall_banner",
"wildflowers",
"wither_rose",
"yellow_banner",
"yellow_wall_banner",
];

/// Loads the block-state table for the launched game version. Must be called
/// once at startup before any world/render code runs.
pub fn init(version: &str) {
Expand Down Expand Up @@ -583,7 +344,7 @@ fn build_table(data: &str, state_data: &str) -> Vec<BlockData> {
.map(|(k, vs)| (intern(k), vs.iter().map(|v| intern(v)).collect()))
.collect();
let is_air = matches!(block.name.as_str(), "air" | "cave_air" | "void_air");
let collides = !NO_COLLISION.contains(&block.name.as_str());
let collides = state_entry.c != 0;

let count: u32 = props.iter().map(|(_, vs)| vs.len() as u32).product();
let face_indices = light_face_indices(state_entry, count as usize);
Expand Down Expand Up @@ -879,6 +640,18 @@ mod tests {
assert_eq!(fluid(BlockState::AIR).kind, FluidKind::Empty);
}

#[test]
fn collision_flags() {
setup();
// Oracles from reference/26.2/decompiled Blocks.java noCollision sites.
assert!(!has_collision(BlockState::AIR));
assert!(!has_collision(find_state("torch", &[])));
assert!(!has_collision(find_state("water", &[("level", "0")])));
assert!(!has_collision(find_state("oak_button", &[])));
assert!(has_collision(find_state("stone", &[])));
assert!(has_collision(find_state("oak_fence", &[])));
}

#[test]
fn light_emission_values() {
setup();
Expand Down
10 changes: 10 additions & 0 deletions tools/blockgen/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,9 @@ struct StateDumpFile {
propagates_skylight_down: Vec<u8>,
can_occlude: Vec<u8>,
use_shape_for_light_occlusion: Vec<u8>,
/// `BlockBehaviour.hasCollision`; block-level, so uniform across each
/// block's states.
has_collision: Vec<u8>,
/// State id (as string) -> 6 face masks, 64 hex chars each, present
/// exactly for states with `can_occlude && use_shape_for_light_occlusion`.
face_masks: std::collections::HashMap<String, [String; 6]>,
Expand Down Expand Up @@ -383,6 +386,7 @@ fn gen_state(dump_path: &str, blocks_path: &str, out_path: &str) -> Result<(), E
"use_shape_for_light_occlusion",
dump.use_shape_for_light_occlusion.len(),
),
("has_collision", dump.has_collision.len()),
] {
if len != n {
return Err(format!("{key} has {len} entries, expected {n}").into());
Expand All @@ -399,6 +403,7 @@ fn gen_state(dump_path: &str, blocks_path: &str, out_path: &str) -> Result<(), E
"use_shape_for_light_occlusion",
dump.use_shape_for_light_occlusion[i],
),
("has_collision", dump.has_collision[i]),
] {
if v > 1 {
return Err(format!("state {i}: {key} is {v}, expected 0/1").into());
Expand Down Expand Up @@ -480,6 +485,11 @@ fn gen_state(dump_path: &str, blocks_path: &str, out_path: &str) -> Result<(), E
] {
write!(line, ", \"{key}\": {}", scalar_or_array(values)?)?;
}
let collision = &dump.has_collision[range.clone()];
if collision.iter().any(|&c| c != collision[0]) {
return Err(format!("{}: has_collision varies across states", block.name).into());
}
write!(line, ", \"c\": {}", collision[0])?;
let masks = &state_masks[range];
if masks.iter().any(Option::is_some) {
if masks.iter().all(|m| *m == masks[0]) {
Expand Down
15 changes: 12 additions & 3 deletions tools/stategen/StateDump.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
import java.util.Map;

/**
* Dumps per-block-state properties (the baked light set) by running vanilla's own code:
* bootstraps the block registry from the server jar on the classpath, then
* iterates Block.BLOCK_STATE_REGISTRY in state-id order.
* Dumps per-block-state properties (the baked light set plus hasCollision) by
* running vanilla's own code: bootstraps the block registry from the server jar
* on the classpath, then iterates Block.BLOCK_STATE_REGISTRY in state-id order.
*
* Everything is reflection so one binary covers both the 26.x API
* (getLightDampening) and the 1.21.x API (getLightBlock); it also means the
Expand Down Expand Up @@ -55,6 +55,7 @@ public static void main(String[] args) throws Exception {
List<Integer> propagates = new ArrayList<>();
List<Integer> canOcclude = new ArrayList<>();
List<Integer> useShape = new ArrayList<>();
List<Integer> hasCollision = new ArrayList<>();
// state id -> 6 face masks (64 hex chars each), only for canOcclude && useShape states
Map<Integer, String[]> faceMasks = new LinkedHashMap<>();

Expand All @@ -71,6 +72,7 @@ public static void main(String[] args) throws Exception {
propagates.add(((Boolean) m.propagatesSkylightDown.invoke(state)) ? 1 : 0);
canOcclude.add(occludes ? 1 : 0);
useShape.add(shaped ? 1 : 0);
hasCollision.add(m.hasCollision.getBoolean(m.getBlock.invoke(state)) ? 1 : 0);
if (occludes && shaped) {
String[] masks = new String[6];
for (int d = 0; d < 6; d++) {
Expand All @@ -91,6 +93,7 @@ public static void main(String[] args) throws Exception {
writeIntArray(w, "propagates_skylight_down", propagates);
writeIntArray(w, "can_occlude", canOcclude);
writeIntArray(w, "use_shape_for_light_occlusion", useShape);
writeIntArray(w, "has_collision", hasCollision);
w.write(" \"face_masks\": {");
boolean first = true;
for (Map.Entry<Integer, String[]> e : faceMasks.entrySet()) {
Expand Down Expand Up @@ -192,6 +195,8 @@ private static final class Methods {
final Method getFaceOcclusionShape;
final Method shapeIsEmpty;
final Method toAabbs;
final Method getBlock;
final Field hasCollision;
private final Class<?> aabbClass;
private final Map<String, Field> aabbFields = new LinkedHashMap<>();

Expand All @@ -206,6 +211,10 @@ private static final class Methods {
Class<?> voxelShape = Class.forName("net.minecraft.world.phys.shapes.VoxelShape");
shapeIsEmpty = voxelShape.getMethod("isEmpty");
toAabbs = voxelShape.getMethod("toAabbs");
getBlock = stateClass.getMethod("getBlock");
hasCollision = Class.forName("net.minecraft.world.level.block.state.BlockBehaviour")
.getDeclaredField("hasCollision");
hasCollision.setAccessible(true);
aabbClass = Class.forName("net.minecraft.world.phys.AABB");
}

Expand Down
Loading