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 / 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));
        }
    }
}