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 sleep.rs
//! Beds: `BlockBed.blockActivated` and the sleeping of `EntityPlayer` and `EntityPlayerMP`.
use common::entity::Entity;
use common::entity::player::{get_up, lie_down};
use common::protocol::Packet;
use common::world::{Block, ChunkPos, bed};
use glam::{DVec3, Vec3};
use super::ServerPlayer;
use crate::entity::Kind;
use crate::level::Level;
/// `EnumStatus`: why a player could not sleep.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
enum SleepStatus {
Ok,
NotPossibleNow,
TooFarAway,
OtherProblem,
}
/// `BlockBed.setBedOccupied`.
fn set_occupied(level: &mut Level, [x, y, z]: [i32; 3], occupied: bool) {
let metadata = (level.metadata(x, y, z) & !4) | (u8::from(occupied) << 2);
level.set_metadata_notify(x, y, z, metadata);
}
/// `EntityPlayer.wakeUpPlayer` without packets: frees the bed and stands up beside it.
pub(super) fn leave_bed(level: &mut Level, entity: &mut Entity, reset_timer: bool) {
if let Some(bed @ [x, y, z]) = entity.sleep.bed
&& level.block(x, y, z) == Block::BED
{
set_occupied(level, bed, false);
}
get_up(entity, level, 0.0, reset_timer);
}
/// `EntityPlayer.verifyRespawnCoordinates`: where to respawn at the bed, if it is still usable.
fn respawn_spot(level: &mut Level, [x, y, z]: [i32; 3]) -> Option<[i32; 3]> {
for (dx, dz) in [(-3, -3), (3, -3), (-3, 3), (3, 3)] {
level.load_chunk(ChunkPos::containing(x + dx, z + dz));
}
if level.block(x, y, z) != Block::BED {
return None;
}
bed::standing_spot(level, [x, y, z], 0)
}
impl ServerPlayer {
/// `BlockBed.blockActivated`.
pub(super) fn use_bed(&mut self, level: &mut Level, [mut x, y, mut z]: [i32; 3]) {
let mut metadata = level.metadata(x, y, z);
if !bed::is_head(metadata) {
let [dx, dz] = bed::HEAD_OFFSETS[bed::direction(metadata)];
(x, z) = (x + dx, z + dz);
if level.block(x, y, z) != Block::BED {
return;
}
metadata = level.metadata(x, y, z);
}
if !level.has_sky() {
level.set_block_notify(x, y, z, Block::AIR);
let [dx, dz] = bed::HEAD_OFFSETS[bed::direction(metadata)];
let (x, z) = (x + dx, z + dz);
if level.block(x, y, z) == Block::BED {
level.set_block_notify(x, y, z, Block::AIR);
}
let center = DVec3::new(f64::from(x), f64::from(y), f64::from(z)) + 0.5;
level.explode(center, 5.0, None, true);
return;
}
let position = [x, y, z];
if bed::is_occupied(metadata) {
let occupied = level.entities.iter().any(|e| {
matches!(e.kind, Kind::Player { .. })
&& e.entity.sleep.sleeping
&& e.entity.sleep.bed == Some(position)
});
if occupied {
self.chat("This bed is occupied");
return;
}
set_occupied(level, position, false);
}
match self.go_to_sleep(level, position) {
SleepStatus::Ok => set_occupied(level, position, true),
SleepStatus::NotPossibleNow => self.chat("You can only sleep at night"),
SleepStatus::TooFarAway | SleepStatus::OtherProblem => {}
}
}
fn chat(&mut self, message: &str) {
self.send(Packet::chat(message));
}
/// `EntityPlayerMP.goToSleep`.
fn go_to_sleep(&mut self, level: &mut Level, bed: [i32; 3]) -> SleepStatus {
let daytime = level.is_daytime();
let Some(mut stored) = level.entities.take_for_tick(self.entity_id) else {
return SleepStatus::OtherProblem;
};
let entity = &mut stored.entity;
let offset = entity.pos - DVec3::from_array(bed.map(f64::from));
let status = if entity.sleep.sleeping
|| entity.dead
|| entity.living.as_ref().is_none_or(|l| l.health <= 0)
{
SleepStatus::OtherProblem
} else if daytime {
SleepStatus::NotPossibleNow
} else if offset.x.abs() > 3.0 || offset.y.abs() > 2.0 || offset.z.abs() > 3.0 {
SleepStatus::TooFarAway
} else {
lie_down(entity, level, bed);
SleepStatus::Ok
};
let (pos, yaw, pitch) = (entity.pos, entity.yaw, entity.pitch);
level.entities.finish_tick(stored);
if status == SleepStatus::Ok {
self.mining = None;
self.teleport(level, pos, yaw, pitch);
let [x, y, z] = bed;
self.send(Packet::Sleep {
entity: self.entity_id,
status: 0,
x,
y: y as i8,
z,
});
}
status
}
pub(crate) fn sleeping(&self, level: &Level) -> bool {
self.entity(level).is_some_and(|e| e.sleep.sleeping)
}
pub(crate) fn fully_asleep(&self, level: &Level) -> bool {
self.entity(level).is_some_and(|e| e.sleep.fully_asleep())
}
/// `EntityPlayerMP.wakeUpPlayer`.
pub fn wake_up(&mut self, level: &mut Level, reset_timer: bool, set_spawn: bool) {
let Some(sleep) = self.entity(level).map(|e| e.sleep) else {
return;
};
if sleep.sleeping {
self.send(Packet::Animation {
entity: self.entity_id,
animation: 3,
});
}
let Some(mut stored) = level.entities.take_for_tick(self.entity_id) else {
return;
};
leave_bed(level, &mut stored.entity, reset_timer);
let (pos, yaw, pitch) = (stored.entity.pos, stored.entity.yaw, stored.entity.pitch);
level.entities.finish_tick(stored);
if set_spawn {
self.bed_spawn = sleep.bed;
}
self.teleport(level, pos, yaw, pitch);
}
/// The sleeping part of `EntityPlayer.onUpdate`: gets up when hurt, when the bed is gone
/// or in the morning.
pub(super) fn tick_sleep(&mut self, level: &mut Level) {
let Some(entity) = level
.entities
.get_mut(self.entity_id)
.map(|e| &mut e.entity)
else {
return;
};
let disturbed = std::mem::take(&mut entity.sleep.disturbed);
entity.sleep.tick();
let sleep = entity.sleep;
if !sleep.sleeping {
return;
}
let [x, y, z] = sleep.bed.unwrap_or_default();
if disturbed || level.block(x, y, z) != Block::BED {
self.wake_up(level, true, false);
} else if level.is_daytime() {
self.wake_up(level, false, true);
}
}
/// The bed respawn of `ServerConfigurationManager.recreatePlayerEntity`, if it is still valid.
pub(super) fn bed_respawn(&mut self, level: &mut Level) -> Option<DVec3> {
let spot = respawn_spot(level, self.bed_spawn?);
if spot.is_none() {
self.bed_spawn = None;
self.send(Packet::GameState { state: 0 });
}
spot.map(|[x, y, z]| Vec3::new(x as f32 + 0.5, y as f32 + 0.1, z as f32 + 0.5).as_dvec3())
}
}