diff --git a/blocks/src/lib.rs b/blocks/src/lib.rs index 4fc10fa..0b7cb6d 100644 --- a/blocks/src/lib.rs +++ b/blocks/src/lib.rs @@ -108,6 +108,9 @@ macro_rules! define_blocks { $(collision $collision:expr,)? $(update_state ($world:ident, $pos:ident) => $update_state:expr,)? $(multipart ($mkey:ident, $mval:ident) => $multipart:expr,)? + $(hardness $hardness:expr,)? + $(harvest_tools [ $($harvest_tool:pat,)+ ],)? + $(best_tools [ $($best_tools:pat,)+ ],)? } )+ ) => ( @@ -271,6 +274,20 @@ macro_rules! define_blocks { } } + #[allow(unused_variables, unreachable_code)] + pub fn get_hardness(&self) -> Option { + match *self { + $( + Block::$name { + $($fname,)? + } => { + $(return Some($hardness);)? + None + } + )+ + } + } + #[allow(unused_variables, unreachable_code)] pub fn update_state(&self, world: &W, pos: Position) -> Block { match *self { @@ -308,6 +325,81 @@ macro_rules! define_blocks { )+ } } + + #[allow(unused_variables, unreachable_code)] + pub fn is_best_tool(&self, tool: &Option) -> bool { + match *self { + $( + Block::$name { .. } => { + $( + return match tool { + $(Some($best_tools) => true,)+ + _ => false, + }; + )? + true + } + )+ + } + } + + #[allow(unused_variables, unreachable_code)] + pub fn can_harvest(&self, tool: &Option) -> bool { + match *self { + $( + Block::$name { .. } => { + $(return match *tool { + $(Some($harvest_tool) => true,)+ + _ => false, + };)? + true + } + )+ + } + } + + #[allow(unused_variables, unreachable_code)] + pub fn get_mining_time(&self, tool: &Option) -> Option { + let mut speed_multiplier = 1.0; + + let tool_multiplier = tool.map(|t| t.get_multiplier()).unwrap_or(1.0); + + let is_best_tool = self.is_best_tool(tool); + let can_harvest: bool = self.can_harvest(tool); + + if is_best_tool { + speed_multiplier = tool_multiplier; + } + + // TODO: Apply tool efficiency + // TODO: Apply haste effect + // TODO: Apply mining fatigue effect + // TODO: Apply in water multiplier + // TODO: Apply on ground multiplier + + let mut damage = match self.get_hardness() { + // Instant mine + Some(n) if n == 0.0 => return Some(std::time::Duration::from_secs_f64(0.05)), + // Impossible to mine + None => return None, + Some(n) => speed_multiplier / n, + }; + + if can_harvest { + damage /= 30.0; + } else { + damage /= 100.0; + } + + // Instant breaking + if damage > 1.0 { + return Some(std::time::Duration::from_secs_f64(0.05)); + } + + let ticks = (1.0 / damage).ceil(); + let seconds = ticks / 20.0; + Some(std::time::Duration::from_secs_f64(seconds)) + } } mod block_registration_functions { @@ -536,6 +628,7 @@ define_blocks! { }, model { ("minecraft", "air") }, collision vec![], + hardness 0.0, } Stone { props { @@ -551,6 +644,9 @@ define_blocks! { }, data Some(variant.data()), model { ("minecraft", variant.as_string() ) }, + hardness 1.5, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } Grass { props { @@ -562,6 +658,8 @@ define_blocks! { variant format!("snowy={}", snowy), tint TintType::Grass, update_state (world, pos) => Block::Grass{snowy: is_snowy(world, pos)}, + hardness 0.6, + best_tools [ Tool::Shovel(_), ], } Dirt { props { @@ -597,10 +695,15 @@ define_blocks! { } else { Block::Dirt{snowy, variant} }, + hardness 0.5, + best_tools [ Tool::Shovel(_), ], } Cobblestone { props {}, model { ("minecraft", "cobblestone") }, + hardness 2.0, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } Planks { props { @@ -615,6 +718,8 @@ define_blocks! { }, data Some(variant.plank_data()), model { ("minecraft", format!("{}_planks", variant.as_string()) ) }, + hardness 2.0, + best_tools [ Tool::Axe(_), ], } Sapling { props { @@ -634,6 +739,8 @@ define_blocks! { model { ("minecraft", format!("{}_sapling", variant.as_string()) ) }, variant format!("stage={}", stage), collision vec![], + hardness 0.0, + best_tools [ Tool::Axe(_), ], } Bedrock { props {}, @@ -651,6 +758,7 @@ define_blocks! { }, model { ("minecraft", "flowing_water") }, collision vec![], + hardness 100.0, } Water { props { @@ -663,6 +771,7 @@ define_blocks! { }, model { ("minecraft", "water") }, collision vec![], + hardness 100.0, } FlowingLava { props { @@ -677,6 +786,7 @@ define_blocks! { }, model { ("minecraft", "flowing_lava") }, collision vec![], + hardness 100.0, } Lava { props { @@ -690,6 +800,7 @@ define_blocks! { }, model { ("minecraft", "lava") }, collision vec![], + hardness 100.0, } Sand { props { @@ -697,28 +808,53 @@ define_blocks! { }, data Some(if red { 1 } else { 0 }), model { ("minecraft", if red { "red_sand" } else { "sand" } ) }, + hardness 0.5, + best_tools [ Tool::Shovel(_), ], } Gravel { props {}, model { ("minecraft", "gravel") }, + hardness 0.6, + best_tools [ Tool::Shovel(_), ], } GoldOre { props {}, model { ("minecraft", "gold_ore") }, + hardness 3.0, + harvest_tools [ + Tool::Pickaxe(ToolMaterial::Iron), + Tool::Pickaxe(ToolMaterial::Diamond), + Tool::Pickaxe(ToolMaterial::Netherite), + ], + best_tools [ Tool::Pickaxe(_), ], } IronOre { props {}, model { ("minecraft", "iron_ore") }, + hardness 3.0, + harvest_tools [ + Tool::Pickaxe(ToolMaterial::Stone), + Tool::Pickaxe(ToolMaterial::Iron), + Tool::Pickaxe(ToolMaterial::Diamond), + Tool::Pickaxe(ToolMaterial::Netherite), + ], + best_tools [ Tool::Pickaxe(_), ], } CoalOre { props {}, model { ("minecraft", "coal_ore") }, + hardness 3.0, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } NetherGoldOre { props {}, data None, offsets |protocol_version| { if protocol_version >= 735 { Some(0) } else { None } }, model { ("minecraft", "nether_gold_ore") }, + hardness 3.0, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } Log { props { @@ -751,6 +887,8 @@ define_blocks! { }, model { ("minecraft", format!("{}_log", variant.as_string()) ) }, variant format!("axis={}", axis.as_string()), + hardness 2.0, + best_tools [ Tool::Axe(_), ], } Wood { props { @@ -774,6 +912,8 @@ define_blocks! { offset Some(variant.offset() * 3 + axis.index()), model { ("minecraft", format!("{}_wood", variant.as_string()) ) }, variant format!("axis={}", axis.as_string()), + hardness 2.0, + best_tools [ Tool::Axe(_), ], } Leaves { props { @@ -808,6 +948,8 @@ define_blocks! { material material::LEAVES, model { ("minecraft", format!("{}_leaves", variant.as_string()) ) }, tint TintType::Foliage, + hardness 0.2, + best_tools [ Tool::Shears, Tool::Hoe(_), ], } Sponge { props { @@ -816,19 +958,38 @@ define_blocks! { data Some(if wet { 1 } else { 0 }), model { ("minecraft", "sponge") }, variant format!("wet={}", wet), + hardness 0.6, + best_tools [ Tool::Hoe(_), ], } Glass { props {}, material material::NON_SOLID, model { ("minecraft", "glass") }, + hardness 0.3, } LapisOre { props {}, model { ("minecraft", "lapis_ore") }, + hardness 3.0, + harvest_tools [ + Tool::Pickaxe(ToolMaterial::Stone), + Tool::Pickaxe(ToolMaterial::Iron), + Tool::Pickaxe(ToolMaterial::Diamond), + Tool::Pickaxe(ToolMaterial::Netherite), + ], + best_tools [ Tool::Pickaxe(_), ], } LapisBlock { props {}, model { ("minecraft", "lapis_block") }, + hardness 3.0, + harvest_tools [ + Tool::Pickaxe(ToolMaterial::Stone), + Tool::Pickaxe(ToolMaterial::Iron), + Tool::Pickaxe(ToolMaterial::Diamond), + Tool::Pickaxe(ToolMaterial::Netherite), + ], + best_tools [ Tool::Pickaxe(_), ], } Dispenser { props { @@ -846,6 +1007,9 @@ define_blocks! { offset Some((facing.offset() << 1) | (if triggered { 0 } else { 1 })), model { ("minecraft", "dispenser") }, variant format!("facing={}", facing.as_string()), + hardness 3.5, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } Sandstone { props { @@ -857,6 +1021,9 @@ define_blocks! { }, data Some(variant.data()), model { ("minecraft", variant.as_string() ) }, + hardness 0.8, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } NoteBlock { props { @@ -885,6 +1052,8 @@ define_blocks! { offsets |protocol_version| (instrument.offsets(protocol_version) .map(|offset| offset * (25 * 2) + ((note as usize) << 1) + if powered { 0 } else { 1 })), model { ("minecraft", "noteblock") }, + hardness 0.8, + best_tools [ Tool::Axe(_), ], } Bed { props { @@ -926,6 +1095,7 @@ define_blocks! { model { ("minecraft", "bed") }, variant format!("facing={},part={}", facing.as_string(), part.as_string()), collision vec![Aabb3::new(Point3::new(0.0, 0.0, 0.0), Point3::new(1.0, 9.0/16.0, 1.0))], + hardness 0.2, } GoldenRail { props { @@ -945,6 +1115,8 @@ define_blocks! { model { ("minecraft", "golden_rail") }, variant format!("powered={},shape={}", powered, shape.as_string()), collision vec![], + hardness 0.7, + best_tools [ Tool::Pickaxe(_), ], } DetectorRail { props { @@ -964,6 +1136,8 @@ define_blocks! { model { ("minecraft", "detector_rail") }, variant format!("powered={},shape={}", powered, shape.as_string()), collision vec![], + hardness 0.7, + best_tools [ Tool::Pickaxe(_), ], } StickyPiston { props { @@ -986,12 +1160,17 @@ define_blocks! { model { ("minecraft", "sticky_piston") }, variant format!("extended={},facing={}", extended, facing.as_string()), collision piston_collision(extended, facing), + hardness 0.5, + best_tools [ Tool::Pickaxe(_), ], } Web { props {}, material material::NON_SOLID, model { ("minecraft", "web") }, collision vec![], + hardness 4.0, + harvest_tools [ Tool::Sword(_), Tool::Shears, ], + best_tools [ Tool::Sword(_), Tool::Shears, ], } TallGrass { props { @@ -1007,6 +1186,8 @@ define_blocks! { model { ("minecraft", variant.as_string() ) }, tint TintType::Grass, collision vec![], + hardness 0.0, + best_tools [ Tool::Axe(_), ], } Seagrass { props {}, @@ -1015,6 +1196,7 @@ define_blocks! { material material::NON_SOLID, model { ("minecraft", "seagrass") }, collision vec![], + hardness 0.0, } TallSeagrass { props { @@ -1028,6 +1210,7 @@ define_blocks! { material material::NON_SOLID, model { ("minecraft", "tall_seagrass") }, collision vec![], + hardness 0.0, } DeadBush { props {}, @@ -1035,6 +1218,8 @@ define_blocks! { material material::NON_SOLID, model { ("minecraft", "dead_bush") }, collision vec![], + hardness 0.0, + best_tools [ Tool::Axe(_), ], } Piston { props { @@ -1057,6 +1242,8 @@ define_blocks! { model { ("minecraft", "piston") }, variant format!("extended={},facing={}", extended, facing.as_string()), collision piston_collision(extended, facing), + hardness 0.5, + best_tools [ Tool::Pickaxe(_), ], } PistonHead { props { @@ -1094,6 +1281,8 @@ define_blocks! { Point3::new(max_x, max_y, max_z) )] }, + hardness 0.5, + best_tools [ Tool::Pickaxe(_), ], } Wool { props { @@ -1118,6 +1307,7 @@ define_blocks! { }, data Some(color.data()), model { ("minecraft", format!("{}_wool", color.as_string()) ) }, + hardness 0.8, } ThermalExpansionRockwool { modid "ThermalExpansion:Rockwool", @@ -1143,6 +1333,7 @@ define_blocks! { }, data Some(color.data()), model { ("minecraft", format!("{}_wool", color.as_string()) ) }, + hardness 0.8, } ThermalFoundationRockwool { modid "thermalfoundation:rockwool", @@ -1168,6 +1359,7 @@ define_blocks! { }, data Some(color.data()), model { ("minecraft", format!("{}_wool", color.as_string()) ) }, + hardness 0.8, } PistonExtension { props { @@ -1191,6 +1383,8 @@ define_blocks! { material material::NON_SOLID, model { ("minecraft", "dandelion") }, collision vec![], + hardness 0.0, + best_tools [ Tool::Axe(_), ], } RedFlower { props { @@ -1214,6 +1408,8 @@ define_blocks! { material material::NON_SOLID, model { ("minecraft", variant.as_string()) }, collision vec![], + hardness 0.0, + best_tools [ Tool::Axe(_), ], } BrownMushroom { props {}, @@ -1223,20 +1419,39 @@ define_blocks! { }, model { ("minecraft", "brown_mushroom") }, collision vec![], + hardness 0.0, + best_tools [ Tool::Axe(_), ], } RedMushroom { props {}, material material::NON_SOLID, model { ("minecraft", "red_mushroom") }, collision vec![], + hardness 0.0, + best_tools [ Tool::Axe(_), ], } GoldBlock { props {}, model { ("minecraft", "gold_block") }, + hardness 3.0, + harvest_tools [ + Tool::Pickaxe(ToolMaterial::Iron), + Tool::Pickaxe(ToolMaterial::Diamond), + Tool::Pickaxe(ToolMaterial::Netherite), + ], + best_tools [ Tool::Pickaxe(_), ], } IronBlock { props {}, model { ("minecraft", "iron_block") }, + hardness 5.0, + harvest_tools [ + Tool::Pickaxe(ToolMaterial::Stone), + Tool::Pickaxe(ToolMaterial::Iron), + Tool::Pickaxe(ToolMaterial::Diamond), + Tool::Pickaxe(ToolMaterial::Netherite), + ], + best_tools [ Tool::Pickaxe(_), ], } DoubleStoneSlab { props { @@ -1269,6 +1484,9 @@ define_blocks! { offset None, model { ("minecraft", format!("{}_double_slab", variant.as_string()) ) }, variant if seamless { "all" } else { "normal" }, + hardness 2.0, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } StoneSlab { props { @@ -1290,10 +1508,16 @@ define_blocks! { model { ("minecraft", format!("{}_slab", variant.as_string()) ) }, variant format!("half={}", half.as_string()), collision slab_collision(half), + hardness 2.0, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } BrickBlock { props {}, model { ("minecraft", "brick_block") }, + hardness 2.0, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } TNT { props { @@ -1302,18 +1526,30 @@ define_blocks! { data Some(if explode { 1 } else { 0 }), offset Some(if explode { 0 } else { 1 }), model { ("minecraft", "tnt") }, + hardness 0.0, } BookShelf { props {}, model { ("minecraft", "bookshelf") }, + hardness 1.5, + best_tools [ Tool::Axe(_), ], } MossyCobblestone { props {}, model { ("minecraft", "mossy_cobblestone") }, + hardness 2.0, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } Obsidian { props {}, model { ("minecraft", "obsidian") }, + hardness 50.0, + harvest_tools [ + Tool::Pickaxe(ToolMaterial::Diamond), + Tool::Pickaxe(ToolMaterial::Netherite), + ], + best_tools [ Tool::Pickaxe(_), ], } Torch { props { @@ -1352,6 +1588,7 @@ define_blocks! { model { ("minecraft", "torch") }, variant format!("facing={}", facing.as_string()), collision vec![], + hardness 0.0, } Fire { props { @@ -1394,6 +1631,7 @@ define_blocks! { "east" => east == (val == "true"), _ => false, }, + hardness 0.0, } SoulFire { props {}, @@ -1401,11 +1639,15 @@ define_blocks! { offsets |protocol_version| { if protocol_version >= 735 { Some(0) } else { None } }, model { ("minecraft", "soul_fire") }, collision vec![], + hardness 0.0, } MobSpawner { props {}, material material::NON_SOLID, model { ("minecraft", "mob_spawner") }, + hardness 5.0, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } OakStairs { props { @@ -1432,6 +1674,8 @@ define_blocks! { variant format!("facing={},half={},shape={}", facing.as_string(), half.as_string(), shape.as_string()), collision stair_collision(facing, shape, half), update_state (world, pos) => Block::OakStairs{facing, half, shape: update_stair_shape(world, pos, facing), waterlogged}, + hardness 2.0, + best_tools [ Tool::Axe(_), ], } Chest { props { @@ -1454,6 +1698,8 @@ define_blocks! { facing.horizontal_offset() * (2 * 3)), material material::NON_SOLID, model { ("minecraft", "chest") }, + hardness 2.5, + best_tools [ Tool::Axe(_), ], } RedstoneWire { props { @@ -1495,18 +1741,35 @@ define_blocks! { "east" => val.contains(east.as_string()), _ => false, }, + hardness 0.0, } DiamondOre { props {}, model { ("minecraft", "diamond_ore") }, + hardness 3.0, + harvest_tools [ + Tool::Pickaxe(ToolMaterial::Iron), + Tool::Pickaxe(ToolMaterial::Diamond), + Tool::Pickaxe(ToolMaterial::Netherite), + ], + best_tools [ Tool::Pickaxe(_), ], } DiamondBlock { props {}, model { ("minecraft", "diamond_block") }, + hardness 5.0, + harvest_tools [ + Tool::Pickaxe(ToolMaterial::Iron), + Tool::Pickaxe(ToolMaterial::Diamond), + Tool::Pickaxe(ToolMaterial::Netherite), + ], + best_tools [ Tool::Pickaxe(_), ], } CraftingTable { props {}, model { ("minecraft", "crafting_table") }, + hardness 2.5, + best_tools [ Tool::Axe(_), ], } Wheat { props { @@ -1517,6 +1780,8 @@ define_blocks! { model { ("minecraft", "wheat") }, variant format!("age={}", age), collision vec![], + hardness 0.0, + best_tools [ Tool::Axe(_), ], } Farmland { props { @@ -1530,6 +1795,8 @@ define_blocks! { Point3::new(0.0, 0.0, 0.0), Point3::new(1.0, 15.0/16.0, 1.0) )], + hardness 0.6, + best_tools [ Tool::Shovel(_), ], } Furnace { props { @@ -1545,6 +1812,9 @@ define_blocks! { offset Some(if lit { 0 } else { 1 } + facing.horizontal_offset() * 2), model { ("minecraft", "furnace") }, variant format!("facing={}", facing.as_string()), + hardness 3.5, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } FurnaceLit { props { @@ -1563,6 +1833,9 @@ define_blocks! { }, model { ("minecraft", "lit_furnace") }, variant format!("facing={}", facing.as_string()), + hardness 3.5, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } StandingSign { props { @@ -1610,6 +1883,8 @@ define_blocks! { material material::INVISIBLE, model { ("minecraft", "standing_sign") }, collision vec![], + hardness 1.0, + best_tools [ Tool::Axe(_), ], } WoodenDoor { props { @@ -1634,6 +1909,8 @@ define_blocks! { let (facing, hinge, open, powered) = update_door_state(world, pos, half, facing, hinge, open, powered); Block::WoodenDoor{facing, half, hinge, open, powered} }, + hardness 3.0, + best_tools [ Tool::Axe(_), ], } Ladder { props { @@ -1650,6 +1927,8 @@ define_blocks! { material material::NON_SOLID, model { ("minecraft", "ladder") }, variant format!("facing={}", facing.as_string()), + hardness 0.4, + best_tools [ Tool::Axe(_), ], } Rail { props { @@ -1671,6 +1950,8 @@ define_blocks! { model { ("minecraft", "rail") }, variant format!("shape={}", shape.as_string()), collision vec![], + hardness 0.7, + best_tools [ Tool::Pickaxe(_), ], } StoneStairs { props { @@ -1697,6 +1978,9 @@ define_blocks! { variant format!("facing={},half={},shape={}", facing.as_string(), half.as_string(), shape.as_string()), collision stair_collision(facing, shape, half), update_state (world, pos) => Block::StoneStairs{facing, half, shape: update_stair_shape(world, pos, facing), waterlogged}, + hardness 2.0, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } WallSign { props { @@ -1733,6 +2017,8 @@ define_blocks! { model { ("minecraft", "wall_sign") }, variant format!("facing={}", facing.as_string()), collision vec![], + hardness 1.0, + best_tools [ Tool::Axe(_), ], } Lever { props { @@ -1755,6 +2041,7 @@ define_blocks! { model { ("minecraft", "lever") }, variant format!("facing={},powered={}", face.variant_with_facing(facing), powered), collision vec![], + hardness 0.5, } StonePressurePlate { props { @@ -1766,6 +2053,9 @@ define_blocks! { model { ("minecraft", "stone_pressure_plate") }, variant format!("powered={}", powered), collision vec![], + hardness 0.5, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } IronDoor { props { @@ -1790,6 +2080,9 @@ define_blocks! { let (facing, hinge, open, powered) = update_door_state(world, pos, half, facing, hinge, open, powered); Block::IronDoor{facing, half, hinge, open, powered} }, + hardness 5.0, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } WoodenPressurePlate { props { @@ -1809,6 +2102,8 @@ define_blocks! { model { ("minecraft", "wooden_pressure_plate") }, variant format!("powered={}", powered), collision vec![], + hardness 0.5, + best_tools [ Tool::Axe(_), ], } RedstoneOre { props { @@ -1817,6 +2112,13 @@ define_blocks! { data if !lit { Some(0) } else { None }, offset Some(if lit { 0 } else { 1 }), model { ("minecraft", if lit { "lit_redstone_ore" } else { "redstone_ore" }) }, + hardness 3.0, + harvest_tools [ + Tool::Pickaxe(ToolMaterial::Iron), + Tool::Pickaxe(ToolMaterial::Diamond), + Tool::Pickaxe(ToolMaterial::Netherite), + ], + best_tools [ Tool::Pickaxe(_), ], } RedstoneOreLit { props {}, @@ -1826,6 +2128,7 @@ define_blocks! { ..material::SOLID }, model { ("minecraft", "lit_redstone_ore") }, + hardness 0.0, } RedstoneTorchUnlit { props { @@ -1852,6 +2155,7 @@ define_blocks! { model { ("minecraft", "unlit_redstone_torch") }, variant format!("facing={}", facing.as_string()), collision vec![], + hardness 0.0, } RedstoneTorchLit { props { @@ -1881,6 +2185,7 @@ define_blocks! { model { ("minecraft", "redstone_torch") }, variant format!("facing={}", facing.as_string()), collision vec![], + hardness 0.0, } RedstoneTorchStanding { props { @@ -1892,6 +2197,7 @@ define_blocks! { model { ("minecraft", if lit { "redstone_torch" } else { "unlit_redstone_torch" }) }, variant "facing=up", collision vec![], + hardness 0.0, } RedstoneTorchWall { props { @@ -1912,6 +2218,7 @@ define_blocks! { model { ("minecraft", if lit { "redstone_torch" } else { "unlit_redstone_torch" }) }, variant format!("facing={}", facing.as_string()), collision vec![], + hardness 0.0, } StoneButton { props { @@ -1933,6 +2240,8 @@ define_blocks! { material material::NON_SOLID, model { ("minecraft", "stone_button") }, variant format!("facing={},powered={}", face.variant_with_facing(facing), powered), + hardness 0.5, + best_tools [ Tool::Pickaxe(_), ], } SnowLayer { props { @@ -1946,6 +2255,9 @@ define_blocks! { Point3::new(0.0, 0.0, 0.0), Point3::new(1.0, (f64::from(layers) - 1.0)/8.0, 1.0), )], + hardness 0.1, + harvest_tools [ Tool::Shovel(_), ], + best_tools [ Tool::Shovel(_), ], } Ice { props {}, @@ -1954,10 +2266,15 @@ define_blocks! { ..material::TRANSPARENT }, model { ("minecraft", "ice") }, + hardness 0.5, + best_tools [ Tool::Pickaxe(_), ], } Snow { props {}, model { ("minecraft", "snow") }, + hardness 0.2, + harvest_tools [ Tool::Shovel(_), ], + best_tools [ Tool::Shovel(_), ], } Cactus { props { @@ -1970,10 +2287,13 @@ define_blocks! { Point3::new(1.0/16.0, 0.0, 1.0/16.0), Point3::new(1.0 - (1.0/16.0), 1.0 - (1.0/16.0), 1.0 - (1.0/16.0)) )], + hardness 0.4, } Clay { props {}, model { ("minecraft", "clay") }, + hardness 0.6, + best_tools [ Tool::Shovel(_), ], } Reeds { props { @@ -1984,6 +2304,8 @@ define_blocks! { model { ("minecraft", "reeds") }, tint TintType::Foliage, collision vec![], + hardness 0.0, + best_tools [ Tool::Axe(_), ], } Jukebox { props { @@ -1992,6 +2314,8 @@ define_blocks! { data Some(if has_record { 1 } else { 0 }), offset Some(if has_record { 0 } else { 1 }), model { ("minecraft", "jukebox") }, + hardness 2.0, + best_tools [ Tool::Axe(_), ], } Fence { props { @@ -2021,6 +2345,8 @@ define_blocks! { "east" => east == (val == "true"), _ => false, }, + hardness 2.0, + best_tools [ Tool::Axe(_), ], } PumpkinFace { props { @@ -2036,16 +2362,23 @@ define_blocks! { offset None, model { ("minecraft", "pumpkin") }, variant format!("facing={}", facing.as_string()), + hardness 1.0, + best_tools [ Tool::Shears, Tool::Axe(_), ], } Pumpkin { props {}, data None::, offset Some(0), model { ("minecraft", "pumpkin") }, + hardness 1.0, + best_tools [ Tool::Shears, Tool::Axe(_), ], } Netherrack { props {}, model { ("minecraft", "netherrack") }, + hardness 0.4, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } SoulSand { props {}, @@ -2055,12 +2388,16 @@ define_blocks! { Point3::new(0.0, 0.0, 0.0), Point3::new(1.0, 7.0/8.0, 1.0) )], + hardness 0.5, + best_tools [ Tool::Shovel(_), ], } SoulSoil { props {}, data None, offsets |protocol_version| { if protocol_version >= 735 { Some(0) } else { None } }, model { ("minecraft", "soul_soil") }, + hardness 0.5, + best_tools [ Tool::Shovel(_), ], } Basalt { props { @@ -2075,6 +2412,9 @@ define_blocks! { _ => unreachable!() }) } else { None } }, model { ("minecraft", "basalt") }, + hardness 1.25, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } PolishedBasalt { props { @@ -2089,12 +2429,16 @@ define_blocks! { _ => unreachable!() }) } else { None } }, model { ("minecraft", "polished_basalt") }, + hardness 1.25, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } SoulTorch { props {}, data None, offsets |protocol_version| { if protocol_version >= 735 { Some(0) } else { None } }, model { ("minecraft", "soul_torch") }, + hardness 0.0, } SoulWallTorch { props { @@ -2108,6 +2452,7 @@ define_blocks! { data None, offsets |protocol_version| { if protocol_version >= 735 { Some(facing.offset()) } else { None } }, model { ("minecraft", "soul_wall_torch") }, + hardness 0.0, } Glowstone { props {}, @@ -2116,6 +2461,7 @@ define_blocks! { ..material::SOLID }, model { ("minecraft", "glowstone") }, + hardness 0.3, } Portal { props { @@ -2148,6 +2494,8 @@ define_blocks! { }, model { ("minecraft", "carved_pumpkin") }, variant format!("facing={}", facing.as_string()), + hardness 1.0, + best_tools [ Tool::Shears, Tool::Axe(_), ], } PumpkinLit { props { @@ -2167,6 +2515,8 @@ define_blocks! { }, model { ("minecraft", "lit_pumpkin") }, variant format!("facing={}", facing.as_string()), + hardness 1.0, + best_tools [ Tool::Shears, Tool::Axe(_), ], } Cake { props { @@ -2180,6 +2530,7 @@ define_blocks! { Point3::new((1.0 + (f64::from(bites) * 2.0)) / 16.0, 0.0, 1.0/16.0), Point3::new(1.0 - (1.0/16.0), 0.5, 1.0 - (1.0/16.0)) )], + hardness 0.5, } Repeater { props { @@ -2206,6 +2557,7 @@ define_blocks! { Point3::new(1.0, 1.0/8.0, 1.0) )], update_state (world, pos) => Repeater{delay, facing, locked: update_repeater_state(world, pos, facing), powered}, + hardness 0.0, } RepeaterPowered { props { @@ -2228,6 +2580,7 @@ define_blocks! { Point3::new(1.0, 1.0/8.0, 1.0) )], update_state (world, pos) => RepeaterPowered{delay, facing, locked: update_repeater_state(world, pos, facing)}, + hardness 0.0, } StainedGlass { props { @@ -2253,6 +2606,7 @@ define_blocks! { data Some(color.data()), material material::TRANSPARENT, model { ("minecraft", format!("{}_stained_glass", color.as_string()) ) }, + hardness 0.3, } TrapDoor { props { @@ -2292,6 +2646,8 @@ define_blocks! { model { ("minecraft", "trapdoor") }, variant format!("facing={},half={},open={}", facing.as_string(), half.as_string(), open), collision trapdoor_collision(facing, half, open), + hardness 3.0, + best_tools [ Tool::Axe(_), ], } MonsterEgg { props { @@ -2306,6 +2662,8 @@ define_blocks! { }, data Some(variant.data()), model { ("minecraft", format!("{}_monster_egg", variant.as_string())) }, + hardness if variant == MonsterEggVariant::Stone { 1.0 } else { 0.75 }, + best_tools [ Tool::Pickaxe(_), ], } StoneBrick { props { @@ -2318,6 +2676,9 @@ define_blocks! { }, data Some(variant.data()), model { ("minecraft", variant.as_string() ) }, + hardness 1.5, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } BrownMushroomBlock { props { @@ -2333,6 +2694,8 @@ define_blocks! { offset mushroom_block_offset(is_stem, west, up, south, north, east, down), model { ("minecraft", "brown_mushroom_block") }, variant format!("variant={}", mushroom_block_variant(is_stem, west, up, south, north, east, down)), + hardness 0.2, + best_tools [ Tool::Axe(_), ], } RedMushroomBlock { props { @@ -2348,6 +2711,8 @@ define_blocks! { offset mushroom_block_offset(is_stem, west, up, south, north, east, down), model { ("minecraft", "red_mushroom_block") }, variant format!("variant={}", mushroom_block_variant(is_stem, west, up, south, north, east, down)), + hardness 0.2, + best_tools [ Tool::Axe(_), ], } MushroomStem { props { @@ -2362,6 +2727,8 @@ define_blocks! { offset mushroom_block_offset(false, west, up, south, north, east, down), model { ("minecraft", "mushroom_stem") }, variant "variant=all_stem".to_string(), + hardness 0.2, + best_tools [ Tool::Axe(_), ], } IronBars { props { @@ -2393,6 +2760,9 @@ define_blocks! { "east" => east == (val == "true"), _ => false, }, + hardness 5.0, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } Chain { props { @@ -2421,6 +2791,9 @@ define_blocks! { } }, model { ("minecraft", "chain") }, + hardness 5.0, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } GlassPane { props { @@ -2450,10 +2823,13 @@ define_blocks! { "east" => east == (val == "true"), _ => false, }, + hardness 0.3, } MelonBlock { props {}, model { ("minecraft", "melon_block") }, + hardness 1.0, + best_tools [ Tool::Shears, Tool::Axe(_), ], } AttachedPumpkinStem { props { @@ -2482,6 +2858,8 @@ define_blocks! { Block::AttachedPumpkinStem{facing} }, + hardness 0.0, + best_tools [ Tool::Axe(_), ], } AttachedMelonStem { props { @@ -2510,6 +2888,8 @@ define_blocks! { Block::AttachedMelonStem{facing} }, + hardness 0.0, + best_tools [ Tool::Axe(_), ], } PumpkinStem { props { @@ -2546,6 +2926,8 @@ define_blocks! { Block::PumpkinStem{age, facing} }, + hardness 0.0, + best_tools [ Tool::Axe(_), ], } MelonStem { props { @@ -2582,6 +2964,8 @@ define_blocks! { Block::MelonStem{age, facing} }, + hardness 0.0, + best_tools [ Tool::Axe(_), ], } Vine { props { @@ -2614,6 +2998,8 @@ define_blocks! { let up = mat.renderable && (mat.should_cull_against || mat.never_cull /* Because leaves */); Vine{up, south, west, north, east} }, + hardness 0.2, + best_tools [ Tool::Axe(_), ], } FenceGate { props { @@ -2639,6 +3025,8 @@ define_blocks! { open, powered }, + hardness 2.0, + best_tools [ Tool::Axe(_), ], } BrickStairs { props { @@ -2665,6 +3053,9 @@ define_blocks! { variant format!("facing={},half={},shape={}", facing.as_string(), half.as_string(), shape.as_string()), collision stair_collision(facing, shape, half), update_state (world, pos) => Block::BrickStairs{facing, half, shape: update_stair_shape(world, pos, facing), waterlogged}, + hardness 2.0, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } StoneBrickStairs { props { @@ -2691,6 +3082,9 @@ define_blocks! { variant format!("facing={},half={},shape={}", facing.as_string(), half.as_string(), shape.as_string()), collision stair_collision(facing, shape, half), update_state (world, pos) => Block::StoneBrickStairs{facing, half, shape: update_stair_shape(world, pos, facing), waterlogged}, + hardness 1.5, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } Mycelium { props { @@ -2702,6 +3096,8 @@ define_blocks! { model { ("minecraft", "mycelium") }, variant format!("snowy={}", snowy), update_state (world, pos) => Block::Mycelium{snowy: is_snowy(world, pos)}, + hardness 0.6, + best_tools [ Tool::Shovel(_), ], } Waterlily { props {}, @@ -2712,10 +3108,15 @@ define_blocks! { Point3::new(1.0/16.0, 0.0, 1.0/16.0), Point3::new(15.0/16.0, 3.0/32.0, 15.0/16.0)) ], + hardness 0.0, + best_tools [ Tool::Axe(_), ], } NetherBrick { props {}, model { ("minecraft", "nether_brick") }, + hardness 2.0, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } NetherBrickFence { props { @@ -2753,6 +3154,9 @@ define_blocks! { "east" => east == (val == "true"), _ => false, }, + hardness 2.0, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } NetherBrickStairs { props { @@ -2779,6 +3183,9 @@ define_blocks! { variant format!("facing={},half={},shape={}", facing.as_string(), half.as_string(), shape.as_string()), collision stair_collision(facing, shape, half), update_state (world, pos) => Block::NetherBrickStairs{facing, half, shape: update_stair_shape(world, pos, facing), waterlogged}, + hardness 2.0, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } NetherWart { props { @@ -2789,6 +3196,8 @@ define_blocks! { model { ("minecraft", "nether_wart") }, variant format!("age={}", age), collision vec![], + hardness 0.0, + best_tools [ Tool::Axe(_), ], } EnchantingTable { props {}, @@ -2798,6 +3207,9 @@ define_blocks! { Point3::new(0.0, 0.0, 0.0), Point3::new(1.0, 0.75, 1.0)) ], + hardness 5.0, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } BrewingStand { props { @@ -2822,6 +3234,9 @@ define_blocks! { "has_bottle_2" => (val == "true") == has_bottle_2, _ => false, }, + hardness 0.5, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } Cauldron { props { @@ -2831,6 +3246,9 @@ define_blocks! { material material::NON_SOLID, model { ("minecraft", "cauldron") }, variant format!("level={}", level), + hardness 2.0, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } EndPortal { props {}, @@ -2878,6 +3296,9 @@ define_blocks! { EndStone { props {}, model { ("minecraft", "end_stone") }, + hardness 3.0, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } DragonEgg { props {}, @@ -2890,10 +3311,12 @@ define_blocks! { Point3::new(1.0/16.0, 0.0, 1.0/16.0), Point3::new(15.0/16.0, 1.0, 15.0/16.0) )], + hardness 3.0, } RedstoneLamp { props {}, model { ("minecraft", "redstone_lamp") }, + hardness 0.3, } RedstoneLampLit { props {}, @@ -2902,6 +3325,7 @@ define_blocks! { ..material::NON_SOLID }, model { ("minecraft", "lit_redstone_lamp") }, + hardness 0.3, } DoubleWoodenSlab { props { @@ -2917,6 +3341,7 @@ define_blocks! { data Some(variant.data()), offset None, model { ("minecraft", format!("{}_double_slab", variant.as_string()) ) }, + hardness 2.0, } WoodenSlab { props { @@ -2936,6 +3361,7 @@ define_blocks! { model { ("minecraft", format!("{}_slab", variant.as_string()) ) }, variant format!("half={}", half.as_string()), collision slab_collision(half), + hardness 2.0, } Cocoa { props { @@ -2970,6 +3396,8 @@ define_blocks! { Point3::new(max_x / 16.0, max_y / 16.0, max_z / 16.0)) ] }, + hardness 0.2, + best_tools [ Tool::Axe(_), ], } SandstoneStairs { props { @@ -2996,11 +3424,21 @@ define_blocks! { variant format!("facing={},half={},shape={}", facing.as_string(), half.as_string(), shape.as_string()), collision stair_collision(facing, shape, half), update_state (world, pos) => Block::SandstoneStairs{facing, half, shape: update_stair_shape(world, pos, facing), waterlogged}, + hardness 0.8, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } EmeraldOre { props {}, material material::SOLID, model { ("minecraft", "emerald_ore") }, + hardness 3.0, + harvest_tools [ + Tool::Pickaxe(ToolMaterial::Iron), + Tool::Pickaxe(ToolMaterial::Diamond), + Tool::Pickaxe(ToolMaterial::Netherite), + ], + best_tools [ Tool::Pickaxe(_), ], } EnderChest { props { @@ -3024,6 +3462,9 @@ define_blocks! { Point3::new(1.0/16.0, 0.0, 1.0/16.0), Point3::new(15.0/16.0, 7.0/8.0, 15.0/16.0) )], + hardness 22.5, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } TripwireHook { props { @@ -3046,6 +3487,7 @@ define_blocks! { model { ("minecraft", "tripwire_hook") }, variant format!("attached={},facing={},powered={}", attached, facing.as_string(), powered), collision vec![], + hardness 0.0, } Tripwire { props { @@ -3101,10 +3543,18 @@ define_blocks! { mojang_cant_even } }, + hardness 0.0, } EmeraldBlock { props {}, model { ("minecraft", "emerald_block") }, + hardness 5.0, + harvest_tools [ + Tool::Pickaxe(ToolMaterial::Iron), + Tool::Pickaxe(ToolMaterial::Diamond), + Tool::Pickaxe(ToolMaterial::Netherite), + ], + best_tools [ Tool::Pickaxe(_), ], } SpruceStairs { props { @@ -3131,6 +3581,8 @@ define_blocks! { variant format!("facing={},half={},shape={}", facing.as_string(), half.as_string(), shape.as_string()), collision stair_collision(facing, shape, half), update_state (world, pos) => Block::SpruceStairs{facing, half, shape: update_stair_shape(world, pos, facing), waterlogged}, + hardness 2.0, + best_tools [ Tool::Axe(_), ], } BirchStairs { props { @@ -3157,6 +3609,8 @@ define_blocks! { variant format!("facing={},half={},shape={}", facing.as_string(), half.as_string(), shape.as_string()), collision stair_collision(facing, shape, half), update_state (world, pos) => Block::BirchStairs{facing, half, shape: update_stair_shape(world, pos, facing), waterlogged}, + hardness 2.0, + best_tools [ Tool::Axe(_), ], } JungleStairs { props { @@ -3183,6 +3637,8 @@ define_blocks! { variant format!("facing={},half={},shape={}", facing.as_string(), half.as_string(), shape.as_string()), collision stair_collision(facing, shape, half), update_state (world, pos) => Block::JungleStairs{facing, half, shape: update_stair_shape(world, pos, facing), waterlogged}, + hardness 2.0, + best_tools [ Tool::Axe(_), ], } CommandBlock { props { @@ -3208,6 +3664,7 @@ define_blocks! { ..material::NON_SOLID }, model { ("minecraft", "beacon") }, + hardness 3.0, } CobblestoneWall { props { @@ -3254,6 +3711,9 @@ define_blocks! { "west" => west == (val == "true"), _ => false, }, + hardness 2.0, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } FlowerPot { props { @@ -3292,6 +3752,7 @@ define_blocks! { }, material material::NON_SOLID, model { ("minecraft", "flower_pot") }, + hardness 0.0, } Carrots { props { @@ -3302,6 +3763,8 @@ define_blocks! { model { ("minecraft", "carrots") }, variant format!("age={}", age), collision vec![], + hardness 0.0, + best_tools [ Tool::Axe(_), ], } Potatoes { props { @@ -3312,6 +3775,8 @@ define_blocks! { model { ("minecraft", "potatoes") }, variant format!("age={}", age), collision vec![], + hardness 0.0, + best_tools [ Tool::Axe(_), ], } WoodenButton { props { @@ -3341,6 +3806,8 @@ define_blocks! { material material::NON_SOLID, model { ("minecraft", "wooden_button") }, variant format!("facing={},powered={}", face.variant_with_facing(facing), powered), + hardness 0.5, + best_tools [ Tool::Axe(_), ], } SkullSkeletonWall { props { @@ -3373,6 +3840,7 @@ define_blocks! { Point3::new(max_x, max_y, max_z) )] }, + hardness 1.0, } SkullSkeleton { @@ -3391,6 +3859,7 @@ define_blocks! { Point3::new(max_x, max_y, max_z) )] }, + hardness 1.0, } SkullWitherSkeletonWall { props { @@ -3419,6 +3888,7 @@ define_blocks! { Point3::new(max_x, max_y, max_z) )] }, + hardness 1.0, } SkullWitherSkeleton { props { @@ -3436,6 +3906,7 @@ define_blocks! { Point3::new(max_x, max_y, max_z) )] }, + hardness 1.0, } ZombieWallHead { props { @@ -3450,6 +3921,7 @@ define_blocks! { offset Some(facing.horizontal_offset()), material material::NON_SOLID, model { ("minecraft", "zombie_wall_head") }, + hardness 1.0, } ZombieHead { props { @@ -3459,6 +3931,7 @@ define_blocks! { offset Some(rotation as usize), material material::NON_SOLID, model { ("minecraft", "zombie_head") }, + hardness 1.0, } PlayerWallHead { props { @@ -3473,6 +3946,7 @@ define_blocks! { offset Some(facing.horizontal_offset()), material material::NON_SOLID, model { ("minecraft", "player_wall_head") }, + hardness 1.0, } PlayerHead { props { @@ -3482,6 +3956,7 @@ define_blocks! { offset Some(rotation as usize), material material::NON_SOLID, model { ("minecraft", "player_head") }, + hardness 1.0, } CreeperWallHead { props { @@ -3496,6 +3971,7 @@ define_blocks! { offset Some(facing.horizontal_offset()), material material::NON_SOLID, model { ("minecraft", "creeper_wall_head") }, + hardness 1.0, } CreeperHead { props { @@ -3505,6 +3981,7 @@ define_blocks! { offset Some(rotation as usize), material material::NON_SOLID, model { ("minecraft", "creeper_head") }, + hardness 1.0, } DragonWallHead { props { @@ -3519,6 +3996,7 @@ define_blocks! { offset Some(facing.horizontal_offset()), material material::NON_SOLID, model { ("minecraft", "dragon_wall_head") }, + hardness 1.0, } DragonHead { props { @@ -3528,6 +4006,7 @@ define_blocks! { offset Some(rotation as usize), material material::NON_SOLID, model { ("minecraft", "dragon_head") }, + hardness 1.0, } Anvil { props { @@ -3555,6 +4034,9 @@ define_blocks! { )], _ => unreachable!(), }, + hardness 5.0, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } TrappedChest { props { @@ -3582,6 +4064,8 @@ define_blocks! { Point3::new(1.0/16.0, 0.0, 1.0/16.0), Point3::new(15.0/16.0, 7.0/8.0, 15.0/16.0) )], + hardness 2.5, + best_tools [ Tool::Axe(_), ], } LightWeightedPressurePlate { props { @@ -3592,6 +4076,9 @@ define_blocks! { model { ("minecraft", "light_weighted_pressure_plate") }, variant format!("power={}", power), collision vec![], + hardness 0.5, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } HeavyWeightedPressurePlate { props { @@ -3602,6 +4089,9 @@ define_blocks! { model { ("minecraft", "heavy_weighted_pressure_plate") }, variant format!("power={}", power), collision vec![], + hardness 0.5, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } ComparatorUnpowered { props { @@ -3627,6 +4117,7 @@ define_blocks! { Point3::new(0.0, 0.0, 0.0), Point3::new(1.0, 1.0/8.0, 1.0) )], + hardness 0.0, } ComparatorPowered { props { @@ -3650,6 +4141,7 @@ define_blocks! { Point3::new(0.0, 0.0, 0.0), Point3::new(1.0, 1.0/8.0, 1.0) )], + hardness 0.0, } DaylightDetector { props { @@ -3665,14 +4157,22 @@ define_blocks! { Point3::new(0.0, 0.0, 0.0), Point3::new(1.0, 3.0/8.0, 1.0) )], + hardness 0.2, + best_tools [ Tool::Axe(_), ], } RedstoneBlock { props {}, model { ("minecraft", "redstone_block") }, + hardness 5.0, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } QuartzOre { props {}, model { ("minecraft", "quartz_ore") }, + hardness 3.0, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } Hopper { props { @@ -3697,6 +4197,9 @@ define_blocks! { material material::NON_SOLID, model { ("minecraft", "hopper") }, variant format!("facing={}", facing.as_string()), + hardness 3.0, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } QuartzBlock { props { @@ -3717,6 +4220,9 @@ define_blocks! { QuartzVariant::PillarEastWest => "quartz_column", } ) }, variant variant.as_string(), + hardness 0.8, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } QuartzStairs { props { @@ -3743,6 +4249,9 @@ define_blocks! { variant format!("facing={},half={},shape={}", facing.as_string(), half.as_string(), shape.as_string()), collision stair_collision(facing, shape, half), update_state (world, pos) => Block::QuartzStairs{facing, half, shape: update_stair_shape(world, pos, facing), waterlogged}, + hardness 0.8, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } ActivatorRail { props { @@ -3762,6 +4271,8 @@ define_blocks! { model { ("minecraft", "activator_rail") }, variant format!("powered={},shape={}", powered, shape.as_string()), collision vec![], + hardness 0.7, + best_tools [ Tool::Pickaxe(_), ], } Dropper { props { @@ -3779,6 +4290,9 @@ define_blocks! { offset Some(if triggered { 0 } else { 1 } + facing.offset() * 2), model { ("minecraft", "dropper") }, variant format!("facing={}", facing.as_string()), + hardness 3.5, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } StainedHardenedClay { props { @@ -3803,6 +4317,9 @@ define_blocks! { }, data Some(color.data()), model { ("minecraft", format!("{}_stained_hardened_clay", color.as_string()) ) }, + hardness 1.25, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } StainedGlassPane { props { @@ -3851,6 +4368,7 @@ define_blocks! { "west" => west == (val == "true"), _ => false, }, + hardness 0.3, } Leaves2 { props { @@ -3868,6 +4386,8 @@ define_blocks! { material material::LEAVES, model { ("minecraft", format!("{}_leaves", variant.as_string()) ) }, tint TintType::Foliage, + hardness 0.2, + best_tools [ Tool::Shears, Tool::Hoe(_), ], } Log2 { props { @@ -3881,6 +4401,8 @@ define_blocks! { offset None, model { ("minecraft", format!("{}_log", variant.as_string()) ) }, variant format!("axis={}", axis.as_string()), + hardness 2.0, + best_tools [ Tool::Axe(_), ], } AcaciaStairs { props { @@ -3907,6 +4429,8 @@ define_blocks! { variant format!("facing={},half={},shape={}", facing.as_string(), half.as_string(), shape.as_string()), collision stair_collision(facing, shape, half), update_state (world, pos) => Block::AcaciaStairs{facing, half, shape: update_stair_shape(world, pos, facing), waterlogged}, + hardness 2.0, + best_tools [ Tool::Axe(_), ], } DarkOakStairs { props { @@ -3933,11 +4457,14 @@ define_blocks! { variant format!("facing={},half={},shape={}", facing.as_string(), half.as_string(), shape.as_string()), collision stair_collision(facing, shape, half), update_state (world, pos) => Block::DarkOakStairs{facing, half, shape: update_stair_shape(world, pos, facing), waterlogged}, + hardness 2.0, + best_tools [ Tool::Axe(_), ], } Slime { props {}, material material::TRANSPARENT, model { ("minecraft", "slime") }, + hardness 0.0, } Barrier { props {}, @@ -3973,6 +4500,9 @@ define_blocks! { model { ("minecraft", "iron_trapdoor") }, variant format!("facing={},half={},open={}", facing.as_string(), half.as_string(), open), collision trapdoor_collision(facing, half, open), + hardness 5.0, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } Prismarine { props { @@ -3984,6 +4514,9 @@ define_blocks! { }, data Some(variant.data()), model { ("minecraft", variant.as_string() ) }, + hardness 1.5, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } PrismarineStairs { props { @@ -4019,6 +4552,9 @@ define_blocks! { variant format!("facing={},half={},shape={}", facing.as_string(), half.as_string(), shape.as_string()), collision stair_collision(facing, shape, half), update_state (world, pos) => Block::PrismarineStairs{facing, half, shape: update_stair_shape(world, pos, facing), waterlogged, variant}, + hardness 1.5, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } PrismarineSlab { props { @@ -4044,6 +4580,9 @@ define_blocks! { }) }, variant format!("type={}", type_.as_string()), collision slab_collision(type_), + hardness 1.5, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } SeaLantern { props {}, @@ -4052,6 +4591,7 @@ define_blocks! { ..material::SOLID }, model { ("minecraft", "sea_lantern") }, + hardness 0.3, } HayBlock { props { @@ -4061,6 +4601,8 @@ define_blocks! { offset Some(match axis { Axis::X => 0, Axis::Y => 1, Axis::Z => 2, _ => unreachable!() }), model { ("minecraft", "hay_block") }, variant format!("axis={}", axis.as_string()), + hardness 0.5, + best_tools [ Tool::Hoe(_), ], } Carpet { props { @@ -4090,18 +4632,27 @@ define_blocks! { Point3::new(0.0, 0.0, 0.0), Point3::new(1.0, 1.0/16.0, 1.0) )], + hardness 0.1, } HardenedClay { props {}, model { ("minecraft", "hardened_clay") }, + hardness 1.25, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } CoalBlock { props {}, model { ("minecraft", "coal_block") }, + hardness 5.0, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } PackedIce { props {}, model { ("minecraft", "packed_ice") }, + hardness 0.5, + best_tools [ Tool::Pickaxe(_), ], } DoublePlant { props { @@ -4126,6 +4677,8 @@ define_blocks! { let (half, variant) = update_double_plant_state(world, pos, half, variant); Block::DoublePlant{half, variant} }, + hardness 0.0, + best_tools [ Tool::Axe(_), ], } StandingBanner { props { @@ -4171,6 +4724,8 @@ define_blocks! { material material::NON_SOLID, model { ("minecraft", "standing_banner") }, variant format!("rotation={}", rotation.as_string()), + hardness 1.0, + best_tools [ Tool::Axe(_), ], } WallBanner { props { @@ -4204,6 +4759,8 @@ define_blocks! { material material::NON_SOLID, model { ("minecraft", "wall_banner") }, variant format!("facing={}", facing.as_string()), + hardness 1.0, + best_tools [ Tool::Axe(_), ], } DaylightDetectorInverted { props { @@ -4218,6 +4775,7 @@ define_blocks! { Point3::new(0.0, 0.0, 0.0), Point3::new(1.0, 3.0/8.0, 1.0) )], + hardness 0.2, } RedSandstone { props { @@ -4229,6 +4787,9 @@ define_blocks! { }, data Some(variant.data()), model { ("minecraft", variant.as_string()) }, + hardness 0.8, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } RedSandstoneStairs { props { @@ -4255,6 +4816,9 @@ define_blocks! { variant format!("facing={},half={},shape={}", facing.as_string(), half.as_string(), shape.as_string()), collision stair_collision(facing, shape, half), update_state (world, pos) => Block::RedSandstoneStairs{facing, half, shape: update_stair_shape(world, pos, facing), waterlogged}, + hardness 0.8, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } WoodenSlabFlat { props { @@ -4279,6 +4843,8 @@ define_blocks! { model { ("minecraft", format!("{}_slab", variant.as_string()) ) }, variant format!("type={}", type_.as_string()), collision slab_collision(type_), + hardness 2.0, + best_tools [ Tool::Axe(_), ], } StoneSlabFlat { props { @@ -4308,6 +4874,9 @@ define_blocks! { model { ("minecraft", format!("{}_slab", variant.as_string()) ) }, variant format!("type={}", type_.as_string()), collision slab_collision(type_), + hardness 2.0, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } DoubleStoneSlab2 { props { @@ -4321,6 +4890,9 @@ define_blocks! { material material::SOLID, model { ("minecraft", format!("{}_double_slab", variant.as_string()) ) }, variant if seamless { "all" } else { "normal" }, + hardness 2.0, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } StoneSlab2 { props { @@ -4333,6 +4905,9 @@ define_blocks! { model { ("minecraft", format!("{}_slab", variant.as_string()) ) }, variant format!("half={}", half.as_string()), collision slab_collision(half), + hardness 2.0, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } SmoothStone { props { @@ -4352,6 +4927,9 @@ define_blocks! { _ => unreachable!(), }), model { ("minecraft", format!("smooth_{}", variant.as_string()) ) }, + hardness 2.0, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } SpruceFenceGate { props { @@ -4377,6 +4955,8 @@ define_blocks! { open, powered }, + hardness 2.0, + best_tools [ Tool::Axe(_), ], } BirchFenceGate { props { @@ -4402,6 +4982,8 @@ define_blocks! { open, powered }, + hardness 2.0, + best_tools [ Tool::Axe(_), ], } JungleFenceGate { props { @@ -4427,6 +5009,8 @@ define_blocks! { open, powered }, + hardness 2.0, + best_tools [ Tool::Axe(_), ], } DarkOakFenceGate { props { @@ -4452,6 +5036,8 @@ define_blocks! { open, powered }, + hardness 2.0, + best_tools [ Tool::Axe(_), ], } AcaciaFenceGate { props { @@ -4477,6 +5063,8 @@ define_blocks! { open, powered }, + hardness 2.0, + best_tools [ Tool::Axe(_), ], } SpruceFence { props { @@ -4506,6 +5094,8 @@ define_blocks! { "east" => east == (val == "true"), _ => false, }, + hardness 2.0, + best_tools [ Tool::Axe(_), ], } BirchFence { props { @@ -4535,6 +5125,8 @@ define_blocks! { "east" => east == (val == "true"), _ => false, }, + hardness 2.0, + best_tools [ Tool::Axe(_), ], } JungleFence { props { @@ -4564,6 +5156,8 @@ define_blocks! { "east" => east == (val == "true"), _ => false, }, + hardness 2.0, + best_tools [ Tool::Axe(_), ], } DarkOakFence { props { @@ -4593,6 +5187,8 @@ define_blocks! { "east" => east == (val == "true"), _ => false, }, + hardness 2.0, + best_tools [ Tool::Axe(_), ], } AcaciaFence { props { @@ -4622,6 +5218,8 @@ define_blocks! { "east" => east == (val == "true"), _ => false, }, + hardness 2.0, + best_tools [ Tool::Axe(_), ], } SpruceDoor { props { @@ -4646,6 +5244,8 @@ define_blocks! { let (facing, hinge, open, powered) = update_door_state(world, pos, half, facing, hinge, open, powered); Block::SpruceDoor{facing, half, hinge, open, powered} }, + hardness 3.0, + best_tools [ Tool::Axe(_), ], } BirchDoor { props { @@ -4670,6 +5270,8 @@ define_blocks! { let (facing, hinge, open, powered) = update_door_state(world, pos, half, facing, hinge, open, powered); Block::BirchDoor{facing, half, hinge, open, powered} }, + hardness 3.0, + best_tools [ Tool::Axe(_), ], } JungleDoor { props { @@ -4694,6 +5296,8 @@ define_blocks! { let (facing, hinge, open, powered) = update_door_state(world, pos, half, facing, hinge, open, powered); Block::JungleDoor{facing, half, hinge, open, powered} }, + hardness 3.0, + best_tools [ Tool::Axe(_), ], } AcaciaDoor { props { @@ -4718,6 +5322,8 @@ define_blocks! { let (facing, hinge, open, powered) = update_door_state(world, pos, half, facing, hinge, open, powered); Block::AcaciaDoor{facing, half, hinge, open, powered} }, + hardness 3.0, + best_tools [ Tool::Axe(_), ], } DarkOakDoor { props { @@ -4742,6 +5348,8 @@ define_blocks! { let (facing, hinge, open, powered) = update_door_state(world, pos, half, facing, hinge, open, powered); Block::DarkOakDoor{facing, half, hinge, open, powered} }, + hardness 3.0, + best_tools [ Tool::Axe(_), ], } EndRod { props { @@ -4779,6 +5387,7 @@ define_blocks! { _ => unreachable!(), } }, + hardness 0.0, } ChorusPlant { props { @@ -4865,6 +5474,8 @@ define_blocks! { "west" => west == (val == "true"), _ => false, }, + hardness 0.4, + best_tools [ Tool::Axe(_), ], } ChorusFlower { props { @@ -4874,10 +5485,15 @@ define_blocks! { material material::NON_SOLID, model { ("minecraft", "chorus_flower") }, variant format!("age={}", age), + hardness 0.4, + best_tools [ Tool::Axe(_), ], } PurpurBlock { props {}, model { ("minecraft", "purpur_block") }, + hardness 1.5, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } PurpurPillar { props { @@ -4887,6 +5503,9 @@ define_blocks! { offset Some(match axis { Axis::X => 0, Axis::Y => 1, Axis::Z => 2, _ => unreachable!() }), model { ("minecraft", "purpur_pillar") }, variant format!("axis={}", axis.as_string()), + hardness 1.5, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } PurpurStairs { props { @@ -4913,6 +5532,9 @@ define_blocks! { variant format!("facing={},half={},shape={}", facing.as_string(), half.as_string(), shape.as_string()), collision stair_collision(facing, shape, half), update_state (world, pos) => Block::PurpurStairs{facing, half, shape: update_stair_shape(world, pos, facing), waterlogged}, + hardness 1.5, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } PurpurDoubleSlab { props { @@ -4920,6 +5542,9 @@ define_blocks! { }, offset None, model { ("minecraft", format!("{}_double_slab", variant.as_string()) ) }, + hardness 1.5, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } PurpurSlab { props { @@ -4932,10 +5557,16 @@ define_blocks! { model { ("minecraft", format!("{}_slab", variant.as_string()) ) }, variant format!("half={},variant=default", half.as_string()), collision slab_collision(half), + hardness 1.5, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } EndBricks { props {}, model { ("minecraft", "end_bricks") }, + hardness 3.0, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } Beetroots { props { @@ -4946,6 +5577,8 @@ define_blocks! { model { ("minecraft", "beetroots") }, variant format!("age={}", age), collision vec![], + hardness 0.0, + best_tools [ Tool::Axe(_), ], } GrassPath { props {}, @@ -4955,6 +5588,8 @@ define_blocks! { Point3::new(0.0, 0.0, 0.0), Point3::new(1.0, 15.0/16.0, 1.0) )], + hardness 0.65, + best_tools [ Tool::Shovel(_), ], } EndGateway { props {}, @@ -5003,18 +5638,27 @@ define_blocks! { data if age == 0 { Some(0) } else { None }, offset Some(age as usize), model { ("minecraft", "frosted_ice") }, + hardness 0.5, } MagmaBlock { props {}, model { ("minecraft", "magma") }, + hardness 0.5, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } NetherWartBlock { props {}, model { ("minecraft", "nether_wart_block") }, + hardness 1.0, + best_tools [ Tool::Hoe(_), ], } RedNetherBrick { props {}, model { ("minecraft", "red_nether_brick") }, + hardness 2.0, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } BoneBlock { props { @@ -5024,6 +5668,9 @@ define_blocks! { offset Some(match axis { Axis::X => 0, Axis::Y => 1, Axis::Z => 2, _ => unreachable!() }), model { ("minecraft", "bone_block") }, variant format!("axis={}", axis.as_string()), + hardness 2.0, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } StructureVoid { props {}, @@ -5034,6 +5681,7 @@ define_blocks! { model { ("minecraft", "structure_void") }, // TODO: a small hit box but no collision collision vec![], + hardness 0.0, } Observer { props { @@ -5051,6 +5699,9 @@ define_blocks! { offset Some(if powered { 0 } else { 1 } + facing.offset() * 2), model { ("minecraft", "observer") }, variant format!("facing={},powered={}", facing.as_string(), powered), + hardness 3.0, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } // TODO: Shulker box textures (1.11+), since there is no model, we use wool for now // The textures should be built from textures/blocks/shulker_top_.png @@ -5069,6 +5720,8 @@ define_blocks! { data None::, offset Some(facing.offset()), model { ("minecraft", "sponge") }, + hardness 2.0, + best_tools [ Tool::Pickaxe(_), ], } WhiteShulkerBox { props { @@ -5084,6 +5737,8 @@ define_blocks! { data Some(facing.index()), offset Some(facing.offset()), model { ("minecraft", "white_wool") }, + hardness 2.0, + best_tools [ Tool::Pickaxe(_), ], } OrangeShulkerBox { props { @@ -5099,6 +5754,8 @@ define_blocks! { data Some(facing.index()), offset Some(facing.offset()), model { ("minecraft", "orange_wool") }, + hardness 2.0, + best_tools [ Tool::Pickaxe(_), ], } MagentaShulkerBox { props { @@ -5114,6 +5771,8 @@ define_blocks! { data Some(facing.index()), offset Some(facing.offset()), model { ("minecraft", "magenta_wool") }, + hardness 2.0, + best_tools [ Tool::Pickaxe(_), ], } LightBlueShulkerBox { props { @@ -5129,6 +5788,8 @@ define_blocks! { data Some(facing.index()), offset Some(facing.offset()), model { ("minecraft", "light_blue_wool") }, + hardness 2.0, + best_tools [ Tool::Pickaxe(_), ], } YellowShulkerBox { props { @@ -5144,6 +5805,8 @@ define_blocks! { data Some(facing.index()), offset Some(facing.offset()), model { ("minecraft", "yellow_wool") }, + hardness 2.0, + best_tools [ Tool::Pickaxe(_), ], } LimeShulkerBox { props { @@ -5159,6 +5822,8 @@ define_blocks! { data Some(facing.index()), offset Some(facing.offset()), model { ("minecraft", "lime_wool") }, + hardness 2.0, + best_tools [ Tool::Pickaxe(_), ], } PinkShulkerBox { props { @@ -5174,6 +5839,8 @@ define_blocks! { data Some(facing.index()), offset Some(facing.offset()), model { ("minecraft", "pink_wool") }, + hardness 2.0, + best_tools [ Tool::Pickaxe(_), ], } GrayShulkerBox { props { @@ -5189,6 +5856,8 @@ define_blocks! { data Some(facing.index()), offset Some(facing.offset()), model { ("minecraft", "gray_wool") }, + hardness 2.0, + best_tools [ Tool::Pickaxe(_), ], } LightGrayShulkerBox { props { @@ -5204,6 +5873,8 @@ define_blocks! { data Some(facing.index()), offset Some(facing.offset()), model { ("minecraft", "light_gray_wool") }, + hardness 2.0, + best_tools [ Tool::Pickaxe(_), ], } CyanShulkerBox { props { @@ -5219,6 +5890,8 @@ define_blocks! { data Some(facing.index()), offset Some(facing.offset()), model { ("minecraft", "cyan_wool") }, + hardness 2.0, + best_tools [ Tool::Pickaxe(_), ], } PurpleShulkerBox { props { @@ -5234,6 +5907,8 @@ define_blocks! { data Some(facing.index()), offset Some(facing.offset()), model { ("minecraft", "purple_wool") }, + hardness 2.0, + best_tools [ Tool::Pickaxe(_), ], } BlueShulkerBox { props { @@ -5249,6 +5924,8 @@ define_blocks! { data Some(facing.index()), offset Some(facing.offset()), model { ("minecraft", "blue_wool") }, + hardness 2.0, + best_tools [ Tool::Pickaxe(_), ], } BrownShulkerBox { props { @@ -5264,6 +5941,8 @@ define_blocks! { data Some(facing.index()), offset Some(facing.offset()), model { ("minecraft", "brown_wool") }, + hardness 2.0, + best_tools [ Tool::Pickaxe(_), ], } GreenShulkerBox { props { @@ -5279,6 +5958,8 @@ define_blocks! { data Some(facing.index()), offset Some(facing.offset()), model { ("minecraft", "green_wool") }, + hardness 2.0, + best_tools [ Tool::Pickaxe(_), ], } RedShulkerBox { props { @@ -5294,6 +5975,8 @@ define_blocks! { data Some(facing.index()), offset Some(facing.offset()), model { ("minecraft", "red_wool") }, + hardness 2.0, + best_tools [ Tool::Pickaxe(_), ], } BlackShulkerBox { props { @@ -5309,6 +5992,8 @@ define_blocks! { data Some(facing.index()), offset Some(facing.offset()), model { ("minecraft", "black_wool") }, + hardness 2.0, + best_tools [ Tool::Pickaxe(_), ], } WhiteGlazedTerracotta { props { @@ -5323,6 +6008,9 @@ define_blocks! { offset Some(facing.horizontal_offset()), model { ("minecraft", "white_glazed_terracotta") }, variant format!("facing={}", facing.as_string()), + hardness 1.4, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } OrangeGlazedTerracotta { props { @@ -5337,6 +6025,9 @@ define_blocks! { offset Some(facing.horizontal_offset()), model { ("minecraft", "orange_glazed_terracotta") }, variant format!("facing={}", facing.as_string()), + hardness 1.4, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } MagentaGlazedTerracotta { props { @@ -5351,6 +6042,9 @@ define_blocks! { offset Some(facing.horizontal_offset()), model { ("minecraft", "magenta_glazed_terracotta") }, variant format!("facing={}", facing.as_string()), + hardness 1.4, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } LightBlueGlazedTerracotta { props { @@ -5365,6 +6059,9 @@ define_blocks! { offset Some(facing.horizontal_offset()), model { ("minecraft", "light_blue_glazed_terracotta") }, variant format!("facing={}", facing.as_string()), + hardness 1.4, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } YellowGlazedTerracotta { props { @@ -5379,6 +6076,9 @@ define_blocks! { offset Some(facing.horizontal_offset()), model { ("minecraft", "yellow_glazed_terracotta") }, variant format!("facing={}", facing.as_string()), + hardness 1.4, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } LimeGlazedTerracotta { props { @@ -5393,6 +6093,9 @@ define_blocks! { offset Some(facing.horizontal_offset()), model { ("minecraft", "lime_glazed_terracotta") }, variant format!("facing={}", facing.as_string()), + hardness 1.4, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } PinkGlazedTerracotta { props { @@ -5407,6 +6110,9 @@ define_blocks! { offset Some(facing.horizontal_offset()), model { ("minecraft", "pink_glazed_terracotta") }, variant format!("facing={}", facing.as_string()), + hardness 1.4, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } GrayGlazedTerracotta { props { @@ -5421,6 +6127,9 @@ define_blocks! { offset Some(facing.horizontal_offset()), model { ("minecraft", "gray_glazed_terracotta") }, variant format!("facing={}", facing.as_string()), + hardness 1.4, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } LightGrayGlazedTerracotta { props { @@ -5435,6 +6144,9 @@ define_blocks! { offset Some(facing.horizontal_offset()), model { ("minecraft", "silver_glazed_terracotta") }, variant format!("facing={}", facing.as_string()), + hardness 1.4, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } CyanGlazedTerracotta { props { @@ -5449,6 +6161,9 @@ define_blocks! { offset Some(facing.horizontal_offset()), model { ("minecraft", "cyan_glazed_terracotta") }, variant format!("facing={}", facing.as_string()), + hardness 1.4, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } PurpleGlazedTerracotta { props { @@ -5463,6 +6178,9 @@ define_blocks! { offset Some(facing.horizontal_offset()), model { ("minecraft", "purple_glazed_terracotta") }, variant format!("facing={}", facing.as_string()), + hardness 1.4, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } BlueGlazedTerracotta { props { @@ -5477,6 +6195,9 @@ define_blocks! { offset Some(facing.horizontal_offset()), model { ("minecraft", "blue_glazed_terracotta") }, variant format!("facing={}", facing.as_string()), + hardness 1.4, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } BrownGlazedTerracotta { props { @@ -5491,6 +6212,9 @@ define_blocks! { offset Some(facing.horizontal_offset()), model { ("minecraft", "brown_glazed_terracotta") }, variant format!("facing={}", facing.as_string()), + hardness 1.4, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } GreenGlazedTerracotta { props { @@ -5505,6 +6229,9 @@ define_blocks! { offset Some(facing.horizontal_offset()), model { ("minecraft", "green_glazed_terracotta") }, variant format!("facing={}", facing.as_string()), + hardness 1.4, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } RedGlazedTerracotta { props { @@ -5519,6 +6246,9 @@ define_blocks! { offset Some(facing.horizontal_offset()), model { ("minecraft", "red_glazed_terracotta") }, variant format!("facing={}", facing.as_string()), + hardness 1.4, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } BlackGlazedTerracotta { props { @@ -5533,6 +6263,9 @@ define_blocks! { offset Some(facing.horizontal_offset()), model { ("minecraft", "black_glazed_terracotta") }, variant format!("facing={}", facing.as_string()), + hardness 1.4, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } Concrete { props { @@ -5557,6 +6290,9 @@ define_blocks! { }, data Some(color.data()), model { ("minecraft", format!("{}_concrete", color.as_string()) ) }, + hardness 1.8, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } ConcretePowder { props { @@ -5581,6 +6317,8 @@ define_blocks! { }, data Some(color.data()), model { ("minecraft", format!("{}_concrete_powder", color.as_string()) ) }, + hardness 0.5, + best_tools [ Tool::Shovel(_), ], } Kelp { props { @@ -5589,18 +6327,22 @@ define_blocks! { data None::, offset Some(age as usize), model { ("minecraft", "kelp") }, + hardness 0.0, } KelpPlant { props {}, data None::, offset Some(0), model { ("minecraft", "kelp_plant") }, + hardness 0.0, } DriedKelpBlock { props {}, data None::, offset Some(0), model { ("minecraft", "dried_kelp_block") }, + hardness 0.5, + best_tools [ Tool::Hoe(_), ], } TurtleEgg { props { @@ -5610,6 +6352,7 @@ define_blocks! { data None::, offset Some((hatch as usize) + ((age - 1) as usize) * 3), model { ("minecraft", "turtle_egg") }, + hardness 0.5, } CoralBlock { props { @@ -5629,6 +6372,9 @@ define_blocks! { data None::, offset Some(variant.offset()), model { ("minecraft", format!("{}_block", variant.as_string())) }, + hardness 1.5, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } Coral { props { @@ -5649,6 +6395,9 @@ define_blocks! { data None::, offset Some(if waterlogged { 0 } else { 1 } + variant.offset() * 2), model { ("minecraft", variant.as_string()) }, + hardness 0.0, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } CoralWallFan { props { @@ -5677,6 +6426,9 @@ define_blocks! { facing.horizontal_offset() * 2 + variant.offset() * (2 * 4)), model { ("minecraft", format!("{}_wall_fan", variant.as_string())) }, + hardness 0.0, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } CoralFan { props { @@ -5698,6 +6450,9 @@ define_blocks! { offset Some(if waterlogged { 0 } else { 1 } + variant.offset() * 2), model { ("minecraft", format!("{}_fan", variant.as_string())) }, + hardness 0.0, + harvest_tools [ Tool::Pickaxe(_), ], + best_tools [ Tool::Pickaxe(_), ], } SeaPickle { props { @@ -5709,12 +6464,15 @@ define_blocks! { ((age - 1) as usize) * 2), model { ("minecraft", "sea_pickle") }, variant format!("age={}", age), + hardness 0.0, } BlueIce { props {}, data None::, offset Some(0), model { ("minecraft", "blue_ice") }, + hardness 2.8, + best_tools [ Tool::Pickaxe(_), ], } Conduit { props { @@ -5724,6 +6482,7 @@ define_blocks! { offset Some(if waterlogged { 0 } else { 1 }), material material::NON_SOLID, model { ("minecraft", "conduit") }, + hardness 3.0, } VoidAir { props {}, @@ -5735,6 +6494,7 @@ define_blocks! { }, model { ("minecraft", "air") }, collision vec![], + hardness 0.0, } CaveAir { props {}, @@ -5746,6 +6506,7 @@ define_blocks! { }, model { ("minecraft", "air") }, collision vec![], + hardness 0.0, } BubbleColumn { props { @@ -5754,18 +6515,21 @@ define_blocks! { data None::, offset Some(if drag { 0 } else { 1 }), model { ("minecraft", "bubble_column") }, + hardness 0.0, } Missing253 { props {}, data Some(0), offset None, model { ("steven", "missing_block") }, + hardness 0.0, } Missing254 { props {}, data Some(0), offset None, model { ("steven", "missing_block") }, + hardness 0.0, } StructureBlock { props { @@ -5880,6 +6644,236 @@ mod tests { } ); } + + #[test] + fn verify_blocks() { + let dirt = Block::Dirt { + snowy: true, + variant: DirtVariant::Normal, + }; + let stone = Block::Stone { + variant: StoneVariant::Normal, + }; + let vine = Block::Vine { + up: false, + south: false, + west: false, + north: true, + east: false, + }; + let pumpkin_lit = Block::PumpkinLit { + facing: Direction::North, + without_face: true, + }; + let cocoa = Block::Cocoa { + age: 1, + facing: Direction::North, + }; + let leaves = Block::Leaves { + variant: TreeVariant::Oak, + decayable: false, + check_decay: false, + distance: 1, + }; + let leaves2 = Block::Leaves2 { + variant: TreeVariant::Oak, + decayable: false, + check_decay: false, + }; + let wool = Block::Wool { + color: ColoredVariant::White, + }; + let tall_seagrass = Block::TallSeagrass { + half: TallSeagrassHalf::Upper, + }; + let data = [ + (dirt, None, Some(0.75)), + (dirt, Some(Tool::Shovel(ToolMaterial::Wood)), Some(0.4)), + (dirt, Some(Tool::Pickaxe(ToolMaterial::Wood)), Some(0.75)), + (stone, None, Some(7.5)), + (stone, Some(Tool::Shovel(ToolMaterial::Wood)), Some(7.5)), + (stone, Some(Tool::Pickaxe(ToolMaterial::Wood)), Some(1.15)), + (Block::Obsidian {}, None, Some(250.0)), + ( + Block::Obsidian {}, + Some(Tool::Pickaxe(ToolMaterial::Wood)), + Some(125.0), + ), + ( + Block::Obsidian {}, + Some(Tool::Pickaxe(ToolMaterial::Stone)), + Some(62.5), + ), + ( + Block::Obsidian {}, + Some(Tool::Pickaxe(ToolMaterial::Iron)), + Some(41.7), + ), + ( + Block::Obsidian {}, + Some(Tool::Pickaxe(ToolMaterial::Diamond)), + Some(9.4), + ), + ( + Block::Obsidian {}, + Some(Tool::Pickaxe(ToolMaterial::Netherite)), + Some(8.35), + ), + ( + Block::Obsidian {}, + Some(Tool::Pickaxe(ToolMaterial::Gold)), + Some(20.85), + ), + (Block::Bedrock {}, None, None), + ( + Block::Bedrock {}, + Some(Tool::Pickaxe(ToolMaterial::Wood)), + None, + ), + ( + Block::Bedrock {}, + Some(Tool::Pickaxe(ToolMaterial::Stone)), + None, + ), + ( + Block::Bedrock {}, + Some(Tool::Pickaxe(ToolMaterial::Iron)), + None, + ), + ( + Block::Bedrock {}, + Some(Tool::Pickaxe(ToolMaterial::Diamond)), + None, + ), + ( + Block::Bedrock {}, + Some(Tool::Pickaxe(ToolMaterial::Netherite)), + None, + ), + ( + Block::Bedrock {}, + Some(Tool::Pickaxe(ToolMaterial::Gold)), + None, + ), + (Block::Web {}, None, Some(20.0)), + ( + Block::Web {}, + Some(Tool::Pickaxe(ToolMaterial::Wood)), + Some(20.0), + ), + (vine, None, Some(0.3)), + (vine, Some(Tool::Pickaxe(ToolMaterial::Wood)), Some(0.3)), + (vine, Some(Tool::Axe(ToolMaterial::Wood)), Some(0.15)), + (vine, Some(Tool::Axe(ToolMaterial::Stone)), Some(0.1)), + (vine, Some(Tool::Axe(ToolMaterial::Iron)), Some(0.05)), + (vine, Some(Tool::Axe(ToolMaterial::Diamond)), Some(0.05)), + (wool, None, Some(1.2)), + (leaves, None, Some(0.3)), + (leaves, Some(Tool::Hoe(ToolMaterial::Wood)), Some(0.15)), + (leaves, Some(Tool::Hoe(ToolMaterial::Stone)), Some(0.1)), + (leaves, Some(Tool::Hoe(ToolMaterial::Iron)), Some(0.05)), + (leaves, Some(Tool::Hoe(ToolMaterial::Diamond)), Some(0.05)), + (leaves2, None, Some(0.3)), + (leaves2, Some(Tool::Hoe(ToolMaterial::Wood)), Some(0.15)), + (leaves2, Some(Tool::Hoe(ToolMaterial::Stone)), Some(0.1)), + (leaves2, Some(Tool::Hoe(ToolMaterial::Iron)), Some(0.05)), + (leaves2, Some(Tool::Hoe(ToolMaterial::Diamond)), Some(0.05)), + (Block::DeadBush {}, None, Some(0.05)), + (Block::DeadBush {}, Some(Tool::Shears), Some(0.05)), + (Block::Seagrass {}, None, Some(0.05)), + (Block::Seagrass {}, Some(Tool::Shears), Some(0.05)), + (tall_seagrass, None, Some(0.05)), + (tall_seagrass, Some(Tool::Shears), Some(0.05)), + (cocoa, None, Some(0.3)), + (cocoa, Some(Tool::Axe(ToolMaterial::Wood)), Some(0.15)), + (cocoa, Some(Tool::Axe(ToolMaterial::Stone)), Some(0.1)), + (cocoa, Some(Tool::Axe(ToolMaterial::Iron)), Some(0.05)), + (cocoa, Some(Tool::Axe(ToolMaterial::Diamond)), Some(0.05)), + (Block::MelonBlock {}, None, Some(1.5)), + ( + Block::MelonBlock {}, + Some(Tool::Axe(ToolMaterial::Wood)), + Some(0.75), + ), + ( + Block::MelonBlock {}, + Some(Tool::Axe(ToolMaterial::Stone)), + Some(0.4), + ), + ( + Block::MelonBlock {}, + Some(Tool::Axe(ToolMaterial::Iron)), + Some(0.25), + ), + ( + Block::MelonBlock {}, + Some(Tool::Axe(ToolMaterial::Diamond)), + Some(0.2), + ), + ( + Block::MelonBlock {}, + Some(Tool::Axe(ToolMaterial::Netherite)), + Some(0.2), + ), + ( + Block::MelonBlock {}, + Some(Tool::Axe(ToolMaterial::Gold)), + Some(0.15), + ), + (Block::Pumpkin {}, None, Some(1.5)), + ( + Block::Pumpkin {}, + Some(Tool::Axe(ToolMaterial::Wood)), + Some(0.75), + ), + ( + Block::Pumpkin {}, + Some(Tool::Axe(ToolMaterial::Stone)), + Some(0.4), + ), + ( + Block::Pumpkin {}, + Some(Tool::Axe(ToolMaterial::Iron)), + Some(0.25), + ), + (pumpkin_lit, None, Some(1.5)), + (pumpkin_lit, Some(Tool::Axe(ToolMaterial::Wood)), Some(0.75)), + (pumpkin_lit, Some(Tool::Axe(ToolMaterial::Stone)), Some(0.4)), + (pumpkin_lit, Some(Tool::Axe(ToolMaterial::Iron)), Some(0.25)), + // TODO: Fix special sword rules + //(Block::Web {}, Some(Tool::Sword(ToolMaterial::Wood)), Some(0.4)), + //(Block::Web {}, Some(Tool::Sword(ToolMaterial::Stone)), Some(0.4)), + //(cocoa, Some(Tool::Sword(ToolMaterial::Stone)), Some(0.2)), + //(leaves, Some(Tool::Sword(ToolMaterial::St + //(leaves2, Some(Tool::Sword(ToolMaterial::Stone)), Some(0.2)), + //(Block::MelonBlock {}, Some(Tool::Sword(ToolMaterial::Stone)), Some(1.0)), + //(Block::Pumpkin {}, Some(Tool::Sword(ToolMaterial::Stone)), Some(1.0)), + //(pumpkin_lit, Some(Tool::Sword(ToolMaterial::Stone)), Some(1.0)), + //(vine, Some(Tool::Sword(ToolMaterial::Stone)), Some(0.2)), + + // TODO: Fix special shears rules + //(Block::Web {}, Some(Tool::Shears), Some(0.4)), + //(wool, Some(Tool::Shears), Some(0.25)), + //(leaves, Some(Tool::Shears), Some(0.05)), + //(leaves2, Some(Tool::Shears), Some(0.05)), + //(vine, Some(Tool::Shears), Some(0.3)), + ]; + for (block, tool, time) in data { + let result = block.get_mining_time(&tool).map(|d| d.as_secs_f64()); + match (time, result) { + (Some(time), Some(result)) => assert_eq!(result, time, + "Expected to mine block {:?} with {:?} in {} seconds, but it took {} seconds", + block, tool, time, result), + (None, Some(result)) => panic!( + "Expected to never mine block {:?} with {:?}, but it took {} seconds", + block, tool, result), + (Some(time), None) => panic!( + "Expected to mine block {:?} with {:?} in {} seconds, but it will never be mined", + block, tool, time), + _ => {}, + } + } + } } fn can_burn(world: &W, pos: Position) -> bool { @@ -7899,3 +8893,49 @@ impl CoralVariant { } } } + +#[derive(Clone, Copy, Debug)] +pub enum ToolMaterial { + Wood, + Stone, + Gold, + Iron, + Diamond, + Netherite, +} + +impl ToolMaterial { + fn get_multiplier(&self) -> f64 { + match *self { + ToolMaterial::Wood => 2.0, + ToolMaterial::Stone => 4.0, + ToolMaterial::Gold => 12.0, + ToolMaterial::Iron => 6.0, + ToolMaterial::Diamond => 8.0, + ToolMaterial::Netherite => 9.0, + } + } +} + +#[derive(Clone, Copy, Debug)] +pub enum Tool { + Pickaxe(ToolMaterial), + Axe(ToolMaterial), + Shovel(ToolMaterial), + Hoe(ToolMaterial), + Sword(ToolMaterial), + Shears, +} + +impl Tool { + fn get_multiplier(&self) -> f64 { + match *self { + Tool::Pickaxe(m) => m.get_multiplier(), + Tool::Axe(m) => m.get_multiplier(), + Tool::Shovel(m) => m.get_multiplier(), + Tool::Hoe(m) => m.get_multiplier(), + Tool::Sword(_) => 1.5, // TODO: Handle different block values. + Tool::Shears => 2.0, // TODO: Handle different block values. + } + } +} diff --git a/protocol/src/protocol/mod.rs b/protocol/src/protocol/mod.rs index 043d180..33377f5 100644 --- a/protocol/src/protocol/mod.rs +++ b/protocol/src/protocol/mod.rs @@ -948,19 +948,36 @@ impl fmt::Debug for VarLong { impl Serializable for Position { fn read_from(buf: &mut R) -> Result { let pos = buf.read_u64::()?; - Ok(Position::new( - ((pos as i64) >> 38) as i32, - ((pos as i64) & 0xFFF) as i32, - ((pos as i64) << 26 >> 38) as i32, - )) + let protocol_version = current_protocol_version(); + if protocol_version < 477 { + Ok(Position::new( + ((pos as i64) >> 38) as i32, + (((pos as i64) >> 26) & 0xFFF) as i32, + ((pos as i64) << 38 >> 38) as i32, + )) + } else { + Ok(Position::new( + ((pos as i64) >> 38) as i32, + ((pos as i64) << 52 >> 52) as i32, + ((pos as i64) << 26 >> 38) as i32, + )) + } } fn write_to(&self, buf: &mut W) -> Result<(), Error> { - let pos = (((self.x as u64) & 0x3FFFFFF) << 38) - | ((self.y as u64) & 0xFFF) - | (((self.z as u64) & 0x3FFFFFF) << 12); + let pos; + let protocol_version = current_protocol_version(); + if protocol_version < 477 { + pos = (((self.x as u64) & 0x3FFFFFF) << 38) + | (((self.y as u64) & 0xFFF) << 26) + | ((self.z as u64) & 0x3FFFFFF); + } else { + pos = (((self.x as u64) & 0x3FFFFFF) << 38) + | ((self.y as u64) & 0xFFF) + | (((self.z as u64) & 0x3FFFFFF) << 12); + } buf.write_u64::(pos)?; - Result::Ok(()) + Ok(()) } } diff --git a/src/entity/mod.rs b/src/entity/mod.rs index d5c80bf..526b9fd 100644 --- a/src/entity/mod.rs +++ b/src/entity/mod.rs @@ -1,3 +1,5 @@ +use steven_blocks as block; +use steven_protocol::protocol::packet; pub mod block_entity; pub mod player; @@ -23,6 +25,8 @@ pub fn add_systems(m: &mut ecs::Manager) { m.add_render_system(sys); let sys = systems::LightEntity::new(m); m.add_render_system(sys); + let sys = systems::ApplyDigging::new(m); + m.add_render_system(sys); block_entity::add_systems(m); } @@ -161,3 +165,36 @@ impl Light { Default::default() } } + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct DiggingState { + pub block: block::Block, + pub position: shared::Position, + pub face: shared::Direction, + pub start: std::time::Instant, + pub finished: bool, +} + +#[derive(Default)] +pub struct Digging { + pub last: Option, + pub current: Option, +} + +impl Digging { + pub fn new() -> Self { + Default::default() + } +} + +#[derive(Default)] +pub struct MouseButtons { + pub left: bool, + pub right: bool, +} + +impl MouseButtons { + pub fn new() -> Self { + Default::default() + } +} diff --git a/src/entity/player.rs b/src/entity/player.rs index a0b6da8..c1fc976 100644 --- a/src/entity/player.rs +++ b/src/entity/player.rs @@ -1,5 +1,6 @@ use super::{ - Bounds, GameInfo, Gravity, Light, Position, Rotation, TargetPosition, TargetRotation, Velocity, + Bounds, Digging, GameInfo, Gravity, Light, MouseButtons, Position, Rotation, TargetPosition, + TargetRotation, Velocity, }; use crate::ecs; use crate::format; @@ -43,6 +44,8 @@ pub fn create_local(m: &mut ecs::Manager) -> ecs::Entity { ); m.add_component_direct(entity, PlayerModel::new("", false, false, true)); m.add_component_direct(entity, Light::new()); + m.add_component_direct(entity, Digging::new()); + m.add_component_direct(entity, MouseButtons::new()); entity } diff --git a/src/entity/systems.rs b/src/entity/systems.rs index af3101f..fe58960 100644 --- a/src/entity/systems.rs +++ b/src/entity/systems.rs @@ -1,9 +1,15 @@ +use std::sync::Arc; +use std::sync::RwLock; + use super::*; use crate::ecs; use crate::render; use crate::shared::Position as BPos; use crate::world; use cgmath::InnerSpace; +use log::debug; +use steven_protocol::protocol; +use steven_protocol::protocol::Conn; pub struct ApplyVelocity { filter: ecs::Filter, @@ -285,3 +291,186 @@ impl ecs::System for LightEntity { } } } + +pub struct ApplyDigging { + filter: ecs::Filter, + mouse_buttons: ecs::Key, + digging: ecs::Key, + conn: ecs::Key>>>, +} + +impl ApplyDigging { + pub fn new(m: &mut ecs::Manager) -> Self { + let mouse_buttons = m.get_key(); + let digging = m.get_key(); + Self { + filter: ecs::Filter::new().with(mouse_buttons).with(digging), + mouse_buttons, + digging, + conn: m.get_key(), + } + } + + fn send_packet(&self, conn: &mut Conn, target: &DiggingState, state: i32) { + match state { + 0 => debug!("Send start dig packet {:?}", target), + 1 => debug!("Send cancel dig packet {:?}", target), + 2 => debug!("Send finish dig packet {:?}", target), + n => panic!("Invalid dig state {}", n), + } + + match conn.protocol_version { + // 1.7.10 + 5 => conn + .write_packet(packet::play::serverbound::PlayerDigging_u8_u8y { + status: state as u8, + x: target.position.x, + y: target.position.y as u8, + z: target.position.z, + face: target.face.index() as u8, + }) + .unwrap(), + // 1.8.9 or v15w39c + 47 | 74 => conn + .write_packet(packet::play::serverbound::PlayerDigging_u8 { + status: state as u8, + location: target.position, + face: target.face.index() as u8, + }) + .unwrap(), + // 1.9+ + _ => conn + .write_packet(packet::play::serverbound::PlayerDigging { + status: protocol::VarInt(state), + location: target.position, + face: target.face.index() as u8, + }) + .unwrap(), + } + } + + fn next_state( + &self, + last: &Option, + mouse_buttons: &MouseButtons, + target: Option<( + shared::Position, + block::Block, + shared::Direction, + Vector3, + )>, + ) -> Option { + // Figure out the next state + if !mouse_buttons.left { + return None; + } + + match (last, target) { + // Started digging + (None, Some((position, block, face, _))) => Some(DiggingState { + block, + face, + position, + start: std::time::Instant::now(), + finished: false, + }), + (Some(current), Some((position, block, face, _))) => { + // Continue digging + if position == current.position { + return last.clone(); + } + + // Start digging a different block. + Some(DiggingState { + block, + face, + position, + start: std::time::Instant::now(), + finished: false, + }) + } + // Not pointing at any target + (_, None) => None, + } + } + + fn is_finished(&self, state: &DiggingState) -> bool { + let mining_time = state.block.get_mining_time(&None); + match mining_time { + Some(mining_time) => { + let finish_time = state.start + mining_time; + finish_time > std::time::Instant::now() + } + None => false, + } + } +} + +impl ecs::System for ApplyDigging { + fn filter(&self) -> &ecs::Filter { + &self.filter + } + + fn update( + &mut self, + m: &mut ecs::Manager, + world: &mut world::World, + renderer: &mut render::Renderer, + ) { + use crate::server::target::{test_block, trace_ray}; + use cgmath::EuclideanSpace; + + let world_entity = m.get_world(); + let mut conn = m + .get_component(world_entity, self.conn) + .unwrap() + .write() + .unwrap(); + let conn = match conn.as_mut() { + Some(conn) => conn, + // Don't keep processing digging operations if the connection was + // closed. + None => return, + }; + + let target = trace_ray( + world, + 4.0, + renderer.camera.pos.to_vec(), + renderer.view_vector.cast().unwrap(), + test_block, + ); + + for e in m.find(&self.filter) { + let mouse_buttons = m.get_component(e, self.mouse_buttons).unwrap(); + let digging = m.get_component_mut(e, self.digging).unwrap(); + + // Update last and current state + std::mem::swap(&mut digging.last, &mut digging.current); + digging.current = self.next_state(&digging.last, mouse_buttons, target); + + // Handle digging packets + match (&digging.last, &mut digging.current) { + // Start the new digging operation. + (None, Some(current)) => self.send_packet(conn, current, 0), + // Cancel the previous digging operation. + (Some(last), None) if !last.finished => self.send_packet(conn, last, 1), + // Move to digging a new block + (Some(last), Some(current)) if last.position != current.position => { + // Cancel the previous digging operation. + if !current.finished { + self.send_packet(conn, last, 1); + } + // Start the new digging operation. + self.send_packet(conn, current, 0); + } + // Finish the new digging operation. + (Some(_), Some(current)) if !self.is_finished(current) && !current.finished => { + self.send_packet(conn, current, 2); + current.finished = true; + } + _ => {} + } + } + } +} diff --git a/src/main.rs b/src/main.rs index 1a05158..d3c5d6b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -738,9 +738,25 @@ fn handle_window_event( height, ); } + + if game.focused { + game.server.on_left_mouse_button(false); + } + } + (ElementState::Pressed, MouseButton::Left) => { + if game.focused { + game.server.on_left_mouse_button(true); + } + } + (ElementState::Released, MouseButton::Right) => { + if game.focused { + game.server.on_right_mouse_button(false); + game.server.on_right_click(&mut game.renderer); + } } (ElementState::Pressed, MouseButton::Right) => { if game.focused { + game.server.on_right_mouse_button(true); game.server.on_right_click(&mut game.renderer); } } diff --git a/src/server/mod.rs b/src/server/mod.rs index 93beeb8..c49d5eb 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -41,7 +41,7 @@ pub mod target; pub struct Server { uuid: protocol::UUID, - conn: Option, + conn: Arc>>, protocol_version: i32, forge_mods: Vec, read_queue: Option>>, @@ -61,6 +61,7 @@ pub struct Server { // Entity accessors game_info: ecs::Key, player_movement: ecs::Key, + mouse_buttons: ecs::Key, gravity: ecs::Key, position: ecs::Key, target_position: ecs::Key, @@ -168,7 +169,7 @@ impl Server { forge_mods, protocol::UUID::from_str(&val.uuid).unwrap(), resources, - Some(write), + Arc::new(RwLock::new(Some(write))), Some(rx), )); } @@ -185,7 +186,7 @@ impl Server { forge_mods, val.uuid, resources, - Some(write), + Arc::new(RwLock::new(Some(write))), Some(rx), )); } @@ -329,7 +330,7 @@ impl Server { forge_mods, uuid, resources, - Some(write), + Arc::new(RwLock::new(Some(write))), Some(rx), )) } @@ -357,7 +358,7 @@ impl Server { vec![], protocol::UUID::default(), resources, - None, + Arc::new(RwLock::new(None)), None, ); let mut rng = rand::thread_rng(); @@ -445,7 +446,7 @@ impl Server { forge_mods: Vec, uuid: protocol::UUID, resources: Arc>, - conn: Option, + conn: Arc>>, read_queue: Option>>, ) -> Server { let mut entities = ecs::Manager::new(); @@ -454,6 +455,7 @@ impl Server { let world_entity = entities.get_world(); let game_info = entities.get_key(); entities.add_component(world_entity, game_info, entity::GameInfo::new()); + entities.add_component(world_entity, entities.get_key(), conn.clone()); let version = resources.read().unwrap().version(); Server { @@ -477,6 +479,7 @@ impl Server { // Entity accessors game_info, player_movement: entities.get_key(), + mouse_buttons: entities.get_key(), gravity: entities.get_key(), position: entities.get_key(), target_position: entities.get_key(), @@ -500,7 +503,7 @@ impl Server { } pub fn disconnect(&mut self, reason: Option) { - self.conn = None; + self.conn.write().unwrap().take(); self.disconnect_reason = reason; if let Some(player) = self.player.take() { self.entities.remove_entity(player); @@ -509,7 +512,7 @@ impl Server { } pub fn is_connected(&self) -> bool { - self.conn.is_some() + self.conn.read().unwrap().is_some() } pub fn tick(&mut self, renderer: &mut render::Renderer, delta: f64) { @@ -656,12 +659,12 @@ impl Server { Err(err) => panic!("Err: {:?}", err), } // Disconnected - if self.conn.is_none() { + if self.conn.read().unwrap().is_none() { break; } } - if self.conn.is_some() { + if self.conn.read().unwrap().is_some() { self.read_queue = Some(rx); } } @@ -793,6 +796,24 @@ impl Server { } } + pub fn on_left_mouse_button(&mut self, pressed: bool) { + if let Some(player) = self.player { + if let Some(mouse_buttons) = self.entities.get_component_mut(player, self.mouse_buttons) + { + mouse_buttons.left = pressed; + } + } + } + + pub fn on_right_mouse_button(&mut self, pressed: bool) { + if let Some(player) = self.player { + if let Some(mouse_buttons) = self.entities.get_component_mut(player, self.mouse_buttons) + { + mouse_buttons.right = pressed; + } + } + } + pub fn on_right_click(&mut self, renderer: &mut render::Renderer) { use crate::shared::Direction; if self.player.is_some() { @@ -900,8 +921,9 @@ impl Server { } } - pub fn write_packet(&mut self, p: T) { - let _ = self.conn.as_mut().unwrap().write_packet(p); // TODO handle errors + pub fn write_packet(&self, p: T) { + let mut conn = self.conn.write().unwrap(); + let _ = conn.as_mut().unwrap().write_packet(p); // TODO handle errors } fn on_keep_alive_i64( @@ -1048,15 +1070,13 @@ impl Server { // TODO: remove wrappers and directly call on Conn fn write_fmlhs_plugin_message(&mut self, msg: &forge::FmlHs) { - let _ = self.conn.as_mut().unwrap().write_fmlhs_plugin_message(msg); // TODO handle errors + let mut conn = self.conn.write().unwrap(); + let _ = conn.as_mut().unwrap().write_fmlhs_plugin_message(msg); // TODO handle errors } fn write_plugin_message(&mut self, channel: &str, data: &[u8]) { - let _ = self - .conn - .as_mut() - .unwrap() - .write_plugin_message(channel, data); // TODO handle errors + let mut conn = self.conn.write().unwrap(); + let _ = conn.as_mut().unwrap().write_plugin_message(channel, data); // TODO handle errors } fn on_game_join_worldnames_ishard_simdist(