feat: some improvements
parent
d176fac420
commit
826565dbae
22
src/main.rs
22
src/main.rs
|
@ -1,13 +1,17 @@
|
||||||
use std::io;
|
|
||||||
|
|
||||||
mod server;
|
mod server;
|
||||||
|
|
||||||
|
use server::{ServerCommand, ServerType};
|
||||||
|
use std::io;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut server =
|
let mut server = server::ServerCommand::new(ServerType::Paper, "1.19.4-545")
|
||||||
server::ServerCommand::new("paper-1.19.4-545.jar", "data/config", "data/worlds")
|
.jar("paper-1.19.4-545.jar")
|
||||||
.mem(512, 1024)
|
.config("data/config")
|
||||||
.spawn()
|
.world("data/worlds")
|
||||||
.unwrap();
|
.backup("data/backups")
|
||||||
|
.mem(512, 1024)
|
||||||
|
.spawn()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let stdin = io::stdin();
|
let stdin = io::stdin();
|
||||||
let input = &mut String::new();
|
let input = &mut String::new();
|
||||||
|
@ -16,7 +20,9 @@ fn main() {
|
||||||
input.clear();
|
input.clear();
|
||||||
stdin.read_line(input);
|
stdin.read_line(input);
|
||||||
println!("input: {}", input.trim());
|
println!("input: {}", input.trim());
|
||||||
server.send_command(input);
|
if let Err(e) = server.send_command(input) {
|
||||||
|
println!("{}", e);
|
||||||
|
};
|
||||||
|
|
||||||
if input.trim() == "stop" {
|
if input.trim() == "stop" {
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -1,32 +1,52 @@
|
||||||
use crate::server::ServerProcess;
|
use crate::server::ServerProcess;
|
||||||
use flate2::write::GzEncoder;
|
|
||||||
use flate2::Compression;
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io;
|
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::{Child, Command, Stdio};
|
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 {
|
pub struct ServerCommand {
|
||||||
|
type_: ServerType,
|
||||||
|
version: String,
|
||||||
java: String,
|
java: String,
|
||||||
jar: PathBuf,
|
jar: PathBuf,
|
||||||
config_dir: PathBuf,
|
config_dir: PathBuf,
|
||||||
world_dir: PathBuf,
|
world_dir: PathBuf,
|
||||||
|
backup_dir: PathBuf,
|
||||||
xms: u32,
|
xms: u32,
|
||||||
xmx: u32,
|
xmx: u32,
|
||||||
child: Option<Child>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ServerCommand {
|
impl ServerCommand {
|
||||||
pub fn new(jar: &str, config_dir: &str, world_dir: &str) -> Self {
|
pub fn new(type_: ServerType, version: &str) -> Self {
|
||||||
ServerCommand {
|
ServerCommand {
|
||||||
|
type_,
|
||||||
|
version: String::from(version),
|
||||||
java: String::from("java"),
|
java: String::from("java"),
|
||||||
jar: Path::new(jar).canonicalize().unwrap(),
|
jar: PathBuf::from("server.jar"),
|
||||||
config_dir: Path::new(config_dir).canonicalize().unwrap(),
|
config_dir: PathBuf::from("config"),
|
||||||
world_dir: Path::new(world_dir).canonicalize().unwrap(),
|
world_dir: PathBuf::from("worlds"),
|
||||||
|
backup_dir: PathBuf::from("backups"),
|
||||||
xms: 1024,
|
xms: 1024,
|
||||||
xmx: 2048,
|
xmx: 2048,
|
||||||
child: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,6 +56,27 @@ impl ServerCommand {
|
||||||
self
|
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 {
|
pub fn mem(mut self, xms: u32, xmx: u32) -> Self {
|
||||||
self.xms = xms;
|
self.xms = xms;
|
||||||
self.xmx = xmx;
|
self.xmx = xmx;
|
||||||
|
@ -52,19 +93,32 @@ impl ServerCommand {
|
||||||
Ok(())
|
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()?;
|
self.accept_eula()?;
|
||||||
|
|
||||||
let child = Command::new(&self.java)
|
let child = Command::new(&self.java)
|
||||||
.current_dir(&self.config_dir)
|
.current_dir(&config_dir)
|
||||||
.arg("-jar")
|
.arg("-jar")
|
||||||
.arg(&self.jar)
|
.arg(&jar)
|
||||||
.arg("--universe")
|
.arg("--universe")
|
||||||
.arg(&self.world_dir)
|
.arg(&world_dir)
|
||||||
.arg("--nogui")
|
.arg("--nogui")
|
||||||
.stdin(Stdio::piped())
|
.stdin(Stdio::piped())
|
||||||
.spawn()?;
|
.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 command;
|
||||||
mod process;
|
mod process;
|
||||||
|
|
||||||
pub use command::ServerCommand;
|
pub use command::{ServerCommand, ServerType};
|
||||||
pub use process::ServerProcess;
|
pub use process::ServerProcess;
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::server::ServerType;
|
||||||
use flate2::write::GzEncoder;
|
use flate2::write::GzEncoder;
|
||||||
use flate2::Compression;
|
use flate2::Compression;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
@ -8,50 +9,91 @@ use std::process::Child;
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
|
|
||||||
pub struct ServerProcess {
|
pub struct ServerProcess {
|
||||||
|
type_: ServerType,
|
||||||
|
version: String,
|
||||||
|
config_dir: PathBuf,
|
||||||
|
world_dir: PathBuf,
|
||||||
|
backup_dir: PathBuf,
|
||||||
child: Child,
|
child: Child,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ServerProcess {
|
impl ServerProcess {
|
||||||
pub fn new(child: Child) -> ServerProcess {
|
pub fn new(
|
||||||
ServerProcess { child }
|
type_: ServerType,
|
||||||
}
|
version: String,
|
||||||
|
config_dir: PathBuf,
|
||||||
pub fn send_command(&mut self, cmd: &str) {
|
world_dir: PathBuf,
|
||||||
match cmd.trim() {
|
backup_dir: PathBuf,
|
||||||
"stop" | "exit" => self.stop(),
|
child: Child,
|
||||||
"backup" => self.backup(),
|
) -> ServerProcess {
|
||||||
s => self.custom(s),
|
ServerProcess {
|
||||||
|
type_,
|
||||||
|
version,
|
||||||
|
config_dir,
|
||||||
|
world_dir,
|
||||||
|
backup_dir,
|
||||||
|
child,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn custom(&mut self, cmd: &str) {
|
pub fn send_command(&mut self, cmd: &str) -> std::io::Result<()> {
|
||||||
let mut stdin = self.child.stdin.take().unwrap();
|
match cmd.trim() {
|
||||||
stdin
|
"stop" | "exit" => self.stop()?,
|
||||||
.write_all(format!("{}\n", cmd.trim()).as_bytes())
|
"backup" => self.backup()?,
|
||||||
.unwrap();
|
s => self.custom(s)?,
|
||||||
stdin.flush().unwrap();
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stop(&mut self) {
|
fn custom(&mut self, cmd: &str) -> std::io::Result<()> {
|
||||||
self.custom("stop");
|
let mut stdin = self.child.stdin.as_ref().unwrap();
|
||||||
self.child.wait();
|
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
|
// Make sure the server isn't modifying the files during the backup
|
||||||
self.custom("save-off");
|
self.custom("save-off")?;
|
||||||
self.custom("save-all");
|
self.custom("save-all")?;
|
||||||
|
|
||||||
// Create a gzip-compressed tarball of the worlds folder
|
// Create a gzip-compressed tarball of the worlds folder
|
||||||
let filename = format!(
|
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 enc = GzEncoder::new(tar_gz, Compression::default());
|
||||||
let mut tar = tar::Builder::new(enc);
|
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