Initial commit

This commit is contained in:
2024-09-01 06:20:49 +03:00
parent bd61f5d591
commit 9263c396a1
81 changed files with 1494 additions and 1521 deletions

View File

@@ -0,0 +1,55 @@
use std::process::exit;
use reqwest;
use tokio::fs::File as AsyncFile;
use tokio::io::AsyncWriteExt;
use tokio::process::Command;
async fn download_file(download_url: &str, local_filename: &str, os_type: &str, auto_update_supported: bool) -> Result<(), Box<dyn std::error::Error>> {
let download_dir = dirs::download_dir().ok_or("[download_file] • Failed to determine download directory")?;
let full_path = download_dir.join(local_filename);
let response = reqwest::get(download_url).await?;
let bytes = response.bytes().await?;
let mut dest_file = AsyncFile::create(&full_path).await?;
dest_file.write_all(&bytes).await?;
println!("[download_file] • File downloaded to: {:?}", full_path);
if auto_update_supported {
let status;
if os_type.to_lowercase() == "Windows".to_lowercase() {
status = Command::new("explorer")
.arg(download_dir.display().to_string())
.status()
.await
.expect("[download_file] • Failed to open downloads folder");
} else if os_type.to_lowercase() == "MacOS".to_lowercase() {
status = Command::new("open")
.arg(full_path.to_str().unwrap_or_default())
.status()
.await
.expect("[download_file] • Failed to execute command");
} else {
status = Command::new(".")
.arg(full_path.to_str().unwrap_or_default())
.status()
.await
.expect("[download_file] • Failed to execute command");
}
if status.success() {
println!("[download_file] • File opened successfully!");
} else {
eprintln!("[download_file] • Failed to open the file. Exit code: {:?}", status.code());
}
}
Ok(())
}
pub async fn init_download(download_url: &str, local_filename: &str, os_type: &str, auto_update_supported: bool) {
println!("[init_download] • Initialize downloading from • {:?}", download_url);
println!("[init_download] • Save local file name • {:?}", local_filename);
if let Err(e) = download_file(download_url, local_filename, os_type, auto_update_supported).await {
eprintln!("[init_download] • An error occurred! Failed to download the file: {}", e);
} else {
println!("[init_download] • Code finishes without errors.");
exit(0)
}
}

View File

@@ -20,6 +20,14 @@ pub async fn finish_login(
crate::state::login_finish(code, flow, &state.pool).await
}
#[tracing::instrument]
pub async fn offline_auth(
name: &str
) -> crate::Result<Credentials> {
let state = State::get().await?;
crate::state::offline_auth(name, &state.pool).await
}
#[tracing::instrument]
pub async fn get_default_user() -> crate::Result<Option<uuid::Uuid>> {
let state = State::get().await?;

View File

@@ -11,6 +11,7 @@ pub mod process;
pub mod profile;
pub mod settings;
pub mod tags;
pub mod download;
pub mod data {
pub use crate::state::{

View File

@@ -16,6 +16,7 @@ use chrono::Utc;
use daedalus as d;
use daedalus::minecraft::{RuleAction, VersionInfo};
use daedalus::modded::LoaderVersion;
use rand::seq::SliceRandom;
use st::Profile;
use std::collections::HashMap;
use tokio::process::Command;
@@ -24,6 +25,8 @@ mod args;
pub mod download;
use crate::state::ACTIVE_STATE;
// All nones -> disallowed
// 1+ true -> allowed
// 1+ false -> disallowed
@@ -672,10 +675,11 @@ pub async fn launch_minecraft(
}
}
let _ = state
.discord_rpc
.set_activity(&format!("Playing {}", profile.name), true)
.await;
let selected_phrase = ACTIVE_STATE.choose(&mut rand::thread_rng()).unwrap();
let _ = state
.discord_rpc
.set_activity(&format!("{} {}", selected_phrase, profile.name), true)
.await;
// Create Minecraft child by inserting it into the state
// This also spawns the process and prepares the subsequent processes

View File

@@ -24,7 +24,7 @@ impl DirectoryInfo {
// init() is not needed for this function
pub fn get_initial_settings_dir() -> Option<PathBuf> {
Self::env_path("THESEUS_CONFIG_DIR")
.or_else(|| Some(dirs::data_dir()?.join("ModrinthApp")))
.or_else(|| Some(dirs::data_dir()?.join("AstralRinthApp")))
}
/// Get all paths needed for Theseus to operate properly

View File

@@ -1,12 +1,17 @@
use std::sync::{atomic::AtomicBool, Arc};
use std::{
sync::{atomic::AtomicBool, Arc},
time::{SystemTime, UNIX_EPOCH},
};
use discord_rich_presence::{
activity::{Activity, Assets},
activity::{Activity, Assets, Timestamps},
DiscordIpc, DiscordIpcClient,
};
use rand::seq::SliceRandom;
use tokio::sync::RwLock;
use crate::state::Profile;
// use crate::state::Profile;
use crate::util::utils;
use crate::State;
pub struct DiscordGuard {
@@ -14,12 +19,29 @@ pub struct DiscordGuard {
connected: Arc<AtomicBool>,
}
pub(crate) const ACTIVE_STATE: [&str; 6] = [
"Explores",
"Travels with",
"Pirating",
"Investigating the",
"Engaged in",
"Conducting",
];
pub(crate) const INACTIVE_STATE: [&str; 6] = [
"Idling...",
"Waiting for the pirate team...",
"Taking a break...",
"Resting...",
"On standby...",
"In a holding pattern...",
];
impl DiscordGuard {
/// Initialize discord IPC client, and attempt to connect to it
/// If it fails, it will still return a DiscordGuard, but the client will be unconnected
pub fn init() -> crate::Result<DiscordGuard> {
let dipc =
DiscordIpcClient::new("1123683254248148992").map_err(|e| {
DiscordIpcClient::new("1190718475832918136").map_err(|e| {
crate::ErrorKind::OtherError(format!(
"Could not create Discord client {}",
e,
@@ -77,11 +99,32 @@ impl DiscordGuard {
return Ok(());
}
let activity = Activity::new().state(msg).assets(
Assets::new()
.large_image("modrinth_simple")
.large_text("Modrinth Logo"),
);
// let activity = Activity::new().state(msg).assets(
// Assets::new()
// .large_image("modrinth_simple")
// .large_text("Modrinth Logo"),
// );
let launcher =
utils::read_package_json().expect("Failed to read package.json");
let build_info = format!("AR • v{}", launcher.version);
let build_download = "https://astralium.su/get/ar";
let time = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Failed to get system time")
.as_secs() as i64;
let activity = Activity::new()
.state(msg)
.assets(
Assets::new()
.large_image("astralrinth_logo")
.large_text(&build_info)
.small_image("astralrinth_logo")
.small_text(&build_download),
)
.timestamps(Timestamps::new().start(time));
// Attempt to set the activity
// If the existing connection fails, attempt to reconnect and try again
@@ -167,20 +210,10 @@ impl DiscordGuard {
return self.clear_activity(true).await;
}
let running_profiles = state.process_manager.get_all();
if let Some(existing_child) = running_profiles.first() {
let prof =
Profile::get(&existing_child.profile_path, &state.pool).await?;
if let Some(prof) = prof {
self.set_activity(
&format!("Playing {}", prof.name),
reconnect_if_fail,
)
.await?;
}
} else {
self.set_activity("Idling...", reconnect_if_fail).await?;
}
let selected_phrase =
INACTIVE_STATE.choose(&mut rand::thread_rng()).unwrap();
self.set_activity(&format!("{}", selected_phrase), reconnect_if_fail)
.await?;
Ok(())
}
}

View File

@@ -197,6 +197,29 @@ pub async fn login_finish(
Ok(credentials)
}
#[tracing::instrument]
pub async fn offline_auth(
name: &str,
exec: impl sqlx::Executor<'_, Database = sqlx::Sqlite> + Copy,
) -> crate::Result<Credentials> {
let random_uuid = Uuid::new_v4();
let access_token = "null".to_string();
let refresh_token = "null".to_string();
let credentials = Credentials {
id: random_uuid,
username: name.to_string(),
access_token: access_token,
refresh_token: refresh_token,
expires: Utc::now() + Duration::days(365 * 99),
active: true,
};
credentials.upsert(exec).await?;
Ok(credentials)
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Credentials {
pub id: Uuid,

View File

@@ -3,6 +3,7 @@ pub mod fetch;
pub mod io;
pub mod jre;
pub mod platform;
pub mod utils;
/// Wrap a builder which uses a mut reference into one which outputs an owned value
macro_rules! wrap_ref_builder {

View File

@@ -0,0 +1,18 @@
use serde::{Deserialize, Serialize};
use tokio::io;
const PACKAGE_JSON_CONTENT: &str =
include_str!("../../../../apps/app-frontend/package.json");
#[derive(Serialize, Deserialize)]
pub struct Launcher {
pub version: String,
pub development_build: bool,
}
pub fn read_package_json() -> io::Result<Launcher> {
// Deserialize the content of package.json into a Launcher struct
let launcher: Launcher = serde_json::from_str(PACKAGE_JSON_CONTENT)?;
Ok(launcher)
}