From 6dea63f8187e4c0581cac6779e766c5193b10e16 Mon Sep 17 00:00:00 2001 From: Hector van der Aa Date: Tue, 9 Dec 2025 00:56:09 +0100 Subject: [PATCH] Config save and load start --- Cargo.lock | 1 + Cargo.toml | 2 +- src/config/version.rs | 9 ++-- src/error.rs | 3 ++ src/instance/handle.rs | 9 ++++ src/server/domain.rs | 95 +++++++++++++++++++++++++++++++++++++++--- 6 files changed, 108 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1d529ac..366a32a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1342,6 +1342,7 @@ checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" dependencies = [ "getrandom 0.3.4", "js-sys", + "serde_core", "wasm-bindgen", ] diff --git a/Cargo.toml b/Cargo.toml index 3fc9d3f..7e3598f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,4 +33,4 @@ thiserror = { version = "2.0.17", optional = true } tokio = { version = "1.48.0", features = ["process", "rt-multi-thread", "macros", "io-std", "io-util"], optional = true } tokio-stream = { version = "0.1.17", features = ["full", "io-util", "signal", "tokio-util"], optional = true } tokio-util = { version = "0.7.17", features = ["full"], optional = true } -uuid = { version = "1.19.0", features = ["v4"], optional = true } +uuid = { version = "1.19.0", features = ["serde", "v4"], optional = true } diff --git a/src/config/version.rs b/src/config/version.rs index 4e26999..37d00c6 100644 --- a/src/config/version.rs +++ b/src/config/version.rs @@ -3,18 +3,19 @@ use std::{ str::FromStr, }; +use serde::{Deserialize, Serialize}; use tokio::sync::watch; use crate::error::VersionError; /// Identifies the type of Minecraft distribution supported by the configuration. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] pub enum MinecraftType { Vanilla, } /// Semantic release version parsed from strings like `1.20.4`. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] pub struct Version { pub major: u32, pub minor: u32, @@ -22,7 +23,7 @@ pub struct Version { } /// Snapshot version parsed from strings like `23w13b`. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] pub struct Snapshot { pub year: u32, pub week: u32, @@ -30,7 +31,7 @@ pub struct Snapshot { } /// Enum covering both release and snapshot Minecraft version formats. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] pub enum MinecraftVersion { Release(Version), Snapshot(Snapshot), diff --git a/src/error.rs b/src/error.rs index 5de34c8..1d2035d 100644 --- a/src/error.rs +++ b/src/error.rs @@ -96,6 +96,9 @@ pub enum ServerError { NoEULA, #[error("Failed to write eula.txt")] WriteEULAFailed, + + #[error("File io error")] + FileIO, } #[cfg(feature = "events")] diff --git a/src/instance/handle.rs b/src/instance/handle.rs index 78be3c1..7cc90f4 100644 --- a/src/instance/handle.rs +++ b/src/instance/handle.rs @@ -20,6 +20,7 @@ use crate::{ stream::{EventPayload, InternalEvent}, }, error::{HandleError, ServerError, SubscribeError}, + server::domain::MineGuardConfig, }; use super::{InstanceData, InstanceStatus}; @@ -44,6 +45,14 @@ pub struct InstanceHandle { } impl InstanceHandle { + pub fn new_with_config(config: MineGuardConfig) -> Result { + InstanceHandle::new_with_params( + config.server_dir, + config.jar_path, + config.mc_version, + config.mc_type, + ) + } pub fn new_with_params( root_dir: PathBuf, jar_path: PathBuf, diff --git a/src/server/domain.rs b/src/server/domain.rs index c0d087d..3c9c011 100644 --- a/src/server/domain.rs +++ b/src/server/domain.rs @@ -1,7 +1,9 @@ use std::{ops::RangeInclusive, path::PathBuf, str::FromStr}; +use serde::{Deserialize, Serialize}; +use serde_json::json; use tokio::{ - fs::{File, create_dir}, + fs::{File, create_dir, read, read_dir}, io::{self, AsyncWriteExt}, sync::{RwLock, watch}, }; @@ -9,21 +11,23 @@ use tokio_stream::wrappers::BroadcastStream; use uuid::Uuid; use crate::{ - config::{MinecraftType, MinecraftVersion, StreamSource, Version, stream::InstanceEvent}, + config::{self, MinecraftType, MinecraftVersion, StreamSource, Version, stream::InstanceEvent}, error::{CreationError, ServerError, SubscribeError}, instance::InstanceHandle, manifests::vanilla::{VanillaManifestV2, VanillaManifestV2Version, VanillaReleaseManifest}, server, }; +#[derive(Debug, Clone, Deserialize, Serialize)] pub struct MineGuardConfig { uuid: Uuid, - server_dir: PathBuf, - jar_path: PathBuf, - mc_version: MinecraftVersion, - mc_type: MinecraftType, + pub server_dir: PathBuf, + pub jar_path: PathBuf, + pub mc_version: MinecraftVersion, + pub mc_type: MinecraftType, } +#[derive(Debug)] pub struct MineGuardServer { pub handle: RwLock, pub config: RwLock, @@ -42,6 +46,15 @@ impl MineGuardConfig { } impl MineGuardServer { + async fn load_cfg_handle( + config: MineGuardConfig, + handle: InstanceHandle, + ) -> Result { + Ok(Self { + config: RwLock::new(config), + handle: RwLock::new(handle), + }) + } pub async fn create( mc_version: MinecraftVersion, mc_type: MinecraftType, @@ -62,6 +75,11 @@ impl MineGuardServer { .await .map_err(|_| CreationError::DirectoryError)?; + let internal_dir = server_root.join(".mineguard"); + create_dir(internal_dir) + .await + .map_err(|_| CreationError::DirectoryError)?; + let mut url = String::new(); if mc_type == MinecraftType::Vanilla { @@ -162,4 +180,69 @@ impl MineGuardServer { Ok(()) } + + pub async fn write_config(&self) -> Result<(), ServerError> { + let config_r = self.config.read().await; + let root_path = config_r.server_dir.clone(); + let config_clone = config_r.clone(); + drop(config_r); + + let config_path = root_path.join(".mineguard/config.json"); + + let json = serde_json::to_vec_pretty(&config_clone).map_err(|_| ServerError::FileIO)?; + + File::create(config_path) + .await + .map_err(|_| ServerError::FileIO)? + .write_all(&json) + .await + .map_err(|_| ServerError::FileIO)?; + + Ok(()) + } + + pub async fn load(path: &PathBuf) -> Result { + let config_path = path.join(".mineguard/config.json"); + + let data = read(config_path) + .await + .map_err(|_| CreationError::DirectoryError)?; + + let config: MineGuardConfig = + serde_json::from_slice(&data).map_err(|_| CreationError::CreationError)?; + let handle = InstanceHandle::new_with_config(config.clone()) + .map_err(|_| CreationError::CreationError)?; + + MineGuardServer::load_cfg_handle(config, handle).await + } + + pub async fn load_all(path: PathBuf) -> Result, CreationError> { + let mut dirs = Vec::new(); + let mut entries = read_dir(path) + .await + .map_err(|_| CreationError::DirectoryError)?; + + while let Some(entry) = entries + .next_entry() + .await + .map_err(|_| CreationError::DirectoryError)? + { + let meta = entry + .metadata() + .await + .map_err(|_| CreationError::DirectoryError)?; + if meta.is_dir() { + dirs.push(entry.path()); + } + } + + let mut servers: Vec = Vec::new(); + + for v in dirs { + println!("{}", v.to_str().unwrap()); + servers.push(Self::load(&v).await?); + } + + Ok(servers) + } }