composition/
lib.rs

1#[macro_use]
2extern crate lazy_static;
3
4/// Data types for every entity in the game.
5pub mod entity;
6/// Implementations of the data types needed for the Minecraft protocol.
7pub mod mctypes;
8/// The logic for the server.
9pub mod server;
10/// The data types for blocks, chunks, dimensions, and world files.
11pub mod world;
12
13use log::*;
14pub use mctypes::*;
15use serde::{Deserialize, Serialize};
16use std::sync::mpsc::{self, Receiver};
17
18#[derive(Serialize, Deserialize)]
19pub struct Config {
20    pub port: u16,
21    pub max_players: usize,
22    pub motd: String,
23    pub favicon: String,
24}
25
26lazy_static! {
27    static ref CONFIG: Config = {
28        let config_from_file = || -> std::io::Result<Config> {
29            use std::{fs::File, io::prelude::*};
30            let mut data = String::new();
31            let mut file = File::open("composition.toml")?;
32            file.read_to_string(&mut data)?;
33            if let Ok(c) = toml::from_str::<Config>(&data) {
34                Ok(c)
35            } else {
36                Err(std::io::Error::new(
37                    std::io::ErrorKind::Other,
38                    "Could not parse toml",
39                ))
40            }
41        };
42        if let Ok(c) = config_from_file() {
43            c
44        } else {
45            warn!("Could not load config from file, using default");
46            Config {
47                port: 25565,
48                max_players: 20,
49                motd: "Hello world!".to_owned(),
50                favicon: "server-icon.png".to_owned(),
51            }
52        }
53    };
54    static ref FAVICON: std::io::Result<Vec<u8>> = {
55        use std::{fs::File, io::prelude::*};
56        let mut data = vec![];
57        let mut file = File::open(CONFIG.favicon.clone())?;
58        file.read_to_end(&mut data)?;
59        Ok(data)
60    };
61    pub static ref START_TIME: std::time::Instant = std::time::Instant::now();
62}
63
64/// Set up logging, read the config file, etc.
65pub fn init() -> Receiver<()> {
66    // Load the START_TIME static - lazy_static lazy loads the value when first needed.
67    let _ = START_TIME.elapsed();
68    // Set up fern logging.
69    fern::Dispatch::new()
70        .format(move |out, message, record| {
71            out.finish(format_args!(
72                "[{date}][{target}][{level}] {message}",
73                date = chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
74                target = record.target(),
75                level = record.level(),
76                message = message,
77            ))
78        })
79        .level(if cfg!(debug_assertions) {
80            log::LevelFilter::Debug
81        } else {
82            log::LevelFilter::Info
83        })
84        .chain(std::io::stdout())
85        .chain(fern::log_file("output.log").unwrap())
86        .apply()
87        .unwrap();
88    // Set up the ctrl-c handler.
89    let (ctrlc_tx, ctrlc_rx) = mpsc::channel();
90    ctrlc::set_handler(move || {
91        ctrlc_tx.send(()).expect("Ctrl-C receiver disconnected");
92    })
93    .expect("Error setting Ctrl-C handler");
94    ctrlc_rx
95}
96
97/// Start the server.
98pub async fn start_server() -> server::Server {
99    server::Server::new(format!("0.0.0.0:{}", CONFIG.port))
100}