Merge commit 'dbde3c4669af10dd577590ed6980e5bd4552d13c' into feature-clean

This commit is contained in:
2025-06-19 03:45:56 +03:00
364 changed files with 8892 additions and 7026 deletions

View File

@@ -166,9 +166,8 @@ pub async fn test_jre(
path: PathBuf,
major_version: u32,
) -> crate::Result<bool> {
let jre = match jre::check_java_at_filepath(&path).await {
Some(jre) => jre,
None => return Ok(false),
let Some(jre) = jre::check_java_at_filepath(&path).await else {
return Ok(false);
};
let (major, _) = extract_java_majorminor_version(&jre.version)?;
Ok(major == major_version)

View File

@@ -97,12 +97,15 @@ pub struct ATLauncherMod {
// Check if folder has a instance.json that parses
pub async fn is_valid_atlauncher(instance_folder: PathBuf) -> bool {
let instance: String =
io::read_to_string(&instance_folder.join("instance.json"))
.await
.unwrap_or("".to_string());
let instance: Result<ATInstance, serde_json::Error> =
serde_json::from_str::<ATInstance>(&instance);
let instance = serde_json::from_str::<ATInstance>(
&io::read_any_encoding_to_string(
&instance_folder.join("instance.json"),
)
.await
.unwrap_or(("".into(), encoding_rs::UTF_8))
.0,
);
if let Err(e) = instance {
tracing::warn!(
"Could not parse instance.json at {}: {}",
@@ -124,14 +127,17 @@ pub async fn import_atlauncher(
) -> crate::Result<()> {
let atlauncher_instance_path = atlauncher_base_path
.join("instances")
.join(instance_folder.clone());
.join(&instance_folder);
// Load instance.json
let atinstance: String =
io::read_to_string(&atlauncher_instance_path.join("instance.json"))
.await?;
let atinstance: ATInstance =
serde_json::from_str::<ATInstance>(&atinstance)?;
let atinstance = serde_json::from_str::<ATInstance>(
&io::read_any_encoding_to_string(
&atlauncher_instance_path.join("instance.json"),
)
.await
.unwrap_or(("".into(), encoding_rs::UTF_8))
.0,
)?;
// Icon path should be {instance_folder}/instance.png if it exists,
// Second possibility is ATLauncher/configs/images/{safe_pack_name}.png (safe pack name is alphanumeric lowercase)

View File

@@ -36,13 +36,15 @@ pub struct InstalledModpack {
// Check if folder has a minecraftinstance.json that parses
pub async fn is_valid_curseforge(instance_folder: PathBuf) -> bool {
let minecraftinstance: String =
io::read_to_string(&instance_folder.join("minecraftinstance.json"))
.await
.unwrap_or("".to_string());
let minecraftinstance: Result<MinecraftInstance, serde_json::Error> =
serde_json::from_str::<MinecraftInstance>(&minecraftinstance);
minecraftinstance.is_ok()
let minecraft_instance = serde_json::from_str::<MinecraftInstance>(
&io::read_any_encoding_to_string(
&instance_folder.join("minecraftinstance.json"),
)
.await
.unwrap_or(("".into(), encoding_rs::UTF_8))
.0,
);
minecraft_instance.is_ok()
}
pub async fn import_curseforge(
@@ -50,19 +52,20 @@ pub async fn import_curseforge(
profile_path: &str, // path to profile
) -> crate::Result<()> {
// Load minecraftinstance.json
let minecraft_instance: String = io::read_to_string(
&curseforge_instance_folder.join("minecraftinstance.json"),
)
.await?;
let minecraft_instance: MinecraftInstance =
serde_json::from_str::<MinecraftInstance>(&minecraft_instance)?;
let override_title: Option<String> = minecraft_instance.name.clone();
let minecraft_instance = serde_json::from_str::<MinecraftInstance>(
&io::read_any_encoding_to_string(
&curseforge_instance_folder.join("minecraftinstance.json"),
)
.await
.unwrap_or(("".into(), encoding_rs::UTF_8))
.0,
)?;
let override_title = minecraft_instance.name;
let backup_name = format!(
"Curseforge-{}",
curseforge_instance_folder
.file_name()
.map(|a| a.to_string_lossy().to_string())
.unwrap_or("Unknown".to_string())
.map_or("Unknown".to_string(), |a| a.to_string_lossy().to_string())
);
let state = State::get().await?;

View File

@@ -25,12 +25,12 @@ pub struct GDLauncherLoader {
// Check if folder has a config.json that parses
pub async fn is_valid_gdlauncher(instance_folder: PathBuf) -> bool {
let config: String =
io::read_to_string(&instance_folder.join("config.json"))
let config = serde_json::from_str::<GDLauncherConfig>(
&io::read_any_encoding_to_string(&instance_folder.join("config.json"))
.await
.unwrap_or("".to_string());
let config: Result<GDLauncherConfig, serde_json::Error> =
serde_json::from_str::<GDLauncherConfig>(&config);
.unwrap_or(("".into(), encoding_rs::UTF_8))
.0,
);
config.is_ok()
}
@@ -39,18 +39,20 @@ pub async fn import_gdlauncher(
profile_path: &str, // path to profile
) -> crate::Result<()> {
// Load config.json
let config: String =
io::read_to_string(&gdlauncher_instance_folder.join("config.json"))
.await?;
let config: GDLauncherConfig =
serde_json::from_str::<GDLauncherConfig>(&config)?;
let override_title: Option<String> = config.loader.source_name.clone();
let config = serde_json::from_str::<GDLauncherConfig>(
&io::read_any_encoding_to_string(
&gdlauncher_instance_folder.join("config.json"),
)
.await
.unwrap_or(("".into(), encoding_rs::UTF_8))
.0,
)?;
let override_title = config.loader.source_name;
let backup_name = format!(
"GDLauncher-{}",
gdlauncher_instance_folder
.file_name()
.map(|a| a.to_string_lossy().to_string())
.unwrap_or("Unknown".to_string())
.map_or("Unknown".to_string(), |a| a.to_string_lossy().to_string())
);
// Re-cache icon

View File

@@ -26,6 +26,7 @@ enum MMCInstanceEnum {
struct MMCInstanceGeneral {
pub general: MMCInstance,
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(rename_all = "PascalCase")]
pub struct MMCInstance {
@@ -144,9 +145,9 @@ pub async fn is_valid_mmc(instance_folder: PathBuf) -> bool {
let instance_cfg = instance_folder.join("instance.cfg");
let mmc_pack = instance_folder.join("mmc-pack.json");
let mmc_pack = match io::read_to_string(&mmc_pack).await {
Ok(mmc_pack) => mmc_pack,
Err(_) => return false,
let Ok((mmc_pack, _)) = io::read_any_encoding_to_string(&mmc_pack).await
else {
return false;
};
load_instance_cfg(&instance_cfg).await.is_ok()
@@ -155,7 +156,7 @@ pub async fn is_valid_mmc(instance_folder: PathBuf) -> bool {
#[tracing::instrument]
pub async fn get_instances_subpath(config: PathBuf) -> Option<String> {
let launcher = io::read_to_string(&config).await.ok()?;
let launcher = io::read_any_encoding_to_string(&config).await.ok()?.0;
let launcher: MMCLauncherEnum = serde_ini::from_str(&launcher).ok()?;
match launcher {
MMCLauncherEnum::General(p) => Some(p.general.instance_dir),
@@ -165,10 +166,9 @@ pub async fn get_instances_subpath(config: PathBuf) -> Option<String> {
// Loading the INI (instance.cfg) file
async fn load_instance_cfg(file_path: &Path) -> crate::Result<MMCInstance> {
let instance_cfg: String = io::read_to_string(file_path).await?;
let instance_cfg_enum: MMCInstanceEnum =
serde_ini::from_str::<MMCInstanceEnum>(&instance_cfg)?;
match instance_cfg_enum {
match serde_ini::from_str::<MMCInstanceEnum>(
&io::read_any_encoding_to_string(file_path).await?.0,
)? {
MMCInstanceEnum::General(instance_cfg) => Ok(instance_cfg.general),
MMCInstanceEnum::Instance(instance_cfg) => Ok(instance_cfg),
}
@@ -183,9 +183,13 @@ pub async fn import_mmc(
let mmc_instance_path =
mmc_base_path.join("instances").join(instance_folder);
let mmc_pack =
io::read_to_string(&mmc_instance_path.join("mmc-pack.json")).await?;
let mmc_pack: MMCPack = serde_json::from_str::<MMCPack>(&mmc_pack)?;
let mmc_pack = serde_json::from_str::<MMCPack>(
&io::read_any_encoding_to_string(
&mmc_instance_path.join("mmc-pack.json"),
)
.await?
.0,
)?;
let instance_cfg =
load_instance_cfg(&mmc_instance_path.join("instance.cfg")).await?;
@@ -230,7 +234,7 @@ pub async fn import_mmc(
// Kept separate as we may in the future want to add special handling for modrinth managed packs
import_mmc_unmanaged(profile_path, minecraft_folder, "Imported Modrinth Modpack".to_string(), description, mmc_pack).await?;
}
Some(MMCManagedPackType::Flame) | Some(MMCManagedPackType::ATLauncher) => {
Some(MMCManagedPackType::Flame | MMCManagedPackType::ATLauncher) => {
// For flame/atlauncher managed packs
// Treat as unmanaged, but with 'minecraft' folder instead of '.minecraft'
import_mmc_unmanaged(profile_path, minecraft_folder, "Imported Modpack".to_string(), description, mmc_pack).await?;
@@ -243,7 +247,7 @@ pub async fn import_mmc(
_ => return Err(crate::ErrorKind::InputError("Instance is managed, but managed pack type not specified in instance.cfg".to_string()).into())
}
} else {
// Direclty import unmanaged pack
// Directly import unmanaged pack
import_mmc_unmanaged(
profile_path,
minecraft_folder,

View File

@@ -357,9 +357,7 @@ pub async fn set_profile_information(
}
}
let game_version = if let Some(game_version) = game_version {
game_version
} else {
let Some(game_version) = game_version else {
return Err(crate::ErrorKind::InputError(
"Pack did not specify Minecraft version".to_string(),
)
@@ -393,10 +391,7 @@ pub async fn set_profile_information(
locked: if !ignore_lock {
true
} else {
prof.linked_data
.as_ref()
.map(|x| x.locked)
.unwrap_or(true)
prof.linked_data.as_ref().is_none_or(|x| x.locked)
},
})
}

View File

@@ -152,8 +152,7 @@ pub async fn install_zipped_mrpack_files(
if let Some(env) = project.env {
if env
.get(&EnvType::Client)
.map(|x| x == &SideType::Unsupported)
.unwrap_or(false)
.is_some_and(|x| x == &SideType::Unsupported)
{
return Ok(());
}

View File

@@ -586,7 +586,7 @@ pub async fn get_pack_export_candidates(
.await
.map_err(|e| IOError::with_path(e, &profile_base_dir))?
{
let path: PathBuf = entry.path();
let path = entry.path();
if path.is_dir() {
// Two layers of files/folders if its a folder
let mut read_dir = io::read_dir(&path).await?;
@@ -595,10 +595,10 @@ pub async fn get_pack_export_candidates(
.await
.map_err(|e| IOError::with_path(e, &profile_base_dir))?
{
let path: PathBuf = entry.path();
path_list
.push(pack_get_relative_path(&profile_base_dir, &path)?);
path_list.push(pack_get_relative_path(
&profile_base_dir,
&entry.path(),
)?);
}
} else {
// One layer of files/folders if its a file
@@ -644,7 +644,6 @@ pub async fn run(
/// Run Minecraft using a profile, and credentials for authentication
/// Returns Arc pointer to RwLock to Child
#[tracing::instrument(skip(credentials))]
pub async fn run_credentials(
path: &str,
credentials: &Credentials,
@@ -662,14 +661,15 @@ pub async fn run_credentials(
.hooks
.pre_launch
.as_ref()
.or(settings.hooks.pre_launch.as_ref());
.or(settings.hooks.pre_launch.as_ref())
.filter(|hook_command| !hook_command.is_empty());
if let Some(hook) = pre_launch_hooks {
// TODO: hook parameters
let mut cmd = hook.split(' ');
if let Some(command) = cmd.next() {
let full_path = get_full_path(&profile.path).await?;
let result = Command::new(command)
.args(cmd.collect::<Vec<&str>>())
.args(cmd)
.current_dir(&full_path)
.spawn()
.map_err(|e| IOError::with_path(e, &full_path))?
@@ -692,7 +692,12 @@ pub async fn run_credentials(
.clone()
.unwrap_or(settings.extra_launch_args);
let wrapper = profile.hooks.wrapper.clone().or(settings.hooks.wrapper);
let wrapper = profile
.hooks
.wrapper
.clone()
.or(settings.hooks.wrapper)
.filter(|hook_command| !hook_command.is_empty());
let memory = profile.memory.unwrap_or(settings.memory);
let resolution =
@@ -704,8 +709,12 @@ pub async fn run_credentials(
.unwrap_or(settings.custom_env_vars);
// Post post exit hooks
let post_exit_hook =
profile.hooks.post_exit.clone().or(settings.hooks.post_exit);
let post_exit_hook = profile
.hooks
.post_exit
.clone()
.or(settings.hooks.post_exit)
.filter(|hook_command| !hook_command.is_empty());
// Any options.txt settings that we want set, add here
let mut mc_set_options: Vec<(String, String)> = vec![];
@@ -872,15 +881,12 @@ pub async fn create_mrpack_json(
env.insert(EnvType::Client, SideType::Required);
env.insert(EnvType::Server, SideType::Required);
let primary_file =
if let Some(primary_file) = version.files.first() {
primary_file
} else {
return Some(Err(crate::ErrorKind::OtherError(
format!("No primary file found for mod at: {path}"),
)
.as_error()));
};
let Some(primary_file) = version.files.first() else {
return Some(Err(crate::ErrorKind::OtherError(format!(
"No primary file found for mod at: {path}"
))
.as_error()));
};
let file_size = primary_file.size;
let downloads = vec![primary_file.url.clone()];

View File

@@ -255,7 +255,7 @@ async fn get_all_worlds_in_profile(
AttachedWorldData::get_all_for_instance(profile_path, &state.pool)
.await?;
if !attached_data.is_empty() {
for world in worlds.iter_mut() {
for world in &mut worlds {
if let Some(data) = attached_data
.get(&(world.world_type(), world.world_id().to_owned()))
{