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 client src stats mod.rs
mod storage;
pub use storage::Store;
use common::stats::{ACHIEVEMENT, ACHIEVEMENTS};
use rustc_hash::FxHashMap;
use std::path::Path;
use std::sync::{Arc, Mutex};
use std::time::Instant;
pub type Shared = Arc<Mutex<Stats>>;
#[derive(Default)]
pub struct Stats {
pub values: FxHashMap<i32, i32>,
/// `GuiAchievement`: the achievement shown, since when, and whether it is only a hint.
pub toast: Option<(usize, Instant, bool)>,
pub(crate) unsent: FxHashMap<i32, i32>,
pub(crate) revision: u64,
pub(crate) guids: FxHashMap<i32, &'static str>,
}
impl Stats {
/// Reads the statistic GUIDs from `achievement/map.txt`, kept for the rest of the process.
pub fn load(assets: &Path) -> std::io::Result<Self> {
let guids = std::fs::read_to_string(assets.join("achievement/map.txt"))?
.leak()
.lines()
.filter_map(|line| {
let (id, guid) = line.trim().split_once(',')?;
Some((id.parse().ok()?, guid))
})
.collect();
Ok(Self {
guids,
..Default::default()
})
}
pub fn get(&self, id: i32) -> i32 {
self.values.get(&id).copied().unwrap_or(0)
}
pub fn unlocked(&self, index: usize) -> bool {
self.get(ACHIEVEMENT + index as i32) > 0
}
pub fn available(&self, index: usize) -> bool {
ACHIEVEMENTS[index]
.parent
.is_none_or(|parent| self.unlocked(parent))
}
/// `GuiAchievement.queueAchievementInformation`: how to earn a locked achievement.
pub fn hint(&mut self, index: usize) {
let now = Instant::now();
let since = now.checked_sub(std::time::Duration::from_millis(2500));
self.toast = Some((index, since.unwrap_or(now), true));
}
pub fn add(&mut self, id: i32, amount: i32) {
if amount <= 0 || !self.guids.contains_key(&id) {
return;
}
if let Ok(index) = usize::try_from(id - ACHIEVEMENT)
&& index < ACHIEVEMENTS.len()
{
if self.unlocked(index) || !self.available(index) {
return;
}
self.toast = Some((index, Instant::now(), false));
}
let total = self.values.entry(id).or_default();
*total = total.saturating_add(amount);
let unsent = self.unsent.entry(id).or_default();
*unsent = unsent.saturating_add(amount);
self.revision = self.revision.wrapping_add(1);
}
pub fn server(&mut self, id: i32, amount: i8) {
if !common::stats::independent(id) {
self.add(id, i32::from(amount));
}
}
}