forked from didirus/AstralRinth
Event handling (#75)
* working on amcros * fleshed out draft * added feature support * finished loading * Fixed issue with multiple data types in macro * Working, and added more loading uses * added window scopes * clippy, fmt * working other variants * fmt; clippy * prettier * refactored emissions to use increment * fixed deadlock * doc changes * clippy, prettier * uuid change * restructured events to util * loading restructure * merge fixes * comments mistake * better cfg tauri feature structuring * added extra fields to some loading enum variants * removed Option<> * added pack + version labels * doc change
This commit is contained in:
@@ -4,6 +4,9 @@ use tokio::io::{AsyncBufReadExt, BufReader};
|
||||
use tokio::process::{ChildStderr, ChildStdout};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::event::emit::emit_process;
|
||||
use crate::event::ProcessPayloadType;
|
||||
|
||||
use super::Profile;
|
||||
|
||||
// Child processes (instances of Minecraft)
|
||||
@@ -13,6 +16,7 @@ pub struct Children(HashMap<u32, Arc<RwLock<MinecraftChild>>>);
|
||||
// Minecraft Child, bundles together the PID, the actual Child, and the easily queryable stdout and stderr streams
|
||||
#[derive(Debug)]
|
||||
pub struct MinecraftChild {
|
||||
pub uuid: uuid::Uuid,
|
||||
pub pid: u32,
|
||||
pub profile_path: PathBuf, //todo: make UUID when profiles are recognized by UUID
|
||||
pub child: tokio::process::Child,
|
||||
@@ -28,12 +32,14 @@ impl Children {
|
||||
// Inserts a child process to keep track of, and returns a reference to the container struct MinecraftChild
|
||||
// The threads for stdout and stderr are spawned here
|
||||
// Unlike a Hashmap's 'insert', this directly returns the reference to the Child rather than any previously stored Child that may exist
|
||||
pub fn insert_process(
|
||||
pub async fn insert_process(
|
||||
&mut self,
|
||||
pid: u32,
|
||||
profile_path: PathBuf,
|
||||
mut child: tokio::process::Child,
|
||||
) -> Arc<RwLock<MinecraftChild>> {
|
||||
) -> crate::Result<Arc<RwLock<MinecraftChild>>> {
|
||||
let uuid = uuid::Uuid::new_v4();
|
||||
|
||||
// Create std watcher threads for stdout and stderr
|
||||
let stdout = SharedOutput::new();
|
||||
if let Some(child_stdout) = child.stdout.take() {
|
||||
@@ -54,8 +60,17 @@ impl Children {
|
||||
});
|
||||
}
|
||||
|
||||
emit_process(
|
||||
uuid,
|
||||
pid,
|
||||
ProcessPayloadType::Launched,
|
||||
"Launched Minecraft",
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Create MinecraftChild
|
||||
let mchild = MinecraftChild {
|
||||
uuid,
|
||||
pid,
|
||||
profile_path,
|
||||
child,
|
||||
@@ -64,7 +79,7 @@ impl Children {
|
||||
};
|
||||
let mchild = Arc::new(RwLock::new(mchild));
|
||||
self.0.insert(pid, mchild.clone());
|
||||
mchild
|
||||
Ok(mchild)
|
||||
}
|
||||
|
||||
// Returns a ref to the child
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
//! Theseus state management system
|
||||
use crate::config::sled_config;
|
||||
use crate::event::emit::emit_loading;
|
||||
|
||||
use crate::event::emit::init_loading;
|
||||
use crate::event::LoadingBarType;
|
||||
use crate::jre;
|
||||
use crate::loading_join;
|
||||
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::{OnceCell, RwLock, Semaphore};
|
||||
|
||||
@@ -68,6 +74,8 @@ impl State {
|
||||
LAUNCHER_STATE
|
||||
.get_or_try_init(|| {
|
||||
async {
|
||||
|
||||
let loading_bar = init_loading(LoadingBarType::StateInit, 100.0, "Initializing launcher...").await?;
|
||||
// Directories
|
||||
let directories = DirectoryInfo::init().await?;
|
||||
|
||||
@@ -77,6 +85,8 @@ impl State {
|
||||
.path(directories.database_file())
|
||||
.open()?;
|
||||
|
||||
emit_loading(&loading_bar, 10.0, None).await?;
|
||||
|
||||
// Settings
|
||||
let mut settings =
|
||||
Settings::init(&directories.settings_file()).await?;
|
||||
@@ -87,11 +97,17 @@ impl State {
|
||||
let io_semaphore =
|
||||
RwLock::new(Semaphore::new(io_semaphore_max));
|
||||
|
||||
let metadata_fut = Metadata::init(&database);
|
||||
let profiles_fut =
|
||||
Profiles::init(&directories, &io_semaphore);
|
||||
|
||||
// Launcher data
|
||||
let (metadata, profiles) = tokio::try_join! {
|
||||
Metadata::init(&database),
|
||||
Profiles::init(&directories, &io_semaphore),
|
||||
}?;
|
||||
let (metadata, profiles) = loading_join! {
|
||||
Some(&loading_bar), 20.0, Some("Initializing metadata and profiles...");
|
||||
metadata_fut, profiles_fut
|
||||
};
|
||||
|
||||
emit_loading(&loading_bar, 10.0, None).await?;
|
||||
let users = Users::init(&database)?;
|
||||
|
||||
let children = Children::new();
|
||||
@@ -101,13 +117,16 @@ impl State {
|
||||
// On launcher initialization, attempt a tag fetch after tags init
|
||||
let mut tags = Tags::init(&database)?;
|
||||
if let Err(tag_fetch_err) =
|
||||
tags.fetch_update(&io_semaphore).await
|
||||
tags.fetch_update(&io_semaphore,Some(&loading_bar)).await
|
||||
{
|
||||
tracing::error!(
|
||||
"Failed to fetch tags on launcher init: {}",
|
||||
tag_fetch_err
|
||||
);
|
||||
};
|
||||
|
||||
emit_loading(&loading_bar, 10.0, None).await?;
|
||||
|
||||
// On launcher initialization, if global java variables are unset, try to find and set them
|
||||
// (they are required for the game to launch)
|
||||
if settings.java_globals.count() == 0 {
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
use super::settings::{Hooks, MemorySettings, WindowSize};
|
||||
use crate::config::MODRINTH_API_URL;
|
||||
use crate::data::DirectoryInfo;
|
||||
use crate::event::emit::{
|
||||
emit_profile, init_loading, loading_try_for_each_concurrent,
|
||||
};
|
||||
use crate::event::{LoadingBarType, ProfilePayloadType};
|
||||
use crate::state::projects::Project;
|
||||
use crate::state::{ModrinthVersion, ProjectType};
|
||||
use crate::util::fetch::{fetch, fetch_json, write, write_cached_icon};
|
||||
@@ -17,6 +21,7 @@ use std::{
|
||||
};
|
||||
use tokio::sync::Semaphore;
|
||||
use tokio::{fs, sync::RwLock};
|
||||
use uuid::Uuid;
|
||||
|
||||
const PROFILE_JSON_PATH: &str = "profile.json";
|
||||
|
||||
@@ -28,6 +33,7 @@ pub const CURRENT_FORMAT_VERSION: u32 = 1;
|
||||
// Represent a Minecraft instance.
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct Profile {
|
||||
pub uuid: Uuid, // todo: will be used in restructure to refer to profiles
|
||||
pub path: PathBuf,
|
||||
pub metadata: ProfileMetadata,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
@@ -90,6 +96,7 @@ pub struct JavaSettings {
|
||||
impl Profile {
|
||||
#[tracing::instrument]
|
||||
pub async fn new(
|
||||
uuid: Uuid,
|
||||
name: String,
|
||||
version: String,
|
||||
path: PathBuf,
|
||||
@@ -102,6 +109,7 @@ impl Profile {
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
uuid,
|
||||
path: canonicalize(path)?,
|
||||
metadata: ProfileMetadata {
|
||||
name,
|
||||
@@ -368,7 +376,14 @@ impl Profiles {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub fn insert(&mut self, profile: Profile) -> crate::Result<&Self> {
|
||||
pub async fn insert(&mut self, profile: Profile) -> crate::Result<&Self> {
|
||||
emit_profile(
|
||||
profile.uuid,
|
||||
profile.path.clone(),
|
||||
&profile.metadata.name,
|
||||
ProfilePayloadType::Added,
|
||||
)
|
||||
.await?;
|
||||
self.0.insert(
|
||||
canonicalize(&profile.path)?
|
||||
.to_str()
|
||||
@@ -387,6 +402,7 @@ impl Profiles {
|
||||
path: &'a Path,
|
||||
) -> crate::Result<&Self> {
|
||||
self.insert(Self::read_profile_from_dir(&canonicalize(path)?).await?)
|
||||
.await
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
@@ -404,9 +420,21 @@ impl Profiles {
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn sync(&self) -> crate::Result<&Self> {
|
||||
stream::iter(self.0.iter())
|
||||
.map(Ok::<_, crate::Error>)
|
||||
.try_for_each_concurrent(None, |(path, profile)| async move {
|
||||
let loading_bar = init_loading(
|
||||
LoadingBarType::ProfileSync,
|
||||
100.0,
|
||||
"Syncing profiles...",
|
||||
)
|
||||
.await?;
|
||||
let num_futs = self.0.len();
|
||||
loading_try_for_each_concurrent(
|
||||
stream::iter(self.0.iter()).map(Ok::<_, crate::Error>),
|
||||
None,
|
||||
Some(&loading_bar),
|
||||
100.0,
|
||||
num_futs,
|
||||
None,
|
||||
|(path, profile)| async move {
|
||||
let json = serde_json::to_vec(&profile)?;
|
||||
|
||||
let json_path = Path::new(&path.to_string_lossy().to_string())
|
||||
@@ -414,8 +442,9 @@ impl Profiles {
|
||||
|
||||
fs::write(json_path, json).await?;
|
||||
Ok::<_, crate::Error>(())
|
||||
})
|
||||
.await?;
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ use serde::{Deserialize, Serialize};
|
||||
use tokio::sync::{RwLock, Semaphore};
|
||||
|
||||
use crate::config::{BINCODE_CONFIG, MODRINTH_API_URL};
|
||||
use crate::event::LoadingBarId;
|
||||
use crate::loading_join;
|
||||
use crate::util::fetch::fetch_json;
|
||||
|
||||
const CATEGORIES_DB_TREE: &[u8] = b"categories";
|
||||
@@ -139,6 +141,7 @@ impl Tags {
|
||||
pub async fn fetch_update(
|
||||
&mut self,
|
||||
semaphore: &RwLock<Semaphore>,
|
||||
loading_bar: Option<&LoadingBarId>,
|
||||
) -> crate::Result<()> {
|
||||
let categories = format!("{MODRINTH_API_URL}tag/category");
|
||||
let loaders = format!("{MODRINTH_API_URL}tag/loader");
|
||||
@@ -147,6 +150,50 @@ impl Tags {
|
||||
let donation_platforms =
|
||||
format!("{MODRINTH_API_URL}tag/donation_platform");
|
||||
let report_types = format!("{MODRINTH_API_URL}tag/report_type");
|
||||
|
||||
let categories_fut = fetch_json::<Vec<Category>>(
|
||||
Method::GET,
|
||||
&categories,
|
||||
None,
|
||||
None,
|
||||
semaphore,
|
||||
);
|
||||
let loaders_fut = fetch_json::<Vec<Loader>>(
|
||||
Method::GET,
|
||||
&loaders,
|
||||
None,
|
||||
None,
|
||||
semaphore,
|
||||
);
|
||||
let game_versions_fut = fetch_json::<Vec<GameVersion>>(
|
||||
Method::GET,
|
||||
&game_versions,
|
||||
None,
|
||||
None,
|
||||
semaphore,
|
||||
);
|
||||
let licenses_fut = fetch_json::<Vec<License>>(
|
||||
Method::GET,
|
||||
&licenses,
|
||||
None,
|
||||
None,
|
||||
semaphore,
|
||||
);
|
||||
let donation_platforms_fut = fetch_json::<Vec<DonationPlatform>>(
|
||||
Method::GET,
|
||||
&donation_platforms,
|
||||
None,
|
||||
None,
|
||||
semaphore,
|
||||
);
|
||||
let report_types_fut = fetch_json::<Vec<String>>(
|
||||
Method::GET,
|
||||
&report_types,
|
||||
None,
|
||||
None,
|
||||
semaphore,
|
||||
);
|
||||
|
||||
let (
|
||||
categories,
|
||||
loaders,
|
||||
@@ -154,50 +201,14 @@ impl Tags {
|
||||
licenses,
|
||||
donation_platforms,
|
||||
report_types,
|
||||
) = tokio::try_join!(
|
||||
fetch_json::<Vec<Category>>(
|
||||
Method::GET,
|
||||
&categories,
|
||||
None,
|
||||
None,
|
||||
semaphore
|
||||
),
|
||||
fetch_json::<Vec<Loader>>(
|
||||
Method::GET,
|
||||
&loaders,
|
||||
None,
|
||||
None,
|
||||
semaphore
|
||||
),
|
||||
fetch_json::<Vec<GameVersion>>(
|
||||
Method::GET,
|
||||
&game_versions,
|
||||
None,
|
||||
None,
|
||||
semaphore
|
||||
),
|
||||
fetch_json::<Vec<License>>(
|
||||
Method::GET,
|
||||
&licenses,
|
||||
None,
|
||||
None,
|
||||
semaphore
|
||||
),
|
||||
fetch_json::<Vec<DonationPlatform>>(
|
||||
Method::GET,
|
||||
&donation_platforms,
|
||||
None,
|
||||
None,
|
||||
semaphore
|
||||
),
|
||||
fetch_json::<Vec<String>>(
|
||||
Method::GET,
|
||||
&report_types,
|
||||
None,
|
||||
None,
|
||||
semaphore
|
||||
),
|
||||
)?;
|
||||
) = loading_join!(loading_bar, 0.5, None;
|
||||
categories_fut,
|
||||
loaders_fut,
|
||||
game_versions_fut,
|
||||
licenses_fut,
|
||||
donation_platforms_fut,
|
||||
report_types_fut
|
||||
);
|
||||
|
||||
// Store the tags in the database
|
||||
self.0.categories.insert(
|
||||
|
||||
Reference in New Issue
Block a user