alex/beta-minecraft-rust
Minecraft Beta 1.7.3 Rewrite
BRANCH main
SNAPSHOT Draw terrain GPU-driven from packed per-section buffers with GPU occlusion culling, add a render distance slider (2-128) with a quad/disk shape, cut chunk bookkeeping at high render distances, and fix a light underflow at y=0
DOWNLOAD
root crates server src entity nbt.rs
use common::entity::mob::MobKind;
use common::entity::painting::Painting;
use common::item::ItemStack;
use common::nbt::{Compound, Tag};
use common::world::Block;
use glam::DVec3;
use super::{Arrow, Boat, Cart, Entities, Fireball, Kind, Mob, ServerEntity, Stuck, Thrown};
impl Stuck {
fn read(nbt: &Compound) -> Self {
Self {
tile: [nbt.short("xTile"), nbt.short("yTile"), nbt.short("zTile")].map(i32::from),
block: Block(nbt.byte("inTile") as u8),
grounded: nbt.bool("inGround"),
..Default::default()
}
}
fn write_tile(&self, nbt: &mut Compound) {
for (key, value) in ["xTile", "yTile", "zTile"].into_iter().zip(self.tile) {
nbt.set_short(key, value as i16);
}
nbt.set_byte("inTile", self.block.0 as i8);
}
}
impl ServerEntity {
pub fn read(nbt: &Compound, entities: &mut Entities) -> Option<Self> {
let shake = i32::from(nbt.byte("shake") as u8);
let kind = match nbt.string("id") {
"Item" => {
let stack = ItemStack::read_nbt(nbt.compound("Item")?);
if stack.count <= 0 {
return None;
}
Kind::Item {
stack,
age: i32::from(nbt.short("Age")),
health: i32::from(nbt.short("Health")) & 255,
pickup_delay: 0,
}
}
"FallingSand" => Kind::Falling {
block: Block(nbt.byte("Tile") as u8),
age: 0,
},
"Painting" => Kind::Painting {
painting: Painting::from_name(
nbt.string("Motive"),
[nbt.int("TileX"), nbt.int("TileY"), nbt.int("TileZ")],
nbt.byte("Dir") as u8,
),
check: 0,
},
"PrimedTnt" => Kind::Tnt {
fuse: i32::from(nbt.byte("Fuse")),
},
"Minecart" => Kind::Cart(Cart::read(nbt)),
"Boat" => Kind::Boat(Boat::default()),
"Fireball" => Kind::Fireball(Fireball {
stuck: Stuck::read(nbt),
shake,
..Default::default()
}),
"Snowball" => Kind::Thrown(Thrown {
egg: false,
owner: None,
air_ticks: 0,
}),
"Arrow" => Kind::Arrow(Arrow {
stuck: Stuck::read(nbt),
metadata: nbt.byte("inData") as u8,
shake,
player: nbt.bool("player"),
..Default::default()
}),
name => {
let mut mob = Mob::new(MobKind::from_name(name)?);
mob.color = nbt.byte("Color") as u8 & 15;
mob.sheared = nbt.bool("Sheared");
mob.saddled = nbt.bool("Saddle");
mob.powered = nbt.bool("powered");
mob.dropped = nbt.contains("Health") && nbt.short("Health") <= 0;
Kind::Mob(mob)
}
};
let [Tag::Double(x), Tag::Double(y), Tag::Double(z)] = nbt.list("Pos") else {
return None;
};
let pos = DVec3::new(*x, *y, *z);
if !pos.is_finite() {
return None;
}
let mut stored = entities.create(kind);
let entity = &mut stored.entity;
if let Kind::Mob(mob) = &mut stored.kind {
let living = entity.living.as_mut()?;
living.health = if nbt.contains("Health") {
i32::from(nbt.short("Health"))
} else {
10
};
living.hurt_time = i32::from(nbt.short("HurtTime"));
living.death_time = i32::from(nbt.short("DeathTime"));
living.attack_time = i32::from(nbt.short("AttackTime"));
// `EntityChicken`: the egg countdown is not saved but drawn on construction
if mob.kind == MobKind::Chicken {
mob.egg_timer += entity.rand.next_int(6000);
}
mob.special.read(entity, nbt);
}
entity.set_position(pos);
entity.prev_pos = pos;
entity.last_tick_pos = pos;
if let [Tag::Double(x), Tag::Double(y), Tag::Double(z)] = nbt.list("Motion") {
entity.motion = DVec3::new(*x, *y, *z).map(|v| {
if v.is_finite() && v.abs() <= 10.0 {
v
} else {
0.0
}
});
}
if let [Tag::Float(yaw), Tag::Float(pitch)] = nbt.list("Rotation") {
entity.set_rotation(*yaw, *pitch);
entity.prev_yaw = entity.yaw;
entity.prev_pitch = entity.pitch;
}
if let Kind::Painting { painting, .. } = &stored.kind {
let yaw = f32::from(painting.direction) * 90.0;
entity.set_location_and_angles(painting.position(), yaw, 0.0);
entity.bb = painting.bounds();
}
entity.fall_distance = nbt.float("FallDistance");
entity.fire = i32::from(nbt.short("Fire"));
entity.air = i32::from(nbt.short("Air"));
entity.on_ground = nbt.bool("OnGround");
stored.extra = nbt.clone();
Some(stored)
}
pub fn write(&self) -> Option<Compound> {
if self.entity.dead {
return None;
}
let mut nbt = self.extra.clone();
match &self.kind {
Kind::Player { .. } | Kind::Hook(_) => return None,
Kind::Painting { painting, .. } => {
nbt.set_string("id", "Painting");
nbt.set_string("Motive", painting.art().name);
nbt.set_byte("Dir", painting.direction as i8);
for (key, coordinate) in
["TileX", "TileY", "TileZ"].into_iter().zip(painting.anchor)
{
nbt.set_int(key, coordinate);
}
}
Kind::Boat(_) => nbt.set_string("id", "Boat"),
Kind::Cart(cart) => {
nbt.set_string("id", "Minecart");
cart.write(&mut nbt);
}
Kind::Tnt { fuse } => {
nbt.set_string("id", "PrimedTnt");
nbt.set_byte("Fuse", *fuse as i8);
}
Kind::Item {
stack, age, health, ..
} => {
nbt.set_string("id", "Item");
let mut item = Compound::default();
stack.write_nbt(&mut item);
nbt.set_compound("Item", item);
nbt.set_short("Age", *age as i16);
nbt.set_short("Health", *health as i16);
}
Kind::Falling { block, .. } => {
nbt.set_string("id", "FallingSand");
nbt.set_byte("Tile", block.0 as i8);
}
Kind::Mob(mob) => {
nbt.set_string("id", mob.kind.name());
mob.special.write(&mut nbt);
match mob.kind {
MobKind::Sheep => {
nbt.set_byte("Color", mob.color as i8);
nbt.set_bool("Sheared", mob.sheared);
}
MobKind::Pig => nbt.set_bool("Saddle", mob.saddled),
MobKind::Creeper => nbt.set_bool("powered", mob.powered),
_ => {}
}
if let Some(living) = &self.entity.living {
nbt.set_short("Health", living.health as i16);
nbt.set_short("HurtTime", living.hurt_time as i16);
nbt.set_short("DeathTime", living.death_time as i16);
nbt.set_short("AttackTime", living.attack_time as i16);
}
}
Kind::Thrown(thrown) => {
if thrown.egg {
return None;
}
nbt.set_string("id", "Snowball");
for key in ["xTile", "yTile", "zTile"] {
if !nbt.contains(key) {
nbt.set_short(key, -1);
}
}
for key in ["inTile", "shake", "inGround"] {
if !nbt.contains(key) {
nbt.set_byte(key, 0);
}
}
}
Kind::Fireball(fireball) => {
nbt.set_string("id", "Fireball");
fireball.stuck.write_tile(&mut nbt);
nbt.set_byte("shake", fireball.shake as i8);
nbt.set_bool("inGround", fireball.stuck.grounded);
}
Kind::Arrow(arrow) => {
nbt.set_string("id", "Arrow");
arrow.stuck.write_tile(&mut nbt);
nbt.set_byte("inData", arrow.metadata as i8);
nbt.set_byte("shake", arrow.shake as i8);
nbt.set_bool("inGround", arrow.stuck.grounded);
nbt.set_bool("player", arrow.player);
}
}
let entity = &self.entity;
nbt.set_list(
"Pos",
vec![
Tag::Double(entity.pos.x),
Tag::Double(entity.pos.y + f64::from(entity.y_size)),
Tag::Double(entity.pos.z),
],
);
nbt.set_list("Motion", entity.motion.to_array().map(Tag::Double).to_vec());
nbt.set_list(
"Rotation",
vec![Tag::Float(entity.yaw), Tag::Float(entity.pitch)],
);
nbt.set_float("FallDistance", entity.fall_distance);
nbt.set_short("Fire", entity.fire as i16);
nbt.set_short("Air", entity.air as i16);
nbt.set_bool("OnGround", entity.on_ground);
Some(nbt)
}
}