mc173/serde/chunk/
block_entity_nbt.rs1use glam::IVec3;
4
5use crate::block_entity::note_block::NoteBlockBlockEntity;
6use crate::block_entity::dispenser::DispenserBlockEntity;
7use crate::block_entity::furnace::FurnaceBlockEntity;
8use crate::block_entity::jukebox::JukeboxBlockEntity;
9use crate::block_entity::spawner::SpawnerBlockEntity;
10use crate::block_entity::piston::PistonBlockEntity;
11use crate::block_entity::chest::ChestBlockEntity;
12use crate::block_entity::sign::SignBlockEntity;
13use crate::block_entity::BlockEntity;
14use crate::entity::EntityKind;
15use crate::item::ItemStack;
16use crate::geom::Face;
17
18use crate::serde::nbt::{NbtParseError, NbtCompound, NbtCompoundParse};
19
20use super::entity_kind_nbt;
21use super::slot_nbt;
22
23pub fn from_nbt(comp: NbtCompoundParse) -> Result<(IVec3, Box<BlockEntity>), NbtParseError> {
24
25 let x = comp.get_int("x")?;
26 let y = comp.get_int("y")?;
27 let z = comp.get_int("z")?;
28
29 let id = comp.get_string("id")?;
30 let block_entity = Box::new(match id {
31 "Chest" => {
32 let mut chest = ChestBlockEntity::default();
33 slot_nbt::from_nbt_to_inv(comp.get_list("Items")?, &mut chest.inv[..])?;
34 BlockEntity::Chest(chest)
35 }
36 "Furnace" => {
37 let mut inv = [ItemStack::EMPTY; 3];
38 slot_nbt::from_nbt_to_inv(comp.get_list("Items")?, &mut inv[..])?;
39 let mut furnace = FurnaceBlockEntity::default();
40 furnace.input_stack = inv[0];
41 furnace.fuel_stack = inv[1];
42 furnace.output_stack = inv[2];
43 furnace.burn_remaining_ticks = comp.get_short("BurnTime")?.max(0) as u16;
44 furnace.smelt_ticks = comp.get_short("CookTime")?.max(0) as u16;
45 BlockEntity::Furnace(furnace)
47 }
48 "Trap" => {
49 let mut dispenser = DispenserBlockEntity::default();
50 slot_nbt::from_nbt_to_inv(comp.get_list("Items")?, &mut dispenser.inv[..])?;
51 BlockEntity::Dispenser(dispenser)
52 }
53 "MobSpawner" => {
54 let mut spawner = SpawnerBlockEntity::default();
55 spawner.entity_kind = entity_kind_nbt::from_nbt(comp.get_string("EntityId")?).unwrap_or(EntityKind::Pig);
56 spawner.remaining_time = comp.get_short("Delay")? as u16;
57 BlockEntity::Spawner(spawner)
58 }
59 "Music" => {
60 let mut note_block = NoteBlockBlockEntity::default();
61 note_block.note = comp.get_byte("note")? as u8;
62 BlockEntity::NoteBlock(note_block)
63 }
64 "Piston" => {
65 let mut piston = PistonBlockEntity::default();
66 piston.block = comp.get_int("blockId")? as u8;
67 piston.metadata = comp.get_int("blockData")? as u8;
68 piston.face = match comp.get_int("facing")? {
69 0 => Face::NegY,
70 1 => Face::PosY,
71 2 => Face::NegZ,
72 3 => Face::PosZ,
73 4 => Face::NegX,
74 _ => Face::PosX,
75 };
76 piston.progress = comp.get_float("progress")?;
77 piston.extending = comp.get_boolean("extending")?;
78 BlockEntity::Piston(piston)
79 }
80 "Sign" => {
81 let mut sign = SignBlockEntity::default();
82 for (i, key) in ["Text1", "Text2", "Text3", "Text4"].into_iter().enumerate() {
83 sign.lines[i] = comp.get_string(key)?.to_string();
84 }
85 BlockEntity::Sign(sign)
86 }
87 "RecordPlayer" => {
88 BlockEntity::Jukebox(JukeboxBlockEntity {
89 record: comp.get_int("Record")? as u32
90 })
91 }
92 _ => return Err(NbtParseError::new(format!("{}/id", comp.path()), "valid block entity id"))
93 });
94
95 Ok((IVec3::new(x, y, z), block_entity))
96
97}
98
99pub fn to_nbt<'a>(comp: &'a mut NbtCompound, pos: IVec3, block_entity: &BlockEntity) -> &'a mut NbtCompound {
100
101 comp.insert("x", pos.x);
102 comp.insert("y", pos.y);
103 comp.insert("z", pos.z);
104
105 match block_entity {
106 BlockEntity::Chest(chest) => {
107 comp.insert("id", "Chest");
108 comp.insert("Items", slot_nbt::to_nbt_from_inv(&chest.inv[..]));
109 }
110 BlockEntity::Furnace(furnace) => {
111 comp.insert("id", "Furnace");
112 comp.insert("Items", slot_nbt::to_nbt_from_inv(&[furnace.input_stack, furnace.fuel_stack, furnace.output_stack]));
113 comp.insert("BurnTime", furnace.burn_remaining_ticks);
114 comp.insert("CookTime", furnace.smelt_ticks);
115 }
116 BlockEntity::Dispenser(dispenser) => {
117 comp.insert("id", "Trap");
118 comp.insert("Items", slot_nbt::to_nbt_from_inv(&dispenser.inv[..]));
119 }
120 BlockEntity::Spawner(spawner) => {
121 comp.insert("id", "MobSpawner");
122 comp.insert("EntityId", entity_kind_nbt::to_nbt(spawner.entity_kind).unwrap_or(format!("Pig")));
123 comp.insert("Delay", spawner.remaining_time.min(i16::MAX as _) as i16);
124 }
125 BlockEntity::NoteBlock(note_block) => {
126 comp.insert("id", "Music");
127 comp.insert("note", note_block.note);
128 }
129 BlockEntity::Piston(piston) => {
130 comp.insert("id", "Piston");
131 comp.insert("blockId", piston.block as u32);
132 comp.insert("blockData", piston.metadata as u32);
133 comp.insert("facing", match piston.face {
134 Face::NegY => 0i32,
135 Face::PosY => 1,
136 Face::NegZ => 2,
137 Face::PosZ => 3,
138 Face::NegX => 4,
139 Face::PosX => 5,
140 });
141 comp.insert("progress", piston.progress);
142 comp.insert("extending", piston.extending);
143 }
144 BlockEntity::Sign(sign) => {
145 comp.insert("id", "Sign");
146 for (i, key) in ["Text1", "Text2", "Text3", "Text4"].into_iter().enumerate() {
147 comp.insert(key, sign.lines[i].as_str());
148 }
149 }
150 BlockEntity::Jukebox(jukebox) => {
151 comp.insert("id", "RecordPlayer");
152 comp.insert("Record", jukebox.record);
153 }
154 }
155
156 comp
157
158}