Theme

alex/beta-minecraft-rust

Minecraft Beta 1.7.3 Rewrite

BRANCH main
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
Download file

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())
    }
}