feat: some improvements
parent
d176fac420
commit
826565dbae
22
src/main.rs
22
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;
|
||||
|
|
|
@ -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<Child>,
|
||||
}
|
||||
|
||||
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<T: AsRef<Path>>(mut self, path: T) -> Self {
|
||||
self.jar = PathBuf::from(path.as_ref());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn config<T: AsRef<Path>>(mut self, path: T) -> Self {
|
||||
self.config_dir = PathBuf::from(path.as_ref());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn world<T: AsRef<Path>>(mut self, path: T) -> Self {
|
||||
self.world_dir = PathBuf::from(path.as_ref());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn backup<T: AsRef<Path>>(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<ServerProcess> {
|
||||
pub fn spawn(self) -> std::io::Result<ServerProcess> {
|
||||
// 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,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
mod command;
|
||||
mod process;
|
||||
|
||||
pub use command::ServerCommand;
|
||||
pub use command::{ServerCommand, ServerType};
|
||||
pub use process::ServerProcess;
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue