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 / level / updates.rs

use common::protocol::Packet;
use common::world::chunk::{self, HEIGHT};
use common::world::{Chunk, ChunkPos};

/// Port of the change batching of `PlayerInstance`: up to ten distinct positions and their
/// bounding box. Ten or more changes resend the whole box instead.
#[derive(Clone, Copy, Default)]
pub struct BlockUpdates {
    /// `x << 12 | z << 8 | y`, chunk-local.
    positions: [u16; 10],
    count: usize,
    min: [u8; 3],
    max: [u8; 3],
}

impl BlockUpdates {
    /// `markBlockNeedsUpdate` with chunk-local coordinates.
    pub fn mark(&mut self, x: u8, y: u8, z: u8) {
        let p = [x, y, z];
        if self.count == 0 {
            self.min = p;
            self.max = p;
        }
        for (axis, &v) in p.iter().enumerate() {
            self.min[axis] = self.min[axis].min(v);
            self.max[axis] = self.max[axis].max(v);
        }
        if self.count < self.positions.len() {
            let packed = u16::from(x) << 12 | u16::from(z) << 8 | u16::from(y);
            if !self.positions[..self.count].contains(&packed) {
                self.positions[self.count] = packed;
                self.count += 1;
            }
        }
    }

    /// The packets that bring a client up to date: one change, a batch, or the changed box.
    pub fn packets(&self, chunk: &Chunk) -> Vec<Packet> {
        let ChunkPos { x: cx, z: cz } = chunk.pos;
        let entry = |packed: u16| {
            let (x, z, y) = (
                usize::from(packed >> 12),
                usize::from(packed >> 8 & 15),
                usize::from(packed & 255),
            );
            let index = chunk::index(x, y, z);
            (
                chunk.blocks().get(index).0,
                chunk.metadata.get(index),
                [x, y, z],
            )
        };
        match self.count {
            0 => Vec::new(),
            1 => {
                let (block, metadata, [x, y, z]) = entry(self.positions[0]);
                vec![Packet::BlockChange {
                    x: cx * 16 + x as i32,
                    y: y as u8,
                    z: cz * 16 + z as i32,
                    block,
                    metadata,
                }]
            }
            10 => {
                let min_y = usize::from(self.min[1]) / 2 * 2;
                let max_y = ((usize::from(self.max[1]) / 2 + 1) * 2).min(HEIGHT);
                let xs = usize::from(self.min[0])..usize::from(self.max[0]) + 1;
                let zs = usize::from(self.min[2])..usize::from(self.max[2]) + 1;
                let mut data = Vec::new();
                chunk.write_region(&mut data, xs.clone(), min_y..max_y, zs.clone());
                vec![Packet::MapChunk {
                    x: cx * 16 + xs.start as i32,
                    y: min_y as i16,
                    z: cz * 16 + zs.start as i32,
                    size: [
                        (xs.len() - 1) as u8,
                        (max_y - min_y - 1) as u8,
                        (zs.len() - 1) as u8,
                    ],
                    data: data.into(),
                }]
            }
            count => vec![Packet::MultiBlockChange {
                chunk_x: cx,
                chunk_z: cz,
                changes: self.positions[..count]
                    .iter()
                    .map(|&packed| {
                        let (block, metadata, _) = entry(packed);
                        (packed as i16, block, metadata)
                    })
                    .collect(),
            }],
        }
    }
}