Theme

alex/beta-minecraft-rust

Minecraft Beta 1.7.3 Rewrite

BRANCH main
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
Download file

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())
    }
}