Skip to content

Commit

Permalink
Fabric support + mold linker for actions
Browse files Browse the repository at this point in the history
  • Loading branch information
DerCommander323 committed Nov 19, 2023
1 parent 5791a89 commit 8c2fba2
Show file tree
Hide file tree
Showing 9 changed files with 73 additions and 45 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/build-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ jobs:
with:
workspaces: src-tauri

- name: Setup mold as the linker
uses: rui314/setup-mold@main

- name: Install Tauri-CLI
run: cargo install tauri-cli

Expand Down
13 changes: 8 additions & 5 deletions src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,13 +111,14 @@ pub fn notify(app_handle: &AppHandle, name: &str, text: &str, status: Notificati
}

/// Checks if the checksum of the file at `path` matches `checksum` and downloads it from `url` if not.
pub async fn download_file_checked(client: &Client, checksum: &String, path: &PathBuf, url: &String) {
if !path.is_file() || {
pub async fn download_file_checked(client: &Client, checksum: Option<&String>, path: &PathBuf, url: &String) {
if !path.is_file() || if let Some(csum) = checksum {
if let Ok(contents) = fs::read(&path) {
let contents_checksum = Sha1::from(contents).digest().to_string();
&contents_checksum != checksum
&contents_checksum != csum
} else { true }
} {
} else { false }
{
download_file(client, path, url).await
} else {
debug!("Skipped downloading {}", path.to_string_lossy())
Expand All @@ -128,7 +129,9 @@ async fn download_file(client: &Client, path: &PathBuf, url: &String) {
debug!("Downloading to {} from {url}", path.to_string_lossy());
let response = client.get(url).send().await.unwrap();
if let Some(parent_path) = path.parent() {
create_dir_all(parent_path).expect(&format!("Failed to create directories: {}", parent_path.to_string_lossy()));
if !parent_path.exists() {
create_dir_all(parent_path).expect(&format!("Failed to create directories: {}", parent_path.to_string_lossy()));
}
}
let mut file = std::fs::File::create(path).expect(&format!("Failed create file: {}", path.to_string_lossy()));
let mut content = Cursor::new(response.bytes().await.unwrap());
Expand Down
24 changes: 16 additions & 8 deletions src-tauri/src/minecraft/launching/launching.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use log::{info, debug, warn};
use reqwest::Client;
use tauri::AppHandle;

use crate::{auth::get_active_account, minecraft::{launching::mc_structs::MCVersionManifest, modloaders::modloaders::ModLoaders}, authentication::auth_structs::MCAccount, notify, NotificationState};
use crate::{auth::get_active_account, minecraft::{launching::mc_structs::MCVersionManifest, modloaders::{modloaders::{ModLoaders, LoaderManifests}, fabric::FabricVersionManifest}}, authentication::auth_structs::MCAccount, notify, NotificationState};

use super::mc_structs::MCVersionDetails;

Expand All @@ -28,7 +28,7 @@ pub async fn launch_instance(
) -> Result<(), String> {
info!("Launching: {minecraft_path}, Version: {version_id}, id: {instance_id}");

let args = get_arguments(version_id, minecraft_path.clone()).await?;
let args = get_arguments(version_id, loader, loader_version, minecraft_path.clone()).await?;

debug!("Args: {:#?}\nCustom Args: {}", args, additional_args);
info!("Launching NOW!");
Expand All @@ -42,39 +42,47 @@ pub async fn launch_instance(
.spawn()
.or_else(|err| Err(format!("Failed to run Minecraft command: {}", err.to_string())))?;

notify(&app_handle, &format!("notification_{}_status", instance_id), "Instance launched successfully!", NotificationState::Success);
notify(&app_handle, &format!("{}_status", instance_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!("notification_{}_status", instance_id), "Instance exited successfully.", NotificationState::Success);
notify(&app_handle, &format!("{}_status", instance_id), "Instance exited successfully.", NotificationState::Success);
} else {
warn!("{minecraft_path} exited (crashed) with status {}", exit_status);
notify(&app_handle, &format!("notification_{}_status", instance_id), &format!("Instance crashed with code {}", exit_status.code().unwrap_or(323)), NotificationState::Error);
notify(&app_handle, &format!("{}_status", instance_id), &format!("Instance crashed with code {}", exit_status.code().unwrap_or(323)), NotificationState::Error);
}

Ok(())
}

async fn get_arguments(version_id: String, minecraft_path: String) -> Result<Args, String> {
async fn get_arguments(version_id: String, loader: ModLoaders, loader_version: String, minecraft_path: String) -> Result<Args, String> {
let client = Client::new();
let account = get_active_account()
.or(Err("Could not get the selected account!".to_string()))?;

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

debug!("Got compact version info: {:?}", compact_version);
info!("Getting extended version info from {}", compact_version.url);

let version = compact_version.get_manifest(&client)
let mut version = compact_version.get_manifest(&client)
.await
.ok_or("Could not get extended Minecraft version details!".to_string())?;

match loader {
ModLoaders::Forge => todo!(),
ModLoaders::Fabric => if let Some(mf) = FabricVersionManifest::get(version_id, loader_version, &client).await {
version.merge_with(LoaderManifests::Fabric(mf))
},
_ => {},
}

debug!("Got extended version info. (not listed due to length)");

info!("Beginning argument parsing...");
Expand Down
2 changes: 1 addition & 1 deletion src-tauri/src/minecraft/launching/libraries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ impl MCLibrary {
if let Some(artifact) = &self.downloads.artifact {
download_file_checked(
&client,
&artifact.sha1,
artifact.sha1.as_ref(),
&self.get_path(),
&artifact.url
).await
Expand Down
9 changes: 1 addition & 8 deletions src-tauri/src/minecraft/launching/mc_structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ use std::collections::HashMap;
use serde::{Serialize, Deserialize};
use serde_json::Value;

use crate::minecraft::modloaders::fabric::FabricVersionManifest;


#[derive(Debug, Serialize, Deserialize)]
pub struct MCVersionList {
Expand All @@ -31,11 +29,6 @@ pub struct MCLatest {
pub snapshot: String
}

pub enum VersionManifests {
Vanilla(MCVersionManifest),
Fabric(FabricVersionManifest)
}


#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
Expand Down Expand Up @@ -180,7 +173,7 @@ pub struct MCLibraryDownloadsArtifacts {
pub path: String,
pub url: String,
pub size: u32,
pub sha1: String
pub sha1: Option<String>
}

#[derive(Debug, Serialize, Deserialize)]
Expand Down
43 changes: 32 additions & 11 deletions src-tauri/src/minecraft/launching/versions.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use std::{iter, path::PathBuf, fs};

use log::error;
use log::{error, warn};
use reqwest::Client;

use crate::{get_client_jar_dir, download_file_checked, get_log4j_dir, get_assets_dir, minecraft::modloaders::modloaders::ModLoaders};
use crate::{get_client_jar_dir, download_file_checked, get_log4j_dir, get_assets_dir, minecraft::modloaders::modloaders::LoaderManifests};

use super::mc_structs::{MCLibrary, MCRule, MCVersionList, MCVersionDetails, MCVersionManifest, MCJvmArg, MCValue, MCGameArg, AssetIndexFile, VersionManifests};
use super::mc_structs::{MCLibrary, MCRule, MCVersionList, MCVersionDetails, MCVersionManifest, MCJvmArg, MCValue, MCGameArg, AssetIndexFile, MCLibraryDownloads, MCLibraryDownloadsArtifacts};

const VERSION_URL: &str = "https://piston-meta.mojang.com/mc/game/version_manifest_v2.json";

Expand Down Expand Up @@ -135,7 +135,7 @@ impl MCVersionManifest {
let path = get_client_jar_dir().join(format!("{}.jar", self.id));
download_file_checked(
client,
&self.downloads.client.sha1,
Some(&self.downloads.client.sha1),
&path,
&self.downloads.client.url
).await;
Expand All @@ -147,7 +147,7 @@ impl MCVersionManifest {
let path = get_log4j_dir().join(&logging.client.file.id);
download_file_checked(
client,
&logging.client.file.sha1,
Some(&logging.client.file.sha1),
&path,
&logging.client.file.url
).await;
Expand All @@ -162,7 +162,7 @@ impl MCVersionManifest {
if !index_path.exists() {
download_file_checked(
client,
&self.asset_index.sha1,
Some(&self.asset_index.sha1),
index_path,
&self.asset_index.url
).await;
Expand All @@ -174,7 +174,7 @@ impl MCVersionManifest {
let url = format!("https://resources.download.minecraft.net/{}/{}", &asset.1.hash[..2], asset.1.hash);
download_file_checked(
client,
&asset.1.hash,
Some(&asset.1.hash),
&assets_dir.join("objects").join(&asset.1.hash[..2]).join(&asset.1.hash),
&url
).await;
Expand All @@ -184,18 +184,39 @@ impl MCVersionManifest {
assets_dir.to_string_lossy().to_string()
}

pub fn merge_with(&mut self, other: VersionManifests) {
pub fn merge_with(&mut self, other: LoaderManifests) {
match other {
VersionManifests::Vanilla(_) => todo!(), // should never happen
VersionManifests::Fabric(mut fabric) => {
LoaderManifests::Fabric(mut fabric) => {
self.id = fabric.id;
self.main_class = fabric.main_class;


if let Some(args) = &mut self.arguments {
args.game.append(&mut fabric.arguments.game);
args.jvm.append(&mut fabric.arguments.jvm);
}

self.libraries.append(
&mut fabric.libraries.iter().map(|lib| {
let (raw_path, raw_name) = lib.name.split_once(":").expect("Fabric Library should contain a : seperator!");
let path = format!("{}/{}/{}.jar", raw_path.replace(".", "/"), raw_name.replace(":", "/"), raw_name.replace(":", "-"));

warn!("path: {path}");
MCLibrary {
downloads: MCLibraryDownloads {
artifact: Some(MCLibraryDownloadsArtifacts {
path: path.to_string(),
url: format!("{}/{}", lib.url, path),
size: 0,
sha1: None,
}),
classifiers: None
},
name: lib.name.to_string(),
rules: None,
natives: None,
}
}).collect()
)
},
}
}
Expand Down
15 changes: 4 additions & 11 deletions src-tauri/src/minecraft/modloaders/fabric.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,27 @@ use log::{info, error};
use reqwest::Client;
use serde::{Deserialize, Serialize};

use crate::minecraft::launching::mc_structs::{MCArguments, MCAssetIndex, MCDownloads, MCJavaVersion, MCLogging};
use crate::minecraft::launching::mc_structs::MCArguments;


#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FabricVersionManifest {
pub arguments: MCArguments,
pub minecraft_arguments: Option<String>,
pub asset_index: MCAssetIndex,
pub assets: String,
pub compliance_level: u16,
pub downloads: MCDownloads,
pub id: String,
pub java_version: MCJavaVersion,
pub libraries: Vec<FabricLibrary>,
pub main_class: String,
pub minimum_launcher_version: u16,
pub inherits_from: String,
pub release_time: String,
pub time: String,
pub logging: Option<MCLogging>,
#[serde(rename = "type")]
pub typ: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct FabricLibrary {
name: String,
url: String
pub name: String,
pub url: String
}

impl FabricVersionManifest {
Expand Down
7 changes: 7 additions & 0 deletions src-tauri/src/minecraft/modloaders/modloaders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@ use std::fmt;

use serde::{Deserialize, Serialize};

use super::fabric::FabricVersionManifest;


pub enum LoaderManifests {
Fabric(FabricVersionManifest)
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum ModLoaders {
Vanilla,
Expand Down
2 changes: 1 addition & 1 deletion src/scripts/instances.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ export async function launchInstance(mcPath, instanceName, mcVer, instanceId, lo
console.log(`Using java path: ${java.path}, with args ${java.args}`)
const unlisten = await listen(`notification_${instanceId}_status`, event => {
console.warn(event)
finishNotification(`notification_${instanceId}_status`, event.payload.text, event.payload.status)
finishNotification(`instance_launch_${instanceId}`, event.payload.text, event.payload.status)
})
await invoke('launch_instance', {
minecraftPath: mcPath, versionId: mcVer, javaPath: java.path, additionalArgs: java.args, instanceId, loaderVersion, loader
Expand Down

0 comments on commit 8c2fba2

Please sign in to comment.