1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
#[macro_use]
extern crate lazy_static;

/// Data types for every entity in the game.
pub mod entity;
/// Implementations of the data types needed for the Minecraft protocol.
pub mod mctypes;
/// The logic for the server.
pub mod server;
/// The data types for blocks, chunks, dimensions, and world files.
pub mod world;

use log::*;
pub use mctypes::*;
use serde::{Deserialize, Serialize};
use std::sync::mpsc::{self, Receiver};

#[derive(Serialize, Deserialize)]
pub struct Config {
    pub port: u16,
    pub max_players: usize,
    pub motd: String,
    pub favicon: String,
}

lazy_static! {
    static ref CONFIG: Config = {
        let config_from_file = || -> std::io::Result<Config> {
            use std::{fs::File, io::prelude::*};
            let mut data = String::new();
            let mut file = File::open("composition.toml")?;
            file.read_to_string(&mut data)?;
            if let Ok(c) = toml::from_str::<Config>(&data) {
                Ok(c)
            } else {
                Err(std::io::Error::new(
                    std::io::ErrorKind::Other,
                    "Could not parse toml",
                ))
            }
        };
        if let Ok(c) = config_from_file() {
            c
        } else {
            warn!("Could not load config from file, using default");
            Config {
                port: 25565,
                max_players: 20,
                motd: "Hello world!".to_owned(),
                favicon: "server-icon.png".to_owned(),
            }
        }
    };
    static ref FAVICON: std::io::Result<Vec<u8>> = {
        use std::{fs::File, io::prelude::*};
        let mut data = vec![];
        let mut file = File::open(CONFIG.favicon.clone())?;
        file.read_to_end(&mut data)?;
        Ok(data)
    };
    pub static ref START_TIME: std::time::Instant = std::time::Instant::now();
}

/// Set up logging, read the config file, etc.
pub fn init() -> Receiver<()> {
    // Load the START_TIME static - lazy_static lazy loads the value when first needed.
    let _ = START_TIME.elapsed();
    // Set up fern logging.
    fern::Dispatch::new()
        .format(move |out, message, record| {
            out.finish(format_args!(
                "[{date}][{target}][{level}] {message}",
                date = chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
                target = record.target(),
                level = record.level(),
                message = message,
            ))
        })
        .level(if cfg!(debug_assertions) {
            log::LevelFilter::Debug
        } else {
            log::LevelFilter::Info
        })
        .chain(std::io::stdout())
        .chain(fern::log_file("output.log").unwrap())
        .apply()
        .unwrap();
    // Set up the ctrl-c handler.
    let (ctrlc_tx, ctrlc_rx) = mpsc::channel();
    ctrlc::set_handler(move || {
        ctrlc_tx.send(()).expect("Ctrl-C receiver disconnected");
    })
    .expect("Error setting Ctrl-C handler");
    ctrlc_rx
}

/// Start the server.
pub async fn start_server() -> server::Server {
    server::Server::new(format!("0.0.0.0:{}", CONFIG.port))
}