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 / dedicated / properties.rs

use std::fs::File;
use std::io::{self, BufReader, BufWriter};
use std::path::PathBuf;

use java_properties::{PropertiesError, PropertiesIter, PropertiesWriter};

/// `PropertyManager`: `server.properties`, which records each default when first requested.
pub struct Properties {
    path: PathBuf,
    entries: Vec<(String, String)>,
}

impl Properties {
    pub fn load(path: PathBuf) -> Self {
        let mut properties = Self {
            path,
            entries: Vec::new(),
        };
        let loaded = match File::open(&properties.path) {
            Ok(file) => PropertiesIter::new(BufReader::new(file))
                .read_into(|key, value| properties.put(key, value))
                .map_err(|error| {
                    tracing::warn!("Failed to load {}: {error}", properties.path.display());
                }),
            Err(error) if error.kind() == io::ErrorKind::NotFound => {
                tracing::warn!("{} does not exist", properties.path.display());
                Err(())
            }
            Err(error) => {
                tracing::warn!("Failed to load {}: {error}", properties.path.display());
                Err(())
            }
        };
        if loaded.is_err() {
            tracing::info!("Generating new properties file");
            properties.save();
        }
        properties
    }

    fn get(&self, key: &str) -> Option<&str> {
        self.entries
            .iter()
            .find(|(k, _)| k == key)
            .map(|(_, v)| v.as_str())
    }

    fn put(&mut self, key: String, value: String) {
        match self.entries.iter_mut().find(|(k, _)| *k == key) {
            Some((_, v)) => *v = value,
            None => self.entries.push((key, value)),
        }
    }

    /// A setting the original never had, so it is not recorded.
    pub fn optional_int(&self, key: &str) -> Option<i32> {
        self.get(key)?.parse().ok()
    }

    pub fn string(&mut self, key: &str, default: &str) -> String {
        if self.get(key).is_none() {
            self.put(key.into(), default.into());
            self.save();
        }
        self.get(key).unwrap_or(default).to_owned()
    }

    /// `Integer.parseInt`, falling back to the default.
    pub fn int(&mut self, key: &str, default: i32) -> i32 {
        self.string(key, &default.to_string())
            .parse()
            .unwrap_or_else(|_| {
                self.put(key.into(), default.to_string());
                default
            })
    }

    /// `Boolean.parseBoolean`.
    pub fn bool(&mut self, key: &str, default: bool) -> bool {
        self.string(key, &default.to_string())
            .eq_ignore_ascii_case("true")
    }

    pub fn set_bool(&mut self, key: &str, value: bool) {
        self.put(key.into(), value.to_string());
        self.save();
    }

    fn save(&self) {
        let result = (|| -> Result<(), PropertiesError> {
            let file = File::create(&self.path)?;
            let mut writer = PropertiesWriter::new(BufWriter::new(file));
            writer.set_comment_prefix("#")?;
            writer.write_comment("Minecraft server properties")?;
            writer.write_comment(
                &chrono::Local::now()
                    .format("%a %b %d %H:%M:%S %Z %Y")
                    .to_string(),
            )?;
            for (key, value) in &self.entries {
                writer.write(key, value)?;
            }
            writer.finish()
        })();
        if let Err(error) = result {
            tracing::warn!("Failed to save {}: {error}", self.path.display());
        }
    }
}