Skip to content

Commit

Permalink
Improve the shuffling around of instance data + some restructuring
Browse files Browse the repository at this point in the history
  • Loading branch information
DerCommander323 committed Dec 17, 2023
1 parent 1857afc commit 7ddb313
Show file tree
Hide file tree
Showing 12 changed files with 274 additions and 172 deletions.
53 changes: 26 additions & 27 deletions src-tauri/src/minecraft/instances.rs
Original file line number Diff line number Diff line change
@@ -1,38 +1,34 @@
use std::fs::{DirEntry, read_to_string, write};

use chrono::NaiveDateTime;
use configparser::ini::Ini;
use log::debug;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use tauri::{Manager, AppHandle};

use super::modloaders::modloaders::{self, ModLoaders};

#[derive(Clone, serde::Serialize)]
struct SimpleInstance {
name: String,
icon: String,
path: String,
id: u32,
mc_version: String,
modloader: ModLoader,
last_played: LastPlayed,
instance_type: InstanceType
#[derive(Clone, Serialize, Deserialize)]
pub struct SimpleInstance {
pub name: String,
pub icon: String,
pub path: String,
pub id: u32,
pub mc_version: String,
pub modloader: ModLoader,
pub last_played: Option<NaiveDateTime>,
pub instance_type: InstanceType
}

#[derive(Clone, serde::Serialize)]
enum LastPlayed {
String(String),
Epoch(u64),
Never
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ModLoader {
pub name: String,
pub typ: modloaders::ModLoaders,
pub version: String
}
#[derive(Clone, Debug, serde::Serialize)]
struct ModLoader {
name: String,
typ: modloaders::ModLoaders,
version: String
}
#[derive(Clone, Debug, serde::Serialize)]
enum InstanceType {
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum InstanceType {
CurseForge,
MultiMC
}
Expand Down Expand Up @@ -79,7 +75,10 @@ pub fn handle_instance_cf(dir: DirEntry, app_handle: AppHandle) {
}
}
},
last_played: if let Some(string) = json["lastPlayed"].as_str() { LastPlayed::String(string.into()) } else { LastPlayed::Never },
last_played: if let Some(string) = json["lastPlayed"].as_str() {
let time = NaiveDateTime::parse_and_remainder(string, "%Y-%m-%dT%H:%M:%S").unwrap().0;
if time.timestamp() < 5 { None } else { Some(time) }
} else { None },
instance_type: InstanceType::CurseForge,
}.into())
}
Expand Down Expand Up @@ -129,9 +128,9 @@ pub fn handle_instance_mmc(dir: DirEntry, app_handle: AppHandle) {
},
last_played: if let Some(last_launch_time) = config.get("default","lastLaunchTime") {
if let Ok(last_played) = last_launch_time.parse::<u64>() {
LastPlayed::Epoch(last_played)
} else { LastPlayed::Never }
} else { LastPlayed::Never },
NaiveDateTime::from_timestamp_millis(last_played.try_into().unwrap())
} else { None }
} else { None },
instance_type: InstanceType::MultiMC
});
}
Expand Down
21 changes: 19 additions & 2 deletions src-tauri/src/minecraft/java.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
use std::process::Command;

use log::{info, warn};
use log::{*};
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
pub struct JavaDetails {
pub path: String,
pub label: String,
pub version: String,
pub xmx: u32,
pub xms: u32,
pub args: String
}

#[tauri::command(async)]
pub fn get_java_version(path: String, args: String) -> Result<String, String> {
Expand All @@ -22,4 +33,10 @@ pub fn get_java_version(path: String, args: String) -> Result<String, String> {
Err(String::from_utf8(output).unwrap())
}
}
}
}

impl JavaDetails {
pub fn get_args(&self) -> String {
format!("-Xmx{}M -Xms{}M {}", self.xmx, self.xms, self.args)
}
}
51 changes: 25 additions & 26 deletions src-tauri/src/minecraft/launching/launching.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use std::process::Command;

use log::{info, debug, warn};
use log::{*};
use reqwest::Client;
use tauri::AppHandle;

use crate::{accounts::get_active_account, minecraft::{launching::mc_structs::MCVersionManifest, modloaders::modloaders::ModLoaders}, authentication::auth_structs::MCAccount, notify, NotificationState, get_library_dir, get_classpath_separator};
use crate::{accounts::get_active_account, minecraft::{launching::mc_structs::MCVersionManifest, instances::SimpleInstance, java::JavaDetails}, authentication::auth_structs::MCAccount, notify, NotificationState, get_library_dir, get_classpath_separator};

use super::mc_structs::MCVersionDetails;

Expand All @@ -17,57 +17,56 @@ struct Args {

#[tauri::command(async)]
pub async fn launch_instance(
minecraft_path: String,
version_id: String,
loader_version: String,
loader: ModLoaders,
java_path: String,
additional_args: String,
instance_id: u32,
instance: SimpleInstance,
java: JavaDetails,
app_handle: AppHandle
) -> Result<(), String> {
info!("Launching: {minecraft_path}, Version: {version_id}, id: {instance_id}");
let SimpleInstance { path, name: _, icon: _, id, mc_version, modloader: _, last_played: _, instance_type: _ } = instance.clone();
info!("Launching: {path}, Version: {mc_version}, id: {id}");

let args = get_arguments(version_id, loader, loader_version, minecraft_path.clone(), &java_path).await?;
let args = get_arguments(&instance, &java).await?;
let additional_args = java.get_args();

debug!("Args: {:#?}\nCustom Args: {}", args, additional_args);
info!("Launching NOW!");

let mut process = Command::new(java_path)
.current_dir(&minecraft_path)
let mut process = Command::new(java.path)
.current_dir(&path)
.args(additional_args.split_whitespace())
.args(args.jvm)
.arg(args.main_class)
.args(args.game)
.spawn()
.or_else(|err| Err(format!("Failed to run Minecraft command: {}", err.to_string())))?;

notify(&app_handle, &format!("{}_status", instance_id), "Instance launched successfully!", NotificationState::Success);
notify(&app_handle, &format!("{}_status", id), "Instance launched successfully!", NotificationState::Success);

let exit_status = process.wait().expect("Failed to wait on Java process! How did this happen?");
info!("Exited with status: {}", exit_status);

if exit_status.success() {
info!("{minecraft_path} exited successfully.");
notify(&app_handle, &format!("{}_status", instance_id), "Instance exited successfully.", NotificationState::Success);
info!("{path} exited successfully.");
notify(&app_handle, &format!("{}_status", id), "Instance exited successfully.", NotificationState::Success);
} else {
warn!("{minecraft_path} exited (crashed) with status {}", exit_status);
notify(&app_handle, &format!("{}_status", instance_id), &format!("Instance crashed with code {}", exit_status.code().unwrap_or(323)), NotificationState::Error);
warn!("{path} exited (crashed) with status {}", exit_status);
notify(&app_handle, &format!("{}_status", id), &format!("Instance crashed with code {}", exit_status.code().unwrap_or(323)), NotificationState::Error);
}

Ok(())
}

async fn get_arguments(version_id: String, loader: ModLoaders, loader_version: String, minecraft_path: String, java_path: &str) -> Result<Args, String> {
async fn get_arguments(instance: &SimpleInstance, java: &JavaDetails) -> Result<Args, String> {
let client = Client::new();

let loader = instance.modloader.typ;

let mut account = get_active_account()
.ok_or("Could not get the selected account!".to_string())?;

account.refresh(&client, false).await;

info!("Getting version details for {version_id}");
let compact_version = MCVersionDetails::from_id(version_id.clone(), &client)
info!("Getting version details for {}", instance.mc_version);
let compact_version = MCVersionDetails::from_id(instance.mc_version.clone(), &client)
.await
.ok_or("Could not get Minecraft version details!".to_string())?;

Expand All @@ -81,14 +80,14 @@ async fn get_arguments(version_id: String, loader: ModLoaders, loader_version: S
debug!("Pre-downloading client jar...");
version.get_client_jar(&client).await;

if let Some(mf) = loader.get_manifest(&version_id, &loader_version, &client).await {
if let Some(mf) = loader.get_manifest(&instance.mc_version, &instance.modloader.version, &client).await {
info!("Merging with manifest of {loader} Loader...");
version.merge_with(mf)
}

info!("Finished getting manifest.");

loader.prepare_launch(&version_id, &loader_version, &client, java_path).await;
loader.prepare_launch(&instance.mc_version, &instance.modloader.version, &client, &java.path).await;

info!("Beginning argument parsing...");
Ok(
Expand All @@ -100,13 +99,13 @@ async fn get_arguments(version_id: String, loader: ModLoaders, loader_version: S
},
account,
version,
minecraft_path,
&instance.path,
&client
).await
)
}

async fn parse_arguments(args_struct: Args, account: MCAccount, version: MCVersionManifest, minecraft_path: String, client: &Client) -> Args {
async fn parse_arguments(args_struct: Args, account: MCAccount, version: MCVersionManifest, minecraft_path: &str, client: &Client) -> Args {
let replacements = vec![
("${auth_player_name}", account.mc_profile.name),
("${auth_uuid}", account.mc_profile.id),
Expand All @@ -123,7 +122,7 @@ async fn parse_arguments(args_struct: Args, account: MCAccount, version: MCVersi
("${natives_directory}", format!("{minecraft_path}/natives")),
("${launcher_name}", "yamcl".to_string()),
("${launcher_version}", "323".to_string()),
("${game_directory}", minecraft_path),
("${game_directory}", minecraft_path.to_string()),
("${user_type}", "msa".to_string()),
("${resolution_width}", 1200.to_string()),
("${resolution_height}", 800.to_string()),
Expand Down
9 changes: 6 additions & 3 deletions src-tauri/src/minecraft/launching/manifests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,9 +221,12 @@ impl MCVersionManifest {
self.id = forge.id;
self.main_class = forge.main_class;

if let Some(args) = &mut self.arguments {
args.game.append(&mut forge.arguments.game);
args.jvm.append(&mut forge.arguments.jvm);
if let (Some(args), Some(forge_args)) = (&mut self.arguments, &mut forge.arguments) {
args.game.append(&mut forge_args.game);
args.jvm.append(&mut forge_args.jvm);
}
if let Some(forge_mcargs) = &mut forge.minecraft_arguments {
self.minecraft_arguments = Some(forge_mcargs.to_string())
}

for lib in &mut forge.libraries {
Expand Down
8 changes: 7 additions & 1 deletion src-tauri/src/minecraft/modloaders/forge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ use super::forge_installer::{ForgeInstaller, get_manifest_path, get_install_prof
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ForgeVersionManifest {
pub arguments: MCArguments,
pub arguments: Option<MCArguments>,
pub minecraft_arguments: Option<String>,
pub id: String,
pub libraries: Vec<MCLibrary>,
pub main_class: String,
Expand Down Expand Up @@ -74,6 +75,11 @@ impl ForgeInstallProfile {
pub async fn download_libraries(&mut self, client: &Client) {
info!("Downloading installer libraries...");
for lib in &mut self.libraries {
if let Some(artifact) = &mut lib.downloads.artifact {
if artifact.url.is_empty() && lib.name.contains("minecraftforge") {
artifact.url = format!("https://maven.minecraftforge.net/{}", maven_identifier_to_path(&lib.name))
}
}
lib.download_checked(client).await;
}
}
Expand Down
42 changes: 26 additions & 16 deletions src-tauri/src/minecraft/modloaders/forge_installer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,11 @@ impl ForgeInstaller {
pub async fn extract_needed(mc_ver: &str, forge_ver: &str, client: &Client) {
let installer = Self::download(&mc_ver, &forge_ver, client).await;


debug!("Extracting installer jar...");
let jar = jars::jar(
installer,
JarOptionBuilder::builder()
.targets(&vec!["version.json", "install_profile.json", "data", "META-INF"])
.targets(&vec!["version.json", "install_profile.json", "data"])
.ext("the library has a bug that requires both of these to be set") // otherwise it will always extract all files
.build()
).expect("Failed to extract Forge installer jar!");
Expand Down Expand Up @@ -93,20 +92,7 @@ impl ForgeProcessor {

let args = self.parse_args(install_profile, side);

let jar = jars::jar(
get_library_dir().join(maven_identifier_to_path(&self.jar)),
JarOptionBuilder::builder().keep_meta_info().target("META-INF/MANIFEST.MF").build()
).expect("Failed to open jar!");

let jar_mf = String::from_utf8_lossy(
jar.files.iter().find(|&(path, _)| {
path == "META-INF/MANIFEST.MF"
}).expect("Could not find MANIFEST.MF in jar!").1
);

let main_class = jar_mf.split("\n").find(|&line| {
line.starts_with("Main-Class:")
}).unwrap().split_once(": ").unwrap().1.trim();
let main_class = get_jar_main_class(get_library_dir().join(maven_identifier_to_path(&self.jar)));

info!("Running processor...");

Expand Down Expand Up @@ -169,6 +155,30 @@ pub enum Side {
Client
}

pub fn get_jar_main_class(jar_path: PathBuf) -> String {
let jar = jars::jar(
&jar_path,
JarOptionBuilder::builder()
.keep_meta_info().target("META-INF/MANIFEST.MF")
.ext("MF")
.build()
).expect(&format!("Failed to open jar {jar_path:?}"));

let jar_mf = String::from_utf8_lossy(
jar.files.iter().find(|&(path, _)| {
path == "META-INF/MANIFEST.MF"
}).expect(&format!("Could not find MANIFEST.MF in jar {jar_path:?}")).1
);

let main_class = jar_mf.split("\n").find(|&line| {
line.starts_with("Main-Class:")
}).expect(&format!("Could not find main class in jar manifest {jar_mf}"))
.split_once(": ")
.unwrap()
.1.trim();

main_class.to_string()
}

pub fn get_installer_extracts_dir(mc_ver: &str, forge_ver: &str) -> PathBuf {
get_forge_cache_dir().join(format!("forge-{mc_ver}-{forge_ver}"))
Expand Down
Loading

0 comments on commit 7ddb313

Please sign in to comment.