chore: lint
This commit is contained in:
parent
d7ca81c2ed
commit
2ebb8197ac
48 changed files with 618 additions and 435 deletions
|
|
@ -1,4 +1,5 @@
|
|||
use onelauncher_core::{constants::{NATIVE_ARCH, TARGET_OS}, store::Core};
|
||||
use onelauncher_core::constants::{NATIVE_ARCH, TARGET_OS};
|
||||
use onelauncher_core::store::Core;
|
||||
use serde::Serialize;
|
||||
use specta::Type;
|
||||
|
||||
|
|
@ -14,7 +15,6 @@ pub struct ProgramInfo {
|
|||
|
||||
#[must_use]
|
||||
pub fn get_program_info() -> ProgramInfo {
|
||||
|
||||
ProgramInfo {
|
||||
launcher_version: Core::get().launcher_version.clone(),
|
||||
webview_version: tauri::webview_version().unwrap_or_else(|_| "UNKNOWN".into()),
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
// === API ===
|
||||
pub const CURSEFORGE_API_KEY: &str = "$2a$10$6utA1UNSmFPrE/Lh7b7ndeeGmiOkjKNY8kpFB0fsmE/d42ZAfFgCe";
|
||||
pub const DISCORD_CLIENT_ID: &str = "1274084193193955408";
|
||||
pub const DISCORD_CLIENT_ID: &str = "1274084193193955408";
|
||||
|
|
|
|||
|
|
@ -7,7 +7,10 @@ pub trait OneLauncherApi {
|
|||
|
||||
async fn open_dev_tools<R: Runtime>(webview_window: tauri::WebviewWindow<R>);
|
||||
|
||||
async fn set_window_style<R: Runtime>(webview_window: tauri::WebviewWindow<R>, decorations: bool);
|
||||
async fn set_window_style<R: Runtime>(
|
||||
webview_window: tauri::WebviewWindow<R>,
|
||||
decorations: bool,
|
||||
);
|
||||
}
|
||||
|
||||
#[taurpc::ipc_type]
|
||||
|
|
@ -15,7 +18,6 @@ pub struct OneLauncherApiImpl;
|
|||
|
||||
#[taurpc::resolvers]
|
||||
impl OneLauncherApi for OneLauncherApiImpl {
|
||||
|
||||
async fn return_error(self) -> LauncherResult<()> {
|
||||
let err = DaoError::NotFound.into();
|
||||
Err(err)
|
||||
|
|
@ -26,7 +28,11 @@ impl OneLauncherApi for OneLauncherApiImpl {
|
|||
webview_window.open_devtools();
|
||||
}
|
||||
|
||||
async fn set_window_style<R: Runtime>(self, webview_window: tauri::WebviewWindow<R>, decorations: bool) {
|
||||
async fn set_window_style<R: Runtime>(
|
||||
self,
|
||||
webview_window: tauri::WebviewWindow<R>,
|
||||
decorations: bool,
|
||||
) {
|
||||
webview_window.set_decorations(decorations).ok();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use onelauncher_core::{constants::{NATIVE_ARCH, TARGET_OS}, store::Core};
|
||||
use onelauncher_core::constants::{NATIVE_ARCH, TARGET_OS};
|
||||
use onelauncher_core::store::Core;
|
||||
use serde::Serialize;
|
||||
use specta::Type;
|
||||
|
||||
|
|
@ -14,7 +15,6 @@ pub struct ProgramInfo {
|
|||
|
||||
#[must_use]
|
||||
pub fn get_program_info() -> ProgramInfo {
|
||||
|
||||
ProgramInfo {
|
||||
launcher_version: Core::get().launcher_version.clone(),
|
||||
webview_version: tauri::webview_version().unwrap_or_else(|_| "UNKNOWN".into()),
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
// === API ===
|
||||
pub const CURSEFORGE_API_KEY: &str = "$2a$10$6utA1UNSmFPrE/Lh7b7ndeeGmiOkjKNY8kpFB0fsmE/d42ZAfFgCe";
|
||||
pub const DISCORD_CLIENT_ID: &str = "1274084193193955408";
|
||||
pub const DISCORD_CLIENT_ID: &str = "1274084193193955408";
|
||||
|
|
|
|||
|
|
@ -1,39 +1,44 @@
|
|||
use onelauncher_entity::clusters;
|
||||
|
||||
use crate::{error::LauncherResult, store::Dirs, utils::io};
|
||||
use crate::error::LauncherResult;
|
||||
use crate::store::Dirs;
|
||||
use crate::utils::io;
|
||||
|
||||
pub async fn get_screenshots(cluster: &clusters::Model) -> LauncherResult<Vec<String>> {
|
||||
let dir = cluster.folder_name.clone();
|
||||
let path = Dirs::get_clusters_dir().await?.join(dir).join("screenshots");
|
||||
let dir = cluster.folder_name.clone();
|
||||
let path = Dirs::get_clusters_dir()
|
||||
.await?
|
||||
.join(dir)
|
||||
.join("screenshots");
|
||||
|
||||
if !path.exists() {
|
||||
io::create_dir(&path).await?;
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
if !path.exists() {
|
||||
io::create_dir(&path).await?;
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
|
||||
let mut list = vec![];
|
||||
let mut files = io::read_dir(path).await?;
|
||||
while let Ok(Some(entry)) = files.next_entry().await {
|
||||
list.push(entry.file_name().to_string_lossy().to_string());
|
||||
}
|
||||
let mut list = vec![];
|
||||
let mut files = io::read_dir(path).await?;
|
||||
while let Ok(Some(entry)) = files.next_entry().await {
|
||||
list.push(entry.file_name().to_string_lossy().to_string());
|
||||
}
|
||||
|
||||
Ok(list)
|
||||
}
|
||||
|
||||
pub async fn get_worlds(cluster: &clusters::Model) -> LauncherResult<Vec<String>> {
|
||||
let dir = cluster.folder_name.clone();
|
||||
let path = Dirs::get_clusters_dir().await?.join(dir).join("saves");
|
||||
let dir = cluster.folder_name.clone();
|
||||
let path = Dirs::get_clusters_dir().await?.join(dir).join("saves");
|
||||
|
||||
if !path.exists() {
|
||||
io::create_dir(&path).await?;
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
if !path.exists() {
|
||||
io::create_dir(&path).await?;
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
|
||||
let mut list = vec![];
|
||||
let mut files = io::read_dir(path).await?;
|
||||
while let Ok(Some(entry)) = files.next_entry().await {
|
||||
list.push(entry.file_name().to_string_lossy().to_string());
|
||||
}
|
||||
let mut list = vec![];
|
||||
let mut files = io::read_dir(path).await?;
|
||||
while let Ok(Some(entry)) = files.next_entry().await {
|
||||
list.push(entry.file_name().to_string_lossy().to_string());
|
||||
}
|
||||
|
||||
Ok(list)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ use chrono::Utc;
|
|||
use dao::ClusterId;
|
||||
use interpulse::api::minecraft::VersionInfo;
|
||||
use onelauncher_entity::cluster_stage::ClusterStage;
|
||||
use onelauncher_entity::{clusters, java_versions};
|
||||
use onelauncher_entity::icon::Icon;
|
||||
use onelauncher_entity::loader::GameLoader;
|
||||
use onelauncher_entity::{clusters, java_versions};
|
||||
use sea_orm::ActiveValue::Set;
|
||||
use tokio::process::Command;
|
||||
|
||||
|
|
@ -17,8 +17,8 @@ use crate::store::ingress::{IngressType, SubIngress};
|
|||
use crate::store::{Dirs, State};
|
||||
use crate::utils::io::{self, IOError};
|
||||
|
||||
pub mod dao;
|
||||
pub mod content;
|
||||
pub mod dao;
|
||||
|
||||
mod sync;
|
||||
pub use sync::*;
|
||||
|
|
@ -109,7 +109,10 @@ pub async fn create_cluster(
|
|||
}
|
||||
|
||||
/// Make a cluster playable (installs all necessary game files and dependencies required to play)
|
||||
pub async fn prepare_cluster(cluster: &mut clusters::Model, force: Option<bool>) -> LauncherResult<&clusters::Model> {
|
||||
pub async fn prepare_cluster(
|
||||
cluster: &mut clusters::Model,
|
||||
force: Option<bool>,
|
||||
) -> LauncherResult<&clusters::Model> {
|
||||
tracing::debug!("preparing cluster {}", cluster.name);
|
||||
|
||||
// if cluster.stage == ClusterStage::Ready {
|
||||
|
|
@ -117,7 +120,6 @@ pub async fn prepare_cluster(cluster: &mut clusters::Model, force: Option<bool>)
|
|||
// return Ok(cluster);
|
||||
// }
|
||||
|
||||
|
||||
dao::update_cluster(cluster, async |mut cluster| {
|
||||
cluster.stage = Set(ClusterStage::Downloading);
|
||||
Ok(cluster)
|
||||
|
|
@ -161,7 +163,8 @@ async fn install_cluster(cluster: &clusters::Model, force: Option<bool>) -> Laun
|
|||
// TASK 1
|
||||
ingress_id.set_ingress_message("fetching data").await?;
|
||||
let setting_profile =
|
||||
setting_profiles::dao::get_profile_or_default(cluster.setting_profile_name.as_ref()).await?;
|
||||
setting_profiles::dao::get_profile_or_default(cluster.setting_profile_name.as_ref())
|
||||
.await?;
|
||||
|
||||
let state = State::get().await?;
|
||||
let mut metadata = state.metadata.write().await;
|
||||
|
|
@ -193,7 +196,9 @@ async fn install_cluster(cluster: &clusters::Model, force: Option<bool>) -> Laun
|
|||
)
|
||||
.await?;
|
||||
|
||||
let java_version = if let Some(version) = java::get_recommended_java(&version_info, Some(&setting_profile)).await? {
|
||||
let java_version = if let Some(version) =
|
||||
java::get_recommended_java(&version_info, Some(&setting_profile)).await?
|
||||
{
|
||||
version
|
||||
} else {
|
||||
let Some(ver) = &version_info.java_version else {
|
||||
|
|
@ -204,10 +209,22 @@ async fn install_cluster(cluster: &clusters::Model, force: Option<bool>) -> Laun
|
|||
};
|
||||
|
||||
// TASK 3
|
||||
download_minecraft(&version_info, &java_version.arch, Some(&SubIngress::new(&ingress_id, INGRESS_STEP)), force).await?;
|
||||
download_minecraft(
|
||||
&version_info,
|
||||
&java_version.arch,
|
||||
Some(&SubIngress::new(&ingress_id, INGRESS_STEP)),
|
||||
force,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// TASK 4
|
||||
run_forge_processors(cluster, &mut version_info, java_version, &SubIngress::new(&ingress_id, INGRESS_STEP)).await?;
|
||||
run_forge_processors(
|
||||
cluster,
|
||||
&mut version_info,
|
||||
java_version,
|
||||
&SubIngress::new(&ingress_id, INGRESS_STEP),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -265,23 +282,23 @@ async fn run_forge_processors(
|
|||
server => "";
|
||||
}
|
||||
|
||||
ingress.set_ingress_message("running forge processors").await?;
|
||||
ingress
|
||||
.set_ingress_message("running forge processors")
|
||||
.await?;
|
||||
let total_length = processors.len();
|
||||
for (index, processor) in processors.iter().enumerate() {
|
||||
if let Some(sides) = &processor.sides
|
||||
&& !sides.contains(&String::from("client")) {
|
||||
continue;
|
||||
}
|
||||
&& !sides.contains(&String::from("client"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut cp = processor.classpath.clone();
|
||||
cp.push(processor.jar.clone());
|
||||
|
||||
let child = Command::new(&java_version.absolute_path)
|
||||
.arg("-cp")
|
||||
.arg(arguments::get_classpath_library(
|
||||
&libraries,
|
||||
&cp,
|
||||
)?)
|
||||
.arg(arguments::get_classpath_library(&libraries, &cp)?)
|
||||
.arg(
|
||||
arguments::main_class(arguments::get_library(&libraries, &processor.jar, false)?)
|
||||
.await?
|
||||
|
|
@ -307,7 +324,9 @@ async fn run_forge_processors(
|
|||
.into());
|
||||
}
|
||||
|
||||
ingress.set_ingress_message(&format!("running forge processors {index}/{total_length}")).await?;
|
||||
ingress
|
||||
.set_ingress_message(&format!("running forge processors {index}/{total_length}"))
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
@ -315,11 +334,16 @@ async fn run_forge_processors(
|
|||
|
||||
pub async fn update_playtime(id: ClusterId, duration: i64) -> LauncherResult<clusters::Model> {
|
||||
dao::update_cluster_by_id(id, async |mut cluster| {
|
||||
let overall_played = cluster.overall_played.take().unwrap_or_default().unwrap_or_default();
|
||||
let overall_played = cluster
|
||||
.overall_played
|
||||
.take()
|
||||
.unwrap_or_default()
|
||||
.unwrap_or_default();
|
||||
|
||||
cluster.overall_played = Set(Some(overall_played + duration));
|
||||
cluster.last_played = Set(Some(Utc::now()));
|
||||
|
||||
Ok(cluster)
|
||||
}).await
|
||||
}
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
use std::{collections::HashSet, path::PathBuf};
|
||||
use std::collections::HashSet;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use onelauncher_entity::clusters;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{error::{DaoError, LauncherResult}, send_error, store::Dirs, utils::io};
|
||||
use crate::error::{DaoError, LauncherResult};
|
||||
use crate::send_error;
|
||||
use crate::store::Dirs;
|
||||
use crate::utils::io;
|
||||
|
||||
use super::dao::{self, ClusterId};
|
||||
|
||||
|
|
@ -42,10 +46,7 @@ pub async fn sync_clusters() -> LauncherResult<Vec<ClusterId>> {
|
|||
missing_ids.push(cluster.id);
|
||||
|
||||
let path = cluster.folder_name.clone();
|
||||
send_error!(
|
||||
"cluster {} is missing from the filesystem",
|
||||
path.clone()
|
||||
);
|
||||
send_error!("cluster {} is missing from the filesystem", path.clone());
|
||||
}
|
||||
|
||||
checked_folders.insert(dirs.clusters_dir().join(cluster.folder_name.clone()));
|
||||
|
|
@ -56,22 +57,22 @@ pub async fn sync_clusters() -> LauncherResult<Vec<ClusterId>> {
|
|||
// and checks if they are in the database
|
||||
let cluster_dir = Dirs::get_clusters_dir().await?;
|
||||
if cluster_dir.exists() {
|
||||
let mut stream = io::read_dir(cluster_dir).await?;
|
||||
while let Ok(Some(entry)) = stream.next_entry().await {
|
||||
let path = entry.path();
|
||||
if !path.is_dir() || checked_folders.contains(&path) {
|
||||
// Skip if it's not a directory or if we've already checked it
|
||||
continue;
|
||||
}
|
||||
let mut stream = io::read_dir(cluster_dir).await?;
|
||||
while let Ok(Some(entry)) = stream.next_entry().await {
|
||||
let path = entry.path();
|
||||
if !path.is_dir() || checked_folders.contains(&path) {
|
||||
// Skip if it's not a directory or if we've already checked it
|
||||
continue;
|
||||
}
|
||||
|
||||
// We have a directory that is not in the database
|
||||
if let Err(err) = sync_from_fs_to_db(&path).await {
|
||||
tracing::error!("failed to sync cluster from fs to db: {}", err);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
io::create_dir_all(cluster_dir).await?;
|
||||
}
|
||||
// We have a directory that is not in the database
|
||||
if let Err(err) = sync_from_fs_to_db(&path).await {
|
||||
tracing::error!("failed to sync cluster from fs to db: {}", err);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
io::create_dir_all(cluster_dir).await?;
|
||||
}
|
||||
|
||||
Ok(missing_ids)
|
||||
}
|
||||
|
|
@ -80,7 +81,9 @@ pub async fn sync_clusters() -> LauncherResult<Vec<ClusterId>> {
|
|||
/// Checks if the cluster in the database exists and if the directory exists.
|
||||
#[tracing::instrument]
|
||||
pub async fn sync_cluster(cluster: &clusters::Model) -> LauncherResult<SyncAction> {
|
||||
let path = Dirs::get_clusters_dir().await?.join(cluster.folder_name.clone());
|
||||
let path = Dirs::get_clusters_dir()
|
||||
.await?
|
||||
.join(cluster.folder_name.clone());
|
||||
if !path.exists() {
|
||||
return Ok(SyncAction::MissingFs);
|
||||
}
|
||||
|
|
@ -119,5 +122,6 @@ async fn sync_from_fs_to_db(path: &PathBuf) -> LauncherResult<clusters::Model> {
|
|||
Err(anyhow::anyhow!(
|
||||
"TODO: failed to sync cluster from fs to db: {}",
|
||||
path.display()
|
||||
).into())
|
||||
}
|
||||
)
|
||||
.into())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use onelauncher_entity::prelude::model::*;
|
|||
use tokio::process::Command;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::api::cluster::{prepare_cluster, update_playtime, ClusterError};
|
||||
use crate::api::cluster::{ClusterError, prepare_cluster, update_playtime};
|
||||
use crate::api::game::{arguments, log, metadata};
|
||||
use crate::api::java::{self, JavaError};
|
||||
use crate::api::setting_profiles::dao::get_profile_by_name;
|
||||
|
|
@ -32,9 +32,10 @@ pub async fn launch_minecraft(
|
|||
prepare_cluster(cluster, force).await?;
|
||||
let mut settings = get_global_profile().await;
|
||||
if let Some(name) = &cluster.setting_profile_name
|
||||
&& let Some(profile) = get_profile_by_name(name).await? {
|
||||
settings.merge(profile);
|
||||
}
|
||||
&& let Some(profile) = get_profile_by_name(name).await?
|
||||
{
|
||||
settings.merge(profile);
|
||||
}
|
||||
|
||||
let state = State::get().await?;
|
||||
let dirs = Dirs::get().await?;
|
||||
|
|
@ -68,9 +69,7 @@ pub async fn launch_minecraft(
|
|||
|
||||
let version_name = loader_version
|
||||
.as_ref()
|
||||
.map_or(version.id.clone(), |it| {
|
||||
format!("{}-{}", version.id, it.id)
|
||||
});
|
||||
.map_or(version.id.clone(), |it| format!("{}-{}", version.id, it.id));
|
||||
|
||||
let version_info =
|
||||
metadata::download_version_info(&version, loader_version.as_ref(), None, None).await?;
|
||||
|
|
@ -188,11 +187,16 @@ pub async fn launch_minecraft(
|
|||
let censors = log::create_censors(&creds);
|
||||
|
||||
if let Some(discord) = &state.rpc {
|
||||
discord.set_message(
|
||||
&format!("Playing {}", cluster.name),
|
||||
Some(Timestamps::new().start(Utc::now().timestamp())),
|
||||
).await;
|
||||
discord
|
||||
.set_message(
|
||||
&format!("Playing {}", cluster.name),
|
||||
Some(Timestamps::new().start(Utc::now().timestamp())),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
state.processes.spawn(cluster.id, cwd.clone(), creds, censors, &settings, command).await
|
||||
state
|
||||
.processes
|
||||
.spawn(cluster.id, cwd.clone(), creds, censors, &settings, command)
|
||||
.await
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,9 +5,7 @@ use crate::store::credentials::MinecraftCredentials;
|
|||
pub type CensorMap = HashMap<String, String>;
|
||||
|
||||
#[must_use]
|
||||
pub fn create_censors(
|
||||
creds: &MinecraftCredentials,
|
||||
) -> CensorMap {
|
||||
pub fn create_censors(creds: &MinecraftCredentials) -> CensorMap {
|
||||
let mut map = HashMap::new();
|
||||
|
||||
let username = whoami::username();
|
||||
|
|
@ -24,7 +22,10 @@ pub fn create_censors(
|
|||
|
||||
map.insert(creds.username.clone(), "{MC_USERNAME}".to_string());
|
||||
map.insert(creds.id.as_simple().to_string(), "{MC_UUID}".to_string());
|
||||
map.insert(creds.id.as_hyphenated().to_string(), "{MC_UUID}".to_string());
|
||||
map.insert(
|
||||
creds.id.as_hyphenated().to_string(),
|
||||
"{MC_UUID}".to_string(),
|
||||
);
|
||||
|
||||
map
|
||||
}
|
||||
|
|
@ -33,4 +34,4 @@ pub fn censor_line(censors: &CensorMap, line: &mut String) {
|
|||
for (key, value) in censors {
|
||||
*line = line.replace(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,9 +76,10 @@ pub fn validate_os_rule(rule: &OsRule, java_arch: &str, updated: bool) -> bool {
|
|||
}
|
||||
|
||||
if let Some(version) = &rule.version
|
||||
&& let Ok(regex) = Regex::new(version.as_str()) {
|
||||
rule_match &= regex.is_match(&sysinfo::System::os_version().unwrap_or_default());
|
||||
}
|
||||
&& let Ok(regex) = Regex::new(version.as_str())
|
||||
{
|
||||
rule_match &= regex.is_match(&sysinfo::System::os_version().unwrap_or_default());
|
||||
}
|
||||
|
||||
rule_match
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,31 +67,33 @@ pub async fn send_ingress_message(
|
|||
}
|
||||
|
||||
/// Sends an ingress update with the given increment
|
||||
pub async fn send_ingress(
|
||||
id: &IngressId,
|
||||
increment: f64,
|
||||
) -> LauncherResult<()> {
|
||||
pub async fn send_ingress(id: &IngressId, increment: f64) -> LauncherResult<()> {
|
||||
send_ingress_message(id, increment, None).await
|
||||
}
|
||||
|
||||
/// Sends an ingress update with the given message (does not increment)
|
||||
pub async fn set_ingress_message(
|
||||
id: &IngressId,
|
||||
message: &str,
|
||||
) -> LauncherResult<()> {
|
||||
pub async fn set_ingress_message(id: &IngressId, message: &str) -> LauncherResult<()> {
|
||||
send_ingress_message(id, 0.0, Some(message)).await
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait IngressSendExt {
|
||||
async fn send_ingress_message(&self, increment: f64, message: Option<&str>) -> LauncherResult<()>;
|
||||
async fn send_ingress_message(
|
||||
&self,
|
||||
increment: f64,
|
||||
message: Option<&str>,
|
||||
) -> LauncherResult<()>;
|
||||
async fn send_ingress(&self, increment: f64) -> LauncherResult<()>;
|
||||
async fn set_ingress_message(&self, message: &str) -> LauncherResult<()>;
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl IngressSendExt for IngressId {
|
||||
async fn send_ingress_message(&self, increment: f64, message: Option<&str>) -> LauncherResult<()> {
|
||||
async fn send_ingress_message(
|
||||
&self,
|
||||
increment: f64,
|
||||
message: Option<&str>,
|
||||
) -> LauncherResult<()> {
|
||||
send_ingress_message(self, increment, message).await
|
||||
}
|
||||
|
||||
|
|
@ -106,7 +108,11 @@ impl IngressSendExt for IngressId {
|
|||
|
||||
#[async_trait::async_trait]
|
||||
impl IngressSendExt for Option<&IngressId> {
|
||||
async fn send_ingress_message(&self, increment: f64, message: Option<&str>) -> LauncherResult<()> {
|
||||
async fn send_ingress_message(
|
||||
&self,
|
||||
increment: f64,
|
||||
message: Option<&str>,
|
||||
) -> LauncherResult<()> {
|
||||
if let Some(id) = self {
|
||||
id.send_ingress_message(increment, message).await
|
||||
} else {
|
||||
|
|
@ -133,7 +139,11 @@ impl IngressSendExt for Option<&IngressId> {
|
|||
|
||||
#[async_trait::async_trait]
|
||||
impl IngressSendExt for &SubIngress<'_> {
|
||||
async fn send_ingress_message(&self, increment: f64, message: Option<&str>) -> LauncherResult<()> {
|
||||
async fn send_ingress_message(
|
||||
&self,
|
||||
increment: f64,
|
||||
message: Option<&str>,
|
||||
) -> LauncherResult<()> {
|
||||
self.id.send_ingress_message(increment, message).await
|
||||
}
|
||||
|
||||
|
|
@ -148,7 +158,11 @@ impl IngressSendExt for &SubIngress<'_> {
|
|||
|
||||
#[async_trait::async_trait]
|
||||
impl IngressSendExt for Option<&SubIngress<'_>> {
|
||||
async fn send_ingress_message(&self, increment: f64, message: Option<&str>) -> LauncherResult<()> {
|
||||
async fn send_ingress_message(
|
||||
&self,
|
||||
increment: f64,
|
||||
message: Option<&str>,
|
||||
) -> LauncherResult<()> {
|
||||
if let Some(id) = self {
|
||||
id.send_ingress_message(increment, message).await
|
||||
} else {
|
||||
|
|
@ -175,7 +189,7 @@ impl IngressSendExt for Option<&SubIngress<'_>> {
|
|||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use crate::api::ingress::{init_ingress, IngressSendExt};
|
||||
use crate::api::ingress::{IngressSendExt, init_ingress};
|
||||
use crate::api::proxy::ProxyDynamic;
|
||||
use crate::initialize_core;
|
||||
use crate::store::CoreOptions;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
|
||||
use onelauncher_entity::java_versions;
|
||||
use sea_orm::{ActiveModelTrait, ActiveValue::Set, ColumnTrait, EntityTrait, Order, QueryFilter, QueryOrder};
|
||||
use sea_orm::ActiveValue::Set;
|
||||
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, Order, QueryFilter, QueryOrder};
|
||||
|
||||
use crate::{error::LauncherResult, store::State};
|
||||
use crate::error::LauncherResult;
|
||||
use crate::store::State;
|
||||
|
||||
use super::{JavaError, JavaInfo};
|
||||
|
||||
|
|
@ -21,15 +23,18 @@ pub async fn get_latest_java() -> LauncherResult<Option<java_versions::Model>> {
|
|||
}
|
||||
|
||||
/// Accepts a path to a JRE folder and a [`JavaInfo`] and converts it to a [`java_versions::ActiveModel`]
|
||||
pub fn get_java_model(absolute_path: &Path, info: &JavaInfo) -> LauncherResult<java_versions::ActiveModel> {
|
||||
pub fn get_java_model(
|
||||
absolute_path: &Path,
|
||||
info: &JavaInfo,
|
||||
) -> LauncherResult<java_versions::ActiveModel> {
|
||||
let major_version: u32 = if info.java_version[0..2].eq("1.") {
|
||||
info.java_version[2..3].parse()
|
||||
} else {
|
||||
let split = info.java_version.split_once('.').unwrap_or_default();
|
||||
split.0.parse()
|
||||
}.or_else(
|
||||
|_| info.java_version.parse()
|
||||
).map_err(|e| JavaError::ParseVersion(info.java_version.clone(), e))?;
|
||||
}
|
||||
.or_else(|_| info.java_version.parse())
|
||||
.map_err(|e| JavaError::ParseVersion(info.java_version.clone(), e))?;
|
||||
|
||||
Ok(java_versions::ActiveModel {
|
||||
absolute_path: Set(absolute_path.to_string_lossy().to_string()),
|
||||
|
|
@ -84,7 +89,10 @@ pub async fn get_latest_java_by_major(major: u32) -> LauncherResult<Option<java_
|
|||
}
|
||||
|
||||
/// Accepts a path to a JRE folder and returns the inserted entry
|
||||
pub async fn insert_java(absolute_path: PathBuf, info: JavaInfo) -> LauncherResult<java_versions::Model> {
|
||||
pub async fn insert_java(
|
||||
absolute_path: PathBuf,
|
||||
info: JavaInfo,
|
||||
) -> LauncherResult<java_versions::Model> {
|
||||
let db = &State::get().await?.db;
|
||||
|
||||
let model = get_java_model(&absolute_path, &info)?;
|
||||
|
|
@ -93,7 +101,9 @@ pub async fn insert_java(absolute_path: PathBuf, info: JavaInfo) -> LauncherResu
|
|||
}
|
||||
|
||||
/// Accepts a path to JRE folders and returns the inserted entries
|
||||
pub async fn insert_java_many(java: Vec<(PathBuf, JavaInfo)>) -> LauncherResult<Vec<java_versions::Model>> {
|
||||
pub async fn insert_java_many(
|
||||
java: Vec<(PathBuf, JavaInfo)>,
|
||||
) -> LauncherResult<Vec<java_versions::Model>> {
|
||||
let db = &State::get().await?.db;
|
||||
|
||||
let mut models = Vec::new();
|
||||
|
|
@ -105,4 +115,4 @@ pub async fn insert_java_many(java: Vec<(PathBuf, JavaInfo)>) -> LauncherResult<
|
|||
Ok(java_versions::Entity::insert_many(models)
|
||||
.exec_with_returning_many(db)
|
||||
.await?)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
use onelauncher_entity::loader::GameLoader;
|
||||
use onelauncher_entity::package::PackageType;
|
||||
use onelauncher_entity::{cluster_packages, clusters, packages};
|
||||
use sea_orm::{
|
||||
ActiveModelTrait, ColumnTrait, EntityTrait, IntoActiveModel, QueryFilter,
|
||||
};
|
||||
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, IntoActiveModel, QueryFilter};
|
||||
|
||||
use crate::error::{DaoError, LauncherResult};
|
||||
use crate::store::State;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
use onelauncher_entity::loader::GameLoader;
|
||||
use onelauncher_entity::package::Provider;
|
||||
|
||||
use crate::{api::packages::data::SearchResult, error::LauncherResult};
|
||||
use crate::api::packages::data::SearchResult;
|
||||
use crate::error::LauncherResult;
|
||||
use crate::utils::pagination::Paginated;
|
||||
|
||||
use super::data::{ManagedPackage, ManagedUser, ManagedVersion, PackageAuthor, SearchQuery};
|
||||
|
|
@ -70,11 +71,7 @@ impl ProviderExt for Provider {
|
|||
hashes: &[String],
|
||||
) -> LauncherResult<Vec<ManagedVersion>> {
|
||||
match self {
|
||||
Self::Modrinth => {
|
||||
ModrinthProviderImpl
|
||||
.get_versions_by_hashes(hashes)
|
||||
.await
|
||||
}
|
||||
Self::Modrinth => ModrinthProviderImpl.get_versions_by_hashes(hashes).await,
|
||||
_ => todo!("unimplemented provider"),
|
||||
}
|
||||
}
|
||||
|
|
@ -91,11 +88,7 @@ impl ProviderExt for Provider {
|
|||
author: PackageAuthor,
|
||||
) -> LauncherResult<Vec<ManagedUser>> {
|
||||
match self {
|
||||
Self::Modrinth => {
|
||||
ModrinthProviderImpl
|
||||
.get_users_from_author(author)
|
||||
.await
|
||||
}
|
||||
Self::Modrinth => ModrinthProviderImpl.get_users_from_author(author).await,
|
||||
_ => todo!("unimplemented provider"),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
use super::ProviderExt;
|
||||
use crate::api::packages::data::{
|
||||
ManagedPackage, ManagedUser, ManagedVersion, ManagedVersionDependency, ManagedVersionFile, PackageAuthor, PackageDependencyType, PackageDonationUrl, PackageGallery, PackageLicense, PackageLinks, PackageReleaseType, PackageSide, PackageStatus, SearchQuery, SearchResult, DEFAULT_LIMIT
|
||||
DEFAULT_LIMIT, ManagedPackage, ManagedUser, ManagedVersion, ManagedVersionDependency,
|
||||
ManagedVersionFile, PackageAuthor, PackageDependencyType, PackageDonationUrl, PackageGallery,
|
||||
PackageLicense, PackageLinks, PackageReleaseType, PackageSide, PackageStatus, SearchQuery,
|
||||
SearchResult,
|
||||
};
|
||||
use crate::error::LauncherResult;
|
||||
use crate::utils::http;
|
||||
|
|
@ -52,25 +55,41 @@ impl ProviderExt for ModrinthProviderImpl {
|
|||
|
||||
if let Some(categories) = &filters.categories {
|
||||
for category in categories {
|
||||
builder.and(Facet("categories".to_string(), FacetOperation::Eq, category.to_string()));
|
||||
builder.and(Facet(
|
||||
"categories".to_string(),
|
||||
FacetOperation::Eq,
|
||||
category.to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(mc_versions) = &filters.game_versions {
|
||||
for mc_version in mc_versions {
|
||||
builder.and(Facet("versions".to_string(), FacetOperation::Eq, mc_version.to_string()));
|
||||
builder.and(Facet(
|
||||
"versions".to_string(),
|
||||
FacetOperation::Eq,
|
||||
mc_version.to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(package_types) = &filters.package_types {
|
||||
for package_type in package_types {
|
||||
builder.and(Facet("project_type".to_string(), FacetOperation::Eq, package_type_to_string(package_type)));
|
||||
builder.and(Facet(
|
||||
"project_type".to_string(),
|
||||
FacetOperation::Eq,
|
||||
package_type_to_string(package_type),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(loaders) = &filters.loaders {
|
||||
for loader in loaders {
|
||||
builder.and(Facet("categories".to_string(), FacetOperation::Eq, loader.to_string()));
|
||||
builder.and(Facet(
|
||||
"categories".to_string(),
|
||||
FacetOperation::Eq,
|
||||
loader.to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -166,7 +185,10 @@ impl ProviderExt for ModrinthProviderImpl {
|
|||
// Ok(http::fetch_json(Method::GET, url_v3!("/organizations/{slug}/projects"), None, None).await?)
|
||||
// }
|
||||
|
||||
async fn get_users_from_author(&self, author: PackageAuthor) -> LauncherResult<Vec<ManagedUser>> {
|
||||
async fn get_users_from_author(
|
||||
&self,
|
||||
author: PackageAuthor,
|
||||
) -> LauncherResult<Vec<ManagedUser>> {
|
||||
let (team_id, org_id) = match author {
|
||||
PackageAuthor::Team { team_id, org_id } => (team_id, org_id),
|
||||
PackageAuthor::Users(users) => {
|
||||
|
|
@ -188,14 +210,24 @@ impl ProviderExt for ModrinthProviderImpl {
|
|||
// pub members: Vec<TeamMember>,
|
||||
}
|
||||
|
||||
let organization = http::fetch_json::<Organization>(Method::GET, url_v3!("/organization/{org_id}"), None, None).await?;
|
||||
let organization = http::fetch_json::<Organization>(
|
||||
Method::GET,
|
||||
url_v3!("/organization/{org_id}"),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let org_user = ManagedUser {
|
||||
id: organization.id.clone(),
|
||||
username: organization.name,
|
||||
avatar_url: organization.icon_url,
|
||||
bio: Some(organization.description),
|
||||
url: Some(format!("{}/organization/{}", Provider::Modrinth.website(), organization.id)),
|
||||
url: Some(format!(
|
||||
"{}/organization/{}",
|
||||
Provider::Modrinth.website(),
|
||||
organization.id
|
||||
)),
|
||||
is_organization_user: true,
|
||||
role: None,
|
||||
};
|
||||
|
|
@ -407,7 +439,7 @@ impl From<ModrinthPackage> for ManagedPackage {
|
|||
},
|
||||
status: value.status,
|
||||
downloads: value.downloads,
|
||||
gallery: value.gallery.into_iter().map(Into::into).collect()
|
||||
gallery: value.gallery.into_iter().map(Into::into).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -522,11 +554,7 @@ impl From<ModrinthUser> for ManagedUser {
|
|||
Self {
|
||||
id: value.id.clone(),
|
||||
username: value.username,
|
||||
url: Some(format!(
|
||||
"{}user/{}",
|
||||
Provider::Modrinth.website(),
|
||||
value.id
|
||||
)),
|
||||
url: Some(format!("{}user/{}", Provider::Modrinth.website(), value.id)),
|
||||
avatar_url: value.avatar_url,
|
||||
bio: value.bio,
|
||||
is_organization_user: false,
|
||||
|
|
@ -538,24 +566,24 @@ impl From<ModrinthUser> for ManagedUser {
|
|||
#[derive(Deserialize)]
|
||||
pub struct ModrinthGallery {
|
||||
#[serde(rename = "raw_url")]
|
||||
pub url: String,
|
||||
pub url: String,
|
||||
#[serde(rename = "url")]
|
||||
pub thumbnail_url: String,
|
||||
pub title: Option<String>,
|
||||
pub description: Option<String>,
|
||||
pub featured: Option<bool>
|
||||
pub thumbnail_url: String,
|
||||
pub title: Option<String>,
|
||||
pub description: Option<String>,
|
||||
pub featured: Option<bool>,
|
||||
}
|
||||
|
||||
impl From<ModrinthGallery> for PackageGallery {
|
||||
fn from(value: ModrinthGallery) -> Self {
|
||||
Self {
|
||||
url: value.url,
|
||||
thumbnail_url: value.thumbnail_url,
|
||||
title: value.title,
|
||||
description: value.description,
|
||||
featured: value.featured
|
||||
}
|
||||
}
|
||||
fn from(value: ModrinthGallery) -> Self {
|
||||
Self {
|
||||
url: value.url,
|
||||
thumbnail_url: value.thumbnail_url,
|
||||
title: value.title,
|
||||
description: value.description,
|
||||
featured: value.featured,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
|
||||
use onelauncher_entity::package::Provider;
|
||||
|
||||
use crate::{api::packages::{self, provider::ProviderExt}, error::LauncherResult, store::{Core, CoreOptions, Dirs}};
|
||||
use crate::api::packages::provider::ProviderExt;
|
||||
use crate::api::packages::{self};
|
||||
use crate::error::LauncherResult;
|
||||
use crate::store::{Core, CoreOptions, Dirs};
|
||||
|
||||
#[tokio::test]
|
||||
pub async fn test_get_provider() -> LauncherResult<()> {
|
||||
|
|
@ -22,7 +24,9 @@ pub async fn test_get_multiple() -> LauncherResult<()> {
|
|||
|
||||
let provider = Provider::Modrinth;
|
||||
|
||||
let res = provider.get_multiple(&["oneconfig".to_string(), "chatting".to_string()]).await?;
|
||||
let res = provider
|
||||
.get_multiple(&["oneconfig".to_string(), "chatting".to_string()])
|
||||
.await?;
|
||||
|
||||
assert_eq!(res.len(), 2);
|
||||
|
||||
|
|
@ -35,7 +39,9 @@ pub async fn test_get_versions() -> LauncherResult<()> {
|
|||
|
||||
let provider = Provider::Modrinth;
|
||||
|
||||
let res = provider.get_versions(&["ZvlCAdEF".to_string(), "GQANlg7p".to_string()]).await?;
|
||||
let res = provider
|
||||
.get_versions(&["ZvlCAdEF".to_string(), "GQANlg7p".to_string()])
|
||||
.await?;
|
||||
|
||||
assert_eq!(res.len(), 2);
|
||||
|
||||
|
|
@ -51,7 +57,7 @@ pub async fn test_download_chatting() -> LauncherResult<()> {
|
|||
let pkg = provider.get("chatting").await?;
|
||||
let ver_id = pkg.version_ids.first().expect("No version found");
|
||||
let versions = provider.get_versions(&[ver_id.clone()]).await?;
|
||||
let ver = versions.first().expect("No version found");
|
||||
let ver = versions.first().expect("No version found");
|
||||
|
||||
let db_model = packages::download_package(&pkg, ver, None).await;
|
||||
assert!(db_model.is_ok_and(|m| m.display_name == pkg.name));
|
||||
|
|
|
|||
|
|
@ -6,18 +6,8 @@ use crate::store::processes::Process;
|
|||
#[derive(Debug, Clone, Serialize)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum ProcessPayload {
|
||||
Starting {
|
||||
command: String,
|
||||
},
|
||||
Started {
|
||||
process: Process,
|
||||
},
|
||||
Stopped {
|
||||
pid: u32,
|
||||
exit_code: i32,
|
||||
},
|
||||
Output {
|
||||
pid: u32,
|
||||
output: String,
|
||||
},
|
||||
}
|
||||
Starting { command: String },
|
||||
Started { process: Process },
|
||||
Stopped { pid: u32, exit_code: i32 },
|
||||
Output { pid: u32, output: String },
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use serde::Serialize;
|
||||
|
||||
use crate::{error::LauncherResult, store::proxy::ProxyState};
|
||||
use crate::error::LauncherResult;
|
||||
use crate::store::proxy::ProxyState;
|
||||
|
||||
#[onelauncher_macro::specta]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
#[cfg(feature = "tauri")] pub mod proxy_tauri;
|
||||
#[cfg(feature = "cli")] pub mod proxy_cli;
|
||||
#[cfg(feature = "cli")]
|
||||
pub mod proxy_cli;
|
||||
pub mod proxy_empty;
|
||||
#[cfg(feature = "tauri")]
|
||||
pub mod proxy_tauri;
|
||||
|
||||
#[cfg(feature = "tauri")] pub use proxy_tauri::ProxyTauri;
|
||||
#[cfg(feature = "cli")] pub use proxy_cli::ProxyCli;
|
||||
pub use proxy_empty::ProxyEmpty;
|
||||
#[cfg(feature = "cli")]
|
||||
pub use proxy_cli::ProxyCli;
|
||||
pub use proxy_empty::ProxyEmpty;
|
||||
#[cfg(feature = "tauri")]
|
||||
pub use proxy_tauri::ProxyTauri;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,11 @@ use indicatif::ProgressBar;
|
|||
use tokio::sync::RwLock;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{api::{processes::ProcessPayload, proxy::{event::LauncherEvent, LauncherProxy}}, constants::CLI_TOTAL_INGRESS, LauncherResult};
|
||||
use crate::LauncherResult;
|
||||
use crate::api::processes::ProcessPayload;
|
||||
use crate::api::proxy::LauncherProxy;
|
||||
use crate::api::proxy::event::LauncherEvent;
|
||||
use crate::constants::CLI_TOTAL_INGRESS;
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct ProxyCli {
|
||||
|
|
@ -39,35 +43,37 @@ impl LauncherProxy for ProxyCli {
|
|||
let progress = ProgressBar::new(CLI_TOTAL_INGRESS);
|
||||
progress.set_message(ingress.message.clone());
|
||||
progress.set_position(0);
|
||||
progress.set_style(indicatif::ProgressStyle::default_bar().template(
|
||||
"{spinner:.green}, [{elapsed_precise}] [{bar:.lime/green}] {pos}/{len} {msg}"
|
||||
).unwrap().progress_chars("#>-"));
|
||||
progress.set_style(
|
||||
indicatif::ProgressStyle::default_bar()
|
||||
.template(
|
||||
"{spinner:.green}, [{elapsed_precise}] [{bar:.lime/green}] {pos}/{len} {msg}",
|
||||
)
|
||||
.unwrap()
|
||||
.progress_chars("#>-"),
|
||||
);
|
||||
|
||||
feeds.insert(ingress.id, progress);
|
||||
}
|
||||
},
|
||||
}
|
||||
LauncherEvent::Message(message) => {
|
||||
println!("[{:?}] {}", message.level, message.message);
|
||||
},
|
||||
LauncherEvent::Process(process) => {
|
||||
match process {
|
||||
ProcessPayload::Starting { command } => {
|
||||
println!("Starting process: {command}");
|
||||
},
|
||||
ProcessPayload::Started { process } => {
|
||||
println!("Process started: {process:#?}");
|
||||
},
|
||||
ProcessPayload::Stopped { pid, exit_code } => {
|
||||
println!("Process {pid} exited with code {exit_code}");
|
||||
},
|
||||
ProcessPayload::Output { pid, output } => {
|
||||
println!("Process {pid}: {output}");
|
||||
},
|
||||
}
|
||||
}
|
||||
LauncherEvent::Process(process) => match process {
|
||||
ProcessPayload::Starting { command } => {
|
||||
println!("Starting process: {command}");
|
||||
}
|
||||
ProcessPayload::Started { process } => {
|
||||
println!("Process started: {process:#?}");
|
||||
}
|
||||
ProcessPayload::Stopped { pid, exit_code } => {
|
||||
println!("Process {pid} exited with code {exit_code}");
|
||||
}
|
||||
ProcessPayload::Output { pid, output } => {
|
||||
println!("Process {pid}: {output}");
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
use crate::{api::proxy::{event::LauncherEvent, LauncherProxy}, LauncherResult};
|
||||
use crate::LauncherResult;
|
||||
use crate::api::proxy::LauncherProxy;
|
||||
use crate::api::proxy::event::LauncherEvent;
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct ProxyEmpty;
|
||||
|
|
@ -17,17 +19,19 @@ impl LauncherProxy for ProxyEmpty {
|
|||
LauncherEvent::Ingress(ingress) => {
|
||||
println!(
|
||||
"{} [{}] {}",
|
||||
ingress.percent.map_or("100%".to_string(), |p| format!("{:.2}%", p * 100.0)),
|
||||
ingress
|
||||
.percent
|
||||
.map_or("100%".to_string(), |p| format!("{:.2}%", p * 100.0)),
|
||||
ingress.id,
|
||||
ingress.message
|
||||
);
|
||||
},
|
||||
}
|
||||
LauncherEvent::Message(message) => {
|
||||
println!("[{:?}] {}", message.level, message.message);
|
||||
},
|
||||
}
|
||||
LauncherEvent::Process(payload) => {
|
||||
println!("{payload:#?}");
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
@ -44,5 +48,4 @@ impl LauncherProxy for ProxyEmpty {
|
|||
println!("shown window");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,10 +19,7 @@ pub enum MessageLevel {
|
|||
|
||||
pub fn send_message(level: MessageLevel, message: String) {
|
||||
tokio::spawn(async move {
|
||||
let payload = MessagePayload {
|
||||
level,
|
||||
message,
|
||||
};
|
||||
let payload = MessagePayload { level, message };
|
||||
|
||||
send_event(super::event::LauncherEvent::Message(payload)).await;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
pub mod event;
|
||||
mod r#impl;
|
||||
pub mod message;
|
||||
pub mod event;
|
||||
|
||||
pub use r#impl::*;
|
||||
|
||||
|
|
@ -15,5 +15,7 @@ pub trait LauncherProxy: Send + Sync + std::fmt::Debug {
|
|||
fn show_main_window(&self) -> crate::LauncherResult<()>;
|
||||
}
|
||||
|
||||
#[cfg(feature = "cli")] pub type ProxyDynamic = ProxyCli;
|
||||
#[cfg(not(feature = "cli"))] pub type ProxyDynamic = ProxyEmpty;
|
||||
#[cfg(feature = "cli")]
|
||||
pub type ProxyDynamic = ProxyCli;
|
||||
#[cfg(not(feature = "cli"))]
|
||||
pub type ProxyDynamic = ProxyEmpty;
|
||||
|
|
|
|||
|
|
@ -6,8 +6,7 @@ use crate::store::State;
|
|||
|
||||
pub mod dao {
|
||||
use onelauncher_entity::setting_profiles;
|
||||
use sea_orm::IntoActiveModel;
|
||||
use sea_orm::{ActiveModelTrait, EntityTrait};
|
||||
use sea_orm::{ActiveModelTrait, EntityTrait, IntoActiveModel};
|
||||
|
||||
use crate::error::{DaoError, LauncherResult};
|
||||
use crate::store::State;
|
||||
|
|
@ -59,37 +58,45 @@ pub mod dao {
|
|||
}
|
||||
|
||||
pub async fn update_profile_by_name<B>(
|
||||
name: &str,
|
||||
block: B,
|
||||
) -> LauncherResult<setting_profiles::Model>
|
||||
where B: AsyncFnOnce(setting_profiles::ActiveModel) -> LauncherResult<setting_profiles::ActiveModel> {
|
||||
let state = State::get().await?;
|
||||
let db = &state.db;
|
||||
name: &str,
|
||||
block: B,
|
||||
) -> LauncherResult<setting_profiles::Model>
|
||||
where
|
||||
B: AsyncFnOnce(
|
||||
setting_profiles::ActiveModel,
|
||||
) -> LauncherResult<setting_profiles::ActiveModel>,
|
||||
{
|
||||
let state = State::get().await?;
|
||||
let db = &state.db;
|
||||
|
||||
let model = get_profile_by_name(name).await?.ok_or(DaoError::NotFound)?;
|
||||
let model = block(model.into_active_model()).await?;
|
||||
let model = model.update(db).await?;
|
||||
let model = get_profile_by_name(name).await?.ok_or(DaoError::NotFound)?;
|
||||
let model = block(model.into_active_model()).await?;
|
||||
let model = model.update(db).await?;
|
||||
|
||||
Ok(model)
|
||||
}
|
||||
Ok(model)
|
||||
}
|
||||
|
||||
/// Updates an existing profile in the database.
|
||||
pub async fn update_profile<B>(
|
||||
profile: &mut setting_profiles::Model,
|
||||
block: B,
|
||||
) -> LauncherResult<&mut setting_profiles::Model>
|
||||
where B: AsyncFnOnce(setting_profiles::ActiveModel) -> LauncherResult<setting_profiles::ActiveModel> {
|
||||
let state = State::get().await?;
|
||||
let db = &state.db;
|
||||
/// Updates an existing profile in the database.
|
||||
pub async fn update_profile<B>(
|
||||
profile: &mut setting_profiles::Model,
|
||||
block: B,
|
||||
) -> LauncherResult<&mut setting_profiles::Model>
|
||||
where
|
||||
B: AsyncFnOnce(
|
||||
setting_profiles::ActiveModel,
|
||||
) -> LauncherResult<setting_profiles::ActiveModel>,
|
||||
{
|
||||
let state = State::get().await?;
|
||||
let db = &state.db;
|
||||
|
||||
let model = profile.clone().into_active_model();
|
||||
let model = block(model).await?;
|
||||
let model = model.update(db).await?;
|
||||
let model = profile.clone().into_active_model();
|
||||
let model = block(model).await?;
|
||||
let model = model.update(db).await?;
|
||||
|
||||
*profile = model;
|
||||
*profile = model;
|
||||
|
||||
Ok(profile)
|
||||
}
|
||||
Ok(profile)
|
||||
}
|
||||
|
||||
pub async fn delete_profile_by_name(name: &str) -> LauncherResult<()> {
|
||||
let state = State::get().await?;
|
||||
|
|
|
|||
|
|
@ -2,4 +2,4 @@ mod events;
|
|||
pub use events::*;
|
||||
|
||||
mod commands;
|
||||
pub use commands::*;
|
||||
pub use commands::*;
|
||||
|
|
|
|||
|
|
@ -30,7 +30,8 @@ pub const FEATURED_PACKAGES_URL: &str = "https://polyfrost.org/meta/onelauncher/
|
|||
/// <https://mclo.gs>/ API base url.
|
||||
pub const MCLOGS_API_URL: &str = "https://api.mclo.gs/1";
|
||||
/// <https://skyclient.co/> metadata base url.
|
||||
pub const SKYCLIENT_BASE_URL: &str = "https://raw.githubusercontent.com/SkyblockClient/SkyblockClient-REPO/refs/heads/main/v1";
|
||||
pub const SKYCLIENT_BASE_URL: &str =
|
||||
"https://raw.githubusercontent.com/SkyblockClient/SkyblockClient-REPO/refs/heads/main/v1";
|
||||
|
||||
// =========== Hacky Mojang-spec OS constants ===========
|
||||
#[cfg(target_os = "windows")]
|
||||
|
|
|
|||
|
|
@ -27,25 +27,61 @@ pub enum LauncherError {
|
|||
DaoError(#[from] DaoError),
|
||||
|
||||
#[error("json error: {0}")]
|
||||
SerdeError(#[from] #[skip] serde_json::Error),
|
||||
SerdeError(
|
||||
#[from]
|
||||
#[skip]
|
||||
serde_json::Error,
|
||||
),
|
||||
#[error(transparent)]
|
||||
AnyhowError(#[from] #[skip] anyhow::Error),
|
||||
AnyhowError(
|
||||
#[from]
|
||||
#[skip]
|
||||
anyhow::Error,
|
||||
),
|
||||
#[error("database error: {0}")]
|
||||
DbError(#[from] #[skip] sea_orm::DbErr),
|
||||
DbError(
|
||||
#[from]
|
||||
#[skip]
|
||||
sea_orm::DbErr,
|
||||
),
|
||||
#[error("http error: {0}")]
|
||||
ReqwestError(#[from] #[skip] reqwest::Error),
|
||||
ReqwestError(
|
||||
#[from]
|
||||
#[skip]
|
||||
reqwest::Error,
|
||||
),
|
||||
#[error("meta error: {0}")]
|
||||
InterpulseError(#[from] #[skip] interpulse::Error),
|
||||
InterpulseError(
|
||||
#[from]
|
||||
#[skip]
|
||||
interpulse::Error,
|
||||
),
|
||||
#[error(transparent)]
|
||||
RegexError(#[from] #[skip] regex::Error),
|
||||
RegexError(
|
||||
#[from]
|
||||
#[skip]
|
||||
regex::Error,
|
||||
),
|
||||
#[error("couldn't acquire semaphore: {0}")]
|
||||
SemaphoreError(#[from] #[skip] tokio::sync::AcquireError),
|
||||
SemaphoreError(
|
||||
#[from]
|
||||
#[skip]
|
||||
tokio::sync::AcquireError,
|
||||
),
|
||||
#[error(transparent)]
|
||||
UrlError(#[from] #[skip] url::ParseError),
|
||||
UrlError(
|
||||
#[from]
|
||||
#[skip]
|
||||
url::ParseError,
|
||||
),
|
||||
|
||||
#[cfg(feature = "tauri")]
|
||||
#[error(transparent)]
|
||||
TauriError(#[from] #[skip] tauri::Error),
|
||||
TauriError(
|
||||
#[from]
|
||||
#[skip]
|
||||
tauri::Error,
|
||||
),
|
||||
}
|
||||
|
||||
pub type LauncherResult<T> = Result<T, LauncherError>;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use std::{ops::Deref, sync::Arc};
|
||||
use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
|
||||
use tokio::sync::OnceCell;
|
||||
use tracing_subscriber::fmt::format::FmtSpan;
|
||||
|
|
@ -48,9 +49,9 @@ impl Core {
|
|||
}
|
||||
|
||||
pub async fn initialize(options: CoreOptions) -> LauncherResult<()> {
|
||||
CORE_STATE.get_or_try_init(|| async move {
|
||||
Ok::<_, LauncherError>(Self::new(options))
|
||||
}).await?;
|
||||
CORE_STATE
|
||||
.get_or_try_init(|| async move { Ok::<_, LauncherError>(Self::new(options)) })
|
||||
.await?;
|
||||
|
||||
tracing::info!("core initialized successfully");
|
||||
|
||||
|
|
@ -58,10 +59,7 @@ impl Core {
|
|||
}
|
||||
|
||||
pub fn get() -> Arc<Self> {
|
||||
CORE_STATE
|
||||
.get()
|
||||
.expect("core was not initialized")
|
||||
.clone()
|
||||
CORE_STATE.get().expect("core was not initialized").clone()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -72,4 +70,3 @@ impl Deref for Core {
|
|||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1019,7 +1019,7 @@ pub enum MinecraftAuthError {
|
|||
PKCS8Error(
|
||||
#[from]
|
||||
#[skip]
|
||||
p256::pkcs8::Error
|
||||
p256::pkcs8::Error,
|
||||
),
|
||||
#[error("failed to serialize JSON during MSA step {step:?}: {source}")]
|
||||
SerializeError {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
use std::{path::PathBuf, time::Duration};
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
|
||||
use onelauncher_migration::MigratorTrait;
|
||||
use sea_orm::{ConnectOptions, Database, DatabaseConnection};
|
||||
|
||||
use crate::{utils::io::IOError, LauncherResult};
|
||||
use crate::LauncherResult;
|
||||
use crate::utils::io::IOError;
|
||||
|
||||
use super::Dirs;
|
||||
|
||||
|
|
@ -30,4 +32,4 @@ pub async fn create_pool_from_path(path: PathBuf) -> LauncherResult<DatabaseConn
|
|||
pub async fn create_pool() -> LauncherResult<DatabaseConnection> {
|
||||
let path = Dirs::get_db_file().await?;
|
||||
create_pool_from_path(path).await
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::path::PathBuf;
|
|||
use onelauncher_entity::package::{PackageType, Provider};
|
||||
use tokio::sync::OnceCell;
|
||||
|
||||
use crate::{utils, LauncherResult};
|
||||
use crate::{LauncherResult, utils};
|
||||
|
||||
use super::Core;
|
||||
|
||||
|
|
@ -24,9 +24,9 @@ pub enum DirectoryError {
|
|||
|
||||
impl Dirs {
|
||||
pub async fn get() -> LauncherResult<&'static Self> {
|
||||
DIRS_GLOBAL.get_or_try_init(async || {
|
||||
Self::initialize()
|
||||
}).await
|
||||
DIRS_GLOBAL
|
||||
.get_or_try_init(async || Self::initialize())
|
||||
.await
|
||||
}
|
||||
|
||||
fn initialize() -> LauncherResult<Self> {
|
||||
|
|
@ -36,9 +36,7 @@ impl Dirs {
|
|||
|
||||
tracing::info!("using base directory '{}'", base_dir.display());
|
||||
|
||||
Ok(Self {
|
||||
base_dir
|
||||
})
|
||||
Ok(Self { base_dir })
|
||||
}
|
||||
|
||||
/// Get the base directory for the launcher.
|
||||
|
|
@ -46,7 +44,6 @@ impl Dirs {
|
|||
pub const fn base_dir(&self) -> &PathBuf {
|
||||
&self.base_dir
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
macro_rules! dirs_impl {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
use std::sync::{atomic::AtomicBool, Arc};
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
|
||||
use discord_rich_presence::{activity::{Activity, Assets, Button, Timestamps}, DiscordIpc, DiscordIpcClient};
|
||||
use discord_rich_presence::activity::{Activity, Assets, Button, Timestamps};
|
||||
use discord_rich_presence::{DiscordIpc, DiscordIpcClient};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::error::LauncherResult;
|
||||
|
|
@ -15,11 +17,13 @@ pub struct DiscordRPC {
|
|||
|
||||
impl DiscordRPC {
|
||||
pub fn initialize() -> LauncherResult<Self> {
|
||||
let client_id = Core::get().discord_client_id.clone()
|
||||
let client_id = Core::get()
|
||||
.discord_client_id
|
||||
.clone()
|
||||
.ok_or(DiscordError::MissingClientId)?;
|
||||
|
||||
let mut client = DiscordIpcClient::new(client_id.as_str())
|
||||
.map_err(|_| DiscordError::ConnectError)?;
|
||||
let mut client =
|
||||
DiscordIpcClient::new(client_id.as_str()).map_err(|_| DiscordError::ConnectError)?;
|
||||
|
||||
let connected = client.connect().is_ok();
|
||||
|
||||
|
|
@ -34,18 +38,15 @@ impl DiscordRPC {
|
|||
self.set_activity(
|
||||
Activity::new()
|
||||
.state(msg)
|
||||
.buttons(
|
||||
vec![Button::new("Website", "https://polyfrost.org/")]
|
||||
)
|
||||
.timestamps(
|
||||
timestamp.unwrap_or_else(|| Timestamps::new().start(self.started_at))
|
||||
)
|
||||
.buttons(vec![Button::new("Website", "https://polyfrost.org/")])
|
||||
.timestamps(timestamp.unwrap_or_else(|| Timestamps::new().start(self.started_at)))
|
||||
.assets(
|
||||
Assets::new()
|
||||
.large_image("onelauncher_logo_512")
|
||||
.large_text("OneLauncher"),
|
||||
)
|
||||
).await
|
||||
),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn set_activity(&self, activity: Activity<'_>) -> bool {
|
||||
|
|
@ -102,7 +103,8 @@ impl DiscordRPC {
|
|||
}
|
||||
|
||||
fn set_connected(&self, connected: bool) {
|
||||
self.connected.store(connected, std::sync::atomic::Ordering::Relaxed);
|
||||
self.connected
|
||||
.store(connected, std::sync::atomic::Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -113,4 +115,4 @@ pub enum DiscordError {
|
|||
MissingClientId,
|
||||
#[error("couldn't connect to Discord IPC")]
|
||||
ConnectError,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ use serde::{Deserialize, Serialize};
|
|||
use tokio::sync::RwLock;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::api::proxy::event::try_send_event;
|
||||
use crate::LauncherResult;
|
||||
use crate::api::ingress::IngressPayload;
|
||||
use crate::api::proxy::event::try_send_event;
|
||||
|
||||
use super::State;
|
||||
|
||||
|
|
@ -88,8 +88,7 @@ impl IngressProcessor {
|
|||
total: ingress.total,
|
||||
};
|
||||
|
||||
try_send_event(crate::api::proxy::event::LauncherEvent::Ingress(payload))
|
||||
.await?;
|
||||
try_send_event(crate::api::proxy::event::LauncherEvent::Ingress(payload)).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -219,8 +218,7 @@ impl Drop for IngressId {
|
|||
total: ingress.total,
|
||||
};
|
||||
|
||||
try_send_event(crate::api::proxy::event::LauncherEvent::Ingress(payload))
|
||||
.await?;
|
||||
try_send_event(crate::api::proxy::event::LauncherEvent::Ingress(payload)).await?;
|
||||
|
||||
tracing::trace!(
|
||||
"exited at {}% for ingress {:?}",
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ use super::Dirs;
|
|||
pub struct Metadata {
|
||||
initialized: bool,
|
||||
inner: MetadataInner,
|
||||
version_loader_cache: HashMap<String, Vec<GameLoader>>
|
||||
version_loader_cache: HashMap<String, Vec<GameLoader>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
|
|
@ -46,7 +46,7 @@ pub enum MetadataError {
|
|||
ParseError(
|
||||
#[from]
|
||||
#[skip]
|
||||
serde_json::Error
|
||||
serde_json::Error,
|
||||
),
|
||||
#[error("no matching loader found")]
|
||||
NoMatchingLoader,
|
||||
|
|
@ -65,7 +65,6 @@ impl Metadata {
|
|||
Self::default()
|
||||
}
|
||||
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub async fn get_vanilla_or_fetch(&mut self) -> LauncherResult<&VanillaManifest> {
|
||||
if !self.initialized() {
|
||||
|
|
@ -76,7 +75,10 @@ impl Metadata {
|
|||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
pub async fn get_modded_or_fetch(&mut self, loader: &GameLoader) -> LauncherResult<&ModdedManifest> {
|
||||
pub async fn get_modded_or_fetch(
|
||||
&mut self,
|
||||
loader: &GameLoader,
|
||||
) -> LauncherResult<&ModdedManifest> {
|
||||
if !loader.is_modded() {
|
||||
return Err(MetadataError::NotModdedManifest(*loader).into());
|
||||
}
|
||||
|
|
@ -89,7 +91,10 @@ impl Metadata {
|
|||
}
|
||||
|
||||
pub fn get_vanilla(&self) -> LauncherResult<&VanillaManifest> {
|
||||
self.inner.minecraft.as_ref().ok_or_else(|| MetadataError::FetchError.into())
|
||||
self.inner
|
||||
.minecraft
|
||||
.as_ref()
|
||||
.ok_or_else(|| MetadataError::FetchError.into())
|
||||
}
|
||||
|
||||
pub fn get_modded(&self, loader: &GameLoader) -> LauncherResult<&ModdedManifest> {
|
||||
|
|
@ -103,7 +108,8 @@ impl Metadata {
|
|||
GameLoader::Fabric => self.inner.fabric.as_ref(),
|
||||
GameLoader::Quilt => self.inner.quilt.as_ref(),
|
||||
_ => None,
|
||||
}.ok_or_else(|| MetadataError::FetchError.into())
|
||||
}
|
||||
.ok_or_else(|| MetadataError::FetchError.into())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
|
|
@ -144,18 +150,16 @@ impl Metadata {
|
|||
($var:tt, $is_modded:tt) => {
|
||||
if self.inner.$var.is_none() {
|
||||
match GameLoader::from_str(stringify!($var)) {
|
||||
Ok(loader) => {
|
||||
match check!(_fetch, loader, $is_modded) {
|
||||
Ok(data) => {
|
||||
self.inner.$var = Some(data);
|
||||
changed += 1;
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::error!("failed to fetch manifest for {}: {}", loader, err);
|
||||
}
|
||||
Ok(loader) => match check!(_fetch, loader, $is_modded) {
|
||||
Ok(data) => {
|
||||
self.inner.$var = Some(data);
|
||||
changed += 1;
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::error!("failed to fetch manifest for {}: {}", loader, err);
|
||||
}
|
||||
},
|
||||
Err(err) => tracing::error!("{err}")
|
||||
Err(err) => tracing::error!("{err}"),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
|
@ -225,7 +229,10 @@ impl Metadata {
|
|||
/// Returns a list of loaders that are compatible with the given Minecraft version.
|
||||
///
|
||||
/// WARNING: Can be heavy in certain cases!
|
||||
pub async fn get_loaders_for_version(&mut self, mc_version: &str) -> LauncherResult<Vec<GameLoader>> {
|
||||
pub async fn get_loaders_for_version(
|
||||
&mut self,
|
||||
mc_version: &str,
|
||||
) -> LauncherResult<Vec<GameLoader>> {
|
||||
if !self.initialized() {
|
||||
self.initialize().await?;
|
||||
}
|
||||
|
|
@ -248,7 +255,8 @@ impl Metadata {
|
|||
}
|
||||
}
|
||||
|
||||
self.version_loader_cache.insert(mc_version.to_owned(), loaders.clone());
|
||||
self.version_loader_cache
|
||||
.insert(mc_version.to_owned(), loaders.clone());
|
||||
|
||||
Ok(loaders)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
mod core_options;
|
||||
pub mod credentials;
|
||||
pub(super) mod db;
|
||||
mod dirs;
|
||||
pub mod discord;
|
||||
pub mod ingress;
|
||||
pub mod metadata;
|
||||
pub mod processes;
|
||||
pub mod proxy;
|
||||
pub mod semaphore;
|
||||
pub mod metadata;
|
||||
pub mod credentials;
|
||||
pub mod processes;
|
||||
mod core_options;
|
||||
mod dirs;
|
||||
mod settings;
|
||||
mod state;
|
||||
|
||||
pub use core_options::*;
|
||||
pub use dirs::*;
|
||||
pub use settings::*;
|
||||
pub use state::*;
|
||||
pub use core_options::*;
|
||||
|
|
@ -187,7 +187,8 @@ impl ProcessStore {
|
|||
send_error!("{err:?}");
|
||||
}
|
||||
|
||||
command.stdout(Stdio::piped())
|
||||
command
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.stdin(Stdio::null());
|
||||
|
||||
|
|
@ -324,7 +325,10 @@ async fn run_hook(hook: &str, cwd: PathBuf) -> LauncherResult<Option<i32>> {
|
|||
}
|
||||
}
|
||||
|
||||
async fn wait_for_exit(cluster_id: ClusterId, child: &Arc<RwLock<ChildType>>) -> LauncherResult<i32> {
|
||||
async fn wait_for_exit(
|
||||
cluster_id: ClusterId,
|
||||
child: &Arc<RwLock<ChildType>>,
|
||||
) -> LauncherResult<i32> {
|
||||
let mut last_updated = Utc::now();
|
||||
|
||||
loop {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
use std::{ops::Deref, sync::Arc};
|
||||
use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use tokio::sync::OnceCell;
|
||||
|
||||
use crate::{api::proxy::LauncherProxy, LauncherResult};
|
||||
use crate::LauncherResult;
|
||||
use crate::api::proxy::LauncherProxy;
|
||||
|
||||
static PROXY_STATE: OnceCell<Arc<ProxyState>> = OnceCell::const_new();
|
||||
|
||||
|
|
@ -38,4 +40,4 @@ impl ProxyState {
|
|||
.ok_or_else(|| anyhow!("proxy state not initialized"))
|
||||
.cloned()?)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,12 +5,15 @@ use tokio::sync::{OnceCell, Semaphore};
|
|||
static STORE_STATE: OnceCell<Arc<SemaphoreStore>> = OnceCell::const_new();
|
||||
|
||||
pub struct SemaphoreStore {
|
||||
pub fetch: Arc<Semaphore>
|
||||
pub fetch: Arc<Semaphore>,
|
||||
}
|
||||
|
||||
impl SemaphoreStore {
|
||||
pub async fn get() -> Arc<Self> {
|
||||
STORE_STATE.get_or_init(|| async { Self::initialize() }).await.clone()
|
||||
STORE_STATE
|
||||
.get_or_init(|| async { Self::initialize() })
|
||||
.await
|
||||
.clone()
|
||||
}
|
||||
|
||||
#[tracing::instrument]
|
||||
|
|
@ -23,4 +26,4 @@ impl SemaphoreStore {
|
|||
pub async fn fetch() -> Arc<Semaphore> {
|
||||
Self::get().await.fetch.clone()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
use onelauncher_entity::setting_profiles;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{send_warning, utils::io, LauncherResult};
|
||||
use crate::utils::io;
|
||||
use crate::{LauncherResult, send_warning};
|
||||
|
||||
use super::Dirs;
|
||||
|
||||
|
|
@ -15,7 +16,7 @@ pub struct Settings {
|
|||
pub discord_enabled: bool,
|
||||
pub max_concurrent_requests: usize,
|
||||
pub settings_version: u32,
|
||||
pub native_window_frame: bool
|
||||
pub native_window_frame: bool,
|
||||
}
|
||||
|
||||
impl Default for Settings {
|
||||
|
|
@ -40,7 +41,7 @@ impl Settings {
|
|||
Err(err) => {
|
||||
send_warning!("Failed to read settings file: {}", err);
|
||||
Self::default()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -57,4 +58,4 @@ impl Settings {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,15 +3,15 @@ use std::sync::Arc;
|
|||
use sea_orm::DatabaseConnection;
|
||||
use tokio::sync::{OnceCell, RwLock};
|
||||
|
||||
use crate::store::Dirs;
|
||||
use crate::LauncherResult;
|
||||
use crate::store::Dirs;
|
||||
|
||||
use super::credentials::CredentialsStore;
|
||||
use super::processes::ProcessStore;
|
||||
use super::Settings;
|
||||
use super::credentials::CredentialsStore;
|
||||
use super::discord::DiscordRPC;
|
||||
use super::ingress::IngressProcessor;
|
||||
use super::metadata::Metadata;
|
||||
use super::processes::ProcessStore;
|
||||
|
||||
/// The static [`OnceCell<RwLock<State>>`] for storing the global runtime launcher state.
|
||||
static LAUNCHER_STATE: OnceCell<Arc<State>> = OnceCell::const_new();
|
||||
|
|
@ -32,8 +32,7 @@ impl State {
|
|||
Ok(LAUNCHER_STATE
|
||||
.get_or_try_init(Self::initialize)
|
||||
.await?
|
||||
.clone()
|
||||
)
|
||||
.clone())
|
||||
}
|
||||
|
||||
pub fn initialized() -> bool {
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@ use tokio_stream::StreamExt;
|
|||
|
||||
use crate::api::ingress::IngressSendExt;
|
||||
use crate::error::LauncherResult;
|
||||
use crate::store::Core;
|
||||
use crate::store::ingress::{SubIngress, SubIngressExt};
|
||||
use crate::store::semaphore::SemaphoreStore;
|
||||
use crate::store::Core;
|
||||
|
||||
use super::crypto::{CryptoError, HashAlgorithm};
|
||||
use super::io;
|
||||
|
|
@ -82,13 +82,15 @@ pub async fn fetch_advanced(
|
|||
match req.send().await {
|
||||
Err(err) => {
|
||||
if attempt <= Core::get().fetch_attempts {
|
||||
tracing::error!("error occurred whilst fetching on attempt {attempt}: {err} retrying request...");
|
||||
tracing::error!(
|
||||
"error occurred whilst fetching on attempt {attempt}: {err} retrying request..."
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
return Err(err.into())
|
||||
},
|
||||
Ok(res) => break res
|
||||
return Err(err.into());
|
||||
}
|
||||
Ok(res) => break res,
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -102,13 +104,12 @@ pub async fn fetch_advanced(
|
|||
let mut bytes = Vec::new();
|
||||
|
||||
while let Some(item) = stream.next().await {
|
||||
let chunk =
|
||||
item.or(Err(anyhow::anyhow!("no value for fetch bytes")))?;
|
||||
let chunk = item.or(Err(anyhow::anyhow!("no value for fetch bytes")))?;
|
||||
bytes.append(&mut chunk.to_vec());
|
||||
|
||||
ingress.send_ingress(
|
||||
(chunk.len() as f64 / total_size as f64) * ingress.total,
|
||||
).await?;
|
||||
ingress
|
||||
.send_ingress((chunk.len() as f64 / total_size as f64) * ingress.total)
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok(Bytes::from(bytes))
|
||||
|
|
@ -131,7 +132,7 @@ pub async fn fetch_advanced(
|
|||
}
|
||||
.into());
|
||||
}
|
||||
},
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::error!("failed to calculate hash for {url}: {err}");
|
||||
return Err(err.into());
|
||||
|
|
@ -178,7 +179,10 @@ pub async fn download_advanced(
|
|||
ingress: Option<&SubIngress<'_>>,
|
||||
) -> LauncherResult<Bytes> {
|
||||
const TASKS: f64 = 3.0;
|
||||
let ingress_step = ingress.ingress_total().map(|total| total / TASKS).unwrap_or_default();
|
||||
let ingress_step = ingress
|
||||
.ingress_total()
|
||||
.map(|total| total / TASKS)
|
||||
.unwrap_or_default();
|
||||
|
||||
let bytes = fetch_advanced(
|
||||
method,
|
||||
|
|
@ -209,7 +213,7 @@ pub async fn download(
|
|||
url: &str,
|
||||
path: impl AsRef<Path>,
|
||||
hash: Option<(HashAlgorithm, &str)>,
|
||||
ingress: Option<&SubIngress<'_>>
|
||||
ingress: Option<&SubIngress<'_>>,
|
||||
) -> LauncherResult<Bytes> {
|
||||
download_advanced(method, url, path, None, None, hash, ingress).await
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
use std::{path::PathBuf, sync::Arc};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_compression::tokio::bufread::GzipDecoder;
|
||||
use async_stream::try_stream;
|
||||
use async_zip::StoredZipEntry;
|
||||
use async_zip::base::read::WithoutEntry;
|
||||
use futures::{pin_mut, Stream, TryStreamExt};
|
||||
use futures::{Stream, TryStreamExt, pin_mut};
|
||||
use serde::Serialize;
|
||||
use serde::de::DeserializeOwned;
|
||||
use tempfile::TempDir;
|
||||
|
|
@ -27,11 +28,23 @@ pub enum IOError {
|
|||
path: String,
|
||||
},
|
||||
#[error(transparent)]
|
||||
IOError(#[from] #[skip] std::io::Error),
|
||||
IOError(
|
||||
#[from]
|
||||
#[skip]
|
||||
std::io::Error,
|
||||
),
|
||||
#[error("json deserialization error: {0}")]
|
||||
DeserializeError(#[from] #[skip] serde_json::Error),
|
||||
DeserializeError(
|
||||
#[from]
|
||||
#[skip]
|
||||
serde_json::Error,
|
||||
),
|
||||
#[error(transparent)]
|
||||
AsyncZipError(#[from] #[skip] async_zip::error::ZipError),
|
||||
AsyncZipError(
|
||||
#[from]
|
||||
#[skip]
|
||||
async_zip::error::ZipError,
|
||||
),
|
||||
}
|
||||
|
||||
impl<P: AsRef<std::path::Path>> From<(P, std::io::Error)> for IOError {
|
||||
|
|
@ -283,9 +296,16 @@ where
|
|||
/// Reads a zip archive from a byte array and returns a stream of entries.
|
||||
pub fn stream_zip_entries_bytes(
|
||||
data: Vec<u8>,
|
||||
) -> impl Stream<Item = Result<(
|
||||
usize, StoredZipEntry, Arc<async_zip::base::read::mem::ZipFileReader>
|
||||
), IOError>> {
|
||||
) -> impl Stream<
|
||||
Item = Result<
|
||||
(
|
||||
usize,
|
||||
StoredZipEntry,
|
||||
Arc<async_zip::base::read::mem::ZipFileReader>,
|
||||
),
|
||||
IOError,
|
||||
>,
|
||||
> {
|
||||
try_stream! {
|
||||
let reader = Arc::new(async_zip::base::read::mem::ZipFileReader::new(data).await?);
|
||||
let entries = reader.file().entries();
|
||||
|
|
@ -319,9 +339,10 @@ pub async fn unzip_bytes_filtered(
|
|||
let file_name = entry.filename().as_str()?;
|
||||
|
||||
if let Some(filter) = &filter_entries
|
||||
&& !filter(file_name) {
|
||||
continue;
|
||||
}
|
||||
&& !filter(file_name)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
let path = dest_path.as_ref().join(sanitize_path(file_name));
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
pub mod crypto;
|
||||
pub mod os_ext;
|
||||
pub mod http;
|
||||
pub mod icon;
|
||||
pub mod io;
|
||||
pub mod pagination;
|
||||
pub mod os_ext;
|
||||
pub mod pagination;
|
||||
|
|
|
|||
|
|
@ -45,4 +45,4 @@ impl OsExt for Os {
|
|||
_ => Self::Unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Debug;
|
||||
use std::marker::Send;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[onelauncher_macro::specta]
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct Paginated<T>
|
||||
where T: Clone + Serialize + Send + Sync {
|
||||
where
|
||||
T: Clone + Serialize + Send + Sync,
|
||||
{
|
||||
pub total: usize,
|
||||
pub offset: usize,
|
||||
pub limit: usize,
|
||||
|
|
@ -23,9 +25,14 @@ impl<T: Clone + Serialize + Send + Sync> Debug for Paginated<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl <T: Clone + Serialize + Send + Sync> Paginated<T> {
|
||||
impl<T: Clone + Serialize + Send + Sync> Paginated<T> {
|
||||
#[must_use]
|
||||
pub const fn new(total: usize, offset: usize, limit: usize, items: Vec<T>) -> Self {
|
||||
Self { total, offset, limit, items }
|
||||
Self {
|
||||
total,
|
||||
offset,
|
||||
limit,
|
||||
items,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
use proc_macro::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
use syn::{parse_macro_input, Data, DeriveInput, Fields};
|
||||
use syn::{Data, DeriveInput, Fields, parse_macro_input};
|
||||
|
||||
pub fn error_attr(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(item as DeriveInput);
|
||||
let input = parse_macro_input!(item as DeriveInput);
|
||||
|
||||
let Data::Enum(data_enum) = &input.data else {
|
||||
return syn::Error::new_spanned(&input.ident, "Only enums are supported")
|
||||
|
|
@ -98,82 +98,76 @@ pub fn error_attr(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
|||
}
|
||||
};
|
||||
|
||||
let output = quote! {
|
||||
#[cfg_attr(feature = "specta", derive(onelauncher_macro::SerializedError))]
|
||||
#input
|
||||
let output = quote! {
|
||||
#[cfg_attr(feature = "specta", derive(onelauncher_macro::SerializedError))]
|
||||
#input
|
||||
|
||||
#serialize_impl
|
||||
};
|
||||
};
|
||||
|
||||
output.into()
|
||||
output.into()
|
||||
}
|
||||
|
||||
pub fn error_derive(input: TokenStream) -> TokenStream {
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
let enum_name = &input.ident;
|
||||
let specta_name = format_ident!("{}SpectaType", enum_name);
|
||||
let mut variants = vec![];
|
||||
let enum_name = &input.ident;
|
||||
let specta_name = format_ident!("{}SpectaType", enum_name);
|
||||
let mut variants = vec![];
|
||||
|
||||
let Data::Enum(data_enum) = &input.data else {
|
||||
return syn::Error::new_spanned(enum_name, "Only enums are supported")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
};
|
||||
let Data::Enum(data_enum) = &input.data else {
|
||||
return syn::Error::new_spanned(enum_name, "Only enums are supported")
|
||||
.to_compile_error()
|
||||
.into();
|
||||
};
|
||||
|
||||
for variant in &data_enum.variants {
|
||||
let name = &variant.ident;
|
||||
let ty = match &variant.fields {
|
||||
Fields::Unit => quote! { String },
|
||||
for variant in &data_enum.variants {
|
||||
let name = &variant.ident;
|
||||
let ty = match &variant.fields {
|
||||
Fields::Unit => quote! { String },
|
||||
|
||||
Fields::Unnamed(fields) => {
|
||||
// Handle newtype-style or tuple-like enums
|
||||
if fields.unnamed.len() == 1 {
|
||||
let field = &fields.unnamed[0];
|
||||
let field_ty = &field.ty;
|
||||
Fields::Unnamed(fields) => {
|
||||
// Handle newtype-style or tuple-like enums
|
||||
if fields.unnamed.len() == 1 {
|
||||
let field = &fields.unnamed[0];
|
||||
let field_ty = &field.ty;
|
||||
|
||||
let has_from = field
|
||||
.attrs
|
||||
.iter()
|
||||
.any(|a| a.path().is_ident("from"));
|
||||
let has_from = field.attrs.iter().any(|a| a.path().is_ident("from"));
|
||||
|
||||
let has_skip = field
|
||||
.attrs
|
||||
.iter()
|
||||
.any(|a| a.path().is_ident("skip"));
|
||||
let has_skip = field.attrs.iter().any(|a| a.path().is_ident("skip"));
|
||||
|
||||
if has_from && !has_skip {
|
||||
quote! {
|
||||
if has_from && !has_skip {
|
||||
quote! {
|
||||
#field_ty
|
||||
}
|
||||
} else {
|
||||
quote! { String }
|
||||
}
|
||||
} else {
|
||||
quote! { String }
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! { String }
|
||||
}
|
||||
} else {
|
||||
quote! { String }
|
||||
}
|
||||
}
|
||||
|
||||
Fields::Named(_) => {
|
||||
quote! { String } // Simplification: treat all struct-like variants as String
|
||||
}
|
||||
};
|
||||
Fields::Named(_) => {
|
||||
quote! { String } // Simplification: treat all struct-like variants as String
|
||||
}
|
||||
};
|
||||
|
||||
variants.push(quote! {
|
||||
#name(#ty)
|
||||
});
|
||||
}
|
||||
variants.push(quote! {
|
||||
#name(#ty)
|
||||
});
|
||||
}
|
||||
|
||||
let enum_name_str = enum_name.to_string();
|
||||
let output = quote! {
|
||||
#[derive(Debug, specta::Type, serde::Serialize)]
|
||||
#[specta(remote = #enum_name)]
|
||||
#[derive(Debug, specta::Type, serde::Serialize)]
|
||||
#[specta(remote = #enum_name)]
|
||||
#[specta(tag = "type", content = "data")]
|
||||
#[specta(rename = #enum_name_str)]
|
||||
pub enum #specta_name {
|
||||
#(#variants,)*
|
||||
}
|
||||
};
|
||||
pub enum #specta_name {
|
||||
#(#variants,)*
|
||||
}
|
||||
};
|
||||
|
||||
output.into()
|
||||
}
|
||||
output.into()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
use quote::quote;
|
||||
use syn::{parse_macro_input, ItemFn};
|
||||
use syn::{ItemFn, parse_macro_input};
|
||||
|
||||
pub fn pin(_attr: proc_macro::TokenStream, item: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
pub fn pin(
|
||||
_attr: proc_macro::TokenStream,
|
||||
item: proc_macro::TokenStream,
|
||||
) -> proc_macro::TokenStream {
|
||||
let input = parse_macro_input!(item as ItemFn);
|
||||
|
||||
let attrs = &input.attrs;
|
||||
|
|
@ -19,4 +22,4 @@ pub fn pin(_attr: proc_macro::TokenStream, item: proc_macro::TokenStream) -> pro
|
|||
};
|
||||
|
||||
result.into()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue