From 826565dbaef9138b114cf0b2de07d529929e68b4 Mon Sep 17 00:00:00 2001 From: Chewing_Bever Date: Sat, 3 Jun 2023 14:18:39 +0200 Subject: [PATCH] feat: some improvements --- src/main.rs | 22 +++++++---- src/server/command.rs | 82 +++++++++++++++++++++++++++++++------- src/server/mod.rs | 2 +- src/server/process.rs | 92 +++++++++++++++++++++++++++++++------------ 4 files changed, 150 insertions(+), 48 deletions(-) diff --git a/src/main.rs b/src/main.rs index 43e1272..38b2f6b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,13 +1,17 @@ -use std::io; - mod server; +use server::{ServerCommand, ServerType}; +use std::io; + fn main() { - let mut server = - server::ServerCommand::new("paper-1.19.4-545.jar", "data/config", "data/worlds") - .mem(512, 1024) - .spawn() - .unwrap(); + let mut server = server::ServerCommand::new(ServerType::Paper, "1.19.4-545") + .jar("paper-1.19.4-545.jar") + .config("data/config") + .world("data/worlds") + .backup("data/backups") + .mem(512, 1024) + .spawn() + .unwrap(); let stdin = io::stdin(); let input = &mut String::new(); @@ -16,7 +20,9 @@ fn main() { input.clear(); stdin.read_line(input); println!("input: {}", input.trim()); - server.send_command(input); + if let Err(e) = server.send_command(input) { + println!("{}", e); + }; if input.trim() == "stop" { break; diff --git a/src/server/command.rs b/src/server/command.rs index da142fc..10864dc 100644 --- a/src/server/command.rs +++ b/src/server/command.rs @@ -1,32 +1,52 @@ use crate::server::ServerProcess; -use flate2::write::GzEncoder; -use flate2::Compression; use std::fs::File; -use std::io; use std::io::Write; use std::path::{Path, PathBuf}; use std::process::{Child, Command, Stdio}; +use std::fmt; + +pub enum ServerType { + Paper, + Forge, + Vanilla, +} + +impl fmt::Display for ServerType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let s = match self { + ServerType::Paper => "PaperMC", + ServerType::Forge => "Forge", + ServerType::Vanilla => "Vanilla", + }; + + write!(f, "{}", s) + } +} pub struct ServerCommand { + type_: ServerType, + version: String, java: String, jar: PathBuf, config_dir: PathBuf, world_dir: PathBuf, + backup_dir: PathBuf, xms: u32, xmx: u32, - child: Option, } impl ServerCommand { - pub fn new(jar: &str, config_dir: &str, world_dir: &str) -> Self { + pub fn new(type_: ServerType, version: &str) -> Self { ServerCommand { + type_, + version: String::from(version), java: String::from("java"), - jar: Path::new(jar).canonicalize().unwrap(), - config_dir: Path::new(config_dir).canonicalize().unwrap(), - world_dir: Path::new(world_dir).canonicalize().unwrap(), + jar: PathBuf::from("server.jar"), + config_dir: PathBuf::from("config"), + world_dir: PathBuf::from("worlds"), + backup_dir: PathBuf::from("backups"), xms: 1024, xmx: 2048, - child: None, } } @@ -36,6 +56,27 @@ impl ServerCommand { self } + pub fn jar>(mut self, path: T) -> Self { + self.jar = PathBuf::from(path.as_ref()); + self + } + + pub fn config>(mut self, path: T) -> Self { + self.config_dir = PathBuf::from(path.as_ref()); + self + } + + pub fn world>(mut self, path: T) -> Self { + self.world_dir = PathBuf::from(path.as_ref()); + self + } + + pub fn backup>(mut self, path: T) -> Self { + self.backup_dir = PathBuf::from(path.as_ref()); + + self + } + pub fn mem(mut self, xms: u32, xmx: u32) -> Self { self.xms = xms; self.xmx = xmx; @@ -52,19 +93,32 @@ impl ServerCommand { Ok(()) } - pub fn spawn(&mut self) -> std::io::Result { + pub fn spawn(self) -> std::io::Result { + // To avoid any issues, we use absolute paths for everything when spawning the process + let jar = self.jar.canonicalize()?; + let config_dir = self.config_dir.canonicalize()?; + let world_dir = self.world_dir.canonicalize()?; + let backup_dir = self.backup_dir.canonicalize()?; + self.accept_eula()?; let child = Command::new(&self.java) - .current_dir(&self.config_dir) + .current_dir(&config_dir) .arg("-jar") - .arg(&self.jar) + .arg(&jar) .arg("--universe") - .arg(&self.world_dir) + .arg(&world_dir) .arg("--nogui") .stdin(Stdio::piped()) .spawn()?; - Ok(ServerProcess::new(child)) + Ok(ServerProcess::new( + self.type_, + self.version, + config_dir, + world_dir, + backup_dir, + child, + )) } } diff --git a/src/server/mod.rs b/src/server/mod.rs index a43ca4f..e3e3131 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,5 +1,5 @@ mod command; mod process; -pub use command::ServerCommand; +pub use command::{ServerCommand, ServerType}; pub use process::ServerProcess; diff --git a/src/server/process.rs b/src/server/process.rs index 69c4425..a633b4e 100644 --- a/src/server/process.rs +++ b/src/server/process.rs @@ -1,3 +1,4 @@ +use crate::server::ServerType; use flate2::write::GzEncoder; use flate2::Compression; use std::fs::File; @@ -8,50 +9,91 @@ use std::process::Child; use std::process::{Command, Stdio}; pub struct ServerProcess { + type_: ServerType, + version: String, + config_dir: PathBuf, + world_dir: PathBuf, + backup_dir: PathBuf, child: Child, } impl ServerProcess { - pub fn new(child: Child) -> ServerProcess { - ServerProcess { child } - } - - pub fn send_command(&mut self, cmd: &str) { - match cmd.trim() { - "stop" | "exit" => self.stop(), - "backup" => self.backup(), - s => self.custom(s), + pub fn new( + type_: ServerType, + version: String, + config_dir: PathBuf, + world_dir: PathBuf, + backup_dir: PathBuf, + child: Child, + ) -> ServerProcess { + ServerProcess { + type_, + version, + config_dir, + world_dir, + backup_dir, + child, } } - fn custom(&mut self, cmd: &str) { - let mut stdin = self.child.stdin.take().unwrap(); - stdin - .write_all(format!("{}\n", cmd.trim()).as_bytes()) - .unwrap(); - stdin.flush().unwrap(); + pub fn send_command(&mut self, cmd: &str) -> std::io::Result<()> { + match cmd.trim() { + "stop" | "exit" => self.stop()?, + "backup" => self.backup()?, + s => self.custom(s)?, + } + + Ok(()) } - pub fn stop(&mut self) { - self.custom("stop"); - self.child.wait(); + fn custom(&mut self, cmd: &str) -> std::io::Result<()> { + let mut stdin = self.child.stdin.as_ref().unwrap(); + stdin.write_all(format!("{}\n", cmd.trim()).as_bytes())?; + stdin.flush()?; + + Ok(()) } - pub fn backup(&mut self) { + pub fn stop(&mut self) -> std::io::Result<()> { + self.custom("stop")?; + self.child.wait()?; + + Ok(()) + } + + pub fn backup(&mut self) -> std::io::Result<()> { // Make sure the server isn't modifying the files during the backup - self.custom("save-off"); - self.custom("save-all"); + self.custom("save-off")?; + self.custom("save-all")?; // Create a gzip-compressed tarball of the worlds folder let filename = format!( "{}", - chrono::offset::Local::now().format("backups/%Y-%m-%d_%H-%M-%S.tar.gz") + chrono::offset::Local::now().format("%Y-%m-%d_%H-%M-%S.tar.gz") ); - let tar_gz = std::fs::File::create(filename).unwrap(); + let path = self.backup_dir.join(&filename); + let tar_gz = std::fs::File::create(path)?; let enc = GzEncoder::new(tar_gz, Compression::default()); let mut tar = tar::Builder::new(enc); - tar.append_dir_all("worlds", "worlds").unwrap(); - self.custom("save-on"); + tar.append_dir_all("worlds", &self.world_dir)?; + + // We don't store all files in the config, as this would include caches + tar.append_path_with_name(self.config_dir.join("server.properties"), "config/server.properties")?; + + + // We add a file to the backup describing for what version it was made + let info = format!("{} {}", self.type_, self.version); + let info_bytes = info.as_bytes(); + let mut header = tar::Header::new_gnu(); + header.set_size(info_bytes.len().try_into().unwrap()); + + tar.append_data(&mut header, "info.txt", info_bytes)?; + + // tar.append_dir_all("config", &self.config_dir)?; + // + // Backup file gets finalized in the drop + + self.custom("save-on") } }