Add method of storing launcher data, fix forge 1.17+, add launcher settings

This commit is contained in:
Jai A
2021-12-19 22:48:50 -07:00
parent e9851a8e23
commit 9d74e84c01
15 changed files with 338 additions and 150 deletions
+99
View File
@@ -0,0 +1,99 @@
use crate::data::DataError;
use once_cell::sync;
use serde::{Deserialize, Serialize};
use tokio::sync::{RwLock, RwLockReadGuard};
const META_FILE: &str = "meta.json";
const META_URL: &str = "https://meta.modrinth.com/gamedata";
static METADATA: sync::OnceCell<RwLock<Metadata>> = sync::OnceCell::new();
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct Metadata {
pub minecraft: daedalus::minecraft::VersionManifest,
pub forge: daedalus::modded::Manifest,
pub fabric: daedalus::modded::Manifest,
}
impl Metadata {
pub async fn init() -> Result<(), DataError> {
let meta_path = crate::LAUNCHER_WORK_DIR.join(META_FILE);
if meta_path.exists() {
let meta_data = std::fs::read_to_string(meta_path)
.map(|x| serde_json::from_str::<Metadata>(&*x).ok())
.ok()
.flatten();
if let Some(metadata) = meta_data {
METADATA.get_or_init(|| RwLock::new(metadata));
}
}
let future = async {
for attempt in 0..=3 {
let res = async {
let new = Self::fetch().await?;
std::fs::write(
crate::LAUNCHER_WORK_DIR.join(META_FILE),
&*serde_json::to_string(&new)?,
)?;
if let Some(metadata) = METADATA.get() {
*metadata.write().await = new;
} else {
METADATA.get_or_init(|| RwLock::new(new));
}
Ok::<(), DataError>(())
}
.await;
match res {
Ok(_) => {
break;
}
Err(_) if attempt <= 3 => continue,
Err(err) => {
log::warn!("Unable to fetch launcher metadata: {}", err)
}
};
}
};
if METADATA.get().is_some() {
tokio::task::spawn(future);
} else {
future.await;
}
Ok(())
}
pub async fn fetch() -> Result<Self, DataError> {
let (game, forge, fabric) = futures::future::join3(
daedalus::minecraft::fetch_version_manifest(Some(&*format!(
"{}/minecraft/v0/manifest.json",
META_URL
))),
daedalus::modded::fetch_manifest(&*format!("{}/forge/v0/manifest.json", META_URL)),
daedalus::modded::fetch_manifest(&*format!("{}/fabric/v0/manifest.json", META_URL)),
)
.await;
Ok(Self {
minecraft: game?,
forge: forge?,
fabric: fabric?,
})
}
pub async fn get<'a>() -> Result<RwLockReadGuard<'a, Self>, DataError> {
Ok(METADATA
.get()
.ok_or_else(|| DataError::InitializedError("metadata".to_string()))?
.read()
.await)
}
}
+22
View File
@@ -0,0 +1,22 @@
use std::io;
pub use meta::Metadata;
pub use settings::Settings;
mod meta;
mod settings;
#[derive(thiserror::Error, Debug)]
pub enum DataError {
#[error("I/O error while reading data: {0}")]
IOError(#[from] io::Error),
#[error("Daedalus error: {0}")]
DaedalusError(#[from] daedalus::Error),
#[error("Attempted to access {0} without initializing it!")]
InitializedError(String),
#[error("Error while serializing/deserializing data")]
SerdeError(#[from] serde_json::Error),
}
+96
View File
@@ -0,0 +1,96 @@
use crate::data::DataError;
use once_cell::sync;
use serde::{Deserialize, Serialize};
use tokio::sync::{RwLock, RwLockReadGuard};
const SETTINGS_FILE: &str = "settings.json";
static SETTINGS: sync::OnceCell<RwLock<Settings>> = sync::OnceCell::new();
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct Settings {
pub memory: i32,
pub game_resolution: (i32, i32),
pub custom_java_args: String,
pub java_8_path: Option<String>,
pub java_17_path: Option<String>,
pub wrapper_command: Option<String>,
}
impl Default for Settings {
fn default() -> Self {
Self {
memory: 2048,
game_resolution: (854, 480),
custom_java_args: "".to_string(),
java_8_path: None,
java_17_path: None,
wrapper_command: None,
}
}
}
impl Settings {
pub async fn init() -> Result<(), DataError> {
let settings_path = crate::LAUNCHER_WORK_DIR.join(SETTINGS_FILE);
if settings_path.exists() {
let settings_data = std::fs::read_to_string(settings_path)
.map(|x| serde_json::from_str::<Settings>(&*x).ok())
.ok()
.flatten();
if let Some(settings) = settings_data {
SETTINGS.get_or_init(|| RwLock::new(settings));
}
}
if SETTINGS.get().is_none() {
let new = Self::default();
std::fs::write(
crate::LAUNCHER_WORK_DIR.join(SETTINGS_FILE),
&*serde_json::to_string(&new)?,
)?;
SETTINGS.get_or_init(|| RwLock::new(new));
}
Ok(())
}
pub async fn load() -> Result<(), DataError> {
let new = serde_json::from_str::<Settings>(&*std::fs::read_to_string(
crate::LAUNCHER_WORK_DIR.join(SETTINGS_FILE),
)?)?;
let write = &mut *SETTINGS
.get()
.ok_or_else(|| DataError::InitializedError("settings".to_string()))?
.write()
.await;
*write = new;
Ok(())
}
pub async fn save() -> Result<(), DataError> {
let settings = Self::get().await?;
std::fs::write(
crate::LAUNCHER_WORK_DIR.join(SETTINGS_FILE),
&*serde_json::to_string(&*settings)?,
)?;
Ok(())
}
pub async fn get<'a>() -> Result<RwLockReadGuard<'a, Self>, DataError> {
Ok(SETTINGS
.get()
.ok_or_else(|| DataError::InitializedError("settings".to_string()))?
.read()
.await)
}
}