alex/beta-minecraft-rust
Minecraft Beta 1.7.3 Rewrite
BRANCH main
SNAPSHOT Draw terrain GPU-driven from packed per-section buffers with GPU occlusion culling, add a render distance slider (2-128) with a quad/disk shape, cut chunk bookkeeping at high render distances, and fix a light underflow at y=0
DOWNLOAD
root crates common src world block.rs
use super::Material;
/// Block id as stored in chunks and sent over the wire.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
#[repr(transparent)]
pub struct Block(pub u8);
/// How the block is tessellated, mirroring `Block.getRenderType`.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Shape {
None,
Cube,
Cross,
Torch,
Fire,
Fluid,
Wire,
Crops,
Door,
Ladder,
Rail,
Stairs,
Fence,
Lever,
Cactus,
Bed,
Repeater,
PistonBase,
PistonExtension,
}
/// `StepSound`: the sounds of walking on, placing and breaking a block.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum StepSound {
Powder,
Stone,
Wood,
Gravel,
Grass,
Cloth,
Sand,
Metal,
Glass,
}
impl StepSound {
/// `getStepSound`.
pub const fn step(self) -> &'static str {
match self {
Self::Powder | Self::Stone | Self::Metal | Self::Glass => "step.stone",
Self::Wood => "step.wood",
Self::Gravel => "step.gravel",
Self::Grass => "step.grass",
Self::Cloth => "step.cloth",
Self::Sand => "step.sand",
}
}
/// `stepSoundDir`: the sound of placing and breaking.
pub const fn dig(self) -> &'static str {
match self {
Self::Glass => "random.glass",
Self::Sand => "step.gravel",
_ => self.step(),
}
}
pub const fn volume(self) -> f32 {
1.0
}
pub const fn pitch(self) -> f32 {
match self {
Self::Metal => 1.5,
_ => 1.0,
}
}
}
/// Render pass, mirroring `Block.getRenderBlockPass`.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Layer {
Opaque,
Translucent,
}
#[derive(Clone, Copy, Debug)]
pub struct Properties {
pub name: &'static str,
pub material: Material,
pub texture: u8,
pub shape: Shape,
pub layer: Layer,
/// Value of the static `Block.opaqueCubeLookup` table.
pub opaque_cube: bool,
pub light_opacity: u8,
pub light_emission: u8,
/// `Block.renderAsNormalBlock`.
pub normal: bool,
/// Owns a tile entity (`BlockContainer`).
pub container: bool,
/// `blockHardness`, -1 for unbreakable blocks.
pub hardness: f32,
/// `blockResistance`, five times the explosion resistance.
pub resistance: f32,
pub sound: StepSound,
pub slipperiness: f32,
}
impl Properties {
const fn new(name: &'static str, material: Material, texture: u8) -> Self {
Self {
name,
material,
texture,
shape: Shape::Cube,
layer: Layer::Opaque,
opaque_cube: true,
light_opacity: 255,
light_emission: 0,
normal: true,
container: false,
hardness: 0.0,
resistance: 0.0,
sound: StepSound::Powder,
slipperiness: 0.6,
}
}
const fn strength(mut self, hardness: f32, resistance: f32, sound: StepSound) -> Self {
self.hardness = hardness;
self.resistance = resistance;
self.sound = sound;
self
}
const fn slippery(mut self, slipperiness: f32) -> Self {
self.slipperiness = slipperiness;
self
}
const fn shape(mut self, shape: Shape) -> Self {
self.shape = shape;
self.special()
}
/// A transparent block that does not render as a full cube.
const fn special(mut self) -> Self {
self.normal = false;
self.transparent()
}
const fn container(mut self) -> Self {
self.container = true;
self
}
const fn transparent(mut self) -> Self {
self.opaque_cube = false;
self.light_opacity = 0;
self
}
const fn translucent(mut self) -> Self {
self.layer = Layer::Translucent;
self
}
const fn opacity(mut self, opacity: u8) -> Self {
self.light_opacity = opacity;
self
}
const fn emission(mut self, emission: u8) -> Self {
self.light_emission = emission;
self
}
}
macro_rules! blocks {
($($id:literal $const_name:ident = $props:expr;)*) => {
impl Block {
$(pub const $const_name: Self = Self($id);)*
}
static PROPERTIES: [Option<Properties>; 256] = {
let mut table = [None; 256];
$(table[$id] = Some($props);)*
table
};
};
}
use Material as M;
use Properties as P;
use Shape as S;
use StepSound as St;
blocks! {
1 STONE = P::new("stone", M::Rock, 1).strength(1.5, 30.0, St::Stone);
2 GRASS = P::new("grass", M::Grass, 3).strength(0.6, 3.0, St::Grass);
3 DIRT = P::new("dirt", M::Ground, 2).strength(0.5, 2.5, St::Gravel);
4 COBBLESTONE = P::new("stonebrick", M::Rock, 16).strength(2.0, 30.0, St::Stone);
5 PLANKS = P::new("wood", M::Wood, 4).strength(2.0, 15.0, St::Wood);
6 SAPLING = P::new("sapling", M::Plants, 15).shape(S::Cross).strength(0.0, 0.0, St::Grass);
7 BEDROCK = P::new("bedrock", M::Rock, 17).strength(-1.0, 18000000.0, St::Stone);
8 FLOWING_WATER = P::new("water", M::Water, 205).shape(S::Fluid).opacity(3).translucent().strength(100.0, 500.0, St::Powder);
9 WATER = P::new("water", M::Water, 205).shape(S::Fluid).opacity(3).translucent().strength(100.0, 500.0, St::Powder);
10 FLOWING_LAVA = P::new("lava", M::Lava, 237).shape(S::Fluid).opacity(255).emission(15).strength(0.0, 0.0, St::Powder);
11 LAVA = P::new("lava", M::Lava, 237).shape(S::Fluid).opacity(255).emission(15).strength(100.0, 500.0, St::Powder);
12 SAND = P::new("sand", M::Sand, 18).strength(0.5, 2.5, St::Sand);
13 GRAVEL = P::new("gravel", M::Sand, 19).strength(0.6, 3.0, St::Gravel);
14 GOLD_ORE = P::new("oreGold", M::Rock, 32).strength(3.0, 15.0, St::Stone);
15 IRON_ORE = P::new("oreIron", M::Rock, 33).strength(3.0, 15.0, St::Stone);
16 COAL_ORE = P::new("oreCoal", M::Rock, 34).strength(3.0, 15.0, St::Stone);
17 LOG = P::new("log", M::Wood, 20).strength(2.0, 10.0, St::Wood);
18 LEAVES = P::new("leaves", M::Leaves, 52).opacity(1).strength(0.2, 1.0, St::Grass);
19 SPONGE = P::new("sponge", M::Sponge, 48).strength(0.6, 3.0, St::Grass);
20 GLASS = P::new("glass", M::Glass, 49).transparent().strength(0.3, 1.5, St::Glass);
21 LAPIS_ORE = P::new("oreLapis", M::Rock, 160).strength(3.0, 15.0, St::Stone);
22 LAPIS_BLOCK = P::new("blockLapis", M::Rock, 144).strength(3.0, 15.0, St::Stone);
23 DISPENSER = P::new("dispenser", M::Rock, 45).container().strength(3.5, 17.5, St::Stone);
24 SANDSTONE = P::new("sandStone", M::Rock, 192).strength(0.8, 4.0, St::Stone);
25 NOTE_BLOCK = P::new("musicBlock", M::Wood, 74).container().strength(0.8, 4.0, St::Powder);
26 BED = P::new("bed", M::Cloth, 134).shape(S::Bed).strength(0.2, 1.0, St::Powder);
27 POWERED_RAIL = P::new("goldenRail", M::Circuits, 179).shape(S::Rail).strength(0.7, 3.5, St::Metal);
28 DETECTOR_RAIL = P::new("detectorRail", M::Circuits, 195).shape(S::Rail).strength(0.7, 3.5, St::Metal);
29 STICKY_PISTON = P::new("pistonStickyBase", M::Piston, 106).shape(S::PistonBase).strength(0.5, 2.5, St::Stone);
30 WEB = P::new("web", M::Web, 11).shape(S::Cross).opacity(1).strength(4.0, 20.0, St::Powder);
31 TALL_GRASS = P::new("tallgrass", M::Plants, 39).shape(S::Cross).strength(0.0, 0.0, St::Grass);
32 DEAD_BUSH = P::new("deadbush", M::Plants, 55).shape(S::Cross).strength(0.0, 0.0, St::Grass);
33 PISTON = P::new("pistonBase", M::Piston, 107).shape(S::PistonBase).strength(0.5, 2.5, St::Stone);
34 PISTON_HEAD = P::new("pistonExtension", M::Piston, 107).shape(S::PistonExtension).strength(0.5, 2.5, St::Stone);
35 WOOL = P::new("cloth", M::Cloth, 64).strength(0.8, 4.0, St::Cloth);
36 MOVING_PISTON = P::new("pistonMoving", M::Piston, 0).shape(S::None).container().strength(-1.0, 0.0, St::Powder);
37 DANDELION = P::new("flower", M::Plants, 13).shape(S::Cross).strength(0.0, 0.0, St::Grass);
38 ROSE = P::new("rose", M::Plants, 12).shape(S::Cross).strength(0.0, 0.0, St::Grass);
39 BROWN_MUSHROOM = P::new("mushroom", M::Plants, 29).shape(S::Cross).emission(1).strength(0.0, 0.0, St::Grass);
40 RED_MUSHROOM = P::new("mushroom", M::Plants, 28).shape(S::Cross).strength(0.0, 0.0, St::Grass);
41 GOLD_BLOCK = P::new("blockGold", M::Iron, 23).strength(3.0, 30.0, St::Metal);
42 IRON_BLOCK = P::new("blockIron", M::Iron, 22).strength(5.0, 30.0, St::Metal);
43 DOUBLE_SLAB = P::new("stoneSlab", M::Rock, 6).strength(2.0, 30.0, St::Stone);
44 SLAB = P::new("stoneSlab", M::Rock, 6).special().opacity(255).strength(2.0, 30.0, St::Stone);
45 BRICKS = P::new("brick", M::Rock, 7).strength(2.0, 30.0, St::Stone);
46 TNT = P::new("tnt", M::Tnt, 8).strength(0.0, 0.0, St::Grass);
47 BOOKSHELF = P::new("bookshelf", M::Wood, 35).strength(1.5, 7.5, St::Wood);
48 MOSSY_COBBLESTONE = P::new("stoneMoss", M::Rock, 36).strength(2.0, 30.0, St::Stone);
49 OBSIDIAN = P::new("obsidian", M::Rock, 37).strength(10.0, 6000.0, St::Stone);
50 TORCH = P::new("torch", M::Circuits, 80).shape(S::Torch).emission(14).strength(0.0, 0.0, St::Wood);
51 FIRE = P::new("fire", M::Fire, 31).shape(S::Fire).emission(15).strength(0.0, 0.0, St::Wood);
52 SPAWNER = P::new("mobSpawner", M::Rock, 65).transparent().container().strength(5.0, 25.0, St::Metal);
53 OAK_STAIRS = P::new("stairsWood", M::Wood, 4).shape(S::Stairs).opacity(255).strength(2.0, 15.0, St::Wood);
54 CHEST = P::new("chest", M::Wood, 26).container().strength(2.5, 12.5, St::Wood);
55 REDSTONE_WIRE = P::new("redstoneDust", M::Circuits, 164).shape(S::Wire).strength(0.0, 0.0, St::Powder);
56 DIAMOND_ORE = P::new("oreDiamond", M::Rock, 50).strength(3.0, 15.0, St::Stone);
57 DIAMOND_BLOCK = P::new("blockDiamond", M::Iron, 24).strength(5.0, 30.0, St::Metal);
58 CRAFTING_TABLE = P::new("workbench", M::Wood, 59).strength(2.5, 12.5, St::Wood);
59 WHEAT = P::new("crops", M::Plants, 88).shape(S::Crops).strength(0.0, 0.0, St::Grass);
60 FARMLAND = P::new("farmland", M::Ground, 87).special().opacity(255).strength(0.6, 3.0, St::Gravel);
61 FURNACE = P::new("furnace", M::Rock, 45).container().strength(3.5, 17.5, St::Stone);
62 LIT_FURNACE = P::new("furnace", M::Rock, 45).emission(13).container().strength(3.5, 17.5, St::Stone);
63 SIGN = P::new("sign", M::Wood, 4).shape(S::None).container().strength(1.0, 5.0, St::Wood);
64 WOODEN_DOOR = P::new("doorWood", M::Wood, 97).shape(S::Door).strength(3.0, 15.0, St::Wood);
65 LADDER = P::new("ladder", M::Circuits, 83).shape(S::Ladder).strength(0.4, 2.0, St::Wood);
66 RAIL = P::new("rail", M::Circuits, 128).shape(S::Rail).strength(0.7, 3.5, St::Metal);
67 COBBLESTONE_STAIRS = P::new("stairsStone", M::Rock, 16).shape(S::Stairs).opacity(255).strength(2.0, 30.0, St::Stone);
68 WALL_SIGN = P::new("sign", M::Wood, 4).shape(S::None).container().strength(1.0, 5.0, St::Wood);
69 LEVER = P::new("lever", M::Circuits, 96).shape(S::Lever).strength(0.5, 2.5, St::Wood);
70 STONE_PRESSURE_PLATE = P::new("pressurePlate", M::Rock, 1).special().strength(0.5, 2.5, St::Stone);
71 IRON_DOOR = P::new("doorIron", M::Iron, 98).shape(S::Door).strength(5.0, 25.0, St::Metal);
72 WOODEN_PRESSURE_PLATE = P::new("pressurePlate", M::Wood, 4).special().strength(0.5, 2.5, St::Wood);
73 REDSTONE_ORE = P::new("oreRedstone", M::Rock, 51).strength(3.0, 15.0, St::Stone);
74 LIT_REDSTONE_ORE = P::new("oreRedstone", M::Rock, 51).emission(9).strength(3.0, 15.0, St::Stone);
75 UNLIT_REDSTONE_TORCH = P::new("notGate", M::Circuits, 115).shape(S::Torch).strength(0.0, 0.0, St::Wood);
76 REDSTONE_TORCH = P::new("notGate", M::Circuits, 99).shape(S::Torch).emission(7).strength(0.0, 0.0, St::Wood);
77 BUTTON = P::new("button", M::Circuits, 1).special().strength(0.5, 2.5, St::Stone);
78 SNOW_LAYER = P::new("snow", M::Snow, 66).special().strength(0.1, 0.5, St::Cloth);
79 ICE = P::new("ice", M::Ice, 67).transparent().opacity(3).translucent().strength(0.5, 2.5, St::Glass).slippery(0.98);
80 SNOW = P::new("snow", M::BuiltSnow, 66).strength(0.2, 1.0, St::Cloth);
81 CACTUS = P::new("cactus", M::Cactus, 70).shape(S::Cactus).strength(0.4, 2.0, St::Cloth);
82 CLAY = P::new("clay", M::Clay, 72).strength(0.6, 3.0, St::Gravel);
83 SUGAR_CANE = P::new("reeds", M::Plants, 73).shape(S::Cross).strength(0.0, 0.0, St::Grass);
84 JUKEBOX = P::new("jukebox", M::Wood, 74).container().strength(2.0, 30.0, St::Stone);
85 FENCE = P::new("fence", M::Wood, 4).shape(S::Fence).strength(2.0, 15.0, St::Wood);
86 PUMPKIN = P::new("pumpkin", M::Pumpkin, 102).strength(1.0, 5.0, St::Wood);
87 NETHERRACK = P::new("hellrock", M::Rock, 103).strength(0.4, 2.0, St::Stone);
88 SOUL_SAND = P::new("hellsand", M::Sand, 104).strength(0.5, 2.5, St::Sand);
89 GLOWSTONE = P::new("lightgem", M::Rock, 105).emission(15).strength(0.3, 1.5, St::Glass);
90 PORTAL = P::new("portal", M::Portal, 14).special().translucent().emission(11).strength(-1.0, 0.0, St::Glass);
91 JACK_O_LANTERN = P::new("litpumpkin", M::Pumpkin, 102).emission(15).strength(1.0, 5.0, St::Wood);
92 CAKE = P::new("cake", M::Cake, 121).special().strength(0.5, 2.5, St::Cloth);
93 REPEATER = P::new("diode", M::Circuits, 6).shape(S::Repeater).strength(0.0, 0.0, St::Wood);
94 POWERED_REPEATER = P::new("diode", M::Circuits, 6).shape(S::Repeater).emission(9).strength(0.0, 0.0, St::Wood);
95 LOCKED_CHEST = P::new("lockedchest", M::Wood, 26).emission(15).strength(0.0, 0.0, St::Wood);
96 TRAPDOOR = P::new("trapdoor", M::Wood, 84).special().strength(3.0, 15.0, St::Wood);
}
impl Block {
pub fn fire_rates(self) -> (i32, i32) {
match self {
Self::PLANKS | Self::FENCE | Self::OAK_STAIRS => (5, 20),
Self::LOG => (5, 5),
Self::LEAVES | Self::WOOL => (30, 60),
Self::BOOKSHELF => (30, 20),
Self::TNT => (15, 100),
Self::TALL_GRASS => (60, 100),
_ => (0, 0),
}
}
pub const AIR: Self = Self(0);
pub fn properties(self) -> Option<&'static Properties> {
PROPERTIES[self.0 as usize].as_ref()
}
pub fn material(self) -> Material {
self.properties().map_or(Material::Air, |p| p.material)
}
pub fn is_air(self) -> bool {
self == Self::AIR
}
pub fn is_opaque_cube(self) -> bool {
self.properties().is_some_and(|p| p.opaque_cube)
}
pub fn light_opacity(self) -> u8 {
self.properties().map_or(0, |p| p.light_opacity)
}
pub fn light_emission(self) -> u8 {
self.properties().map_or(0, |p| p.light_emission)
}
pub fn hardness(self) -> f32 {
self.properties().map_or(0.0, |p| p.hardness)
}
pub fn slipperiness(self) -> f32 {
self.properties().map_or(0.6, |p| p.slipperiness)
}
pub fn sound(self) -> StepSound {
self.properties().map_or(StepSound::Powder, |p| p.sound)
}
pub fn is_container(self) -> bool {
self.properties().is_some_and(|p| p.container)
}
/// `World.isBlockNormalCube`: an opaque material rendered as a full cube.
pub fn is_normal_cube(self) -> bool {
self.properties()
.is_some_and(|p| p.normal && p.material.is_opaque())
}
}