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 player combat.rs
use common::entity::mob::MobKind;
use common::entity::vehicle::VehicleKind;
use common::inventory::take;
use common::item::{Item, ItemKind, ItemStack};
use common::world::{Block, shape};
use glam::DVec3;
use super::ServerPlayer;
use crate::entity::Kind;
use crate::level::{DroppedItem, Level};
impl ServerPlayer {
pub(super) fn use_entity(&mut self, level: &mut Level, id: i32, attack: bool) {
if self.window != 0 || self.died {
return;
}
let Some(source) = self.entity(level).cloned() else {
return;
};
let Some(target) = level.entities.get(id) else {
return;
};
if target.entity.dead
|| (!matches!(
target.kind,
Kind::Painting { .. } | Kind::Fireball(_) | Kind::Boat(_) | Kind::Cart(_)
) && target.entity.living.as_ref().is_none_or(|l| l.health <= 0))
|| id == source.id
{
return;
}
let eye = self.eye(level);
let point = target.entity.pos + DVec3::Y * f64::from(target.entity.eye_height());
if source.pos.distance_squared(target.entity.pos) >= 36.0
|| shape::ray_trace(level, eye, point, false, false).is_some()
{
return;
}
if attack {
let living_target = target.entity.living.is_some();
let held = self.inventory.held();
let mut damage = held.map_or(1, |stack| stack.damage_vs_entity());
if source.motion.y < 0.0 {
damage += 1;
}
level.attack_entity(id, Some(&source), damage);
if living_target {
self.send_stat(2020, damage);
}
if let Some(target) = level
.entities
.get(id)
.filter(|e| e.entity.living.as_ref().is_some_and(|l| l.health > 0))
.map(|e| e.entity.clone())
{
level.alert_wolves(&source, &self.name, &target, false);
}
let wear = match held.and_then(|s| s.item.properties()).map(|p| p.kind) {
Some(ItemKind::Sword(_)) => 1,
Some(ItemKind::Tool(..)) => 2,
_ => 0,
};
if living_target {
if wear > 0
&& let Some(stack) = held
{
self.used_item(stack);
}
self.damage_held(wear);
}
// `ItemSaddle.hitEntity` saddles a pig as well
if held.is_some_and(|stack| stack.item == Item::SADDLE)
&& let Some(target) = level.entities.get_mut(id)
&& let Kind::Mob(mob) = &mut target.kind
&& mob.kind == MobKind::Pig
&& !mob.saddled
{
mob.saddled = true;
let chunk = target.chunk();
level.mark_entity_chunk(chunk);
take(self.inventory.held_mut(), 1);
}
} else {
let Some(mut stored) = level.entities.take_for_tick(id) else {
return;
};
if let Kind::Mob(mob) = &mut stored.kind
&& mob.kind == MobKind::Wolf
&& let Some(consume) =
level.use_wolf(&mut stored.entity, mob, &self.name, self.inventory.held())
{
if consume {
take(self.inventory.held_mut(), 1);
}
level.mark_entity_chunk(stored.chunk());
level.entities.finish_tick(stored);
return self.sync_container(level);
}
let rider = stored.entity.rider;
// Vehicles only refuse another player; a mob passenger is thrown out
let seat_free = rider.is_none_or(|rider| {
rider == self.entity_id
|| !level
.entities
.get(rider)
.is_some_and(|e| matches!(e.kind, Kind::Player { .. }))
});
let saddle_free = rider.is_none_or(|rider| rider == self.entity_id);
let ride = match &mut stored.kind {
Kind::Cart(cart) => {
let kind = cart.kind;
if kind == VehicleKind::PoweredCart {
if self.inventory.held().is_some_and(|s| s.item == Item::COAL) {
take(self.inventory.held_mut(), 1);
cart.fuel += 1200;
}
cart.push = (stored.entity.pos - source.pos).with_y(0.0);
level.mark_entity_chunk(stored.chunk());
}
kind == VehicleKind::Minecart && seat_free
}
Kind::Boat(_) => seat_free,
Kind::Mob(mob) if mob.kind == MobKind::Pig && mob.saddled && saddle_free => true,
kind => {
if let Kind::Mob(mob) = kind
&& let Some(held) = self.inventory.held()
{
match (mob.kind, held.item) {
(MobKind::Cow, Item::BUCKET) => {
*self.inventory.held_mut() = Some(ItemStack::one(Item::MILK_BUCKET))
}
(MobKind::Sheep, Item::SHEARS) if !mob.sheared => {
mob.sheared = true;
for _ in 0..2 + stored.entity.rand.next_int(3) {
// `EntitySheep.interact` pushes each wool item a little further
let random = &mut stored.entity.rand;
let lift = f64::from(random.next_float() * 0.05);
let mut spread = || {
f64::from((random.next_float() - random.next_float()) * 0.1)
};
let impulse = DVec3::new(spread(), lift, spread());
let pos = stored.entity.pos + DVec3::Y;
let wool = Item::from_block(Block::WOOL);
level.dropped_items.push(DroppedItem {
x: pos.x,
y: pos.y,
z: pos.z,
stack: ItemStack::new(wool, 1, i32::from(mob.color)),
motion: None,
impulse,
pickup_delay: 10,
});
}
self.damage_held(1);
}
(MobKind::Sheep, Item::DYE)
if !mob.sheared && mob.color != (!held.damage as u8 & 15) =>
{
mob.color = !held.damage as u8 & 15;
take(self.inventory.held_mut(), 1);
}
(MobKind::Pig, Item::SADDLE) if !mob.saddled => {
mob.saddled = true;
take(self.inventory.held_mut(), 1);
}
_ => {}
}
}
// The client asks for the generic item use separately
level.mark_entity_chunk(stored.chunk());
false
}
};
let storage =
matches!(&stored.kind, Kind::Cart(cart) if cart.kind == VehicleKind::StorageCart);
level.entities.finish_tick(stored);
if storage {
self.open_cart(level, id);
} else if ride {
self.mount(level, Some(id));
}
}
self.sync_container(level);
}
}