From 11f2d2ae1347ff56753935923021fe9f9f8c47e8 Mon Sep 17 00:00:00 2001
From: iceiix <43691553+iceiix@users.noreply.github.com>
Date: Fri, 28 Dec 2018 21:11:42 -0800
Subject: [PATCH] 1.13.2 (404) multiprotocol support (#67)
Adds support for 1.13.2 protocol (404)
Expands https://github.com/iceiix/steven/issues/18 Enhance protocol support
Metadata:
* Support 1.13.2 slot data format, bool and varint item id, optional damage (moved to NBT)
https://wiki.vg/index.php?title=Slot_Data&type=revision&diff=14363&oldid=7835
Packets:
* Add 1.13.2 packets, and implement all the command data parsers
https://wiki.vg/Command_Data#Parsers
* Send new plugin channel minecraft:brand
https://wiki.vg/Plugin_channels#minecraft:brand
* Add 1.13.2 metadata format, with shifted IDs
https://wiki.vg/Entity_metadata#Entity_Metadata_Format
* Implement particle entity metadata
* Add structures for 16 new packets
Blocks: The Flattening:
* Assign flattened IDs in correct order using new 'offset' macro token
* Assign hierarchical (pre-flattening) block IDs sequentially by counting Some data
* Split VANILLA_ID_MAP into flat/hier struct, to support before and after the flattening
* Extend travis build time to 20 minutes because the blocks macro takes a long time
* Support both flat/hier blocks by passing protocol_version to by_vanilla_id
Add block states and offsets for all blocks, replacing metadata for 1.13+:
* Add stripped logs and what was Log2 to Log
* Add the Wood blocks, should be called bark, previously Axis::None Log
* Add leaves distance and offset
* Add jungle/acacia to Leaves moved from Leaves2
* Add dispenser offsets, direction
* Add note block states
* Add offset None to Missing253 and Missing254, no holes in block states of 1.13.2
* Add bed colors
* Add seagrass, tall seagrass, remove redundant deadgrass, and piston offset
* Add torch, TNT, fire offsets, remove slabs
* Add furnance offset, merges lit into a property
* Add pressure plate offsets, new pressure plates, redstone ore/lit merged
* Add lever offsets, new directions from ceiling/floor, rename LeverDirections
* Add redstone torch offsets, new blocks since lit/unlit is now merged, and standing/wall is split
* Change lever to split face/facing, rm LeverDirection, add AttachedFace
* Add stone button offsets, face/facing similar to lever
* Move face/facing data and variant to AttachedFace, reuse for lever/stonebutton
* Add data_with_facing_and_powered() to AttachedFace, for lever/stonebutton
* Add wooden button offsets each wood
* Add pumpkin without a face
* Add carved pumpkin, portal offsets
* Add lit pumpkin (as jack-o-lantern) offsets after carved pumpkin
* Add repeater offsets, merged into Repeater
* Change brown mushroom block to booleans instead of MushroomVariant
* Add mushroom block offsets, red/brown mushroom blocks, and a new mushroom stem block
* Add command block, cobblestone walls, and flower pot offsets
Empty flower pot, and potted plants including saplings. Rename
variant DarkOak to DarkOakSaplings because it is a sapling, and
remove the duplicate Dandelion variant which causes duplicate blocks.
* Increase recursion limit in steven_blocks
* Add colored banner offsets
* Add wooden slab including double slab, in a different position for pre-1.13 and 1.13
* StoneSlabVariant::Wood -> StoneSlabVariant::PetrifiedWood
* Add fence_gate_offset() for wooden fence gates
* Add frosted ice age, offset
* Add new blocks: kelp, turtle egg, coral, coral fans, sea pickle, blue ice, smooth stone
* Add new blocks: conduit, void air, cave aid, bubble column, last of the 1.13 blocks
---
.travis.yml | 2 +-
blocks/src/lib.rs | 2046 ++++++++++++++++++++++++++----
shared/src/direction.rs | 23 +
src/entity/block_entity/sign.rs | 4 +-
src/item.rs | 32 +-
src/model/mod.rs | 6 +-
src/protocol/mod.rs | 13 +-
src/protocol/packet.rs | 435 ++++++-
src/protocol/versions.rs | 4 +
src/protocol/versions/v1_12_2.rs | 2 +-
src/protocol/versions/v1_13_2.rs | 169 +++
src/server/mod.rs | 18 +-
src/server/plugin_messages.rs | 10 +-
src/types/metadata.rs | 299 ++++-
src/world/mod.rs | 17 +-
15 files changed, 2765 insertions(+), 315 deletions(-)
create mode 100644 src/protocol/versions/v1_13_2.rs
diff --git a/.travis.yml b/.travis.yml
index 3e32645..c8fc344 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -17,7 +17,7 @@ before_install:
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get install -y gcc libegl1-mesa-dev libgles2-mesa-dev ; fi
script:
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export LIBRARY_PATH="$LIBRARY_PATH:/usr/local/lib" ; fi
- - cargo build --verbose
+ - travis_wait 20 cargo build --verbose
- cargo test --verbose
os:
- linux
diff --git a/blocks/src/lib.rs b/blocks/src/lib.rs
index d43387f..53c0dd8 100644
--- a/blocks/src/lib.rs
+++ b/blocks/src/lib.rs
@@ -1,5 +1,5 @@
-#![recursion_limit="300"]
+#![recursion_limit="600"]
extern crate steven_shared as shared;
@@ -41,6 +41,11 @@ macro_rules! create_ids {
);
}
+struct VanillaIDMap {
+ flat: Vec>,
+ hier: Vec >,
+}
+
macro_rules! define_blocks {
(
$(
@@ -51,6 +56,7 @@ macro_rules! define_blocks {
)*
},
$(data $datafunc:expr,)*
+ $(offset $offsetfunc:expr,)*
$(material $mat:expr,)*
model $model:expr,
$(variant $variant:expr,)*
@@ -77,24 +83,62 @@ macro_rules! define_blocks {
impl Block {
#[allow(unused_variables, unreachable_code)]
- pub fn get_vanilla_id(&self) -> Option {
+ pub fn get_internal_id(&self) -> usize {
+ match *self {
+ $(
+ Block::$name {
+ $($fname,)*
+ } => {
+ internal_ids::$name
+ }
+ )+
+ }
+ }
+
+ #[allow(unused_variables, unreachable_code)]
+ pub fn get_hierarchical_data(&self) -> Option {
match *self {
$(
Block::$name {
$($fname,)*
} => {
$(
- let data: Option = ($datafunc).map(|v| v + (internal_ids::$name << 4));
+ let data: Option = ($datafunc).map(|v| v);
return data;
)*
- Some(internal_ids::$name << 4)
+ Some(0)
}
)+
}
}
- pub fn by_vanilla_id(id: usize) -> Block {
- VANILLA_ID_MAP.get(id).and_then(|v| *v).unwrap_or(Block::Missing{})
+ #[allow(unused_variables, unreachable_code)]
+ pub fn get_flat_offset(&self) -> Option {
+ match *self {
+ $(
+ Block::$name {
+ $($fname,)*
+ } => {
+ $(
+ let offset: Option = ($offsetfunc).map(|v| v);
+ return offset;
+ )*
+ $(
+ let data: Option = ($datafunc).map(|v| v);
+ return data;
+ )*
+ Some(0)
+ }
+ )+
+ }
+ }
+
+ pub fn by_vanilla_id(id: usize, protocol_version: i32) -> Block {
+ if protocol_version >= 404 {
+ VANILLA_ID_MAP.flat.get(id).and_then(|v| *v).unwrap_or(Block::Missing{})
+ } else {
+ VANILLA_ID_MAP.hier.get(id).and_then(|v| *v).unwrap_or(Block::Missing{})
+ }
}
#[allow(unused_variables, unreachable_code)]
@@ -210,8 +254,12 @@ macro_rules! define_blocks {
}
lazy_static! {
- static ref VANILLA_ID_MAP: Vec> = {
- let mut blocks = vec![];
+ static ref VANILLA_ID_MAP: VanillaIDMap = {
+ let mut blocks_flat = vec![];
+ let mut blocks_hier = vec![];
+ let mut flat_id = 0;
+ let mut last_internal_id = 0;
+ let mut hier_block_id = 0;
$({
#[allow(non_camel_case_types, dead_code)]
struct CombinationIter<$($fname),*> {
@@ -301,27 +349,74 @@ macro_rules! define_blocks {
vals.into_iter()
}),*
);
+ let mut last_offset: isize = -1;
for block in iter {
- if let Some(id) = block.get_vanilla_id() {
- if blocks.len() <= id {
- blocks.resize(id + 1, None);
+ let internal_id = block.get_internal_id();
+ let hier_data: Option = block.get_hierarchical_data();
+ let vanilla_id =
+ if let Some(hier_data) = hier_data {
+ if internal_id != last_internal_id {
+ hier_block_id += 1;
+ }
+ last_internal_id = internal_id;
+ Some((hier_block_id << 4) + hier_data)
+ } else {
+ None
+ };
+
+ let offset = block.get_flat_offset();
+ if let Some(offset) = offset {
+ let id = flat_id + offset;
+ /*
+ if let Some(vanilla_id) = vanilla_id {
+ println!("{} block state = {:?} hierarchical {}:{} offset={}", id, block, vanilla_id >> 4, vanilla_id & 0xF, offset);
+ } else {
+ println!("{} block state = {:?} hierarchical none, offset={}", id, block, offset);
}
- if blocks[id].is_none() {
- blocks[id] = Some(block);
+ */
+ if offset as isize > last_offset {
+ last_offset = offset as isize;
+ }
+
+ if blocks_flat.len() <= id {
+ blocks_flat.resize(id + 1, None);
+ }
+ if blocks_flat[id].is_none() {
+ blocks_flat[id] = Some(block);
} else {
panic!(
- "Tried to register {:#?} to {}:{} but {:#?} was already registered",
+ "Tried to register {:#?} to {} but {:#?} was already registered",
block,
- id >> 4,
- id & 0xF,
- blocks[id]
+ id,
+ blocks_flat[id]
);
}
+
+ if let Some(vanilla_id) = vanilla_id {
+ if blocks_hier.len() <= vanilla_id {
+ blocks_hier.resize(vanilla_id + 1, None);
+ }
+ if blocks_hier[vanilla_id].is_none() {
+ blocks_hier[vanilla_id] = Some(block);
+ } else {
+ panic!(
+ "Tried to register {:#?} to {} but {:#?} was already registered",
+ block,
+ id,
+ blocks_hier[vanilla_id]
+ );
+ }
+ }
}
}
+
+ #[allow(unused_assignments)]
+ {
+ flat_id += (last_offset + 1) as usize;
+ }
})+
- blocks
+ VanillaIDMap { flat: blocks_flat, hier: blocks_hier }
};
}
);
@@ -365,6 +460,7 @@ define_blocks! {
snowy: bool = [false, true],
},
data { if snowy { None } else { Some(0) } },
+ offset { if snowy { Some(0) } else { Some(1) } },
model { ("minecraft", "grass") },
variant format!("snowy={}", snowy),
tint TintType::Grass,
@@ -380,6 +476,17 @@ define_blocks! {
],
},
data if !snowy { Some(variant.data()) } else { None },
+ offset {
+ if variant == DirtVariant::Podzol {
+ Some(variant.data() + if snowy { 0 } else { 1 })
+ } else {
+ if snowy {
+ None
+ } else {
+ Some(variant.data())
+ }
+ }
+ },
model { ("minecraft", variant.as_string()) },
variant {
if variant == DirtVariant::Podzol {
@@ -425,6 +532,7 @@ define_blocks! {
stage: u8 = [0, 1],
},
data Some(variant.plank_data() | ((stage as usize) << 3)),
+ offset Some((variant.plank_data() << 1) | (stage as usize)),
material material::NON_SOLID,
model { ("minecraft", format!("{}_sapling", variant.as_string()) ) },
variant format!("stage={}", stage),
@@ -439,6 +547,7 @@ define_blocks! {
level: u8 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
},
data Some(level as usize),
+ offset None,
material Material {
absorbed_light: 2,
..material::TRANSPARENT
@@ -463,6 +572,7 @@ define_blocks! {
level: u8 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
},
data Some(level as usize),
+ offset None,
material Material {
absorbed_light: 15,
emitted_light: 15,
@@ -513,28 +623,85 @@ define_blocks! {
TreeVariant::Oak,
TreeVariant::Spruce,
TreeVariant::Birch,
- TreeVariant::Jungle
+ TreeVariant::Jungle,
+ TreeVariant::Acacia,
+ TreeVariant::DarkOak,
+ TreeVariant::StrippedSpruce,
+ TreeVariant::StrippedBirch,
+ TreeVariant::StrippedJungle,
+ TreeVariant::StrippedAcacia,
+ TreeVariant::StrippedDarkOak,
+ TreeVariant::StrippedOak
],
axis: Axis = [Axis::Y, Axis::Z, Axis::X, Axis::None],
},
- data Some(variant.data() | (axis.index() << 2)),
+ data match variant {
+ TreeVariant::Oak | TreeVariant::Spruce | TreeVariant::Birch | TreeVariant::Jungle =>
+ Some(variant.data() | (axis.index() << 2)),
+ _ => None,
+ },
+ offset match axis {
+ Axis::None => None,
+ Axis::X => Some(variant.offset() * 3 + 0),
+ Axis::Y => Some(variant.offset() * 3 + 1),
+ Axis::Z => Some(variant.offset() * 3 + 2),
+ },
model { ("minecraft", format!("{}_log", variant.as_string()) ) },
variant format!("axis={}", axis.as_string()),
}
+ Wood {
+ props {
+ variant: TreeVariant = [
+ TreeVariant::Oak,
+ TreeVariant::Spruce,
+ TreeVariant::Birch,
+ TreeVariant::Jungle,
+ TreeVariant::Acacia,
+ TreeVariant::DarkOak,
+ TreeVariant::StrippedSpruce,
+ TreeVariant::StrippedBirch,
+ TreeVariant::StrippedJungle,
+ TreeVariant::StrippedAcacia,
+ TreeVariant::StrippedDarkOak,
+ TreeVariant::StrippedOak
+ ],
+ axis: Axis = [Axis::X, Axis::Y, Axis::Z],
+ },
+ data None::,
+ offset Some(variant.offset() * 3 + axis.index()),
+ model { ("minecraft", format!("{}_wood", variant.as_string()) ) },
+ variant format!("axis={}", axis.as_string()),
+ }
Leaves {
props {
variant: TreeVariant = [
TreeVariant::Oak,
TreeVariant::Spruce,
TreeVariant::Birch,
- TreeVariant::Jungle
+ TreeVariant::Jungle,
+ TreeVariant::Acacia,
+ TreeVariant::DarkOak
],
decayable: bool = [false, true],
check_decay: bool = [false, true],
+ distance: u8 = [1, 2, 3, 4, 5, 6, 7],
+ },
+ data match variant {
+ TreeVariant::Oak | TreeVariant::Spruce | TreeVariant::Birch | TreeVariant::Jungle =>
+ if distance == 1 {
+ Some(variant.data()
+ | (if decayable { 0x4 } else { 0x0 })
+ | (if check_decay { 0x8 } else { 0x0 }))
+ } else {
+ None
+ },
+ _ => None,
+ },
+ offset if check_decay {
+ None
+ } else {
+ Some(variant.offset() * (7 * 2) + ((distance as usize - 1) << 1) | (if decayable { 0 } else { 1 }))
},
- data Some(variant.data()
- | (if decayable { 0x4 } else { 0x0 })
- | (if check_decay { 0x8 } else { 0x0 })),
material material::LEAVES,
model { ("minecraft", format!("{}_leaves", variant.as_string()) ) },
tint TintType::Foliage,
@@ -573,6 +740,7 @@ define_blocks! {
triggered: bool = [false, true],
},
data Some(facing.index() | (if triggered { 0x8 } else { 0x0 })),
+ offset Some((facing.offset() << 1) | (if triggered { 0 } else { 1 })),
model { ("minecraft", "dispenser") },
variant format!("facing={}", facing.as_string()),
}
@@ -588,11 +756,46 @@ define_blocks! {
model { ("minecraft", variant.as_string() ) },
}
NoteBlock {
- props {},
+ props {
+ instrument: NoteBlockInstrument = [
+ NoteBlockInstrument::Harp,
+ NoteBlockInstrument::BaseDrum,
+ NoteBlockInstrument::Snare,
+ NoteBlockInstrument::Hat,
+ NoteBlockInstrument::Bass,
+ NoteBlockInstrument::Flute,
+ NoteBlockInstrument::Bell,
+ NoteBlockInstrument::Guitar,
+ NoteBlockInstrument::Chime,
+ NoteBlockInstrument::Xylophone
+ ],
+ note: u8 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24],
+ powered: bool = [true, false],
+ },
+ data if instrument == NoteBlockInstrument::Harp && note == 0 && powered { Some(0) } else { None },
+ offset Some(instrument.offset() * (25 * 2) + ((note as usize) << 1) + if powered { 0 } else { 1 }),
model { ("minecraft", "noteblock") },
}
Bed {
props {
+ color: ColoredVariant = [
+ ColoredVariant::White,
+ ColoredVariant::Orange,
+ ColoredVariant::Magenta,
+ ColoredVariant::LightBlue,
+ ColoredVariant::Yellow,
+ ColoredVariant::Lime,
+ ColoredVariant::Pink,
+ ColoredVariant::Gray,
+ ColoredVariant::Silver,
+ ColoredVariant::Cyan,
+ ColoredVariant::Purple,
+ ColoredVariant::Blue,
+ ColoredVariant::Brown,
+ ColoredVariant::Green,
+ ColoredVariant::Red,
+ ColoredVariant::Black
+ ],
facing: Direction = [
Direction::North,
Direction::South,
@@ -602,9 +805,13 @@ define_blocks! {
occupied: bool = [false, true],
part: BedPart = [BedPart::Head, BedPart::Foot],
},
- data Some(facing.horizontal_index()
+ data if color != ColoredVariant::White { None } else { Some(facing.horizontal_index()
| (if occupied { 0x4 } else { 0x0 })
- | (if part == BedPart::Head { 0x8 } else { 0x0 })),
+ | (if part == BedPart::Head { 0x8 } else { 0x0 }))},
+ offset Some(color.data() * (2 * 2 * 4)
+ + (facing.horizontal_offset() * (2 * 2))
+ + (if occupied { 0 } else { 2 })
+ + (if part == BedPart::Head { 0 } else { 1 })),
material material::NON_SOLID,
model { ("minecraft", "bed") },
variant format!("facing={},part={}", facing.as_string(), part.as_string()),
@@ -623,6 +830,7 @@ define_blocks! {
],
},
data Some(shape.data() | (if powered { 0x8 } else { 0x0 })),
+ offset Some(shape.data() + (if powered { 0 } else { 6 })),
material material::NON_SOLID,
model { ("minecraft", "golden_rail") },
variant format!("powered={},shape={}", powered, shape.as_string()),
@@ -641,6 +849,7 @@ define_blocks! {
],
},
data Some(shape.data() | (if powered { 0x8 } else { 0x0 })),
+ offset Some(shape.data() + (if powered { 0 } else { 6 })),
material material::NON_SOLID,
model { ("minecraft", "detector_rail") },
variant format!("powered={},shape={}", powered, shape.as_string()),
@@ -659,6 +868,7 @@ define_blocks! {
],
},
data Some(facing.index() | (if extended { 0x8 } else { 0x0 })),
+ offset Some(facing.offset() + (if extended { 0 } else { 6 })),
material Material {
should_cull_against: !extended,
..material::NON_SOLID
@@ -687,8 +897,30 @@ define_blocks! {
tint TintType::Grass,
collision vec![],
}
+ Seagrass {
+ props {},
+ data None::,
+ offset Some(0),
+ material material::NON_SOLID,
+ model { ("minecraft", "seagrass") },
+ collision vec![],
+ }
+ TallSeagrass {
+ props {
+ half: TallSeagrassHalf = [
+ TallSeagrassHalf::Upper,
+ TallSeagrassHalf::Lower
+ ],
+ },
+ data None::,
+ offset Some(half.offset()),
+ material material::NON_SOLID,
+ model { ("minecraft", "tall_seagrass") },
+ collision vec![],
+ }
DeadBush {
props {},
+ offset None,
material material::NON_SOLID,
model { ("minecraft", "dead_bush") },
collision vec![],
@@ -706,6 +938,7 @@ define_blocks! {
],
},
data Some(facing.index() | (if extended { 0x8 } else { 0x0 })),
+ offset Some(facing.offset() + (if extended { 0 } else { 6 })),
material Material {
should_cull_against: !extended,
..material::NON_SOLID
@@ -728,6 +961,9 @@ define_blocks! {
variant: PistonType = [PistonType::Normal, PistonType::Sticky],
},
data if !short { Some(facing.index() | if variant == PistonType::Sticky { 0x8 } else { 0x0 })} else { None },
+ offset Some(facing.offset() * 4 +
+ (if short { 0 } else { 2 }) +
+ (if variant == PistonType::Normal { 0 } else { 1 })),
material material::NON_SOLID,
model { ("minecraft", "piston_head") },
variant format!("facing={},short={},type={}", facing.as_string(), short, variant.as_string()),
@@ -773,7 +1009,19 @@ define_blocks! {
model { ("minecraft", format!("{}_wool", color.as_string()) ) },
}
PistonExtension {
- props {},
+ props {
+ facing: Direction = [
+ Direction::Up,
+ Direction::Down,
+ Direction::North,
+ Direction::South,
+ Direction::West,
+ Direction::East
+ ],
+ variant: PistonType = [PistonType::Normal, PistonType::Sticky],
+ },
+ data if facing == Direction::Up && variant == PistonType::Normal { Some(0) } else { None },
+ offset Some(facing.offset() * 2 + (if variant == PistonType::Normal { 0 } else { 1 })),
material material::INVISIBLE,
model { ("minecraft", "piston_extension") },
}
@@ -831,7 +1079,7 @@ define_blocks! {
variant: StoneSlabVariant = [
StoneSlabVariant::Stone,
StoneSlabVariant::Sandstone,
- StoneSlabVariant::Wood,
+ StoneSlabVariant::PetrifiedWood,
StoneSlabVariant::Cobblestone,
StoneSlabVariant::Brick,
StoneSlabVariant::StoneBrick,
@@ -853,6 +1101,7 @@ define_blocks! {
Some(data)
},
+ offset None,
model { ("minecraft", format!("{}_double_slab", variant.as_string()) ) },
variant if seamless { "all" } else { "normal" },
}
@@ -862,7 +1111,7 @@ define_blocks! {
variant: StoneSlabVariant = [
StoneSlabVariant::Stone,
StoneSlabVariant::Sandstone,
- StoneSlabVariant::Wood,
+ StoneSlabVariant::PetrifiedWood,
StoneSlabVariant::Cobblestone,
StoneSlabVariant::Brick,
StoneSlabVariant::StoneBrick,
@@ -871,6 +1120,7 @@ define_blocks! {
],
},
data Some(variant.data() | (if half == BlockHalf::Top { 0x8 } else { 0x0 })),
+ offset None,
material material::NON_SOLID,
model { ("minecraft", format!("{}_slab", variant.as_string()) ) },
variant format!("half={}", half.as_string()),
@@ -885,6 +1135,7 @@ define_blocks! {
explode: bool = [false, true],
},
data Some(if explode { 1 } else { 0 }),
+ offset Some(if explode { 0 } else { 1 }),
model { ("minecraft", "tnt") },
}
BookShelf {
@@ -919,6 +1170,16 @@ define_blocks! {
_ => unreachable!(),
})
},
+ offset {
+ Some(match facing {
+ Direction::Up => 0,
+ Direction::North => 1,
+ Direction::South => 2,
+ Direction::West => 3,
+ Direction::East => 4,
+ _ => unreachable!(),
+ })
+ },
material Material {
emitted_light: 14,
..material::NON_SOLID
@@ -937,6 +1198,13 @@ define_blocks! {
east: bool = [false, true],
},
data if !up && !north && !south && !west && !east { Some(age as usize) } else { None },
+ offset Some(
+ if west { 0 } else { 1<<0 } |
+ if up { 0 } else { 1<<1 } |
+ if south { 0 } else { 1<<2 } |
+ if north { 0 } else { 1<<3 } |
+ if east { 0 } else { 1<<4 } |
+ ((age as usize) << 5)),
material Material {
emitted_light: 15,
..material::NON_SOLID
@@ -983,13 +1251,15 @@ define_blocks! {
StairShape::OuterLeft,
StairShape::OuterRight
],
+ waterlogged: bool = [true, false],
},
- data stair_data(facing, half, shape),
+ data stair_data(facing, half, shape, waterlogged),
+ offset stair_offset(facing, half, shape, waterlogged),
material material::NON_SOLID,
model { ("minecraft", "oak_stairs") },
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: facing, half: half, shape: update_stair_shape(world, pos, facing)},
+ update_state (world, pos) => Block::OakStairs{facing, half, shape: update_stair_shape(world, pos, facing), waterlogged},
}
Chest {
props {
@@ -999,8 +1269,17 @@ define_blocks! {
Direction::West,
Direction::East
],
+ type_: ChestType = [
+ ChestType::Single,
+ ChestType::Left,
+ ChestType::Right
+ ],
+ waterlogged: bool = [true, false],
},
- data Some(facing.index()),
+ data if type_ == ChestType::Single && !waterlogged { Some(facing.index()) } else { None },
+ offset Some(if waterlogged { 0 } else { 1 } +
+ type_.offset() * 2 +
+ facing.horizontal_offset() * (2 * 3)),
material material::NON_SOLID,
model { ("minecraft", "chest") },
}
@@ -1020,6 +1299,12 @@ define_blocks! {
None
}
},
+ offset Some(
+ west.offset() +
+ south.offset() * 3 +
+ (power as usize) * (3 * 3) +
+ north.offset() * (3 * 3 * 16) +
+ east.offset() * (3 * 3 * 16 * 3)),
material material::NON_SOLID,
model { ("minecraft", "redstone_wire") },
tint TintType::Color{r: ((255.0 / 30.0) * (f64::from(power)) + 14.0) as u8, g: 0, b: 0},
@@ -1082,8 +1367,10 @@ define_blocks! {
Direction::West,
Direction::East
],
+ lit: bool = [true, false],
},
- data Some(facing.index()),
+ data if !lit { Some(facing.index()) } else { None },
+ offset Some(if lit { 0 } else { 1 } + facing.horizontal_offset() * 2),
model { ("minecraft", "furnace") },
variant format!("facing={}", facing.as_string()),
}
@@ -1097,6 +1384,7 @@ define_blocks! {
],
},
data Some(facing.index()),
+ offset None,
material Material {
emitted_light: 13,
..material::SOLID
@@ -1124,8 +1412,10 @@ define_blocks! {
Rotation::SouthEast,
Rotation::SouthSouthEast
],
+ waterlogged: bool = [true, false],
},
- data Some(rotation.data()),
+ data if !waterlogged { Some(rotation.data()) } else { None },
+ offset Some(rotation.data() * 2 + if waterlogged { 0 } else { 1 }),
material material::INVISIBLE,
model { ("minecraft", "standing_sign") },
collision vec![],
@@ -1144,6 +1434,7 @@ define_blocks! {
powered: bool = [false, true],
},
data door_data(facing, half, hinge, open, powered),
+ offset door_offset(facing, half, hinge, open, powered),
material material::NON_SOLID,
model { ("minecraft", "wooden_door") },
variant format!("facing={},half={},hinge={},open={}", facing.as_string(), half.as_string(), hinge.as_string(), open),
@@ -1161,8 +1452,10 @@ define_blocks! {
Direction::West,
Direction::East
],
+ waterlogged: bool = [true, false],
},
- data Some(facing.index()),
+ data if !waterlogged { Some(facing.index()) } else { None },
+ offset Some(if waterlogged { 0 } else { 1 } + facing.horizontal_offset() * 2),
material material::NON_SOLID,
model { ("minecraft", "ladder") },
variant format!("facing={}", facing.as_string()),
@@ -1204,13 +1497,15 @@ define_blocks! {
StairShape::OuterLeft,
StairShape::OuterRight
],
+ waterlogged: bool = [true, false],
},
- data stair_data(facing, half, shape),
+ data stair_data(facing, half, shape, waterlogged),
+ offset stair_offset(facing, half, shape, waterlogged),
material material::NON_SOLID,
model { ("minecraft", "stone_stairs") },
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: facing, half: half, shape: update_stair_shape(world, pos, facing)},
+ update_state (world, pos) => Block::StoneStairs{facing, half, shape: update_stair_shape(world, pos, facing), waterlogged},
}
WallSign {
props {
@@ -1220,8 +1515,10 @@ define_blocks! {
Direction::West,
Direction::East
],
+ waterlogged: bool = [true, false],
},
- data Some(facing.index()),
+ data if !waterlogged { Some(facing.index()) } else { None },
+ offset Some(if waterlogged { 0 } else { 1 } + facing.horizontal_offset() * 2),
material material::INVISIBLE,
model { ("minecraft", "wall_sign") },
variant format!("facing={}", facing.as_string()),
@@ -1229,22 +1526,24 @@ define_blocks! {
}
Lever {
props {
- facing: LeverDirection = [
- LeverDirection::North,
- LeverDirection::South,
- LeverDirection::East,
- LeverDirection::West,
- LeverDirection::UpX,
- LeverDirection::DownX,
- LeverDirection::UpZ,
- LeverDirection::DownZ
+ face: AttachedFace = [
+ AttachedFace::Floor,
+ AttachedFace::Wall,
+ AttachedFace::Ceiling
+ ],
+ facing: Direction = [
+ Direction::North,
+ Direction::South,
+ Direction::West,
+ Direction::East
],
powered: bool = [false, true],
},
- data Some(facing.data() | (if powered { 0x8 } else { 0x0 })),
+ data face.data_with_facing_and_powered(facing, powered),
+ offset Some(face.offset() * (4 * 2) + facing.horizontal_offset() * 2 + if powered { 0 } else { 1 }),
material material::NON_SOLID,
model { ("minecraft", "lever") },
- variant format!("facing={},powered={}", facing.as_string(), powered),
+ variant format!("facing={},powered={}", face.variant_with_facing(facing), powered),
collision vec![],
}
StonePressurePlate {
@@ -1252,6 +1551,7 @@ define_blocks! {
powered: bool = [false, true],
},
data Some(if powered { 1 } else { 0 }),
+ offset Some(if powered { 0 } else { 1 }),
material material::NON_SOLID,
model { ("minecraft", "stone_pressure_plate") },
variant format!("powered={}", powered),
@@ -1271,6 +1571,7 @@ define_blocks! {
powered: bool = [false, true],
},
data door_data(facing, half, hinge, open, powered),
+ offset door_offset(facing, half, hinge, open, powered),
material material::NON_SOLID,
model { ("minecraft", "iron_door") },
variant format!("facing={},half={},hinge={},open={}", facing.as_string(), half.as_string(), hinge.as_string(), open),
@@ -1282,20 +1583,34 @@ define_blocks! {
}
WoodenPressurePlate {
props {
+ wood: TreeVariant = [
+ TreeVariant::Oak,
+ TreeVariant::Spruce,
+ TreeVariant::Birch,
+ TreeVariant::Jungle,
+ TreeVariant::Acacia,
+ TreeVariant::DarkOak
+ ],
powered: bool = [false, true],
},
- data Some(if powered { 1 } else { 0 }),
+ data if wood == TreeVariant::Oak { Some(if powered { 1 } else { 0 }) } else { None },
+ offset Some(wood.offset() * 2 + if powered { 0 } else { 1 }),
material material::NON_SOLID,
model { ("minecraft", "wooden_pressure_plate") },
variant format!("powered={}", powered),
collision vec![],
}
RedstoneOre {
- props {},
- model { ("minecraft", "redstone_ore") },
+ props {
+ lit: bool = [true, false],
+ },
+ data if !lit { Some(0) } else { None },
+ offset Some(if lit { 0 } else { 1 }),
+ model { ("minecraft", if lit { "lit_redstone_ore" } else { "redstone_ore" }) },
}
RedstoneOreLit {
props {},
+ offset None,
material Material {
emitted_light: 9,
..material::SOLID
@@ -1322,12 +1637,13 @@ define_blocks! {
_ => unreachable!(),
})
},
+ offset None,
material material::NON_SOLID,
model { ("minecraft", "unlit_redstone_torch") },
variant format!("facing={}", facing.as_string()),
collision vec![],
}
- RedstoneTorch {
+ RedstoneTorchLit {
props {
facing: Direction = [
Direction::East,
@@ -1347,6 +1663,7 @@ define_blocks! {
_ => unreachable!(),
})
},
+ offset None,
material Material {
emitted_light: 7,
..material::NON_SOLID
@@ -1355,30 +1672,57 @@ define_blocks! {
variant format!("facing={}", facing.as_string()),
collision vec![],
}
- StoneButton {
+ RedstoneTorchStanding {
+ props {
+ lit: bool = [true, false],
+ },
+ data None::,
+ offset Some(if lit { 0 } else { 1 }),
+ material material::NON_SOLID,
+ model { ("minecraft", if lit { "redstone_torch" } else { "unlit_redstone_torch" }) },
+ variant "facing=up",
+ collision vec![],
+ }
+ RedstoneTorchWall {
props {
facing: Direction = [
- Direction::Down,
Direction::East,
Direction::West,
Direction::South,
- Direction::North,
- Direction::Up
+ Direction::North
+ ],
+ lit: bool = [true, false],
+ },
+ data None::,
+ offset Some(if lit { 0 } else { 1 } + facing.horizontal_offset() * 2),
+ material Material {
+ emitted_light: 7,
+ ..material::NON_SOLID
+ },
+ model { ("minecraft", if lit { "redstone_torch" } else { "unlit_redstone_torch" }) },
+ variant format!("facing={}", facing.as_string()),
+ collision vec![],
+ }
+ StoneButton {
+ props {
+ face: AttachedFace = [
+ AttachedFace::Floor,
+ AttachedFace::Wall,
+ AttachedFace::Ceiling
+ ],
+ facing: Direction = [
+ Direction::East,
+ Direction::West,
+ Direction::South,
+ Direction::North
],
powered: bool = [false, true],
},
- data Some(match facing {
- Direction::Down => 0,
- Direction::East => 1,
- Direction::West => 2,
- Direction::South => 3,
- Direction::North => 4,
- Direction::Up => 5,
- _ => unreachable!(),
- } | (if powered { 0x8 } else { 0x0 })),
+ data face.data_with_facing_and_powered(facing, powered),
+ offset Some(face.offset() * (4 * 2) + facing.horizontal_offset() * 2 + if powered { 0 } else { 1 }),
material material::NON_SOLID,
model { ("minecraft", "stone_button") },
- variant format!("facing={},powered={}", facing.as_string(), powered),
+ variant format!("facing={},powered={}", face.variant_with_facing(facing), powered),
}
SnowLayer {
props {
@@ -1436,6 +1780,7 @@ define_blocks! {
has_record: bool = [false, true],
},
data Some(if has_record { 1 } else { 0 }),
+ offset Some(if has_record { 0 } else { 1 }),
model { ("minecraft", "jukebox") },
}
Fence {
@@ -1444,14 +1789,20 @@ define_blocks! {
south: bool = [false, true],
west: bool = [false, true],
east: bool = [false, true],
+ waterlogged: bool = [false, true],
},
- data if !north && !south && !east && !west { Some(0) } else { None },
+ data if !north && !south && !east && !west && !waterlogged { Some(0) } else { None },
+ offset Some(if west { 0 } else { 1<<0 } +
+ if waterlogged { 0 } else { 1<<1 } +
+ if south { 0 } else { 1<<2 } +
+ if north { 0 } else { 1<<3 } +
+ if east { 0 } else { 1<<4 }),
material material::NON_SOLID,
model { ("minecraft", "fence") },
collision fence_collision(north, south, west, east),
update_state (world, pos) => {
let (north, south, west, east) = can_connect_sides(world, pos, &can_connect_fence);
- Block::Fence{north: north, south: south, west: west, east: east}
+ Block::Fence{north, south, west, east, waterlogged}
},
multipart (key, val) => match key {
"north" => north == (val == "true"),
@@ -1461,7 +1812,7 @@ define_blocks! {
_ => false,
},
}
- Pumpkin {
+ PumpkinFace {
props {
facing: Direction = [
Direction::North,
@@ -1472,9 +1823,16 @@ define_blocks! {
without_face: bool = [false, true],
},
data Some(facing.horizontal_index() | (if without_face { 0x4 } else { 0x0 })),
+ offset None,
model { ("minecraft", "pumpkin") },
variant format!("facing={}", facing.as_string()),
}
+ Pumpkin {
+ props {},
+ data None::,
+ offset Some(0),
+ model { ("minecraft", "pumpkin") },
+ }
Netherrack {
props {},
model { ("minecraft", "netherrack") },
@@ -1501,6 +1859,7 @@ define_blocks! {
axis: Axis = [Axis::X, Axis::Z],
},
data Some(axis.index()),
+ offset Some(axis.index() - 1),
material Material {
emitted_light: 11,
..material::TRANSPARENT
@@ -1509,6 +1868,24 @@ define_blocks! {
variant format!("axis={}", axis.as_string()),
collision vec![],
}
+ PumpkinCarved {
+ props {
+ facing: Direction = [
+ Direction::North,
+ Direction::South,
+ Direction::West,
+ Direction::East
+ ],
+ },
+ data None::,
+ offset Some(facing.horizontal_offset()),
+ material Material {
+ emitted_light: 15,
+ ..material::SOLID
+ },
+ model { ("minecraft", "carved_pumpkin") },
+ variant format!("facing={}", facing.as_string()),
+ }
PumpkinLit {
props {
facing: Direction = [
@@ -1520,6 +1897,7 @@ define_blocks! {
without_face: bool = [false, true],
},
data Some(facing.horizontal_index() | (if without_face { 0x4 } else { 0x0 })),
+ offset if without_face { None } else { Some(facing.horizontal_offset()) },
material Material {
emitted_light: 15,
..material::SOLID
@@ -1540,7 +1918,7 @@ define_blocks! {
Point3::new(1.0 - (1.0/16.0), 0.5, 1.0 - (1.0/16.0))
)],
}
- RepeaterUnpowered {
+ Repeater {
props {
delay: u8 = [1, 2, 3, 4],
facing: Direction = [
@@ -1550,16 +1928,21 @@ define_blocks! {
Direction::East
],
locked: bool = [false, true],
+ powered: bool = [true, false],
},
- data if !locked { Some(facing.horizontal_index() | (delay as usize - 1) << 2) } else { None },
+ data if powered { None } else { if !locked { Some(facing.horizontal_index() | (delay as usize - 1) << 2) } else { None } },
+ offset Some(if powered { 0 } else { 1<<0 } +
+ if locked { 0 } else { 1<<1 } +
+ facing.horizontal_offset() * (2 * 2) +
+ ((delay - 1) as usize) * (2 * 2 * 4)),
material material::NON_SOLID,
- model { ("minecraft", "unpowered_repeater") },
+ model { ("minecraft", if powered { "powered_repeater" } else { "unpowered_repeater" }) },
variant format!("delay={},facing={},locked={}", delay, facing.as_string(), locked),
collision vec![Aabb3::new(
Point3::new(0.0, 0.0, 0.0),
Point3::new(1.0, 1.0/8.0, 1.0)
)],
- update_state (world, pos) => RepeaterUnpowered{delay: delay, facing: facing, locked: update_repeater_state(world, pos, facing)},
+ update_state (world, pos) => Repeater{delay, facing, locked: update_repeater_state(world, pos, facing), powered},
}
RepeaterPowered {
props {
@@ -1573,6 +1956,7 @@ define_blocks! {
locked: bool = [false, true],
},
data if !locked { Some(facing.horizontal_index() | (delay as usize - 1) << 2) } else { None },
+ offset None,
material material::NON_SOLID,
model { ("minecraft", "powered_repeater") },
variant format!("delay={},facing={},locked={}", delay, facing.as_string(), locked),
@@ -1617,14 +2001,30 @@ define_blocks! {
],
half: BlockHalf = [BlockHalf::Top, BlockHalf::Bottom],
open: bool = [false, true],
+ waterlogged: bool = [true, false],
+ powered: bool = [true, false],
+ wood: TreeVariant = [
+ TreeVariant::Oak,
+ TreeVariant::Spruce,
+ TreeVariant::Birch,
+ TreeVariant::Jungle,
+ TreeVariant::Acacia,
+ TreeVariant::DarkOak
+ ],
},
- data Some(match facing {
+ data if waterlogged || powered || wood != TreeVariant::Oak { None } else { Some(match facing {
Direction::North => 0,
Direction::South => 1,
Direction::West => 2,
Direction::East => 3,
_ => unreachable!(),
- } | (if open { 0x4 } else { 0x0 }) | (if half == BlockHalf::Top { 0x8 } else { 0x0 })),
+ } | (if open { 0x4 } else { 0x0 }) | (if half == BlockHalf::Top { 0x8 } else { 0x0 }))},
+ offset Some(if waterlogged { 0 } else { 1<<0 } +
+ if powered { 0 } else { 1<<1 } +
+ if open { 0 } else { 1<<2 } +
+ if half == BlockHalf::Top { 0 } else { 1<<3 } +
+ facing.horizontal_offset() * (2 * 2 * 2 * 2) +
+ wood.offset() * (2 * 2 * 2 * 2 * 4)),
material material::NON_SOLID,
model { ("minecraft", "trapdoor") },
variant format!("facing={},half={},open={}", facing.as_string(), half.as_string(), open),
@@ -1658,47 +2058,47 @@ define_blocks! {
}
BrownMushroomBlock {
props {
- variant: MushroomVariant = [
- MushroomVariant::East,
- MushroomVariant::North,
- MushroomVariant::NorthEast,
- MushroomVariant::NorthWest,
- MushroomVariant::South,
- MushroomVariant::SouthEast,
- MushroomVariant::SouthWest,
- MushroomVariant::West,
- MushroomVariant::Center,
- MushroomVariant::Stem,
- MushroomVariant::AllInside,
- MushroomVariant::AllOutside,
- MushroomVariant::AllStem
- ],
+ is_stem: bool = [true, false],
+ west: bool = [true, false],
+ up: bool = [true, false],
+ south: bool = [true, false],
+ north: bool = [true, false],
+ east: bool = [true, false],
+ down: bool = [true, false],
},
- data Some(variant.data()),
+ data mushroom_block_data(is_stem, west, up, south, north, east, down),
+ offset mushroom_block_offset(is_stem, west, up, south, north, east, down),
model { ("minecraft", "brown_mushroom_block") },
- variant format!("variant={}", variant.as_string()),
+ variant format!("variant={}", mushroom_block_variant(is_stem, west, up, south, north, east, down)),
}
RedMushroomBlock {
props {
- variant: MushroomVariant = [
- MushroomVariant::East,
- MushroomVariant::North,
- MushroomVariant::NorthEast,
- MushroomVariant::NorthWest,
- MushroomVariant::South,
- MushroomVariant::SouthEast,
- MushroomVariant::SouthWest,
- MushroomVariant::West,
- MushroomVariant::Center,
- MushroomVariant::Stem,
- MushroomVariant::AllInside,
- MushroomVariant::AllOutside,
- MushroomVariant::AllStem
- ],
+ is_stem: bool = [true, false],
+ west: bool = [true, false],
+ up: bool = [true, false],
+ south: bool = [true, false],
+ north: bool = [true, false],
+ east: bool = [true, false],
+ down: bool = [true, false],
},
- data Some(variant.data()),
+ data mushroom_block_data(is_stem, west, up, south, north, east, down),
+ offset mushroom_block_offset(is_stem, west, up, south, north, east, down),
model { ("minecraft", "red_mushroom_block") },
- variant format!("variant={}", variant.as_string()),
+ variant format!("variant={}", mushroom_block_variant(is_stem, west, up, south, north, east, down)),
+ }
+ MushroomStem {
+ props {
+ west: bool = [true, false],
+ up: bool = [true, false],
+ south: bool = [true, false],
+ north: bool = [true, false],
+ east: bool = [true, false],
+ down: bool = [true, false],
+ },
+ data None::,
+ offset mushroom_block_offset(false, west, up, south, north, east, down),
+ model { ("minecraft", "mushroom_stem") },
+ variant format!("variant=all_stem"),
}
IronBars {
props {
@@ -1706,8 +2106,14 @@ define_blocks! {
south: bool = [false, true],
west: bool = [false, true],
east: bool = [false, true],
+ waterlogged: bool = [true, false],
},
- data if !north && !south && !west && !east { Some(0) } else { None },
+ data if !waterlogged && !north && !south && !west && !east { Some(0) } else { None },
+ offset Some(if west { 0 } else { 1<<0 } +
+ if waterlogged { 0 } else { 1<<1 } +
+ if south { 0 } else { 1<<2 } +
+ if north { 0 } else { 1<<3 } +
+ if east { 0 } else { 1<<4 }),
material material::NON_SOLID,
model { ("minecraft", "iron_bars") },
collision pane_collision(north, south, east, west),
@@ -1718,7 +2124,7 @@ define_blocks! {
};
let (north, south, west, east) = can_connect_sides(world, pos, &f);
- Block::IronBars{north: north, south: south, west: west, east: east}
+ Block::IronBars{north, south, west, east, waterlogged}
},
multipart (key, val) => match key {
"north" => north == (val == "true"),
@@ -1734,14 +2140,20 @@ define_blocks! {
south: bool = [false, true],
west: bool = [false, true],
east: bool = [false, true],
+ waterlogged: bool = [true, false],
},
- data if !north && !south && !west && !east { Some(0) } else { None },
+ data if !waterlogged && !north && !south && !west && !east { Some(0) } else { None },
+ offset Some(if west { 0 } else { 1<<0 } +
+ if waterlogged { 0 } else { 1<<1 } +
+ if south { 0 } else { 1<<2 } +
+ if north { 0 } else { 1<<3 } +
+ if east { 0 } else { 1<<4 }),
material material::NON_SOLID,
model { ("minecraft", "glass_pane") },
collision pane_collision(north, south, east, west),
update_state (world, pos) => {
let (north, south, west, east) = can_connect_sides(world, pos, &can_connect_glasspane);
- Block::GlassPane{north: north, south: south, west: west, east: east}
+ Block::GlassPane{north, south, west, east, waterlogged}
},
multipart (key, val) => match key {
"north" => north == (val == "true"),
@@ -1755,6 +2167,62 @@ define_blocks! {
props {},
model { ("minecraft", "melon_block") },
}
+ AttachedPumpkinStem {
+ props {
+ facing: Direction = [
+ Direction::North,
+ Direction::South,
+ Direction::West,
+ Direction::East
+ ],
+ },
+ data None::,
+ offset Some(facing.horizontal_offset()),
+ material material::NON_SOLID,
+ model { ("minecraft", "pumpkin_stem") },
+ variant format!("facing={}", facing.as_string()),
+ collision vec![],
+ update_state (world, pos) => {
+ let facing = match (world.get_block(pos.shift(Direction::East)), world.get_block(pos.shift(Direction::West)),
+ world.get_block(pos.shift(Direction::North)), world.get_block(pos.shift(Direction::South))) {
+ (Block::Pumpkin{ .. }, _, _, _) => Direction::East,
+ (_, Block::Pumpkin{ .. }, _, _) => Direction::West,
+ (_, _, Block::Pumpkin{ .. }, _) => Direction::North,
+ (_, _, _, Block::Pumpkin{ .. }) => Direction::South,
+ _ => Direction::Up,
+ };
+
+ Block::AttachedPumpkinStem{facing}
+ },
+ }
+ AttachedMelonStem {
+ props {
+ facing: Direction = [
+ Direction::North,
+ Direction::South,
+ Direction::West,
+ Direction::East
+ ],
+ },
+ data None::,
+ offset Some(facing.horizontal_offset()),
+ material material::NON_SOLID,
+ model { ("minecraft", "melon_stem") },
+ variant format!("facing={}", facing.as_string()),
+ collision vec![],
+ update_state (world, pos) => {
+ let facing = match (world.get_block(pos.shift(Direction::East)), world.get_block(pos.shift(Direction::West)),
+ world.get_block(pos.shift(Direction::North)), world.get_block(pos.shift(Direction::South))) {
+ (Block::MelonBlock{ .. }, _, _, _) => Direction::East,
+ (_, Block::MelonBlock{ .. }, _, _) => Direction::West,
+ (_, _, Block::MelonBlock{ .. }, _) => Direction::North,
+ (_, _, _, Block::MelonBlock{ .. }) => Direction::South,
+ _ => Direction::Up,
+ };
+
+ Block::AttachedMelonStem{facing}
+ },
+ }
PumpkinStem {
props {
age: u8 = [0, 1, 2, 3, 4, 5, 6, 7],
@@ -1843,6 +2311,11 @@ define_blocks! {
} else {
None
},
+ offset Some(if west { 0 } else { 1<<0 } +
+ if up { 0 } else { 1<<1 } +
+ if south { 0 } else { 1<<2 } +
+ if north { 0 } else { 1<<3 } +
+ if east { 0 } else { 1<<4 }),
material material::NON_SOLID,
model { ("minecraft", "vine") },
variant format!("east={},north={},south={},up={},west={}", east, north, south, up, west),
@@ -1867,6 +2340,7 @@ define_blocks! {
powered: bool = [false, true],
},
data fence_gate_data(facing, in_wall, open, powered),
+ offset fence_gate_offset(facing, in_wall, open, powered),
material material::NON_SOLID,
model { ("minecraft", "fence_gate") },
variant format!("facing={},in_wall={},open={}", facing.as_string(), in_wall, open),
@@ -1894,13 +2368,15 @@ define_blocks! {
StairShape::OuterLeft,
StairShape::OuterRight
],
+ waterlogged: bool = [true, false],
},
- data stair_data(facing, half, shape),
+ data stair_data(facing, half, shape, waterlogged),
+ offset stair_offset(facing, half, shape, waterlogged),
material material::NON_SOLID,
model { ("minecraft", "brick_stairs") },
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: facing, half: half, shape: update_stair_shape(world, pos, facing)},
+ update_state (world, pos) => Block::BrickStairs{facing, half, shape: update_stair_shape(world, pos, facing), waterlogged},
}
StoneBrickStairs {
props {
@@ -1918,19 +2394,22 @@ define_blocks! {
StairShape::OuterLeft,
StairShape::OuterRight
],
+ waterlogged: bool = [true, false],
},
- data stair_data(facing, half, shape),
+ data stair_data(facing, half, shape, waterlogged),
+ offset stair_offset(facing, half, shape, waterlogged),
material material::NON_SOLID,
model { ("minecraft", "stone_brick_stairs") },
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: facing, half: half, shape: update_stair_shape(world, pos, facing)},
+ update_state (world, pos) => Block::StoneBrickStairs{facing, half, shape: update_stair_shape(world, pos, facing), waterlogged},
}
Mycelium {
props {
snowy: bool = [false, true],
},
data if snowy { None } else { Some(0) },
+ offset Some(if snowy { 0 } else { 1 }),
material material::SOLID,
model { ("minecraft", "mycelium") },
variant format!("snowy={}", snowy),
@@ -1956,8 +2435,14 @@ define_blocks! {
south: bool = [false, true],
west: bool = [false, true],
east: bool = [false, true],
+ waterlogged: bool = [true, false],
},
- data if !north && !south && !west && !east { Some(0) } else { None },
+ data if !north && !south && !west && !east && !waterlogged { Some(0) } else { None },
+ offset Some(if west { 0 } else { 1<<0 } +
+ if waterlogged { 0 } else { 1<<1 } +
+ if south { 0 } else { 1<<2 } +
+ if north { 0 } else { 1<<3 } +
+ if east { 0 } else { 1<<4 }),
material material::NON_SOLID,
model { ("minecraft", "nether_brick_fence") },
collision fence_collision(north, south, west, east),
@@ -1974,7 +2459,7 @@ define_blocks! {
};
let (north, south, west, east) = can_connect_sides(world, pos, &f);
- Block::NetherBrickFence{north: north, south: south, west: west, east: east}
+ Block::NetherBrickFence{north, south, west, east, waterlogged}
},
multipart (key, val) => match key {
"north" => north == (val == "true"),
@@ -2000,13 +2485,15 @@ define_blocks! {
StairShape::OuterLeft,
StairShape::OuterRight
],
+ waterlogged: bool = [true, false],
},
- data stair_data(facing, half, shape),
+ data stair_data(facing, half, shape, waterlogged),
+ offset stair_offset(facing, half, shape, waterlogged),
material material::NON_SOLID,
model { ("minecraft", "nether_brick_stairs") },
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: facing, half: half, shape: update_stair_shape(world, pos, facing)},
+ update_state (world, pos) => Block::NetherBrickStairs{facing, half, shape: update_stair_shape(world, pos, facing), waterlogged},
}
NetherWart {
props {
@@ -2036,6 +2523,9 @@ define_blocks! {
data Some((if has_bottle_0 { 0x1 } else { 0x0 })
| (if has_bottle_1 { 0x2 } else { 0x0 })
| (if has_bottle_2 { 0x4 } else { 0x0 })),
+ offset Some(if has_bottle_0 { 0 } else { 1<<0 } +
+ if has_bottle_1 { 0 } else { 1<<1 } +
+ if has_bottle_2 { 0 } else { 1<<2 }),
material Material {
emitted_light: 1,
..material::NON_SOLID
@@ -2077,6 +2567,7 @@ define_blocks! {
],
},
data Some(facing.horizontal_index() | (if eye { 0x4 } else { 0x0 })),
+ offset Some(facing.horizontal_offset() + (if eye { 0 } else { 4 })),
material Material {
emitted_light: 1,
..material::NON_SOLID
@@ -2139,6 +2630,7 @@ define_blocks! {
],
},
data Some(variant.data()),
+ offset None,
model { ("minecraft", format!("{}_double_slab", variant.as_string()) ) },
}
WoodenSlab {
@@ -2154,6 +2646,7 @@ define_blocks! {
],
},
data Some(variant.data() | (if half == BlockHalf::Top { 0x8 } else { 0x0 })),
+ offset None,
material material::NON_SOLID,
model { ("minecraft", format!("{}_slab", variant.as_string()) ) },
variant format!("half={}", half.as_string()),
@@ -2170,6 +2663,7 @@ define_blocks! {
],
},
data Some(facing.horizontal_index() | ((age as usize) << 2)),
+ offset Some(facing.horizontal_offset() + ((age as usize) * 4)),
material material::NON_SOLID,
model { ("minecraft", "cocoa") },
variant format!("age={},facing={}", age, facing.as_string()),
@@ -2208,13 +2702,15 @@ define_blocks! {
StairShape::OuterLeft,
StairShape::OuterRight
],
+ waterlogged: bool = [true, false],
},
- data stair_data(facing, half, shape),
+ data stair_data(facing, half, shape, waterlogged),
+ offset stair_offset(facing, half, shape, waterlogged),
material material::NON_SOLID,
model { ("minecraft", "sandstone_stairs") },
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: facing, half: half, shape: update_stair_shape(world, pos, facing)},
+ update_state (world, pos) => Block::SandstoneStairs{facing, half, shape: update_stair_shape(world, pos, facing), waterlogged},
}
EmeraldOre {
props {},
@@ -2229,8 +2725,10 @@ define_blocks! {
Direction::West,
Direction::East
],
+ waterlogged: bool = [true, false],
},
- data Some(facing.index()),
+ data if waterlogged { None } else { Some(facing.index()) },
+ offset Some(if waterlogged { 0 } else { 1 } + facing.horizontal_offset() * 2),
material Material {
emitted_light: 7,
..material::NON_SOLID
@@ -2256,6 +2754,9 @@ define_blocks! {
data Some(facing.horizontal_index()
| (if attached { 0x4 } else { 0x0 })
| (if powered { 0x8 } else { 0x0 })),
+ offset Some(if powered { 0 } else { 1 } +
+ facing.horizontal_offset() * 2 +
+ if attached { 0 } else { 2 * 4 }),
material material::NON_SOLID,
model { ("minecraft", "tripwire_hook") },
variant format!("attached={},facing={},powered={}", attached, facing.as_string(), powered),
@@ -2280,6 +2781,17 @@ define_blocks! {
} else {
None
},
+ offset if mojang_cant_even {
+ None
+ } else {
+ Some(if west { 0 } else { 1<<0 } +
+ if south { 0 } else { 1<<1 } +
+ if powered { 0 } else { 1<<2 } +
+ if north { 0 } else { 1<<3 } +
+ if east { 0 } else { 1<<4 } +
+ if disarmed { 0 } else { 1<<5 } +
+ if attached { 0 } else { 1<<6 })
+ },
material material::TRANSPARENT,
model { ("minecraft", "tripwire") },
variant format!("attached={},east={},north={},south={},west={}", attached, east, north, south, west),
@@ -2325,13 +2837,15 @@ define_blocks! {
StairShape::OuterLeft,
StairShape::OuterRight
],
+ waterlogged: bool = [true, false],
},
- data stair_data(facing, half, shape),
+ data stair_data(facing, half, shape, waterlogged),
+ offset stair_offset(facing, half, shape, waterlogged),
material material::NON_SOLID,
model { ("minecraft", "spruce_stairs") },
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: facing, half: half, shape: update_stair_shape(world, pos, facing)},
+ update_state (world, pos) => Block::SpruceStairs{facing, half, shape: update_stair_shape(world, pos, facing), waterlogged},
}
BirchStairs {
props {
@@ -2349,13 +2863,15 @@ define_blocks! {
StairShape::OuterLeft,
StairShape::OuterRight
],
+ waterlogged: bool = [true, false],
},
- data stair_data(facing, half, shape),
+ data stair_data(facing, half, shape, waterlogged),
+ offset stair_offset(facing, half, shape, waterlogged),
material material::NON_SOLID,
model { ("minecraft", "birch_stairs") },
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: facing, half: half, shape: update_stair_shape(world, pos, facing)},
+ update_state (world, pos) => Block::BirchStairs{facing, half, shape: update_stair_shape(world, pos, facing), waterlogged},
}
JungleStairs {
props {
@@ -2373,13 +2889,15 @@ define_blocks! {
StairShape::OuterLeft,
StairShape::OuterRight
],
+ waterlogged: bool = [true, false],
},
- data stair_data(facing, half, shape),
+ data stair_data(facing, half, shape, waterlogged),
+ offset stair_offset(facing, half, shape, waterlogged),
material material::NON_SOLID,
model { ("minecraft", "jungle_stairs") },
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: facing, half: half, shape: update_stair_shape(world, pos, facing)},
+ update_state (world, pos) => Block::JungleStairs{facing, half, shape: update_stair_shape(world, pos, facing), waterlogged},
}
CommandBlock {
props {
@@ -2394,6 +2912,7 @@ define_blocks! {
],
},
data Some(facing.index() | (if conditional { 0x8 } else { 0x0 })),
+ offset Some(facing.offset() + (if conditional { 0 } else { 6 })),
model { ("minecraft", "command_block") },
variant format!("conditional={},facing={}", conditional, facing.as_string()),
}
@@ -2416,8 +2935,16 @@ define_blocks! {
CobblestoneWallVariant::Normal,
CobblestoneWallVariant::Mossy
],
+ waterlogged: bool = [true, false],
},
- data if !north && !south && !east && !west && !up { Some(variant.data()) } else { None },
+ data if !north && !south && !east && !west && !up && !waterlogged { Some(variant.data()) } else { None },
+ offset Some(if west { 0 } else { 1<<0 } +
+ if waterlogged { 0 } else { 1<<1 } +
+ if up { 0 } else { 1<<2 } +
+ if south { 0 } else { 1<<3 } +
+ if north { 0 } else { 1<<4 } +
+ if east { 0 } else { 1<<5 } +
+ if variant == CobblestoneWallVariant::Normal { 0 } else { 1<<6 }),
material material::NON_SOLID,
model { ("minecraft", format!("{}_wall", variant.as_string())) },
update_state (world, pos) => {
@@ -2437,7 +2964,7 @@ define_blocks! {
Block::Air{..} => true,
_ => false,
}) || !((north && south && !west && !east) || (!north && !south && west && east));
- Block::CobblestoneWall{up: up, north: north, south: south, west: west, east: east, variant: variant}
+ Block::CobblestoneWall{up, north, south, west, east, variant, waterlogged}
},
multipart (key, val) => match key {
"up" => up == (val == "true"),
@@ -2464,7 +2991,7 @@ define_blocks! {
FlowerPotVariant::DeadBush,
FlowerPotVariant::Fern,
FlowerPotVariant::AcaciaSapling,
- FlowerPotVariant::DarkOak,
+ FlowerPotVariant::DarkOakSapling,
FlowerPotVariant::BlueOrchid,
FlowerPotVariant::Allium,
FlowerPotVariant::AzureBluet,
@@ -2472,12 +2999,12 @@ define_blocks! {
FlowerPotVariant::OrangeTulip,
FlowerPotVariant::WhiteTulip,
FlowerPotVariant::PinkTulip,
- FlowerPotVariant::Oxeye,
- FlowerPotVariant::Dandelion
+ FlowerPotVariant::Oxeye
],
legacy_data: u8 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
},
data if contents == FlowerPotVariant::Empty { Some(legacy_data as usize) } else { None },
+ offset if legacy_data != 0 { None } else { Some(contents.offset()) },
material material::NON_SOLID,
model { ("minecraft", "flower_pot") },
}
@@ -2503,30 +3030,34 @@ define_blocks! {
}
WoodenButton {
props {
+ face: AttachedFace = [
+ AttachedFace::Floor,
+ AttachedFace::Wall,
+ AttachedFace::Ceiling
+ ],
facing: Direction = [
- Direction::Down,
Direction::East,
Direction::West,
Direction::South,
- Direction::North,
- Direction::Up
+ Direction::North
],
powered: bool = [false, true],
+ variant: TreeVariant = [
+ TreeVariant::Oak,
+ TreeVariant::Spruce,
+ TreeVariant::Birch,
+ TreeVariant::Jungle,
+ TreeVariant::Acacia,
+ TreeVariant::DarkOak
+ ],
},
- data Some(match facing {
- Direction::Down => 0,
- Direction::East => 1,
- Direction::West => 2,
- Direction::South => 3,
- Direction::North => 4,
- Direction::Up => 5,
- _ => unreachable!(),
- } | (if powered { 0x8 } else { 0x0 })),
+ data if variant == TreeVariant::Oak { face.data_with_facing_and_powered(facing, powered) } else { None },
+ offset Some(variant.offset() * (3 * 4 * 2) + face.offset() * (4 * 2) + facing.horizontal_offset() * 2 + if powered { 0 } else { 1 }),
material material::NON_SOLID,
model { ("minecraft", "wooden_button") },
- variant format!("facing={},powered={}", facing.as_string(), powered),
+ variant format!("facing={},powered={}", face.variant_with_facing(facing), powered),
}
- Skull {
+ SkullSkeletonWall {
props {
facing: Direction = [
Direction::Up,
@@ -2538,6 +3069,7 @@ define_blocks! {
nodrop: bool = [false, true],
},
data if !nodrop { Some(facing.index()) } else { None },
+ offset if !nodrop && facing != Direction::Up { Some(facing.horizontal_offset()) } else { None },
material material::NON_SOLID,
model { ("minecraft", "skull") },
variant format!("facing={},nodrop={}", facing.as_string(), nodrop),
@@ -2557,6 +3089,161 @@ define_blocks! {
)]
},
}
+ SkullSkeleton
+ {
+ props {
+ rotation: u8 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
+ },
+ data None::,
+ offset Some(rotation as usize),
+ material material::NON_SOLID,
+ model { ("minecraft", "skull") },
+ collision {
+ let (min_x, min_y, min_z, max_x, max_y, max_z) = (0.25, 0.0, 0.25, 0.75, 0.5, 0.75);
+
+ vec![Aabb3::new(
+ Point3::new(min_x, min_y, min_z),
+ Point3::new(max_x, max_y, max_z)
+ )]
+ },
+ }
+ SkullWitherSkeletonWall {
+ props {
+ facing: Direction = [
+ Direction::North,
+ Direction::South,
+ Direction::West,
+ Direction::East
+ ],
+ },
+ data None::,
+ offset Some(facing.horizontal_offset()),
+ material material::NON_SOLID,
+ model { ("minecraft", "skull") },
+ collision {
+ let (min_x, min_y, min_z, max_x, max_y, max_z) = match facing {
+ Direction::North => (0.25, 0.25, 0.5, 0.75, 0.75, 1.0),
+ Direction::South => (0.25, 0.25, 0.0, 0.75, 0.75, 0.5),
+ Direction::West => (0.5, 0.25, 0.25, 1.0, 0.75, 0.75),
+ Direction::East => (0.0, 0.25, 0.25, 0.5, 0.75, 0.75),
+ _ => unreachable!(),
+ };
+
+ vec![Aabb3::new(
+ Point3::new(min_x, min_y, min_z),
+ Point3::new(max_x, max_y, max_z)
+ )]
+ },
+ }
+ SkullWitherSkeleton {
+ props {
+ rotation: u8 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
+ },
+ data None::,
+ offset Some(rotation as usize),
+ material material::NON_SOLID,
+ model { ("minecraft", "skull") },
+ collision {
+ let (min_x, min_y, min_z, max_x, max_y, max_z) = (0.25, 0.0, 0.25, 0.75, 0.5, 0.75);
+
+ vec![Aabb3::new(
+ Point3::new(min_x, min_y, min_z),
+ Point3::new(max_x, max_y, max_z)
+ )]
+ },
+ }
+ ZombieWallHead {
+ props {
+ facing: Direction = [
+ Direction::North,
+ Direction::South,
+ Direction::West,
+ Direction::East
+ ],
+ },
+ data None::,
+ offset Some(facing.horizontal_offset()),
+ material material::NON_SOLID,
+ model { ("minecraft", "zombie_wall_head") },
+ }
+ ZombieHead {
+ props {
+ rotation: u8 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
+ },
+ data None::,
+ offset Some(rotation as usize),
+ material material::NON_SOLID,
+ model { ("minecraft", "zombie_head") },
+ }
+ PlayerWallHead {
+ props {
+ facing: Direction = [
+ Direction::North,
+ Direction::South,
+ Direction::West,
+ Direction::East
+ ],
+ },
+ data None::,
+ offset Some(facing.horizontal_offset()),
+ material material::NON_SOLID,
+ model { ("minecraft", "player_wall_head") },
+ }
+ PlayerHead {
+ props {
+ rotation: u8 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
+ },
+ data None::,
+ offset Some(rotation as usize),
+ material material::NON_SOLID,
+ model { ("minecraft", "player_head") },
+ }
+ CreeperWallHead {
+ props {
+ facing: Direction = [
+ Direction::North,
+ Direction::South,
+ Direction::West,
+ Direction::East
+ ],
+ },
+ data None::,
+ offset Some(facing.horizontal_offset()),
+ material material::NON_SOLID,
+ model { ("minecraft", "creeper_wall_head") },
+ }
+ CreeperHead {
+ props {
+ rotation: u8 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
+ },
+ data None::,
+ offset Some(rotation as usize),
+ material material::NON_SOLID,
+ model { ("minecraft", "creeper_head") },
+ }
+ DragonWallHead {
+ props {
+ facing: Direction = [
+ Direction::North,
+ Direction::South,
+ Direction::West,
+ Direction::East
+ ],
+ },
+ data None::,
+ offset Some(facing.horizontal_offset()),
+ material material::NON_SOLID,
+ model { ("minecraft", "dragon_wall_head") },
+ }
+ DragonHead {
+ props {
+ rotation: u8 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
+ },
+ data None::,
+ offset Some(rotation as usize),
+ material material::NON_SOLID,
+ model { ("minecraft", "dragon_head") },
+ }
Anvil {
props {
damage: u8 = [0, 1, 2],
@@ -2568,6 +3255,7 @@ define_blocks! {
],
},
data Some(facing.horizontal_index() | (match damage { 0 => 0x0, 1 => 0x4, 2 => 0x8, _ => unreachable!() })),
+ offset Some(facing.horizontal_offset() + (damage as usize) * 4),
material material::NON_SOLID,
model { ("minecraft", "anvil") },
variant format!("damage={},facing={}", damage, facing.as_string()),
@@ -2591,8 +3279,17 @@ define_blocks! {
Direction::West,
Direction::East
],
+ type_: ChestType = [
+ ChestType::Single,
+ ChestType::Left,
+ ChestType::Right
+ ],
+ waterlogged: bool = [true, false],
},
- data Some(facing.index()),
+ data if type_ == ChestType::Single && !waterlogged { Some(facing.index()) } else { None },
+ offset Some(if waterlogged { 0 } else { 1 } +
+ type_.offset() * 2 +
+ facing.horizontal_offset() * (2 * 3)),
material material::NON_SOLID,
model { ("minecraft", "trapped_chest") },
variant format!("facing={}", facing.as_string()),
@@ -2635,6 +3332,9 @@ define_blocks! {
data Some(facing.horizontal_index()
| (if mode == ComparatorMode::Subtract { 0x4 } else { 0x0 })
| (if powered { 0x8 } else { 0x0 })),
+ offset Some(if powered { 0 } else { 1<<0 } +
+ if mode == ComparatorMode::Compare { 0 } else { 1<<1 } +
+ facing.horizontal_offset() * (1<<2)),
material material::NON_SOLID,
model { ("minecraft", "unpowered_comparator") },
variant format!("facing={},mode={},powered={}", facing.as_string(), mode.as_string(), powered),
@@ -2657,6 +3357,7 @@ define_blocks! {
data Some(facing.horizontal_index()
| (if mode == ComparatorMode::Subtract { 0x4 } else { 0x0 })
| (if powered { 0x8 } else { 0x0 })),
+ offset None,
material material::NON_SOLID,
model { ("minecraft", "powered_comparator") },
variant format!("facing={},mode={},powered={}", facing.as_string(), mode.as_string(), powered),
@@ -2668,8 +3369,10 @@ define_blocks! {
DaylightDetector {
props {
power: u8 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
+ inverted: bool = [true, false],
},
- data Some(power as usize),
+ data if inverted { None } else { Some(power as usize) },
+ offset Some((power as usize) + if inverted { 0 } else { 16 }),
material material::NON_SOLID,
model { ("minecraft", "daylight_detector") },
variant format!("power={}", power),
@@ -2698,6 +3401,14 @@ define_blocks! {
],
},
data Some(facing.index() | (if enabled { 0x8 } else { 0x0 })),
+ offset Some(match facing {
+ Direction::Down => 0,
+ Direction::North => 1,
+ Direction::South => 2,
+ Direction::West => 3,
+ Direction::East => 4,
+ _ => unreachable!(),
+ } + if enabled { 0 } else { 5 }),
material material::NON_SOLID,
model { ("minecraft", "hopper") },
variant format!("facing={}", facing.as_string()),
@@ -2738,13 +3449,15 @@ define_blocks! {
StairShape::OuterLeft,
StairShape::OuterRight
],
+ waterlogged: bool = [true, false],
},
- data stair_data(facing, half, shape),
+ data stair_data(facing, half, shape, waterlogged),
+ offset stair_offset(facing, half, shape, waterlogged),
material material::NON_SOLID,
model { ("minecraft", "quartz_stairs") },
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: facing, half: half, shape: update_stair_shape(world, pos, facing)},
+ update_state (world, pos) => Block::QuartzStairs{facing, half, shape: update_stair_shape(world, pos, facing), waterlogged},
}
ActivatorRail {
props {
@@ -2759,6 +3472,7 @@ define_blocks! {
powered: bool = [false, true],
},
data Some(shape.data() | (if powered { 0x8 } else { 0x0 })),
+ offset Some(shape.data() + (if powered { 0 } else { 6 })),
material material::NON_SOLID,
model { ("minecraft", "activator_rail") },
variant format!("powered={},shape={}", powered, shape.as_string()),
@@ -2777,6 +3491,7 @@ define_blocks! {
triggered: bool = [false, true],
},
data Some(facing.index() | (if triggered { 0x8 } else { 0x0 })),
+ offset Some(if triggered { 0 } else { 1 } + facing.offset() * 2),
model { ("minecraft", "dropper") },
variant format!("facing={}", facing.as_string()),
}
@@ -2828,14 +3543,21 @@ define_blocks! {
south: bool = [false, true],
east: bool = [false, true],
west: bool = [false, true],
+ waterlogged: bool = [true, false],
},
- data if !north && !south && !east && !west { Some(color.data()) } else { None },
+ data if !north && !south && !east && !west && !waterlogged { Some(color.data()) } else { None },
+ offset Some(if west { 0 } else { 1<<0 } +
+ if waterlogged { 0 } else { 1<<1 } +
+ if south { 0 } else { 1<<2 } +
+ if north { 0 } else { 1<<3 } +
+ if east { 0 } else { 1<<4 } +
+ color.data() * (1<<5)),
material material::TRANSPARENT,
model { ("minecraft", format!("{}_stained_glass_pane", color.as_string()) ) },
collision pane_collision(north, south, east, west),
update_state (world, pos) => {
let (north, south, west, east) = can_connect_sides(world, pos, &can_connect_glasspane);
- Block::StainedGlassPane{color: color, north: north, south: south, west: west, east: east}
+ Block::StainedGlassPane{color, north, south, west, east, waterlogged}
},
multipart (key, val) => match key {
"north" => north == (val == "true"),
@@ -2857,6 +3579,7 @@ define_blocks! {
data Some(variant.data()
| (if decayable { 0x4 } else { 0x0 })
| (if check_decay { 0x8 } else { 0x0 })),
+ offset None,
material material::LEAVES,
model { ("minecraft", format!("{}_leaves", variant.as_string()) ) },
tint TintType::Foliage,
@@ -2870,6 +3593,7 @@ define_blocks! {
],
},
data Some(variant.data() | (axis.index() << 2)),
+ offset None,
model { ("minecraft", format!("{}_log", variant.as_string()) ) },
variant format!("axis={}", axis.as_string()),
}
@@ -2889,13 +3613,15 @@ define_blocks! {
StairShape::OuterLeft,
StairShape::OuterRight
],
+ waterlogged: bool = [true, false],
},
- data stair_data(facing, half, shape),
+ data stair_data(facing, half, shape, waterlogged),
+ offset stair_offset(facing, half, shape, waterlogged),
material material::NON_SOLID,
model { ("minecraft", "acacia_stairs") },
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: facing, half: half, shape: update_stair_shape(world, pos, facing)},
+ update_state (world, pos) => Block::AcaciaStairs{facing, half, shape: update_stair_shape(world, pos, facing), waterlogged},
}
DarkOakStairs {
props {
@@ -2913,13 +3639,15 @@ define_blocks! {
StairShape::OuterLeft,
StairShape::OuterRight
],
+ waterlogged: bool = [true, false],
},
- data stair_data(facing, half, shape),
+ data stair_data(facing, half, shape, waterlogged),
+ offset stair_offset(facing, half, shape, waterlogged),
material material::NON_SOLID,
model { ("minecraft", "dark_oak_stairs") },
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: facing, half: half, shape: update_stair_shape(world, pos, facing)},
+ update_state (world, pos) => Block::DarkOakStairs{facing, half, shape: update_stair_shape(world, pos, facing), waterlogged},
}
Slime {
props {},
@@ -2941,14 +3669,21 @@ define_blocks! {
],
half: BlockHalf = [BlockHalf::Top, BlockHalf::Bottom],
open: bool = [false, true],
+ waterlogged: bool = [true, false],
+ powered: bool = [true, false],
},
- data Some(match facing {
+ data if waterlogged || powered { None } else { Some(match facing {
Direction::North => 0,
Direction::South => 1,
Direction::West => 2,
Direction::East => 3,
_ => unreachable!(),
- } | (if open { 0x4 } else { 0x0 }) | (if half == BlockHalf::Top { 0x8 } else { 0x0 })),
+ } | (if open { 0x4 } else { 0x0 }) | (if half == BlockHalf::Top { 0x8 } else { 0x0 }))},
+ offset Some(if waterlogged { 0 } else { 1<<0 } +
+ if powered { 0 } else { 1<<1 } +
+ if open { 0 } else { 1<<2 } +
+ if half == BlockHalf::Top { 0 } else { 1<<3 } +
+ facing.horizontal_offset() * (1<<4)),
material material::NON_SOLID,
model { ("minecraft", "iron_trapdoor") },
variant format!("facing={},half={},open={}", facing.as_string(), half.as_string(), open),
@@ -2965,6 +3700,66 @@ define_blocks! {
data Some(variant.data()),
model { ("minecraft", variant.as_string() ) },
}
+ PrismarineStairs {
+ props {
+ facing: Direction = [
+ Direction::North,
+ Direction::South,
+ Direction::West,
+ Direction::East
+ ],
+ half: BlockHalf = [BlockHalf::Top, BlockHalf::Bottom],
+ shape: StairShape = [
+ StairShape::Straight,
+ StairShape::InnerLeft,
+ StairShape::InnerRight,
+ StairShape::OuterLeft,
+ StairShape::OuterRight
+ ],
+ waterlogged: bool = [true, false],
+ variant: PrismarineVariant = [
+ PrismarineVariant::Normal,
+ PrismarineVariant::Brick,
+ PrismarineVariant::Dark
+ ],
+ },
+ data None::,
+ offset Some(stair_offset(facing, half, shape, waterlogged).unwrap() + (2 * 5 * 2 * 4) * variant.data()),
+ material material::NON_SOLID,
+ model { ("minecraft", match variant {
+ PrismarineVariant::Normal => "prismarine_stairs",
+ PrismarineVariant::Brick => "prismarine_brick_stairs",
+ PrismarineVariant::Dark => "dark_prismarine_stairs",
+ }) },
+ 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},
+ }
+ PrismarineSlab {
+ props {
+ type_: BlockHalf = [
+ BlockHalf::Top,
+ BlockHalf::Bottom,
+ BlockHalf::Double
+ ],
+ waterlogged: bool = [true, false],
+ variant: PrismarineVariant = [
+ PrismarineVariant::Normal,
+ PrismarineVariant::Brick,
+ PrismarineVariant::Dark
+ ],
+ },
+ data None::,
+ offset Some(if waterlogged { 0 } else { 1 } + type_.offset() * 2 + variant.data() * (2 * 3)),
+ material material::NON_SOLID,
+ model { ("minecraft", match variant {
+ PrismarineVariant::Normal => "prismarine_slab",
+ PrismarineVariant::Brick => "prismarine_brick_slab",
+ PrismarineVariant::Dark => "dark_prismarine_slab",
+ }) },
+ variant format!("type={}", type_.as_string()),
+ collision slab_collision(type_),
+ }
SeaLantern {
props {},
material Material {
@@ -2978,6 +3773,7 @@ define_blocks! {
axis: Axis = [Axis::X, Axis::Y, Axis::Z],
},
data Some(match axis { Axis::X => 0x4, Axis::Y => 0x0, Axis::Z => 0x8, _ => unreachable!() }),
+ offset Some(match axis { Axis::X => 0, Axis::Y => 1, Axis::Z => 2, _ => unreachable!() }),
model { ("minecraft", "hay_block") },
variant format!("axis={}", axis.as_string()),
}
@@ -3035,6 +3831,7 @@ define_blocks! {
],
},
data Some(variant.data() | (if half == BlockHalf::Upper { 0x8 } else { 0x0 })),
+ offset Some(half.offset() + variant.offset() * 2),
material material::NON_SOLID,
model { ("minecraft", variant.as_string()) },
variant format!("half={}", half.as_string()),
@@ -3042,7 +3839,7 @@ define_blocks! {
collision vec![],
update_state (world, pos) => {
let (half, variant) = update_double_plant_state(world, pos, half, variant);
- Block::DoublePlant{half: half, variant: variant}
+ Block::DoublePlant{half, variant}
},
}
StandingBanner {
@@ -3065,8 +3862,27 @@ define_blocks! {
Rotation::SouthEast,
Rotation::SouthSouthEast
],
+ color: ColoredVariant = [
+ ColoredVariant::White,
+ ColoredVariant::Orange,
+ ColoredVariant::Magenta,
+ ColoredVariant::LightBlue,
+ ColoredVariant::Yellow,
+ ColoredVariant::Lime,
+ ColoredVariant::Pink,
+ ColoredVariant::Gray,
+ ColoredVariant::Silver,
+ ColoredVariant::Cyan,
+ ColoredVariant::Purple,
+ ColoredVariant::Blue,
+ ColoredVariant::Brown,
+ ColoredVariant::Green,
+ ColoredVariant::Red,
+ ColoredVariant::Black
+ ],
},
- data Some(rotation.data()),
+ data if color != ColoredVariant::White { None } else { Some(rotation.data()) },
+ offset Some(rotation.data() + color.data() * 16),
material material::NON_SOLID,
model { ("minecraft", "standing_banner") },
variant format!("rotation={}", rotation.as_string()),
@@ -3079,8 +3895,27 @@ define_blocks! {
Direction::West,
Direction::East
],
+ color: ColoredVariant = [
+ ColoredVariant::White,
+ ColoredVariant::Orange,
+ ColoredVariant::Magenta,
+ ColoredVariant::LightBlue,
+ ColoredVariant::Yellow,
+ ColoredVariant::Lime,
+ ColoredVariant::Pink,
+ ColoredVariant::Gray,
+ ColoredVariant::Silver,
+ ColoredVariant::Cyan,
+ ColoredVariant::Purple,
+ ColoredVariant::Blue,
+ ColoredVariant::Brown,
+ ColoredVariant::Green,
+ ColoredVariant::Red,
+ ColoredVariant::Black
+ ],
},
- data Some(facing.index()),
+ data if color != ColoredVariant::White { None } else { Some(facing.index()) },
+ offset Some(facing.horizontal_offset() + color.data() * 4),
material material::NON_SOLID,
model { ("minecraft", "wall_banner") },
variant format!("facing={}", facing.as_string()),
@@ -3090,6 +3925,7 @@ define_blocks! {
power: u8 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
},
data Some(power as usize),
+ offset None,
material material::NON_SOLID,
model { ("minecraft", "daylight_detector_inverted") },
variant format!("power={}", power),
@@ -3125,13 +3961,63 @@ define_blocks! {
StairShape::OuterLeft,
StairShape::OuterRight
],
+ waterlogged: bool = [true, false],
},
- data stair_data(facing, half, shape),
+ data stair_data(facing, half, shape, waterlogged),
+ offset stair_offset(facing, half, shape, waterlogged),
material material::NON_SOLID,
model { ("minecraft", "red_sandstone_stairs") },
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: facing, half: half, shape: update_stair_shape(world, pos, facing)},
+ update_state (world, pos) => Block::RedSandstoneStairs{facing, half, shape: update_stair_shape(world, pos, facing), waterlogged},
+ }
+ WoodenSlabFlat {
+ props {
+ type_: BlockHalf = [
+ BlockHalf::Top,
+ BlockHalf::Bottom,
+ BlockHalf::Double
+ ],
+ waterlogged: bool = [true, false],
+ variant: WoodSlabVariant = [
+ WoodSlabVariant::Oak,
+ WoodSlabVariant::Spruce,
+ WoodSlabVariant::Birch,
+ WoodSlabVariant::Jungle,
+ WoodSlabVariant::Acacia,
+ WoodSlabVariant::DarkOak
+ ],
+ },
+ data None::,
+ offset Some(if waterlogged { 0 } else { 1 } + type_.offset() * 2 + variant.data() * (2 * 3)),
+ material material::NON_SOLID,
+ model { ("minecraft", format!("{}_slab", variant.as_string()) ) },
+ variant format!("type={}", type_.as_string()),
+ collision slab_collision(type_),
+ }
+ StoneSlabFlat {
+ props {
+ type_: BlockHalf = [BlockHalf::Top, BlockHalf::Bottom, BlockHalf::Double],
+ variant: StoneSlabVariant = [
+ StoneSlabVariant::Stone,
+ StoneSlabVariant::Sandstone,
+ StoneSlabVariant::PetrifiedWood,
+ StoneSlabVariant::Cobblestone,
+ StoneSlabVariant::Brick,
+ StoneSlabVariant::StoneBrick,
+ StoneSlabVariant::NetherBrick,
+ StoneSlabVariant::Quartz,
+ StoneSlabVariant::RedSandstone,
+ StoneSlabVariant::Purpur
+ ],
+ waterlogged: bool = [true, false],
+ },
+ data None::,
+ offset Some(if waterlogged { 0 } else { 1 } + type_.offset() * 2 + variant.offset() * (2 * 3)),
+ material material::NON_SOLID,
+ model { ("minecraft", format!("{}_slab", variant.as_string()) ) },
+ variant format!("type={}", type_.as_string()),
+ collision slab_collision(type_),
}
DoubleStoneSlab2 {
props {
@@ -3141,6 +4027,7 @@ define_blocks! {
],
},
data Some(variant.data() | (if seamless { 0x8 } else { 0x0 })),
+ offset None,
material material::SOLID,
model { ("minecraft", format!("{}_double_slab", variant.as_string()) ) },
variant if seamless { "all" } else { "normal" },
@@ -3151,11 +4038,31 @@ define_blocks! {
variant: StoneSlabVariant = [StoneSlabVariant::RedSandstone],
},
data Some(variant.data() | (if half == BlockHalf::Top { 0x8 } else { 0x0 })),
+ offset None,
material material::NON_SOLID,
model { ("minecraft", format!("{}_slab", variant.as_string()) ) },
variant format!("half={}", half.as_string()),
collision slab_collision(half),
}
+ SmoothStone {
+ props {
+ variant: StoneSlabVariant = [
+ StoneSlabVariant::Stone,
+ StoneSlabVariant::Sandstone,
+ StoneSlabVariant::Quartz,
+ StoneSlabVariant::RedSandstone
+ ],
+ },
+ data None::,
+ offset Some(match variant {
+ StoneSlabVariant::Stone => 0,
+ StoneSlabVariant::Sandstone => 1,
+ StoneSlabVariant::Quartz => 2,
+ StoneSlabVariant::RedSandstone => 3,
+ _ => unreachable!(),
+ }),
+ model { ("minecraft", format!("smooth_{}", variant.as_string()) ) },
+ }
SpruceFenceGate {
props {
facing: Direction = [
@@ -3169,6 +4076,7 @@ define_blocks! {
powered: bool = [false, true],
},
data fence_gate_data(facing, in_wall, open, powered),
+ offset fence_gate_offset(facing, in_wall, open, powered),
material material::NON_SOLID,
model { ("minecraft", "spruce_fence_gate") },
variant format!("facing={},in_wall={},open={}", facing.as_string(), in_wall, open),
@@ -3193,6 +4101,7 @@ define_blocks! {
powered: bool = [false, true],
},
data fence_gate_data(facing, in_wall, open, powered),
+ offset fence_gate_offset(facing, in_wall, open, powered),
material material::NON_SOLID,
model { ("minecraft", "birch_fence_gate") },
variant format!("facing={},in_wall={},open={}", facing.as_string(), in_wall, open),
@@ -3217,6 +4126,7 @@ define_blocks! {
powered: bool = [false, true],
},
data fence_gate_data(facing, in_wall, open, powered),
+ offset fence_gate_offset(facing, in_wall, open, powered),
material material::NON_SOLID,
model { ("minecraft", "jungle_fence_gate") },
variant format!("facing={},in_wall={},open={}", facing.as_string(), in_wall, open),
@@ -3241,6 +4151,7 @@ define_blocks! {
powered: bool = [false, true],
},
data fence_gate_data(facing, in_wall, open, powered),
+ offset fence_gate_offset(facing, in_wall, open, powered),
material material::NON_SOLID,
model { ("minecraft", "dark_oak_fence_gate") },
variant format!("facing={},in_wall={},open={}", facing.as_string(), in_wall, open),
@@ -3265,6 +4176,7 @@ define_blocks! {
powered: bool = [false, true],
},
data fence_gate_data(facing, in_wall, open, powered),
+ offset fence_gate_offset(facing, in_wall, open, powered),
material material::NON_SOLID,
model { ("minecraft", "acacia_fence_gate") },
variant format!("facing={},in_wall={},open={}", facing.as_string(), in_wall, open),
@@ -3282,14 +4194,20 @@ define_blocks! {
south: bool = [false, true],
west: bool = [false, true],
east: bool = [false, true],
+ waterlogged: bool = [true, false],
},
- data if !north && !south && !west && !east { Some(0) } else { None },
+ data if !north && !south && !west && !east && !waterlogged { Some(0) } else { None },
+ offset Some(if west { 0 } else { 1<<0 } +
+ if waterlogged { 0 } else { 1<<1 } +
+ if south { 0 } else { 1<<2 } +
+ if north { 0 } else { 1<<3 } +
+ if east { 0 } else { 1<<4 }),
material material::NON_SOLID,
model { ("minecraft", "spruce_fence") },
collision fence_collision(north, south, west, east),
update_state (world, pos) => {
let (north, south, west, east) = can_connect_sides(world, pos, &can_connect_fence);
- Block::SpruceFence{north: north, south: south, west: west, east: east}
+ Block::SpruceFence{north, south, west, east, waterlogged}
},
multipart (key, val) => match key {
"north" => north == (val == "true"),
@@ -3305,14 +4223,20 @@ define_blocks! {
south: bool = [false, true],
west: bool = [false, true],
east: bool = [false, true],
+ waterlogged: bool = [true, false],
},
- data if !north && !south && !west && !east { Some(0) } else { None },
+ data if !north && !south && !west && !east && !waterlogged { Some(0) } else { None },
+ offset Some(if west { 0 } else { 1<<0 } +
+ if waterlogged { 0 } else { 1<<1 } +
+ if south { 0 } else { 1<<2 } +
+ if north { 0 } else { 1<<3 } +
+ if east { 0 } else { 1<<4 }),
material material::NON_SOLID,
model { ("minecraft", "birch_fence") },
collision fence_collision(north, south, west, east),
update_state (world, pos) => {
let (north, south, west, east) = can_connect_sides(world, pos, &can_connect_fence);
- Block::BirchFence{north: north, south: south, west: west, east: east}
+ Block::BirchFence{north, south, west, east, waterlogged}
},
multipart (key, val) => match key {
"north" => north == (val == "true"),
@@ -3328,14 +4252,20 @@ define_blocks! {
south: bool = [false, true],
west: bool = [false, true],
east: bool = [false, true],
+ waterlogged: bool = [true, false],
},
- data if !north && !south && !west && !east { Some(0) } else { None },
+ data if !north && !south && !west && !east && !waterlogged { Some(0) } else { None },
+ offset Some(if west { 0 } else { 1<<0 } +
+ if waterlogged { 0 } else { 1<<1 } +
+ if south { 0 } else { 1<<2 } +
+ if north { 0 } else { 1<<3 } +
+ if east { 0 } else { 1<<4 }),
material material::NON_SOLID,
model { ("minecraft", "jungle_fence") },
collision fence_collision(north, south, west, east),
update_state (world, pos) => {
let (north, south, west, east) = can_connect_sides(world, pos, &can_connect_fence);
- Block::JungleFence{north: north, south: south, west: west, east: east}
+ Block::JungleFence{north, south, west, east, waterlogged}
},
multipart (key, val) => match key {
"north" => north == (val == "true"),
@@ -3351,14 +4281,20 @@ define_blocks! {
south: bool = [false, true],
west: bool = [false, true],
east: bool = [false, true],
+ waterlogged: bool = [true, false],
},
- data if !north && !south && !west && !east { Some(0) } else { None },
+ data if !north && !south && !west && !east && !waterlogged { Some(0) } else { None },
+ offset Some(if west { 0 } else { 1<<0 } +
+ if waterlogged { 0 } else { 1<<1 } +
+ if south { 0 } else { 1<<2 } +
+ if north { 0 } else { 1<<3 } +
+ if east { 0 } else { 1<<4 }),
material material::NON_SOLID,
model { ("minecraft", "dark_oak_fence") },
collision fence_collision(north, south, west, east),
update_state (world, pos) => {
let (north, south, west, east) = can_connect_sides(world, pos, &can_connect_fence);
- Block::DarkOakFence{north: north, south: south, east: east, west: west}
+ Block::DarkOakFence{north, south, west, east, waterlogged}
},
multipart (key, val) => match key {
"north" => north == (val == "true"),
@@ -3374,14 +4310,20 @@ define_blocks! {
south: bool = [false, true],
west: bool = [false, true],
east: bool = [false, true],
+ waterlogged: bool = [true, false],
},
- data if !north && !south && !east && !west { Some(0) } else { None },
+ data if !north && !south && !west && !east && !waterlogged { Some(0) } else { None },
+ offset Some(if west { 0 } else { 1<<0 } +
+ if waterlogged { 0 } else { 1<<1 } +
+ if south { 0 } else { 1<<2 } +
+ if north { 0 } else { 1<<3 } +
+ if east { 0 } else { 1<<4 }),
material material::NON_SOLID,
model { ("minecraft", "acacia_fence") },
collision fence_collision(north, south, west, east),
update_state (world, pos) => {
let (north, south, west, east) = can_connect_sides(world, pos, &can_connect_fence);
- Block::AcaciaFence{north: north, south: south, east: east, west: west}
+ Block::AcaciaFence{north, south, west, east, waterlogged}
},
multipart (key, val) => match key {
"north" => north == (val == "true"),
@@ -3405,6 +4347,7 @@ define_blocks! {
powered: bool = [false, true],
},
data door_data(facing, half, hinge, open, powered),
+ offset door_offset(facing, half, hinge, open, powered),
material material::NON_SOLID,
model { ("minecraft", "spruce_door") },
variant format!("facing={},half={},hinge={},open={}", facing.as_string(), half.as_string(), hinge.as_string(), open),
@@ -3428,6 +4371,7 @@ define_blocks! {
powered: bool = [false, true],
},
data door_data(facing, half, hinge, open, powered),
+ offset door_offset(facing, half, hinge, open, powered),
material material::NON_SOLID,
model { ("minecraft", "birch_door") },
variant format!("facing={},half={},hinge={},open={}", facing.as_string(), half.as_string(), hinge.as_string(), open),
@@ -3451,6 +4395,7 @@ define_blocks! {
powered: bool = [false, true],
},
data door_data(facing, half, hinge, open, powered),
+ offset door_offset(facing, half, hinge, open, powered),
material material::NON_SOLID,
model { ("minecraft", "jungle_door") },
variant format!("facing={},half={},hinge={},open={}", facing.as_string(), half.as_string(), hinge.as_string(), open),
@@ -3474,6 +4419,7 @@ define_blocks! {
powered: bool = [false, true],
},
data door_data(facing, half, hinge, open, powered),
+ offset door_offset(facing, half, hinge, open, powered),
material material::NON_SOLID,
model { ("minecraft", "acacia_door") },
variant format!("facing={},half={},hinge={},open={}", facing.as_string(), half.as_string(), hinge.as_string(), open),
@@ -3497,6 +4443,7 @@ define_blocks! {
powered: bool = [false, true],
},
data door_data(facing, half, hinge, open, powered),
+ offset door_offset(facing, half, hinge, open, powered),
material material::NON_SOLID,
model { ("minecraft", "dark_oak_door") },
variant format!("facing={},half={},hinge={},open={}", facing.as_string(), half.as_string(), hinge.as_string(), open),
@@ -3518,6 +4465,7 @@ define_blocks! {
],
},
data Some(facing.index()),
+ offset Some(facing.offset()),
material Material {
emitted_light: 14,
..material::NON_SOLID
@@ -3552,6 +4500,12 @@ define_blocks! {
east: bool = [false, true],
},
data if !up && !down && !north && !south && !west && !east { Some(0) } else { None },
+ offset Some(if west { 0 } else { 1<<0 } +
+ if up { 0 } else { 1<<1 } +
+ if south { 0 } else { 1<<2 } +
+ if north { 0 } else { 1<<3 } +
+ if east { 0 } else { 1<<4 } +
+ if down { 0 } else { 1<<5 }),
material material::NON_SOLID,
model { ("minecraft", "chorus_plant") },
collision {
@@ -3640,6 +4594,7 @@ define_blocks! {
axis: Axis = [Axis::X, Axis::Y, Axis::Z],
},
data Some(match axis { Axis::X => 0x4, Axis::Y => 0x0, Axis::Z => 0x8, _ => unreachable!() }),
+ offset Some(match axis { Axis::X => 0, Axis::Y => 1, Axis::Z => 2, _ => unreachable!() }),
model { ("minecraft", "purpur_pillar") },
variant format!("axis={}", axis.as_string()),
}
@@ -3659,18 +4614,21 @@ define_blocks! {
StairShape::OuterLeft,
StairShape::OuterRight
],
+ waterlogged: bool = [true, false],
},
- data stair_data(facing, half, shape),
+ data stair_data(facing, half, shape, waterlogged),
+ offset stair_offset(facing, half, shape, waterlogged),
material material::NON_SOLID,
model { ("minecraft", "purpur_stairs") },
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: facing, half: half, shape: update_stair_shape(world, pos, facing)},
+ update_state (world, pos) => Block::PurpurStairs{facing, half, shape: update_stair_shape(world, pos, facing), waterlogged},
}
PurpurDoubleSlab {
props {
variant: StoneSlabVariant = [StoneSlabVariant::Purpur],
},
+ offset None,
model { ("minecraft", format!("{}_double_slab", variant.as_string()) ) },
}
PurpurSlab {
@@ -3679,6 +4637,7 @@ define_blocks! {
variant: StoneSlabVariant = [StoneSlabVariant::Purpur],
},
data if half == BlockHalf::Top { Some(0x8) } else { Some(0) },
+ offset None,
material material::NON_SOLID,
model { ("minecraft", format!("{}_slab", variant.as_string()) ) },
variant format!("half={},variant=default", half.as_string()),
@@ -3726,6 +4685,7 @@ define_blocks! {
],
},
data Some(facing.index() | (if conditional { 0x8 } else { 0x0 })),
+ offset Some(facing.offset() + (if conditional { 0 } else { 6 })),
model { ("minecraft", "repeating_command_block") },
variant format!("conditional={},facing={}", conditional, facing.as_string()),
}
@@ -3742,11 +4702,16 @@ define_blocks! {
],
},
data Some(facing.index() | (if conditional { 0x8 } else { 0x0 })),
+ offset Some(facing.offset() + (if conditional { 0 } else { 6 })),
model { ("minecraft", "chain_command_block") },
variant format!("conditional={},facing={}", conditional, facing.as_string()),
}
FrostedIce {
- props {},
+ props {
+ age: u8 = [ 0, 1, 2, 3 ],
+ },
+ data if age == 0 { Some(0) } else { None },
+ offset Some(age as usize),
model { ("minecraft", "frosted_ice") },
}
MagmaBlock {
@@ -3766,6 +4731,7 @@ define_blocks! {
axis: Axis = [Axis::Y, Axis::Z, Axis::X],
},
data Some(axis.index() << 2),
+ offset Some(match axis { Axis::X => 0, Axis::Y => 1, Axis::Z => 2, _ => unreachable!() }),
model { ("minecraft", "bone_block") },
variant format!("axis={}", axis.as_string()),
}
@@ -3792,12 +4758,28 @@ define_blocks! {
powered: bool = [false, true],
},
data Some(facing.index() | (if powered { 0x8 } else { 0x0 })),
+ offset Some(if powered { 0 } else { 1 } + facing.offset() * 2),
model { ("minecraft", "observer") },
variant format!("facing={},powered={}", facing.as_string(), powered),
}
// 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
// and textures/entity/shulker/shulker_.png
+ ShulkerBox {
+ props {
+ facing: Direction = [
+ Direction::Up,
+ Direction::Down,
+ Direction::North,
+ Direction::South,
+ Direction::West,
+ Direction::East
+ ],
+ },
+ data None::,
+ offset Some(facing.offset()),
+ model { ("minecraft", "sponge") },
+ }
WhiteShulkerBox {
props {
facing: Direction = [
@@ -3810,6 +4792,7 @@ define_blocks! {
],
},
data Some(facing.index()),
+ offset Some(facing.offset()),
model { ("minecraft", "white_wool") },
}
OrangeShulkerBox {
@@ -3824,6 +4807,7 @@ define_blocks! {
],
},
data Some(facing.index()),
+ offset Some(facing.offset()),
model { ("minecraft", "orange_wool") },
}
MagentaShulkerBox {
@@ -3838,6 +4822,7 @@ define_blocks! {
],
},
data Some(facing.index()),
+ offset Some(facing.offset()),
model { ("minecraft", "magenta_wool") },
}
LightBlueShulkerBox {
@@ -3852,6 +4837,7 @@ define_blocks! {
],
},
data Some(facing.index()),
+ offset Some(facing.offset()),
model { ("minecraft", "light_blue_wool") },
}
YellowShulkerBox {
@@ -3866,6 +4852,7 @@ define_blocks! {
],
},
data Some(facing.index()),
+ offset Some(facing.offset()),
model { ("minecraft", "yellow_wool") },
}
LimeShulkerBox {
@@ -3880,6 +4867,7 @@ define_blocks! {
],
},
data Some(facing.index()),
+ offset Some(facing.offset()),
model { ("minecraft", "lime_wool") },
}
PinkShulkerBox {
@@ -3894,6 +4882,7 @@ define_blocks! {
],
},
data Some(facing.index()),
+ offset Some(facing.offset()),
model { ("minecraft", "pink_wool") },
}
GrayShulkerBox {
@@ -3908,6 +4897,7 @@ define_blocks! {
],
},
data Some(facing.index()),
+ offset Some(facing.offset()),
model { ("minecraft", "gray_wool") },
}
LightGrayShulkerBox {
@@ -3922,6 +4912,7 @@ define_blocks! {
],
},
data Some(facing.index()),
+ offset Some(facing.offset()),
model { ("minecraft", "light_gray_wool") },
}
CyanShulkerBox {
@@ -3936,6 +4927,7 @@ define_blocks! {
],
},
data Some(facing.index()),
+ offset Some(facing.offset()),
model { ("minecraft", "cyan_wool") },
}
PurpleShulkerBox {
@@ -3950,6 +4942,7 @@ define_blocks! {
],
},
data Some(facing.index()),
+ offset Some(facing.offset()),
model { ("minecraft", "purple_wool") },
}
BlueShulkerBox {
@@ -3964,6 +4957,7 @@ define_blocks! {
],
},
data Some(facing.index()),
+ offset Some(facing.offset()),
model { ("minecraft", "blue_wool") },
}
BrownShulkerBox {
@@ -3978,6 +4972,7 @@ define_blocks! {
],
},
data Some(facing.index()),
+ offset Some(facing.offset()),
model { ("minecraft", "brown_wool") },
}
GreenShulkerBox {
@@ -3992,6 +4987,7 @@ define_blocks! {
],
},
data Some(facing.index()),
+ offset Some(facing.offset()),
model { ("minecraft", "green_wool") },
}
RedShulkerBox {
@@ -4006,6 +5002,7 @@ define_blocks! {
],
},
data Some(facing.index()),
+ offset Some(facing.offset()),
model { ("minecraft", "red_wool") },
}
BlackShulkerBox {
@@ -4020,6 +5017,7 @@ define_blocks! {
],
},
data Some(facing.index()),
+ offset Some(facing.offset()),
model { ("minecraft", "black_wool") },
}
WhiteGlazedTerracotta {
@@ -4032,6 +5030,7 @@ define_blocks! {
],
},
data Some(facing.horizontal_index()),
+ offset Some(facing.horizontal_offset()),
model { ("minecraft", "white_glazed_terracotta") },
variant format!("facing={}", facing.as_string()),
}
@@ -4045,6 +5044,7 @@ define_blocks! {
],
},
data Some(facing.horizontal_index()),
+ offset Some(facing.horizontal_offset()),
model { ("minecraft", "orange_glazed_terracotta") },
variant format!("facing={}", facing.as_string()),
}
@@ -4058,6 +5058,7 @@ define_blocks! {
],
},
data Some(facing.horizontal_index()),
+ offset Some(facing.horizontal_offset()),
model { ("minecraft", "magenta_glazed_terracotta") },
variant format!("facing={}", facing.as_string()),
}
@@ -4071,6 +5072,7 @@ define_blocks! {
],
},
data Some(facing.horizontal_index()),
+ offset Some(facing.horizontal_offset()),
model { ("minecraft", "light_blue_glazed_terracotta") },
variant format!("facing={}", facing.as_string()),
}
@@ -4084,6 +5086,7 @@ define_blocks! {
],
},
data Some(facing.horizontal_index()),
+ offset Some(facing.horizontal_offset()),
model { ("minecraft", "yellow_glazed_terracotta") },
variant format!("facing={}", facing.as_string()),
}
@@ -4097,6 +5100,7 @@ define_blocks! {
],
},
data Some(facing.horizontal_index()),
+ offset Some(facing.horizontal_offset()),
model { ("minecraft", "lime_glazed_terracotta") },
variant format!("facing={}", facing.as_string()),
}
@@ -4110,6 +5114,7 @@ define_blocks! {
],
},
data Some(facing.horizontal_index()),
+ offset Some(facing.horizontal_offset()),
model { ("minecraft", "pink_glazed_terracotta") },
variant format!("facing={}", facing.as_string()),
}
@@ -4123,6 +5128,7 @@ define_blocks! {
],
},
data Some(facing.horizontal_index()),
+ offset Some(facing.horizontal_offset()),
model { ("minecraft", "gray_glazed_terracotta") },
variant format!("facing={}", facing.as_string()),
}
@@ -4136,6 +5142,7 @@ define_blocks! {
],
},
data Some(facing.horizontal_index()),
+ offset Some(facing.horizontal_offset()),
model { ("minecraft", "silver_glazed_terracotta") },
variant format!("facing={}", facing.as_string()),
}
@@ -4149,6 +5156,7 @@ define_blocks! {
],
},
data Some(facing.horizontal_index()),
+ offset Some(facing.horizontal_offset()),
model { ("minecraft", "cyan_glazed_terracotta") },
variant format!("facing={}", facing.as_string()),
}
@@ -4162,6 +5170,7 @@ define_blocks! {
],
},
data Some(facing.horizontal_index()),
+ offset Some(facing.horizontal_offset()),
model { ("minecraft", "purple_glazed_terracotta") },
variant format!("facing={}", facing.as_string()),
}
@@ -4175,6 +5184,7 @@ define_blocks! {
],
},
data Some(facing.horizontal_index()),
+ offset Some(facing.horizontal_offset()),
model { ("minecraft", "blue_glazed_terracotta") },
variant format!("facing={}", facing.as_string()),
}
@@ -4188,6 +5198,7 @@ define_blocks! {
],
},
data Some(facing.horizontal_index()),
+ offset Some(facing.horizontal_offset()),
model { ("minecraft", "brown_glazed_terracotta") },
variant format!("facing={}", facing.as_string()),
}
@@ -4201,6 +5212,7 @@ define_blocks! {
],
},
data Some(facing.horizontal_index()),
+ offset Some(facing.horizontal_offset()),
model { ("minecraft", "green_glazed_terracotta") },
variant format!("facing={}", facing.as_string()),
}
@@ -4214,6 +5226,7 @@ define_blocks! {
],
},
data Some(facing.horizontal_index()),
+ offset Some(facing.horizontal_offset()),
model { ("minecraft", "red_glazed_terracotta") },
variant format!("facing={}", facing.as_string()),
}
@@ -4227,6 +5240,7 @@ define_blocks! {
],
},
data Some(facing.horizontal_index()),
+ offset Some(facing.horizontal_offset()),
model { ("minecraft", "black_glazed_terracotta") },
variant format!("facing={}", facing.as_string()),
}
@@ -4278,14 +5292,189 @@ define_blocks! {
data Some(color.data()),
model { ("minecraft", format!("{}_concrete_powder", color.as_string()) ) },
}
- Missing253 {
+ Kelp {
+ props {
+ age: u8 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25],
+ },
+ data None::,
+ offset Some(age as usize),
+ model { ("minecraft", "kelp") },
+ }
+ KelpPlant {
props {},
data None::,
+ offset Some(0),
+ model { ("minecraft", "kelp_plant") },
+ }
+ DriedKelpBlock {
+ props {},
+ data None::,
+ offset Some(0),
+ model { ("minecraft", "dried_kelp_block") },
+ }
+ TurtleEgg {
+ props {
+ age: u8 = [1, 2, 3, 4],
+ hatch: u8 = [0, 1, 2],
+ },
+ data None::,
+ offset Some((hatch as usize) + ((age - 1) as usize) * 3),
+ model { ("minecraft", "turtle_egg") },
+ }
+ CoralBlock {
+ props {
+ variant: CoralVariant = [
+ CoralVariant::DeadTube,
+ CoralVariant::DeadBrain,
+ CoralVariant::DeadBubble,
+ CoralVariant::DeadFire,
+ CoralVariant::DeadHorn,
+ CoralVariant::Tube,
+ CoralVariant::Brain,
+ CoralVariant::Bubble,
+ CoralVariant::Fire,
+ CoralVariant::Horn
+ ],
+ },
+ data None::,
+ offset Some(variant.offset()),
+ model { ("minecraft", format!("{}_block", variant.as_string())) },
+ }
+ Coral {
+ props {
+ waterlogged: bool = [true, false],
+ variant: CoralVariant = [
+ CoralVariant::DeadTube,
+ CoralVariant::DeadBrain,
+ CoralVariant::DeadBubble,
+ CoralVariant::DeadFire,
+ CoralVariant::DeadHorn,
+ CoralVariant::Tube,
+ CoralVariant::Brain,
+ CoralVariant::Bubble,
+ CoralVariant::Fire,
+ CoralVariant::Horn
+ ],
+ },
+ data None::,
+ offset Some(if waterlogged { 0 } else { 1 } + variant.offset() * 2),
+ model { ("minecraft", variant.as_string()) },
+ }
+ CoralWallFan {
+ props {
+ facing: Direction = [
+ Direction::North,
+ Direction::South,
+ Direction::West,
+ Direction::East
+ ],
+ waterlogged: bool = [true, false],
+ variant: CoralVariant = [
+ CoralVariant::DeadTube,
+ CoralVariant::DeadBrain,
+ CoralVariant::DeadBubble,
+ CoralVariant::DeadFire,
+ CoralVariant::DeadHorn,
+ CoralVariant::Tube,
+ CoralVariant::Brain,
+ CoralVariant::Bubble,
+ CoralVariant::Fire,
+ CoralVariant::Horn
+ ],
+ },
+ data None::,
+ offset Some(if waterlogged { 0 } else { 1 } +
+ facing.horizontal_offset() * 2 +
+ variant.offset() * (2 * 4)),
+ model { ("minecraft", format!("{}_wall_fan", variant.as_string())) },
+ }
+ CoralFan {
+ props {
+ waterlogged: bool = [true, false],
+ variant: CoralVariant = [
+ CoralVariant::DeadTube,
+ CoralVariant::DeadBrain,
+ CoralVariant::DeadBubble,
+ CoralVariant::DeadFire,
+ CoralVariant::DeadHorn,
+ CoralVariant::Tube,
+ CoralVariant::Brain,
+ CoralVariant::Bubble,
+ CoralVariant::Fire,
+ CoralVariant::Horn
+ ],
+ },
+ data None::,
+ offset Some(if waterlogged { 0 } else { 1 } +
+ variant.offset() * 2),
+ model { ("minecraft", format!("{}_fan", variant.as_string())) },
+ }
+ SeaPickle {
+ props {
+ age: u8 = [1, 2, 3, 4],
+ waterlogged: bool = [true, false],
+ },
+ data None::,
+ offset Some(if waterlogged { 0 } else { 1 } +
+ ((age - 1) as usize) * 2),
+ model { ("minecraft", "sea_pickle") },
+ variant format!("age={}", age),
+ }
+ BlueIce {
+ props {},
+ data None::,
+ offset Some(0),
+ model { ("minecraft", "blue_ice") },
+ }
+ Conduit {
+ props {
+ waterlogged: bool = [true, false],
+ },
+ data None::,
+ offset Some(if waterlogged { 0 } else { 1 }),
+ material material::NON_SOLID,
+ model { ("minecraft", "conduit") },
+ }
+ VoidAir {
+ props {},
+ data None::,
+ offset Some(0),
+ material material::Material {
+ collidable: false,
+ .. material::INVISIBLE
+ },
+ model { ("minecraft", "air") },
+ collision vec![],
+ }
+ CaveAir {
+ props {},
+ data None::,
+ offset Some(0),
+ material material::Material {
+ collidable: false,
+ .. material::INVISIBLE
+ },
+ model { ("minecraft", "air") },
+ collision vec![],
+ }
+ BubbleColumn {
+ props {
+ drag: bool = [true, false],
+ },
+ data None::,
+ offset Some(if drag { 0 } else { 1 }),
+ model { ("minecraft", "bubble_column") },
+ }
+ Missing253 {
+ props {},
+ data Some(0),
+ offset None,
model { ("steven", "missing_block") },
}
Missing254 {
props {},
- data None::,
+ data Some(0),
+ offset None,
model { ("steven", "missing_block") },
}
StructureBlock {
@@ -4427,6 +5616,13 @@ fn fence_gate_data(facing: Direction, in_wall: bool, open: bool, powered: bool)
Some(facing.horizontal_index() | (if open { 0x4 } else { 0x0 }))
}
+fn fence_gate_offset(facing: Direction, in_wall: bool, open: bool, powered: bool) -> Option {
+ Some(if powered { 0 } else { 1<<0 } +
+ if open { 0 } else { 1<<1 } +
+ if in_wall { 0 } else { 1<<2 } +
+ facing.horizontal_offset() * (1<<3))
+}
+
fn fence_gate_collision(facing: Direction, in_wall: bool, open: bool) -> Vec> {
if open { return vec![]; }
@@ -4483,6 +5679,15 @@ fn door_data(facing: Direction, half: DoorHalf, hinge: Side, open: bool, powered
}
}
+fn door_offset(facing: Direction, half: DoorHalf, hinge: Side, open: bool, powered: bool) -> Option {
+ Some(if powered { 0 } else { 1<<0 } +
+ if open { 0 } else { 1<<1 } +
+ if hinge == Side::Left { 0 } else { 1<<2 } +
+ if half == DoorHalf::Upper { 0 } else { 1<<3 } +
+ facing.horizontal_offset() * (1<<4))
+}
+
+
fn update_door_state(world: &W, pos: Position, ohalf: DoorHalf, ofacing: Direction, ohinge: Side, oopen: bool, opowered: bool) -> (Direction, Side, bool, bool) {
let oy = if ohalf == DoorHalf::Upper { -1 } else { 1 };
@@ -4724,12 +5929,20 @@ fn update_stair_shape(world: &W, pos: Position, facing: Directio
StairShape::Straight
}
-fn stair_data(facing: Direction, half: BlockHalf, shape: StairShape) -> Option {
+fn stair_data(facing: Direction, half: BlockHalf, shape: StairShape, waterlogged: bool) -> Option {
if shape != StairShape::Straight { return None; }
+ if waterlogged { return None; }
Some((5 - facing.index()) | (if half == BlockHalf::Top { 0x4 } else { 0x0 }))
}
+fn stair_offset(facing: Direction, half: BlockHalf, shape: StairShape, waterlogged: bool) -> Option {
+ Some(if waterlogged { 0 } else { 1 } +
+ shape.offset() * 2 +
+ if half == BlockHalf::Top { 0 } else { 2 * 5 } +
+ facing.horizontal_offset() * 2 * 5 * 2)
+}
+
#[allow(clippy::many_single_char_names)]
fn stair_collision(facing: Direction, shape: StairShape, half: BlockHalf) -> Vec> {
use std::f64::consts::PI;
@@ -4806,6 +6019,7 @@ fn slab_collision(half: BlockHalf) -> Vec> {
let (min_x, min_y, min_z, max_x, max_y, max_z) = match half {
BlockHalf::Top => (0.0, 0.5, 0.0, 1.0, 1.0, 1.0),
BlockHalf::Bottom => (0.0, 0.0, 0.0, 1.0, 0.5, 1.0),
+ BlockHalf::Double => (0.0, 0.0, 0.0, 1.0, 1.0, 1.0),
_ => unreachable!(),
};
@@ -4916,6 +6130,53 @@ impl SandstoneVariant {
}
}
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+pub enum NoteBlockInstrument {
+ Harp,
+ BaseDrum,
+ Snare,
+ Hat,
+ Bass,
+ Flute,
+ Bell,
+ Guitar,
+ Chime,
+ Xylophone,
+}
+
+impl NoteBlockInstrument {
+ pub fn as_string(self) -> &'static str {
+ match self {
+ NoteBlockInstrument::Harp => "harp",
+ NoteBlockInstrument::BaseDrum => "basedrum",
+ NoteBlockInstrument::Snare => "snare",
+ NoteBlockInstrument::Hat => "hat",
+ NoteBlockInstrument::Bass => "bass",
+ NoteBlockInstrument::Flute => "flute",
+ NoteBlockInstrument::Bell => "bell",
+ NoteBlockInstrument::Guitar => "guitar",
+ NoteBlockInstrument::Chime => "chime",
+ NoteBlockInstrument::Xylophone => "xylophone",
+ }
+ }
+
+ fn offset(self) -> usize {
+ match self {
+ NoteBlockInstrument::Harp => 0,
+ NoteBlockInstrument::BaseDrum => 1,
+ NoteBlockInstrument::Snare => 2,
+ NoteBlockInstrument::Hat => 3,
+ NoteBlockInstrument::Bass => 4,
+ NoteBlockInstrument::Flute => 5,
+ NoteBlockInstrument::Bell => 6,
+ NoteBlockInstrument::Guitar => 7,
+ NoteBlockInstrument::Chime => 8,
+ NoteBlockInstrument::Xylophone => 9,
+ }
+ }
+}
+
+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum RedSandstoneVariant {
Normal,
@@ -4996,59 +6257,61 @@ impl PrismarineVariant {
}
}
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-pub enum MushroomVariant {
- East,
- North,
- NorthEast,
- NorthWest,
- South,
- SouthEast,
- SouthWest,
- West,
- Center,
- Stem,
- AllInside,
- AllOutside,
- AllStem,
+fn mushroom_block_data(is_stem: bool, west: bool, up: bool, south: bool, north: bool, east: bool, down: bool) -> Option {
+ Some(match
+ (is_stem, west, up, south, north, east, down) {
+ (false, false, false, false, false, false, false) => 0,
+ (false, true, false, false, true, false, false) => 1,
+ (false, false, false, false, true, false, false) => 2,
+ (false, false, false, false, true, true, false) => 3,
+ (false, true, false, false, false, false, false) => 4,
+ (false, false, true, false, false, false, false) => 5,
+ (false, false, false, false, false, true, false) => 6,
+ (false, true, false, true, false, false, false) => 7,
+ (false, false, false, true, false, false, false) => 8,
+ (false, false, false, true, false, true, false) => 9,
+ (false, true, false, true, true, true, false) => 10,
+ (false, true, true, true, true, true, true) => 14,
+ (true, false, false, false, false, false, false) => 15,
+ _ => return None,
+ })
}
-impl MushroomVariant {
- pub fn as_string(self) -> &'static str {
- match self {
- MushroomVariant::East => "east",
- MushroomVariant::North => "north",
- MushroomVariant::NorthEast => "north_east",
- MushroomVariant::NorthWest => "north_west",
- MushroomVariant::South => "south",
- MushroomVariant::SouthEast => "south_east",
- MushroomVariant::SouthWest => "south_west",
- MushroomVariant::West => "west",
- MushroomVariant::Center => "center",
- MushroomVariant::Stem => "stem",
- MushroomVariant::AllInside => "all_inside",
- MushroomVariant::AllOutside => "all_outside",
- MushroomVariant::AllStem => "all_stem",
- }
+fn mushroom_block_offset(is_stem: bool, west: bool, up: bool, south: bool, north: bool, east: bool, down: bool) -> Option {
+ if is_stem {
+ None
+ } else {
+ Some(if west { 0 } else { 1<<0 } +
+ if up { 0 } else { 1<<1 } +
+ if south { 0 } else { 1<<2 } +
+ if north { 0 } else { 1<<3 } +
+ if east { 0 } else { 1<<4 } +
+ if down { 0 } else { 1<<5 })
}
+}
- fn data(self) -> usize {
- match self {
- MushroomVariant::AllInside => 0,
- MushroomVariant::NorthWest => 1,
- MushroomVariant::North => 2,
- MushroomVariant::NorthEast => 3,
- MushroomVariant::West => 4,
- MushroomVariant::Center => 5,
- MushroomVariant::East => 6,
- MushroomVariant::SouthWest => 7,
- MushroomVariant::South => 8,
- MushroomVariant::SouthEast => 9,
- MushroomVariant::Stem => 10,
- MushroomVariant::AllOutside => 14,
- MushroomVariant::AllStem => 15,
+
+fn mushroom_block_variant(is_stem: bool, west: bool, up: bool, south: bool, north: bool, east: bool, down: bool) -> String {
+ (if is_stem {
+ "all_stem"
+ } else {
+ match
+ (west, up, south, north, east, down) {
+ (false, false, false, false, false, false) => "all_inside",
+ (true, false, false, true, false, false) => "north_west",
+ (false, false, false, true, false, false) => "north",
+ (false, false, false, true, true, false) => "north_east",
+ (true, false, false, false, false, false) => "west",
+ (false, true, false, false, false, false) => "center",
+ (false, false, false, false, true, false) => "east",
+ (true, false, true, false, false, false) => "south_west",
+ (false, false, true, false, false, false) => "south",
+ (false, false, true, false, true, false) => "south_east",
+ (true, false, true, true, true, false) => "stem",
+ (true, true, true, true, true, true) => "all_outside",
+ _ => "all_stem",
}
- }
+ }).to_string()
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
@@ -5311,46 +6574,6 @@ impl ComparatorMode {
}
}
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-pub enum LeverDirection {
- North,
- South,
- East,
- West,
- UpX,
- DownX,
- UpZ,
- DownZ,
-}
-
-impl LeverDirection {
- pub fn as_string(self) -> &'static str {
- match self {
- LeverDirection::North => "north",
- LeverDirection::South => "south",
- LeverDirection::East => "east",
- LeverDirection::West => "west",
- LeverDirection::UpX => "up_x",
- LeverDirection::DownX => "down_x",
- LeverDirection::UpZ => "up_z",
- LeverDirection::DownZ => "down_z",
- }
- }
-
- pub fn data(self) -> usize {
- match self {
- LeverDirection::DownX => 0,
- LeverDirection::East => 1,
- LeverDirection::West => 2,
- LeverDirection::South => 3,
- LeverDirection::North => 4,
- LeverDirection::UpZ => 5,
- LeverDirection::UpX => 6,
- LeverDirection::DownZ => 7,
- }
- }
-}
-
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum RedstoneSide {
None,
@@ -5366,6 +6589,14 @@ impl RedstoneSide {
RedstoneSide::Up => "up",
}
}
+
+ pub fn offset(self) -> usize {
+ match self {
+ RedstoneSide::Up => 0,
+ RedstoneSide::Side => 1,
+ RedstoneSide::None => 2,
+ }
+ }
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
@@ -5387,7 +6618,7 @@ impl PistonType {
pub enum StoneSlabVariant {
Stone,
Sandstone,
- Wood,
+ PetrifiedWood,
Cobblestone,
Brick,
StoneBrick,
@@ -5402,7 +6633,7 @@ impl StoneSlabVariant {
match self {
StoneSlabVariant::Stone => "stone",
StoneSlabVariant::Sandstone => "sandstone",
- StoneSlabVariant::Wood => "wood_old",
+ StoneSlabVariant::PetrifiedWood => "wood_old",
StoneSlabVariant::Cobblestone => "cobblestone",
StoneSlabVariant::Brick => "brick",
StoneSlabVariant::StoneBrick => "stone_brick",
@@ -5419,7 +6650,7 @@ impl StoneSlabVariant {
StoneSlabVariant::RedSandstone |
StoneSlabVariant::Purpur => 0,
StoneSlabVariant::Sandstone => 1,
- StoneSlabVariant::Wood => 2,
+ StoneSlabVariant::PetrifiedWood => 2,
StoneSlabVariant::Cobblestone => 3,
StoneSlabVariant::Brick => 4,
StoneSlabVariant::StoneBrick => 5,
@@ -5427,6 +6658,21 @@ impl StoneSlabVariant {
StoneSlabVariant::Quartz => 7,
}
}
+
+ fn offset(self) -> usize {
+ match self {
+ StoneSlabVariant::Stone => 0,
+ StoneSlabVariant::Sandstone => 1,
+ StoneSlabVariant::PetrifiedWood => 2,
+ StoneSlabVariant::Cobblestone => 3,
+ StoneSlabVariant::Brick => 4,
+ StoneSlabVariant::StoneBrick => 5,
+ StoneSlabVariant::NetherBrick => 6,
+ StoneSlabVariant::Quartz => 7,
+ StoneSlabVariant::RedSandstone => 8,
+ StoneSlabVariant::Purpur => 9,
+ }
+ }
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
@@ -5469,6 +6715,7 @@ pub enum BlockHalf {
Bottom,
Upper,
Lower,
+ Double,
}
impl BlockHalf {
@@ -5478,6 +6725,15 @@ impl BlockHalf {
BlockHalf::Bottom => "bottom",
BlockHalf::Upper => "upper",
BlockHalf::Lower => "lower",
+ BlockHalf::Double => "double",
+ }
+ }
+
+ pub fn offset(self) -> usize {
+ match self {
+ BlockHalf::Top | BlockHalf::Upper => 0,
+ BlockHalf::Bottom | BlockHalf::Lower => 1,
+ BlockHalf::Double => 2,
}
}
}
@@ -5587,6 +6843,102 @@ impl StairShape {
StairShape::OuterRight => "outer_right",
}
}
+
+ pub fn offset(self) -> usize {
+ match self {
+ StairShape::Straight => 0,
+ StairShape::InnerLeft => 1,
+ StairShape::InnerRight => 2,
+ StairShape::OuterLeft => 3,
+ StairShape::OuterRight => 4,
+ }
+ }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+pub enum AttachedFace {
+ Floor,
+ Wall,
+ Ceiling,
+}
+
+impl AttachedFace {
+ pub fn as_string(self) -> &'static str {
+ match self {
+ AttachedFace::Floor => "floor",
+ AttachedFace::Wall => "wall",
+ AttachedFace::Ceiling => "ceiling",
+ }
+ }
+
+ pub fn offset(self) -> usize {
+ match self {
+ AttachedFace::Floor => 0,
+ AttachedFace::Wall => 1,
+ AttachedFace::Ceiling => 2,
+ }
+ }
+
+ pub fn data_with_facing(self, facing: Direction) -> Option {
+ Some(match (self, facing) {
+ (AttachedFace::Ceiling, Direction::East) => 0,
+ (AttachedFace::Wall, Direction::East) => 1,
+ (AttachedFace::Wall, Direction::West) => 2,
+ (AttachedFace::Wall, Direction::South) => 3,
+ (AttachedFace::Wall, Direction::North) => 4,
+ (AttachedFace::Floor, Direction::South) => 5,
+ (AttachedFace::Floor, Direction::East) => 6,
+ (AttachedFace::Ceiling, Direction::South) => 7,
+ _ => return None,
+ })
+ }
+
+ pub fn data_with_facing_and_powered(self, facing: Direction, powered: bool) -> Option {
+ if let Some(facing_data) = self.data_with_facing(facing) {
+ Some(facing_data | (if powered { 0x8 } else { 0x0 }))
+ } else {
+ None
+ }
+ }
+
+ pub fn variant_with_facing(self, facing: Direction) -> String {
+ match (self, facing) {
+ (AttachedFace::Ceiling, Direction::East) => "down_x",
+ (AttachedFace::Wall, Direction::East) => "east",
+ (AttachedFace::Wall, Direction::West) => "west",
+ (AttachedFace::Wall, Direction::South) => "south",
+ (AttachedFace::Wall, Direction::North) => "north",
+ (AttachedFace::Floor, Direction::South) => "up_z",
+ (AttachedFace::Floor, Direction::East) => "up_x",
+ (AttachedFace::Ceiling, Direction::South) => "down_z",
+ _ => "north", // TODO: support 1.13.2+ new directions
+ }.to_owned()
+ }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+pub enum ChestType {
+ Single,
+ Left,
+ Right,
+}
+
+impl ChestType {
+ pub fn as_string(self) -> &'static str {
+ match self {
+ ChestType::Single => "single",
+ ChestType::Left => "left",
+ ChestType::Right => "right",
+ }
+ }
+
+ pub fn offset(self) -> usize {
+ match self {
+ ChestType::Single => 0,
+ ChestType::Left => 1,
+ ChestType::Right => 2,
+ }
+ }
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
@@ -5625,7 +6977,13 @@ pub enum TreeVariant {
Birch,
Jungle,
Acacia,
- DarkOak
+ DarkOak,
+ StrippedSpruce,
+ StrippedBirch,
+ StrippedJungle,
+ StrippedAcacia,
+ StrippedDarkOak,
+ StrippedOak,
}
impl TreeVariant {
@@ -5637,6 +6995,12 @@ impl TreeVariant {
TreeVariant::Jungle => "jungle",
TreeVariant::Acacia => "acacia",
TreeVariant::DarkOak => "dark_oak",
+ TreeVariant::StrippedSpruce => "stripped_spruce_log",
+ TreeVariant::StrippedBirch => "stripped_birch_log",
+ TreeVariant::StrippedJungle => "stripped_jungle_log",
+ TreeVariant::StrippedAcacia => "stripped_acacia_log",
+ TreeVariant::StrippedDarkOak => "stripped_dark_oak_log",
+ TreeVariant::StrippedOak => "stripped_oak_log"
}
}
@@ -5646,6 +7010,24 @@ impl TreeVariant {
TreeVariant::Spruce | TreeVariant::DarkOak => 1,
TreeVariant::Birch => 2,
TreeVariant::Jungle => 3,
+ _ => panic!("TreeVariant {:?} has no data (1.13+ only)"),
+ }
+ }
+
+ pub fn offset(self) -> usize {
+ match self {
+ TreeVariant::Oak => 0,
+ TreeVariant::Spruce => 1,
+ TreeVariant::Birch => 2,
+ TreeVariant::Jungle => 3,
+ TreeVariant::Acacia => 4,
+ TreeVariant::DarkOak => 5,
+ TreeVariant::StrippedSpruce => 6,
+ TreeVariant::StrippedBirch => 7,
+ TreeVariant::StrippedJungle => 8,
+ TreeVariant::StrippedAcacia => 9,
+ TreeVariant::StrippedDarkOak => 10,
+ TreeVariant::StrippedOak => 11,
}
}
@@ -5657,6 +7039,7 @@ impl TreeVariant {
TreeVariant::Jungle => 3,
TreeVariant::Acacia => 4,
TreeVariant::DarkOak => 5,
+ _ => panic!("TreeVariant {:?} has no plank data (1.13+ only)"),
}
}
}
@@ -5686,6 +7069,28 @@ impl TallGrassVariant {
}
}
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+pub enum TallSeagrassHalf {
+ Upper,
+ Lower,
+}
+
+impl TallSeagrassHalf {
+ pub fn as_string(self) -> &'static str {
+ match self {
+ TallSeagrassHalf::Upper => "upper",
+ TallSeagrassHalf::Lower => "lower",
+ }
+ }
+
+ fn offset(self) -> usize {
+ match self {
+ TallSeagrassHalf::Upper => 0,
+ TallSeagrassHalf::Lower => 1,
+ }
+ }
+}
+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum DoublePlantVariant {
Sunflower,
@@ -5718,6 +7123,17 @@ impl DoublePlantVariant {
DoublePlantVariant::Peony => 5,
}
}
+
+ pub fn offset(self) -> usize {
+ match self {
+ DoublePlantVariant::Sunflower => 0,
+ DoublePlantVariant::Lilac => 1,
+ DoublePlantVariant::RoseBush => 2,
+ DoublePlantVariant::Peony => 3,
+ DoublePlantVariant::DoubleTallgrass => 4,
+ DoublePlantVariant::LargeFern => 5,
+ }
+ }
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
@@ -5735,7 +7151,7 @@ pub enum FlowerPotVariant {
DeadBush,
Fern,
AcaciaSapling,
- DarkOak,
+ DarkOakSapling,
BlueOrchid,
Allium,
AzureBluet,
@@ -5762,7 +7178,7 @@ impl FlowerPotVariant {
FlowerPotVariant::DeadBush => "dead_bush",
FlowerPotVariant::Fern => "fern",
FlowerPotVariant::AcaciaSapling => "acacia_sapling",
- FlowerPotVariant::DarkOak => "dark_oak_sapling",
+ FlowerPotVariant::DarkOakSapling => "dark_oak_sapling",
FlowerPotVariant::BlueOrchid => "blue_orchid",
FlowerPotVariant::Allium => "allium",
FlowerPotVariant::AzureBluet => "houstonia",
@@ -5773,4 +7189,78 @@ impl FlowerPotVariant {
FlowerPotVariant::Oxeye => "oxeye_daisy",
}
}
+
+ pub fn offset(self) -> usize {
+ match self {
+ FlowerPotVariant::Empty => 0,
+ FlowerPotVariant::OakSapling => 1,
+ FlowerPotVariant::SpruceSapling => 2,
+ FlowerPotVariant::BirchSapling => 3,
+ FlowerPotVariant::JungleSapling => 4,
+ FlowerPotVariant::AcaciaSapling => 5,
+ FlowerPotVariant::DarkOakSapling => 6,
+ FlowerPotVariant::Fern => 7,
+ FlowerPotVariant::Dandelion => 8,
+ FlowerPotVariant::Poppy => 9,
+ FlowerPotVariant::BlueOrchid => 10,
+ FlowerPotVariant::Allium => 11,
+ FlowerPotVariant::AzureBluet => 12,
+ FlowerPotVariant::RedTulip => 13,
+ FlowerPotVariant::OrangeTulip => 14,
+ FlowerPotVariant::WhiteTulip => 15,
+ FlowerPotVariant::PinkTulip => 16,
+ FlowerPotVariant::Oxeye => 17,
+ FlowerPotVariant::RedMushroom => 18,
+ FlowerPotVariant::BrownMushroom => 19,
+ FlowerPotVariant::DeadBush => 20,
+ FlowerPotVariant::Cactus => 21,
+ }
+ }
}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
+pub enum CoralVariant {
+ DeadTube,
+ DeadBrain,
+ DeadBubble,
+ DeadFire,
+ DeadHorn,
+ Tube,
+ Brain,
+ Bubble,
+ Fire,
+ Horn,
+}
+
+impl CoralVariant {
+ pub fn as_string(self) -> &'static str {
+ match self {
+ CoralVariant::DeadTube => "dead_tube",
+ CoralVariant::DeadBrain => "dead_brain",
+ CoralVariant::DeadBubble => "dead_bubble",
+ CoralVariant::DeadFire => "dead_fire",
+ CoralVariant::DeadHorn => "dead_horn",
+ CoralVariant::Tube => "dead_tube",
+ CoralVariant::Brain => "brain",
+ CoralVariant::Bubble => "bubble",
+ CoralVariant::Fire => "fire",
+ CoralVariant::Horn => "horn",
+ }
+ }
+
+ pub fn offset(self) -> usize {
+ match self {
+ CoralVariant::DeadTube => 0,
+ CoralVariant::DeadBrain => 1,
+ CoralVariant::DeadBubble => 2,
+ CoralVariant::DeadFire => 3,
+ CoralVariant::DeadHorn => 4,
+ CoralVariant::Tube => 5,
+ CoralVariant::Brain => 6,
+ CoralVariant::Bubble => 7,
+ CoralVariant::Fire => 8,
+ CoralVariant::Horn => 9,
+ }
+ }
+}
+
diff --git a/shared/src/direction.rs b/shared/src/direction.rs
index 0c6b7cd..273dc98 100644
--- a/shared/src/direction.rs
+++ b/shared/src/direction.rs
@@ -105,6 +105,19 @@ impl Direction {
}
}
+ pub fn offset(&self) -> usize {
+ match *self {
+ Direction::North => 0,
+ Direction::East => 1,
+ Direction::South => 2,
+ Direction::West => 3,
+ Direction::Up => 4,
+ Direction::Down => 5,
+ _ => unreachable!(),
+ }
+ }
+
+
pub fn horizontal_index(&self) -> usize {
match *self {
Direction::North => 2,
@@ -115,6 +128,16 @@ impl Direction {
}
}
+ pub fn horizontal_offset(&self) -> usize {
+ match *self {
+ Direction::North => 0,
+ Direction::South => 1,
+ Direction::West => 2,
+ Direction::East => 3,
+ _ => unreachable!(),
+ }
+ }
+
pub fn axis(&self) -> Axis {
match *self {
Direction::Down | Direction::Up => Axis::Y,
diff --git a/src/entity/block_entity/sign.rs b/src/entity/block_entity/sign.rs
index 27fa73c..8171fbf 100644
--- a/src/entity/block_entity/sign.rs
+++ b/src/entity/block_entity/sign.rs
@@ -92,7 +92,7 @@ impl ecs::System for SignRenderer {
let info = m.get_component_mut(e, self.sign_info).unwrap();
info.dirty = false;
match world.get_block(position) {
- Block::WallSign{facing} => {
+ Block::WallSign{facing, ..} => {
info.offset_z = 7.5 / 16.0;
match facing {
Direction::North => {},
@@ -102,7 +102,7 @@ impl ecs::System for SignRenderer {
_ => unreachable!(),
}
},
- Block::StandingSign{rotation} => {
+ Block::StandingSign{rotation, ..} => {
info.offset_y = 5.0 / 16.0;
info.has_stand = true;
info.rotation = -(rotation.data() as f64 / 16.0) * PI * 2.0 + PI;
diff --git a/src/item.rs b/src/item.rs
index 7ed18d6..9d791e9 100644
--- a/src/item.rs
+++ b/src/item.rs
@@ -21,7 +21,7 @@ use byteorder::{BigEndian, WriteBytesExt, ReadBytesExt};
pub struct Stack {
id: isize,
count: isize,
- damage: isize,
+ damage: Option,
tag: Option,
}
@@ -31,7 +31,7 @@ impl Default for Stack {
Stack {
id: -1,
count: 0,
- damage: 0,
+ damage: None,
tag: None,
}
}
@@ -39,14 +39,31 @@ impl Default for Stack {
impl Serializable for Option {
fn read_from(buf: &mut R) -> Result, protocol::Error> {
- let id = buf.read_i16::()?;
+ let protocol_version = unsafe { protocol::CURRENT_PROTOCOL_VERSION };
+
+ if protocol_version >= 404 {
+ let present = buf.read_u8()? != 0;
+ if !present {
+ return Ok(None)
+ }
+ }
+
+ let id = if protocol_version >= 404 {
+ protocol::VarInt::read_from(buf)?.0 as isize
+ } else {
+ buf.read_i16::()? as isize
+ };
+
if id == -1 {
return Ok(None);
}
let count = buf.read_u8()? as isize;
- let damage = buf.read_i16::()? as isize;
-
- let protocol_version = unsafe { protocol::CURRENT_PROTOCOL_VERSION };
+ let damage = if protocol_version >= 404 {
+ // 1.13.2+ stores damage in the NBT
+ None
+ } else {
+ Some(buf.read_i16::()? as isize)
+ };
let tag: Option = if protocol_version >= 47 {
Serializable::read_from(buf)?
@@ -74,9 +91,10 @@ impl Serializable for Option {
fn write_to(&self, buf: &mut W) -> Result<(), protocol::Error> {
match *self {
Some(ref val) => {
+ // TODO: if protocol_version >= 404, send present and id varint, no damage, for 1.13.2
buf.write_i16::(val.id as i16)?;
buf.write_u8(val.count as u8)?;
- buf.write_i16::(val.damage as i16)?;
+ buf.write_i16::(val.damage.unwrap_or(0) as i16)?;
// TODO: compress zlib NBT if 1.7
val.tag.write_to(buf)?;
}
diff --git a/src/model/mod.rs b/src/model/mod.rs
index f9bb0c1..097e4b2 100644
--- a/src/model/mod.rs
+++ b/src/model/mod.rs
@@ -828,14 +828,14 @@ struct BlockFace {
tint_index: i32,
}
-#[derive(Clone)]
+#[derive(Clone, Debug)]
struct Model {
faces: Vec,
ambient_occlusion: bool,
weight: f64,
}
-#[derive(Clone)]
+#[derive(Clone, Debug)]
struct Face {
cull_face: Direction,
facing: Direction,
@@ -1028,7 +1028,7 @@ pub const PRECOMPUTED_VERTS: [&[BlockVertex; 4]; 6] = [
],
];
-#[derive(Clone)]
+#[derive(Clone, Debug)]
pub struct BlockVertex {
pub x: f32,
pub y: f32,
diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs
index c94640c..b54cea6 100644
--- a/src/protocol/mod.rs
+++ b/src/protocol/mod.rs
@@ -37,7 +37,7 @@ use flate2::Compression;
use std::time::{Instant, Duration};
use crate::shared::Position;
-pub const SUPPORTED_PROTOCOLS: [i32; 9] = [340, 316, 315, 210, 109, 107, 74, 47, 5];
+pub const SUPPORTED_PROTOCOLS: [i32; 10] = [404, 340, 316, 315, 210, 109, 107, 74, 47, 5];
// TODO: switch to using thread_local storage?, see https://doc.rust-lang.org/std/macro.thread_local.html
pub static mut CURRENT_PROTOCOL_VERSION: i32 = SUPPORTED_PROTOCOLS[0];
@@ -553,6 +553,17 @@ impl fmt::Debug for LenPrefixedBytes {
}
}
+impl Lengthable for bool {
+ fn into(self) -> usize {
+ if self { 1 } else { 0 }
+ }
+
+ fn from(u: usize) -> bool {
+ u != 0
+ }
+}
+
+
impl Lengthable for u8 {
fn into(self) -> usize {
self as usize
diff --git a/src/protocol/packet.rs b/src/protocol/packet.rs
index 4134491..9f5c2e1 100644
--- a/src/protocol/packet.rs
+++ b/src/protocol/packet.rs
@@ -53,6 +53,10 @@ state_packets!(
packet TeleportConfirm {
field teleport_id: VarInt =,
}
+ packet QueryBlockNBT {
+ field transaction_id: VarInt =,
+ field location: Position =,
+ }
/// TabComplete is sent by the client when the client presses tab in
/// the chat box.
packet TabComplete {
@@ -156,6 +160,15 @@ state_packets!(
field channel: String =,
field data: LenPrefixedBytes =,
}
+ packet EditBook {
+ field new_book: Option =,
+ field is_signing: bool =,
+ field hand: VarInt =,
+ }
+ packet QueryEntityNBT {
+ field transaction_id: VarInt =,
+ field entity_id: VarInt =,
+ }
/// UseEntity is sent when the user interacts (right clicks) or attacks
/// (left clicks) an entity.
packet UseEntity {
@@ -240,10 +253,13 @@ state_packets!(
field yaw: f32 =,
field pitch: f32 =,
}
- /// TODO: Document
+ /// SteerBoat is used to visually update the boat paddles.
packet SteerBoat {
- field unknown: bool =,
- field unknown2: bool =,
+ field left_paddle_turning: bool =,
+ field right_paddle_turning: bool =,
+ }
+ packet PickItem {
+ field slot_to_use: VarInt =,
}
/// CraftRecipeRequest is sent when player clicks a recipe in the crafting book.
packet CraftRecipeRequest {
@@ -308,6 +324,9 @@ state_packets!(
field crafting_book_open: bool = when(|p: &CraftingBookData| p.action.0 == 1),
field crafting_filter: bool = when(|p: &CraftingBookData| p.action.0 == 1),
}
+ packet NameItem {
+ field item_name: String =,
+ }
/// ResourcePackStatus informs the server of the client's current progress
/// in activating the requested resource pack
packet ResourcePackStatus {
@@ -322,17 +341,53 @@ state_packets!(
field action: VarInt =,
field tab_id: String = when(|p: &AdvancementTab| p.action.0 == 0),
}
+ packet SelectTrade {
+ field selected_slot: VarInt =,
+ }
+ packet SetBeaconEffect {
+ field primary_effect: VarInt =,
+ field secondary_effect: VarInt =,
+ }
/// HeldItemChange is sent when the player changes the currently active
/// hotbar slot.
packet HeldItemChange {
field slot: i16 =,
}
+ packet UpdateCommandBlock {
+ field location: Position =,
+ field command: String =,
+ field mode: VarInt =,
+ field flags: u8 =,
+ }
+ packet UpdateCommandBlockMinecart {
+ field entity_id: VarInt =,
+ field command: String =,
+ field track_output: bool =,
+ }
/// CreativeInventoryAction is sent when the client clicks in the creative
/// inventory. This is used to spawn items in creative.
packet CreativeInventoryAction {
field slot: i16 =,
field clicked_item: Option =,
}
+ packet UpdateStructureBlock {
+ field location: Position =,
+ field action: VarInt =,
+ field mode: VarInt =,
+ field name: String =,
+ field offset_x: i8 =,
+ field offset_y: i8 =,
+ field offset_z: i8 =,
+ field size_x: i8 =,
+ field size_y: i8 =,
+ field size_z: i8 =,
+ field mirror: VarInt =,
+ field rotation: VarInt =,
+ field metadata: String =,
+ field integrity: f32 =,
+ field seed: VarLong =,
+ field flags: i8 =,
+ }
/// SetSign sets the text on a sign after placing it.
packet SetSign {
field location: Position =,
@@ -704,6 +759,10 @@ state_packets!(
packet TabCompleteReply {
field matches: LenPrefixed =,
}
+ packet DeclareCommands {
+ field nodes: LenPrefixed =,
+ field root_index: VarInt =,
+ }
/// ServerMessage is a message sent by the server. It could be from a player
/// or just a system message. The Type field controls the location the
/// message is displayed at and when the message is displayed.
@@ -1148,6 +1207,15 @@ state_packets!(
field online: bool =,
field ping: u16 =,
}
+ packet FacePlayer {
+ field feet_eyes: VarInt =,
+ field target_x: f64 =,
+ field target_y: f64 =,
+ field target_z: f64 =,
+ field is_entity: bool =,
+ field entity_id: Option = when(|p: &FacePlayer| p.is_entity),
+ field entity_feet_eyes: Option = when(|p: &FacePlayer| p.is_entity),
+ }
/// TeleportPlayer is sent to change the player's position. The client is expected
/// to reply to the server with the same positions as contained in this packet
/// otherwise will reject future packets.
@@ -1179,12 +1247,21 @@ state_packets!(
field y: u8 =,
field z: i32 =,
}
- packet UnlockRecipes {
+ packet UnlockRecipes_NoSmelting {
field action: VarInt =,
field crafting_book_open: bool =,
field filtering_craftable: bool =,
field recipe_ids: LenPrefixed =,
- field recipe_ids2: LenPrefixed = when(|p: &UnlockRecipes| p.action.0 == 0),
+ field recipe_ids2: LenPrefixed = when(|p: &UnlockRecipes_NoSmelting| p.action.0 == 0),
+ }
+ packet UnlockRecipes_WithSmelting {
+ field action: VarInt =,
+ field crafting_book_open: bool =,
+ field filtering_craftable: bool =,
+ field smelting_book_open: bool =,
+ field filtering_smeltable: bool =,
+ field recipe_ids: LenPrefixed =,
+ field recipe_ids2: LenPrefixed = when(|p: &UnlockRecipes_WithSmelting| p.action.0 == 0),
}
/// EntityDestroy destroys the entities with the ids in the provided slice.
packet EntityDestroy {
@@ -1229,6 +1306,10 @@ state_packets!(
field entity_id: i32 =,
field entity_status: i8 =,
}
+ packet NBTQueryResponse {
+ field transaction_id: VarInt =,
+ field nbt: Option =,
+ }
/// SelectAdvancementTab indicates the client should switch the advancement tab.
packet SelectAdvancementTab {
field has_id: bool =,
@@ -1405,6 +1486,11 @@ state_packets!(
field world_age: i64 =,
field time_of_day: i64 =,
}
+ packet StopSound {
+ field flags: u8 =,
+ field source: Option = when(|p: &StopSound| p.flags & 0x01 != 0),
+ field sound: Option = when(|p: &StopSound| p.flags & 0x02 != 0),
+ }
/// Title configures an on-screen title.
packet Title {
field action: VarInt =,
@@ -1544,6 +1630,14 @@ state_packets!(
field amplifier: i8 =,
field duration: i16 =,
}
+ packet DeclareRecipes {
+ field recipes: LenPrefixed =,
+ }
+ packet Tags {
+ field block_tags: LenPrefixed =,
+ field item_tags: LenPrefixed =,
+ field fluid_tags: LenPrefixed =,
+ }
}
}
login Login {
@@ -1569,6 +1663,11 @@ state_packets!(
field shared_secret: LenPrefixedBytes =,
field verify_token: LenPrefixedBytes =,
}
+ packet LoginPluginResponse {
+ field message_id: VarInt =,
+ field successful: bool =,
+ field data: Vec =,
+ }
}
clientbound Clientbound {
/// LoginDisconnect is sent by the server if there was any issues
@@ -1609,6 +1708,11 @@ state_packets!(
/// Threshold where a packet should be sent compressed
field threshold: VarInt =,
}
+ packet LoginPluginRequest {
+ field message_id: VarInt =,
+ field channel: String =,
+ field data: Vec =,
+ }
}
}
status Status {
@@ -2165,3 +2269,324 @@ pub struct PlayerProperty {
pub value: String,
pub signature: Option,
}
+
+use crate::item;
+type RecipeIngredient = LenPrefixed>;
+
+#[derive(Debug)]
+pub enum RecipeData {
+ Shapeless {
+ group: String,
+ ingredients: LenPrefixed,
+ result: Option,
+ },
+ Shaped {
+ width: VarInt,
+ height: VarInt,
+ group: String,
+ ingredients: Vec,
+ result: Option,
+ },
+ ArmorDye,
+ BookCloning,
+ MapCloning,
+ MapExtending,
+ FireworkRocket,
+ FireworkStar,
+ FireworkStarFade,
+ RepairItem,
+ TippedArrow,
+ BannerDuplicate,
+ BannerAddPattern,
+ ShieldDecoration,
+ ShulkerBoxColoring,
+ Smelting {
+ group: String,
+ ingredient: RecipeIngredient,
+ result: Option,
+ experience: f32,
+ cooking_time: VarInt,
+ },
+}
+
+impl Default for RecipeData {
+ fn default() -> Self {
+ RecipeData::ArmorDye
+ }
+}
+
+#[derive(Debug, Default)]
+pub struct Recipe {
+ pub id: String,
+ pub ty: String,
+ pub data: RecipeData,
+}
+
+impl Serializable for Recipe {
+ fn read_from(buf: &mut R) -> Result {
+ let id = String::read_from(buf)?;
+ let ty = String::read_from(buf)?;
+
+ let data =
+ match ty.as_ref() {
+ "crafting_shapeless" => RecipeData::Shapeless {
+ group: Serializable::read_from(buf)?,
+ ingredients: Serializable::read_from(buf)?,
+ result: Serializable::read_from(buf)?,
+ },
+ "crafting_shaped" => {
+ let width: VarInt = Serializable::read_from(buf)?;
+ let height: VarInt = Serializable::read_from(buf)?;
+ let group: String = Serializable::read_from(buf)?;
+
+ let capacity = width.0 as usize * height.0 as usize;
+
+ let mut ingredients = Vec::with_capacity(capacity);
+ for _ in 0 .. capacity {
+ ingredients.push(Serializable::read_from(buf)?);
+ }
+ let result: Option = Serializable::read_from(buf)?;
+
+ RecipeData::Shaped { width, height, group, ingredients, result }
+ }
+ "crafting_special_armordye" => RecipeData::ArmorDye,
+ "crafting_special_bookcloning" => RecipeData::BookCloning,
+ "crafting_special_mapcloning" => RecipeData::MapCloning,
+ "crafting_special_mapextending" => RecipeData::MapExtending,
+ "crafting_special_firework_rocket" => RecipeData::FireworkRocket,
+ "crafting_special_firework_star" => RecipeData::FireworkStar,
+ "crafting_special_firework_star_fade" => RecipeData::FireworkStarFade,
+ "crafting_special_repairitem" => RecipeData::RepairItem,
+ "crafting_special_tippedarrow" => RecipeData::TippedArrow,
+ "crafting_special_bannerduplicate" => RecipeData::BannerDuplicate,
+ "crafting_special_banneraddpattern" => RecipeData::BannerAddPattern,
+ "crafting_special_shielddecoration" => RecipeData::ShieldDecoration,
+ "crafting_special_shulkerboxcoloring" => RecipeData::ShulkerBoxColoring,
+ "smelting" => RecipeData::Smelting {
+ group: Serializable::read_from(buf)?,
+ ingredient: Serializable::read_from(buf)?,
+ result: Serializable::read_from(buf)?,
+ experience: Serializable::read_from(buf)?,
+ cooking_time: Serializable::read_from(buf)?,
+ },
+ _ => panic!("unrecognized recipe type: {}", ty)
+ };
+
+ Ok(Recipe { id, ty, data })
+ }
+
+ fn write_to(&self, _: &mut W) -> Result<(), Error> {
+ unimplemented!()
+ }
+}
+
+#[derive(Debug, Default)]
+pub struct Tags {
+ pub tag_name: String,
+ pub entries: LenPrefixed,
+}
+
+impl Serializable for Tags {
+ fn read_from(buf: &mut R) -> Result {
+ Ok(Tags {
+ tag_name: Serializable::read_from(buf)?,
+ entries: Serializable::read_from(buf)?,
+ })
+ }
+
+ fn write_to(&self, _: &mut W) -> Result<(), Error> {
+ unimplemented!()
+ }
+}
+
+#[derive(Debug, Default)]
+pub struct CommandNode {
+ pub flags: u8,
+ pub children: LenPrefixed,
+ pub redirect_node: Option,
+ pub name: Option,
+ pub parser: Option,
+ pub properties: Option,
+ pub suggestions_type: Option,
+}
+
+#[derive(Debug, Eq, PartialEq)]
+enum CommandNodeType {
+ Root,
+ Literal,
+ Argument,
+}
+
+#[derive(Debug)]
+pub enum CommandProperty {
+ Bool,
+ Double {
+ flags: u8,
+ min: Option,
+ max: Option,
+ },
+ Float {
+ flags: u8,
+ min: Option,
+ max: Option,
+ },
+ Integer {
+ flags: u8,
+ min: Option,
+ max: Option,
+ },
+ String {
+ token_type: VarInt,
+ },
+ Entity {
+ flags: u8,
+ },
+ GameProfile,
+ BlockPos,
+ Vec3,
+ Vec2,
+ BlockState,
+ BlockPredicate,
+ ItemStack,
+ ItemPredicate,
+ Color,
+ Component,
+ Message,
+ Nbt,
+ NbtPath,
+ Objective,
+ ObjectiveCriteria,
+ Operation,
+ Particle,
+ Rotation,
+ ScoreboardSlot,
+ ScoreHolder {
+ flags: u8,
+ },
+ Swizzle,
+ Team,
+ ItemSlot,
+ ResourceLocation,
+ MobEffect,
+ Function,
+ EntityAnchor,
+ Range {
+ decimals: bool,
+ },
+ ItemEnchantment,
+}
+
+
+impl Serializable for CommandNode {
+ fn read_from(buf: &mut R) -> Result {
+ let flags: u8 = Serializable::read_from(buf)?;
+ let children: LenPrefixed = Serializable::read_from(buf)?;
+
+ let node_type = match flags & 0x03 {
+ 0 => CommandNodeType::Root,
+ 1 => CommandNodeType::Literal,
+ 2 => CommandNodeType::Argument,
+ _ => panic!("unrecognized command node type {}", flags & 0x03),
+ };
+ let _is_executable = flags & 0x04 != 0;
+ let has_redirect = flags & 0x08 != 0;
+ let has_suggestions_type = flags & 0x10 != 0;
+
+ let redirect_node: Option = if has_redirect {
+ Some(Serializable::read_from(buf)?)
+ } else {
+ None
+ };
+
+ let name: Option = if node_type == CommandNodeType::Argument || node_type == CommandNodeType::Literal {
+ Serializable::read_from(buf)?
+ } else {
+ None
+ };
+ let parser: Option = if node_type == CommandNodeType::Argument {
+ Serializable::read_from(buf)?
+ } else {
+ None
+ };
+
+ let properties: Option = if let Some(ref parse) = parser {
+ Some(match parse.as_ref() {
+ "brigadier:bool" => CommandProperty::Bool,
+ "brigadier:double" => {
+ let flags = Serializable::read_from(buf)?;
+ let min = if flags & 0x01 != 0 { Some(Serializable::read_from(buf)?) } else { None };
+ let max = if flags & 0x02 != 0 { Some(Serializable::read_from(buf)?) } else { None };
+ CommandProperty::Double { flags, min, max }
+ },
+ "brigadier:float" => {
+ let flags = Serializable::read_from(buf)?;
+ let min = if flags & 0x01 != 0 { Some(Serializable::read_from(buf)?) } else { None };
+ let max = if flags & 0x02 != 0 { Some(Serializable::read_from(buf)?) } else { None };
+ CommandProperty::Float { flags, min, max }
+ },
+ "brigadier:integer" => {
+ let flags = Serializable::read_from(buf)?;
+ let min = if flags & 0x01 != 0 { Some(Serializable::read_from(buf)?) } else { None };
+ let max = if flags & 0x02 != 0 { Some(Serializable::read_from(buf)?) } else { None };
+ CommandProperty::Integer { flags, min, max }
+ },
+ "brigadier:string" => {
+ CommandProperty::String { token_type: Serializable::read_from(buf)? }
+ },
+ "minecraft:entity" => {
+ CommandProperty::Entity { flags: Serializable::read_from(buf)? }
+ },
+ "minecraft:game_profile" => CommandProperty::GameProfile,
+ "minecraft:block_pos" => CommandProperty::BlockPos,
+ "minecraft:vec3" => CommandProperty::Vec3,
+ "minecraft:vec2" => CommandProperty::Vec2,
+ "minecraft:block_state" => CommandProperty::BlockState,
+ "minecraft:block_predicate" => CommandProperty::BlockPredicate,
+ "minecraft:item_stack" => CommandProperty::ItemStack,
+ "minecraft:item_predicate" => CommandProperty::ItemPredicate,
+ "minecraft:color" => CommandProperty::Color,
+ "minecraft:component" => CommandProperty::Component,
+ "minecraft:message" => CommandProperty::Message,
+ "minecraft:nbt" => CommandProperty::Nbt,
+ "minecraft:nbt_path" => CommandProperty::NbtPath,
+ "minecraft:objective" => CommandProperty::Objective,
+ "minecraft:objective_criteria" => CommandProperty::ObjectiveCriteria,
+ "minecraft:operation" => CommandProperty::Operation,
+ "minecraft:particle" => CommandProperty::Particle,
+ "minecraft:rotation" => CommandProperty::Rotation,
+ "minecraft:scoreboard_slot" => CommandProperty::ScoreboardSlot,
+ "minecraft:score_holder" => {
+ CommandProperty::ScoreHolder { flags: Serializable::read_from(buf)? }
+ },
+ "minecraft:swizzle" => CommandProperty::Swizzle,
+ "minecraft:team" => CommandProperty::Team,
+ "minecraft:item_slot" => CommandProperty::ItemSlot,
+ "minecraft:resource_location" => CommandProperty::ResourceLocation,
+ "minecraft:mob_effect" => CommandProperty::MobEffect,
+ "minecraft:function" => CommandProperty::Function,
+ "minecraft:entity_anchor" => CommandProperty::EntityAnchor,
+ "minecraft:range" => {
+ CommandProperty::Range { decimals: Serializable::read_from(buf)? }
+ },
+ "minecraft:item_enchantment" => CommandProperty::ItemEnchantment,
+ _ => panic!("unsupported command node parser {}", parse),
+ })
+ } else {
+ None
+ };
+
+ let suggestions_type: Option = if has_suggestions_type {
+ Serializable::read_from(buf)?
+ } else {
+ None
+ };
+
+ Ok(CommandNode { flags, children, redirect_node, name, parser, properties, suggestions_type })
+ }
+
+ fn write_to(&self, _: &mut W) -> Result<(), Error> {
+ unimplemented!()
+ }
+}
+
+
diff --git a/src/protocol/versions.rs b/src/protocol/versions.rs
index 2d022fc..5435189 100644
--- a/src/protocol/versions.rs
+++ b/src/protocol/versions.rs
@@ -1,5 +1,6 @@
use crate::protocol::*;
+mod v1_13_2;
mod v1_12_2;
mod v1_11_2;
mod v1_10_2;
@@ -13,6 +14,9 @@ pub fn translate_internal_packet_id_for_version(version: i32, state: State, dir:
match version {
// https://wiki.vg/Protocol_History
// https://wiki.vg/Protocol_version_numbers#Versions_after_the_Netty_rewrite
+ // 1.13.2
+ 404 => v1_13_2::translate_internal_packet_id(state, dir, id, to_internal),
+
// 1.12.2
340 => v1_12_2::translate_internal_packet_id(state, dir, id, to_internal),
diff --git a/src/protocol/versions/v1_12_2.rs b/src/protocol/versions/v1_12_2.rs
index c609790..7dc1454 100644
--- a/src/protocol/versions/v1_12_2.rs
+++ b/src/protocol/versions/v1_12_2.rs
@@ -92,7 +92,7 @@ protocol_packet_ids!(
0x2e => PlayerInfo
0x2f => TeleportPlayer_WithConfirm
0x30 => EntityUsedBed
- 0x31 => UnlockRecipes
+ 0x31 => UnlockRecipes_NoSmelting
0x32 => EntityDestroy
0x33 => EntityRemoveEffect
0x34 => ResourcePackSend
diff --git a/src/protocol/versions/v1_13_2.rs b/src/protocol/versions/v1_13_2.rs
new file mode 100644
index 0000000..3f3c9da
--- /dev/null
+++ b/src/protocol/versions/v1_13_2.rs
@@ -0,0 +1,169 @@
+protocol_packet_ids!(
+ handshake Handshaking {
+ serverbound Serverbound {
+ 0x00 => Handshake
+ }
+ clientbound Clientbound {
+ }
+ }
+ play Play {
+ serverbound Serverbound {
+ 0x00 => TeleportConfirm
+ 0x01 => QueryBlockNBT
+ 0x02 => ChatMessage
+ 0x03 => ClientStatus
+ 0x04 => ClientSettings
+ 0x05 => TabComplete
+ 0x06 => ConfirmTransactionServerbound
+ 0x07 => EnchantItem
+ 0x08 => ClickWindow
+ 0x09 => CloseWindow
+ 0x0a => PluginMessageServerbound
+ 0x0b => EditBook
+ 0x0c => QueryEntityNBT
+ 0x0d => UseEntity
+ 0x0e => KeepAliveServerbound_i64
+ 0x0f => Player
+ 0x10 => PlayerPosition
+ 0x11 => PlayerPositionLook
+ 0x12 => PlayerLook
+ 0x13 => VehicleMove
+ 0x14 => SteerBoat
+ 0x15 => PickItem
+ 0x16 => CraftRecipeRequest
+ 0x17 => ClientAbilities
+ 0x18 => PlayerDigging
+ 0x19 => PlayerAction
+ 0x1a => SteerVehicle
+ 0x1b => CraftingBookData
+ 0x1c => NameItem
+ 0x1d => ResourcePackStatus
+ 0x1e => AdvancementTab
+ 0x1f => SelectTrade
+ 0x20 => SetBeaconEffect
+ 0x21 => HeldItemChange
+ 0x22 => UpdateCommandBlock
+ 0x23 => UpdateCommandBlockMinecart
+ 0x24 => CreativeInventoryAction
+ 0x25 => UpdateStructureBlock
+ 0x26 => SetSign
+ 0x27 => ArmSwing
+ 0x28 => SpectateTeleport
+ 0x29 => PlayerBlockPlacement_f32
+ 0x2a => UseItem
+ }
+ clientbound Clientbound {
+ 0x00 => SpawnObject
+ 0x01 => SpawnExperienceOrb
+ 0x02 => SpawnGlobalEntity
+ 0x03 => SpawnMob
+ 0x04 => SpawnPainting
+ 0x05 => SpawnPlayer_f64
+ 0x06 => Animation
+ 0x07 => Statistics
+ 0x08 => BlockBreakAnimation
+ 0x09 => UpdateBlockEntity
+ 0x0a => BlockAction
+ 0x0b => BlockChange_VarInt
+ 0x0c => BossBar
+ 0x0d => ServerDifficulty
+ 0x0e => ServerMessage
+ 0x0f => MultiBlockChange_VarInt
+ 0x10 => TabCompleteReply
+ 0x11 => DeclareCommands
+ 0x12 => ConfirmTransaction
+ 0x13 => WindowClose
+ 0x14 => WindowOpen
+ 0x15 => WindowItems
+ 0x16 => WindowProperty
+ 0x17 => WindowSetSlot
+ 0x18 => SetCooldown
+ 0x19 => PluginMessageClientbound
+ 0x1a => NamedSoundEffect
+ 0x1b => Disconnect
+ 0x1c => EntityAction
+ 0x1d => NBTQueryResponse
+ 0x1e => Explosion
+ 0x1f => ChunkUnload
+ 0x20 => ChangeGameState
+ 0x21 => KeepAliveClientbound_i64
+ 0x22 => ChunkData
+ 0x23 => Effect
+ 0x24 => Particle
+ 0x25 => JoinGame_i32
+ 0x26 => Maps
+ 0x27 => Entity
+ 0x28 => EntityMove_i16
+ 0x29 => EntityLookAndMove_i16
+ 0x2a => EntityLook_VarInt
+ 0x2b => VehicleTeleport
+ 0x2c => SignEditorOpen
+ 0x2d => CraftRecipeResponse
+ 0x2e => PlayerAbilities
+ 0x2f => CombatEvent
+ 0x30 => PlayerInfo
+ 0x31 => FacePlayer
+ 0x32 => TeleportPlayer_WithConfirm
+ 0x33 => EntityUsedBed
+ 0x34 => UnlockRecipes_WithSmelting
+ 0x35 => EntityDestroy
+ 0x36 => EntityRemoveEffect
+ 0x37 => ResourcePackSend
+ 0x38 => Respawn
+ 0x39 => EntityHeadLook
+ 0x3a => SelectAdvancementTab
+ 0x3b => WorldBorder
+ 0x3c => Camera
+ 0x3d => SetCurrentHotbarSlot
+ 0x3e => ScoreboardDisplay
+ 0x3f => EntityMetadata
+ 0x40 => EntityAttach
+ 0x41 => EntityVelocity
+ 0x42 => EntityEquipment
+ 0x43 => SetExperience
+ 0x44 => UpdateHealth
+ 0x45 => ScoreboardObjective
+ 0x46 => SetPassengers
+ 0x47 => Teams
+ 0x48 => UpdateScore
+ 0x49 => SpawnPosition
+ 0x4a => TimeUpdate
+ 0x4c => StopSound
+ 0x4d => SoundEffect
+ 0x4e => PlayerListHeaderFooter
+ 0x4f => CollectItem
+ 0x50 => EntityTeleport_f64
+ 0x51 => Advancements
+ 0x52 => EntityProperties
+ 0x53 => EntityEffect
+ 0x54 => DeclareRecipes
+ 0x55 => Tags
+ }
+ }
+ login Login {
+ serverbound Serverbound {
+ 0x00 => LoginStart
+ 0x01 => EncryptionResponse
+ 0x02 => LoginPluginResponse
+ }
+ clientbound Clientbound {
+ 0x00 => LoginDisconnect
+ 0x01 => EncryptionRequest
+ 0x02 => LoginSuccess
+ 0x03 => SetInitialCompression
+ 0x04 => LoginPluginRequest
+ }
+ }
+ status Status {
+ serverbound Serverbound {
+ 0x00 => StatusRequest
+ 0x01 => StatusPing
+ }
+ clientbound Clientbound {
+ 0x00 => StatusResponse
+ 0x01 => StatusPong
+ }
+ }
+);
+
+
diff --git a/src/server/mod.rs b/src/server/mod.rs
index 0aacee0..3bbdc4c 100644
--- a/src/server/mod.rs
+++ b/src/server/mod.rs
@@ -242,13 +242,13 @@ impl Server {
if xx == 0 && z == 0 {
continue;
}
- server.world.set_block(Position::new(x + xx, h + 3, z + zz), block::Leaves{ variant: block::TreeVariant::Oak, check_decay: false, decayable: false });
- server.world.set_block(Position::new(x + xx, h + 4, z + zz), block::Leaves{ variant: block::TreeVariant::Oak, check_decay: false, decayable: false });
+ server.world.set_block(Position::new(x + xx, h + 3, z + zz), block::Leaves{ variant: block::TreeVariant::Oak, check_decay: false, decayable: false, distance: 1 });
+ server.world.set_block(Position::new(x + xx, h + 4, z + zz), block::Leaves{ variant: block::TreeVariant::Oak, check_decay: false, decayable: false, distance: 1 });
if xx.abs() <= 1 && zz.abs() <= 1 {
- server.world.set_block(Position::new(x + xx, h + 5, z + zz), block::Leaves{ variant: block::TreeVariant::Oak, check_decay: false, decayable: false });
+ server.world.set_block(Position::new(x + xx, h + 5, z + zz), block::Leaves{ variant: block::TreeVariant::Oak, check_decay: false, decayable: false, distance: 1 });
}
if xx * xx + zz * zz <= 1 {
- server.world.set_block(Position::new(x + xx, h + 6, z + zz), block::Leaves{ variant: block::TreeVariant::Oak, check_decay: false, decayable: false });
+ server.world.set_block(Position::new(x + xx, h + 6, z + zz), block::Leaves{ variant: block::TreeVariant::Oak, check_decay: false, decayable: false, distance: 1 });
}
}
}
@@ -280,7 +280,7 @@ impl Server {
disconnect_reason: None,
just_disconnected: false,
- world: world::World::new(),
+ world: world::World::new(protocol_version),
world_age: 0,
world_time: 0.0,
world_time_target: 0.0,
@@ -701,7 +701,7 @@ impl Server {
}
fn on_respawn(&mut self, respawn: packet::play::clientbound::Respawn) {
- self.world = world::World::new();
+ self.world = world::World::new(self.protocol_version);
let gamemode = Gamemode::from_int((respawn.gamemode & 0x7) as i32);
if let Some(player) = self.player {
@@ -1155,7 +1155,7 @@ impl Server {
}
fn on_block_change(&mut self, location: Position, id: i32) {
- self.world.set_block(location, block::Block::by_vanilla_id(id as usize))
+ self.world.set_block(location, block::Block::by_vanilla_id(id as usize, self.protocol_version))
}
fn on_block_change_varint(&mut self, block_change: packet::play::clientbound::BlockChange_VarInt) {
@@ -1179,7 +1179,7 @@ impl Server {
record.y as i32,
oz + (record.xz & 0xF) as i32
),
- block::Block::by_vanilla_id(record.block_id.0 as usize)
+ block::Block::by_vanilla_id(record.block_id.0 as usize, self.protocol_version)
);
}
}
@@ -1202,7 +1202,7 @@ impl Server {
self.world.set_block(
Position::new(x, y, z),
- block::Block::by_vanilla_id(id as usize)
+ block::Block::by_vanilla_id(id as usize, self.protocol_version)
);
}
}
diff --git a/src/server/plugin_messages.rs b/src/server/plugin_messages.rs
index c0421ab..3eb9faf 100644
--- a/src/server/plugin_messages.rs
+++ b/src/server/plugin_messages.rs
@@ -9,10 +9,18 @@ pub struct Brand {
impl Brand {
pub fn as_message(self) -> PluginMessageServerbound {
+ let protocol_version = unsafe { crate::protocol::CURRENT_PROTOCOL_VERSION };
+
+ let channel_name = if protocol_version >= 404 {
+ "minecraft:brand"
+ } else {
+ "MC|Brand"
+ };
+
let mut data = vec![];
Serializable::write_to(&self.brand, &mut data).unwrap();
PluginMessageServerbound {
- channel: "MC|Brand".into(),
+ channel: channel_name.into(),
data,
}
}
diff --git a/src/types/metadata.rs b/src/types/metadata.rs
index 44ee0a7..a5e8a3e 100644
--- a/src/types/metadata.rs
+++ b/src/types/metadata.rs
@@ -18,6 +18,7 @@ use std::io;
use std::fmt;
use crate::protocol;
use crate::protocol::Serializable;
+use crate::protocol::LenPrefixed;
use crate::format;
use crate::item;
use crate::shared::Position;
@@ -271,13 +272,151 @@ impl Metadata {
u8::write_to(&0xFF, buf)?;
Ok(())
}
+
+ fn read_from113(buf: &mut R) -> Result {
+ let mut m = Self::new();
+ loop {
+ let index = u8::read_from(buf)? as i32;
+ if index == 0xFF {
+ break;
+ }
+ let ty = protocol::VarInt::read_from(buf)?.0;
+ match ty {
+ 0 => m.put_raw(index, i8::read_from(buf)?),
+ 1 => m.put_raw(index, protocol::VarInt::read_from(buf)?.0),
+ 2 => m.put_raw(index, f32::read_from(buf)?),
+ 3 => m.put_raw(index, String::read_from(buf)?),
+ 4 => m.put_raw(index, format::Component::read_from(buf)?),
+ 5 => m.put_raw(index, LenPrefixed::::read_from(buf)?),
+ 6 => m.put_raw(index, Option::::read_from(buf)?),
+ 7 => m.put_raw(index, bool::read_from(buf)?),
+ 8 => m.put_raw(index,
+ [f32::read_from(buf)?,
+ f32::read_from(buf)?,
+ f32::read_from(buf)?]),
+ 9 => m.put_raw(index, Position::read_from(buf)?),
+ 10 => {
+ if bool::read_from(buf)? {
+ m.put_raw(index, Option::::read_from(buf)?);
+ } else {
+ m.put_raw::>(index, None);
+ }
+ }
+ 11 => m.put_raw(index, protocol::VarInt::read_from(buf)?),
+ 12 => {
+ if bool::read_from(buf)? {
+ m.put_raw(index, Option::::read_from(buf)?);
+ } else {
+ m.put_raw::>(index, None);
+ }
+ }
+ 13 => m.put_raw(index, protocol::VarInt::read_from(buf)?.0 as u16),
+ 14 => {
+ let ty = u8::read_from(buf)?;
+ if ty != 0 {
+ let name = nbt::read_string(buf)?;
+ let tag = nbt::Tag::read_from(buf)?;
+
+ m.put_raw(index, nbt::NamedTag(name, tag));
+ }
+ }
+ 15 => panic!("TODO: particle"),
+ _ => return Err(protocol::Error::Err("unknown metadata type".to_owned())),
+ }
+ }
+ Ok(m)
+ }
+
+ fn write_to113(&self, buf: &mut W) -> Result<(), protocol::Error> {
+ for (k, v) in &self.map {
+ (*k as u8).write_to(buf)?;
+ match *v {
+ Value::Byte(ref val) => {
+ u8::write_to(&0, buf)?;
+ val.write_to(buf)?;
+ }
+ Value::Int(ref val) => {
+ u8::write_to(&1, buf)?;
+ protocol::VarInt(*val).write_to(buf)?;
+ }
+ Value::Float(ref val) => {
+ u8::write_to(&2, buf)?;
+ val.write_to(buf)?;
+ }
+ Value::String(ref val) => {
+ u8::write_to(&3, buf)?;
+ val.write_to(buf)?;
+ }
+ Value::FormatComponent(ref val) => {
+ u8::write_to(&4, buf)?;
+ val.write_to(buf)?;
+ }
+ Value::OptionalFormatComponent(ref val) => {
+ u8::write_to(&5, buf)?;
+ val.write_to(buf)?;
+ }
+ Value::OptionalItemStack(ref val) => {
+ u8::write_to(&6, buf)?;
+ val.write_to(buf)?;
+ }
+ Value::Bool(ref val) => {
+ u8::write_to(&7, buf)?;
+ val.write_to(buf)?;
+ }
+ Value::Vector(ref val) => {
+ u8::write_to(&8, buf)?;
+ val[0].write_to(buf)?;
+ val[1].write_to(buf)?;
+ val[2].write_to(buf)?;
+ }
+ Value::Position(ref val) => {
+ u8::write_to(&9, buf)?;
+ val.write_to(buf)?;
+ }
+ Value::OptionalPosition(ref val) => {
+ u8::write_to(&10, buf)?;
+ val.is_some().write_to(buf)?;
+ val.write_to(buf)?;
+ }
+ Value::Direction(ref val) => {
+ u8::write_to(&11, buf)?;
+ val.write_to(buf)?;
+ }
+ Value::OptionalUUID(ref val) => {
+ u8::write_to(&12, buf)?;
+ val.is_some().write_to(buf)?;
+ val.write_to(buf)?;
+ }
+ Value::Block(ref val) => {
+ u8::write_to(&13, buf)?;
+ protocol::VarInt(*val as i32).write_to(buf)?;
+ }
+ Value::NBTTag(ref _val) => {
+ u8::write_to(&14, buf)?;
+ // TODO: write NBT tags metadata
+ //nbt::Tag(*val).write_to(buf)?;
+ }
+ Value::Particle(ref val) => {
+ u8::write_to(&15, buf)?;
+ val.write_to(buf)?;
+ }
+ _ => panic!("unexpected metadata"),
+ }
+ }
+ u8::write_to(&0xFF, buf)?;
+ Ok(())
+ }
+
+
}
impl Serializable for Metadata {
fn read_from(buf: &mut R) -> Result {
let protocol_version = unsafe { protocol::CURRENT_PROTOCOL_VERSION };
- if protocol_version >= 74 {
+ if protocol_version >= 404 {
+ Metadata::read_from113(buf)
+ } else if protocol_version >= 74 {
Metadata::read_from19(buf)
} else {
Metadata::read_from18(buf)
@@ -287,7 +426,9 @@ impl Serializable for Metadata {
fn write_to(&self, buf: &mut W) -> Result<(), protocol::Error> {
let protocol_version = unsafe { protocol::CURRENT_PROTOCOL_VERSION };
- if protocol_version >= 74 {
+ if protocol_version >= 404 {
+ self.write_to113(buf)
+ } else if protocol_version >= 74 {
self.write_to19(buf)
} else {
self.write_to18(buf)
@@ -319,6 +460,7 @@ pub enum Value {
Float(f32),
String(String),
FormatComponent(format::Component),
+ OptionalFormatComponent(LenPrefixed),
OptionalItemStack(Option),
Bool(bool),
Vector([f32; 3]),
@@ -329,6 +471,146 @@ pub enum Value {
OptionalUUID(Option),
Block(u16), // TODO: Proper type
NBTTag(nbt::NamedTag),
+ Particle(ParticleData),
+}
+
+#[derive(Debug)]
+pub enum ParticleData {
+ AmbientEntityEffect,
+ AngryVillager,
+ Barrier,
+ Block {
+ block_state: protocol::VarInt,
+ },
+ Bubble,
+ Cloud,
+ Crit,
+ DamageIndicator,
+ DragonBreath,
+ DrippingLava,
+ DrippingWater,
+ Dust {
+ red: f32,
+ green: f32,
+ blue: f32,
+ scale: f32,
+ },
+ Effect,
+ ElderGuardian,
+ EnchantedHit,
+ Enchant,
+ EndRod,
+ EntityEffect,
+ ExplosionEmitter,
+ Explosion,
+ FallingDust {
+ block_state: protocol::VarInt,
+ },
+ Firework,
+ Fishing,
+ Flame,
+ HappyVillager,
+ Heart,
+ InstantEffect,
+ Item {
+ item: Option,
+ },
+ ItemSlime,
+ ItemSnowball,
+ LargeSmoke,
+ Lava,
+ Mycelium,
+ Note,
+ Poof,
+ Portal,
+ Rain,
+ Smoke,
+ Spit,
+ SquidInk,
+ SweepAttack,
+ TotemOfUndying,
+ Underwater,
+ Splash,
+ Witch,
+ BubblePop,
+ CurrentDown,
+ BubbleColumnUp,
+ Nautilus,
+ Dolphin,
+}
+
+impl Serializable for ParticleData {
+ fn read_from(buf: &mut R) -> Result {
+ let id = protocol::VarInt::read_from(buf)?.0;
+ Ok(match id {
+ 0 => ParticleData::AmbientEntityEffect,
+ 1 => ParticleData::AngryVillager,
+ 2 => ParticleData::Barrier,
+ 3 => ParticleData::Block {
+ block_state: Serializable::read_from(buf)?
+ },
+ 4 => ParticleData::Bubble,
+ 5 => ParticleData::Cloud,
+ 6 => ParticleData::Crit,
+ 7 => ParticleData::DamageIndicator,
+ 8 => ParticleData::DragonBreath,
+ 9 => ParticleData::DrippingLava,
+ 10 => ParticleData::DrippingWater,
+ 11 => ParticleData::Dust {
+ red: Serializable::read_from(buf)?,
+ green: Serializable::read_from(buf)?,
+ blue: Serializable::read_from(buf)?,
+ scale: Serializable::read_from(buf)?,
+ },
+ 12 => ParticleData::Effect,
+ 13 => ParticleData::ElderGuardian,
+ 14 => ParticleData::EnchantedHit,
+ 15 => ParticleData::Enchant,
+ 16 => ParticleData::EndRod,
+ 17 => ParticleData::EntityEffect,
+ 18 => ParticleData::ExplosionEmitter,
+ 19 => ParticleData::Explosion,
+ 20 => ParticleData::FallingDust {
+ block_state: Serializable::read_from(buf)?,
+ },
+ 21 => ParticleData::Firework,
+ 22 => ParticleData::Fishing,
+ 23 => ParticleData::Flame,
+ 24 => ParticleData::HappyVillager,
+ 25 => ParticleData::Heart,
+ 26 => ParticleData::InstantEffect,
+ 27 => ParticleData::Item {
+ item: Serializable::read_from(buf)?,
+ },
+ 28 => ParticleData::ItemSlime,
+ 29 => ParticleData::ItemSnowball,
+ 30 => ParticleData::LargeSmoke,
+ 31 => ParticleData::Lava,
+ 32 => ParticleData::Mycelium,
+ 33 => ParticleData::Note,
+ 34 => ParticleData::Poof,
+ 35 => ParticleData::Portal,
+ 36 => ParticleData::Rain,
+ 37 => ParticleData::Smoke,
+ 38 => ParticleData::Spit,
+ 39 => ParticleData::SquidInk,
+ 40 => ParticleData::SweepAttack,
+ 41 => ParticleData::TotemOfUndying,
+ 42 => ParticleData::Underwater,
+ 43 => ParticleData::Splash,
+ 44 => ParticleData::Witch,
+ 45 => ParticleData::BubblePop,
+ 46 => ParticleData::CurrentDown,
+ 47 => ParticleData::BubbleColumnUp,
+ 48 => ParticleData::Nautilus,
+ 49 => ParticleData::Dolphin,
+ _ => panic!("unrecognized particle data id {}", id),
+ })
+ }
+
+ fn write_to(&self, _buf: &mut W) -> Result<(), protocol::Error> {
+ unimplemented!()
+ }
}
pub trait MetaValue {
@@ -408,6 +690,19 @@ impl MetaValue for format::Component {
}
}
+impl MetaValue for LenPrefixed {
+ fn unwrap(value: &Value) -> &Self {
+ match *value {
+ Value::OptionalFormatComponent(ref val) => val,
+ _ => panic!("incorrect key"),
+ }
+ }
+ fn wrap(self) -> Value {
+ Value::OptionalFormatComponent(self)
+ }
+}
+
+
impl MetaValue for Option {
fn unwrap(value: &Value) -> &Self {
match *value {
diff --git a/src/world/mod.rs b/src/world/mod.rs
index 9ded72a..9900846 100644
--- a/src/world/mod.rs
+++ b/src/world/mod.rs
@@ -43,6 +43,8 @@ pub struct World {
light_updates: VecDeque,
block_entity_actions: VecDeque,
+
+ protocol_version: i32,
}
#[derive(Clone, Debug)]
@@ -79,7 +81,12 @@ struct LightUpdate {
}
impl World {
- pub fn new() -> World { Default::default() }
+ pub fn new(protocol_version: i32) -> World {
+ World {
+ protocol_version,
+ ..Default::default()
+ }
+ }
pub fn is_chunk_loaded(&self, x: i32, z: i32) -> bool {
self.chunks.contains_key(&CPos(x, z))
@@ -612,7 +619,7 @@ impl World {
for bi in 0 .. 4096 {
let id = data.read_u16::()?;
- section.blocks.set(bi, block::Block::by_vanilla_id(id as usize));
+ section.blocks.set(bi, block::Block::by_vanilla_id(id as usize, self.protocol_version));
// Spawn block entities
let b = section.blocks.get(bi);
@@ -798,7 +805,7 @@ impl World {
for bi in 0 .. 4096 {
let id = ((block_add[i].get(bi) as u16) << 12) | ((block_types[i][bi] as u16) << 4) | (block_meta[i].get(bi) as u16);
- section.blocks.set(bi, block::Block::by_vanilla_id(id as usize));
+ section.blocks.set(bi, block::Block::by_vanilla_id(id as usize, self.protocol_version));
// Spawn block entities
let b = section.blocks.get(bi);
@@ -870,7 +877,7 @@ impl World {
let count = VarInt::read_from(&mut data)?.0;
for i in 0 .. count {
let id = VarInt::read_from(&mut data)?.0;
- let bl = block::Block::by_vanilla_id(id as usize);
+ let bl = block::Block::by_vanilla_id(id as usize, self.protocol_version);
mappings.insert(i as usize, bl);
}
}
@@ -880,7 +887,7 @@ impl World {
for bi in 0 .. 4096 {
let id = m.get(bi);
- section.blocks.set(bi, mappings.get(&id).cloned().unwrap_or(block::Block::by_vanilla_id(id)));
+ section.blocks.set(bi, mappings.get(&id).cloned().unwrap_or(block::Block::by_vanilla_id(id, self.protocol_version)));
// Spawn block entities
let b = section.blocks.get(bi);
if block_entity::BlockEntityType::get_block_entity(b).is_some() {