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 maps.rs
use common::item::Item;
use common::map::material_color;
use common::math::floor_f64;
use common::protocol::Packet;
use common::world::{Block, ChunkPos};
use glam::DVec3;
use super::ServerPlayer;
use crate::level::Level;
use crate::save::maps::{Holder, StoredMap};
pub(super) struct Tracking {
revisions: [u64; 128],
next: usize,
calls: u32,
icons: Vec<u8>,
}
impl Default for Tracking {
fn default() -> Self {
Self {
revisions: [u64::MAX; 128],
next: 0,
calls: 0,
icons: Vec::new(),
}
}
}
impl ServerPlayer {
pub(super) fn create_map(&self, level: &mut Level) -> Option<i32> {
let [x, _, z] = self.position(level).to_array().map(floor_f64);
let result = level.maps.lock().unwrap().create([x, z], level.dimension());
let id = result.map_err(|error| level.fail_storage(error)).ok()?;
Some(i32::from(id))
}
pub(super) fn tick_maps(&mut self, level: &mut Level) {
if !self
.inventory
.main
.iter()
.flatten()
.any(|stack| stack.item == Item::MAP)
{
if !self.map_tracking.is_empty() {
level.maps.lock().unwrap().holders.remove(&self.entity_id);
self.map_tracking.clear();
}
return;
}
let store = level.maps.clone();
let mut maps = store.lock().unwrap();
let dimension = level.dimension();
let position = self.position(level);
let yaw = self.entity(level).map_or(0.0, |e| e.yaw);
let mut ids = Vec::new();
let selected = self.inventory.selected;
for (slot, item) in self.inventory.main.iter_mut().enumerate() {
let Some(stack) = item.as_mut().filter(|s| s.item == Item::MAP) else {
continue;
};
let mut id = stack.damage as i16;
let created = match maps.load(id) {
Ok(false) => maps
.create([self.spawn[0], self.spawn[2]], dimension)
.map(Some),
loaded => loaded.map(|_| None),
};
match created {
Ok(Some(created)) => {
id = created;
stack.damage = i32::from(id);
}
Ok(None) => {}
Err(error) => return level.fail_storage(error),
}
if !ids.contains(&id) {
ids.push(id);
}
if slot == selected
&& let Some(map) = maps.loaded.get_mut(&id)
{
update_map(level, position, map);
}
}
maps.holders.retain(|&id, holder| {
holder.dimension != dimension || level.entities.get(id).is_some_and(|e| !e.entity.dead)
});
maps.holders.insert(
self.entity_id,
Holder {
maps: ids.clone(),
position,
yaw,
dimension,
},
);
self.map_tracking.retain(|id, _| ids.contains(id));
for id in ids {
let map = &maps.loaded[&id];
let mut icons = vec![1];
let mut holders: Vec<_> = maps.holders.iter().collect();
holders.sort_unstable_by_key(|(id, _)| **id);
for (_, holder) in holders {
if holder.dimension != map.map.dimension || !holder.maps.contains(&id) {
continue;
}
let scale = (1 << map.map.scale) as f32;
let x = (holder.position.x - f64::from(map.map.center[0])) as f32 / scale;
let z = (holder.position.z - f64::from(map.map.center[1])) as f32 / scale;
if !(-64.0..=64.0).contains(&x) || !(-64.0..=64.0).contains(&z) {
continue;
}
let rotation = if map.map.dimension < 0 {
let t = map.ticks / 10;
((t.wrapping_mul(t)
.wrapping_mul(34187121)
.wrapping_add(t.wrapping_mul(121))
>> 15)
& 15) as u8
} else {
((f64::from(holder.yaw * 16.0 / 360.0) + 0.5) as i32 & 15) as u8
};
icons.extend([
rotation << 4,
(f64::from(x * 2.0) + 0.5) as i32 as u8,
(f64::from(z * 2.0) + 0.5) as i32 as u8,
]);
}
let track = self.map_tracking.entry(id).or_default();
track.calls = track.calls.wrapping_add(1);
let data = if track.calls % 5 == 1 && track.icons != icons {
track.icons = icons.clone();
Some(icons)
} else {
(0..128).find_map(|_| {
let x = track.next * 11 % 128;
track.next = (track.next + 1) % 128;
if track.revisions[x] == map.revisions[x] {
return None;
}
track.revisions[x] = map.revisions[x];
let column = (0..128).map(|y| map.map.colors[x + y * 128]);
Some([0, x as u8, 0].into_iter().chain(column).collect())
})
};
if let Some(data) = data {
self.send(Packet::MapData {
item: Item::MAP.0,
map: id,
data,
});
}
}
}
}
fn update_map(level: &mut Level, position: DVec3, stored: &mut StoredMap) {
if level.dimension() != stored.map.dimension {
return;
}
let center = stored.map.center;
let scale = 1_i32 << stored.map.scale;
let px = floor_f64(position.x - f64::from(center[0])) / scale + 64;
let pz = floor_f64(position.z - f64::from(center[1])) / scale + 64;
let radius = 128 / scale / if level.dimension() < 0 { 2 } else { 1 };
stored.ticks = stored.ticks.wrapping_add(1);
for x in (px - radius + 1).max(0)..(px + radius).min(128) {
if x & 15 != stored.ticks & 15 {
continue;
}
let mut previous_height = 0.0;
let mut changed = false;
for z in (pz - radius - 1).max(-1)..(pz + radius).min(128) {
let dx = x - px;
let dz = z - pz;
let wx = (center[0] / scale + x - 64) * scale;
let wz = (center[1] / scale + z - 64) * scale;
let mut counts = [0; 256];
let mut height = 0.0;
let mut depth = 0;
if level.dimension() < 0 {
let hash = wx.wrapping_add(wz.wrapping_mul(231871));
let hash = hash
.wrapping_mul(hash)
.wrapping_mul(31287121)
.wrapping_add(hash.wrapping_mul(11));
let block = if (hash >> 20) & 1 == 0 {
Block::DIRT
} else {
Block::STONE
};
counts[usize::from(block.0)] = 10;
height = 100.0;
} else {
let pos = ChunkPos::containing(wx, wz);
level.load_chunk(pos);
let chunk = level.chunk(pos).expect("loaded map chunk");
for sx in 0..scale {
for sz in 0..scale {
let lx = ((wx + sx) & 15) as usize;
let lz = ((wz + sz) & 15) as usize;
let mut y = (chunk.height(lx, lz) + 1).min(128);
let mut block = Block::AIR;
if y > 1 {
loop {
block = chunk.block(lx, y - 1, lz);
if !block.is_air() && material_color(block.material()) != 0
|| y == 1
{
break;
}
y -= 1;
}
if block.material().is_liquid() {
let mut below = y as i32 - 1;
loop {
let liquid =
chunk.block(lx, below as usize, lz).material().is_liquid();
depth += 1;
below -= 1;
if below <= 0 || !liquid {
break;
}
}
}
}
height += y as f64 / f64::from(scale * scale);
counts[usize::from(block.0)] += 1;
}
}
}
depth /= scale * scale;
let block = Block(
counts
.iter()
.enumerate()
.max_by_key(|(index, count)| (**count, std::cmp::Reverse(*index)))
.unwrap()
.0 as u8,
);
let color = material_color(block.material());
let mut slope = (height - previous_height) * 4.0 / f64::from(scale + 4)
+ (f64::from((x + z) & 1) - 0.5) * 0.4;
let mut shade = if slope > 0.6 {
2
} else if slope < -0.6 {
0
} else {
1
};
if color == 12 {
slope = f64::from(depth) * 0.1 + f64::from((x + z) & 1) * 0.2;
shade = if slope < 0.5 {
2
} else if slope > 0.9 {
0
} else {
1
};
}
previous_height = height;
let edge = dx * dx + dz * dz > (radius - 2) * (radius - 2);
if z >= 0 && dx * dx + dz * dz < radius * radius && (!edge || (x + z) & 1 != 0) {
let pixel = &mut stored.map.colors[(x + z * 128) as usize];
let next = color * 4 + shade;
if *pixel != next {
*pixel = next;
changed = true;
}
}
}
if changed {
stored.changed(x as usize);
}
}
}