1
0
Files
AstralRinth/theseus/src/launcher/mod.rs
Wyatt Verchere a13b7a2566 added settings API + fixed bug (#62)
* added settings API + fixed bug

* removed redundant API funcs

* comment clarifications

---------

Co-authored-by: Wyatt <wyatt@modrinth.com>
2023-04-03 13:46:04 -07:00

232 lines
7.2 KiB
Rust

//! Logic for launching Minecraft
use crate::state as st;
use daedalus as d;
use dunce::canonicalize;
use std::{path::Path, process::Stdio};
use tokio::process::{Child, Command};
mod args;
pub mod auth;
mod download;
#[tracing::instrument]
pub fn parse_rule(rule: &d::minecraft::Rule) -> bool {
use d::minecraft::{Rule, RuleAction};
let res = match rule {
Rule {
os: Some(ref os), ..
} => crate::util::platform::os_rule(os),
Rule {
features: Some(ref features),
..
} => features.has_demo_resolution.unwrap_or(false),
_ => true,
};
match rule.action {
RuleAction::Allow => res,
RuleAction::Disallow => !res,
}
}
macro_rules! processor_rules {
($dest:expr; $($name:literal : client => $client:expr, server => $server:expr;)+) => {
$(std::collections::HashMap::insert(
$dest,
String::from($name),
daedalus::modded::SidedDataEntry {
client: String::from($client),
server: String::from($server),
},
);)+
}
}
#[allow(clippy::too_many_arguments)]
#[tracing::instrument(skip_all, fields(path = ?instance_path))]
pub async fn launch_minecraft(
game_version: &str,
loader_version: &Option<d::modded::LoaderVersion>,
instance_path: &Path,
java_install: &Path,
java_args: &[String],
env_args: &[(String, String)],
wrapper: &Option<String>,
memory: &st::MemorySettings,
resolution: &st::WindowSize,
credentials: &auth::Credentials,
) -> crate::Result<Child> {
let state = st::State::get().await?;
let instance_path = &canonicalize(instance_path)?;
let version = state
.metadata
.minecraft
.versions
.iter()
.find(|it| it.id == game_version)
.ok_or(crate::ErrorKind::LauncherError(format!(
"Invalid game version: {game_version}"
)))?;
let version_jar = loader_version
.as_ref()
.map_or(version.id.clone(), |it| it.id.clone());
let mut version_info = download::download_version_info(
&state,
version,
loader_version.as_ref(),
)
.await?;
let client_path = state
.directories
.version_dir(&version.id)
.join(format!("{version_jar}.jar"));
download::download_minecraft(&state, &version_info).await?;
st::State::sync().await?;
if let Some(processors) = &version_info.processors {
if let Some(ref mut data) = version_info.data {
processor_rules! {
data;
"SIDE":
client => "client",
server => "";
"MINECRAFT_JAR" :
client => client_path.to_string_lossy(),
server => "";
"MINECRAFT_VERSION":
client => game_version,
server => "";
"ROOT":
client => instance_path.to_string_lossy(),
server => "";
"LIBRARY_DIR":
client => state.directories.libraries_dir().to_string_lossy(),
server => "";
}
for processor in processors {
if let Some(sides) = &processor.sides {
if !sides.contains(&String::from("client")) {
continue;
}
}
let cp = wrap_ref_builder!(cp = processor.classpath.clone() => {
cp.push(processor.jar.clone())
});
let child = Command::new("java")
.arg("-cp")
.arg(args::get_class_paths_jar(
&state.directories.libraries_dir(),
&cp,
)?)
.arg(
args::get_processor_main_class(args::get_lib_path(
&state.directories.libraries_dir(),
&processor.jar,
)?)
.await?
.ok_or_else(|| {
crate::ErrorKind::LauncherError(format!(
"Could not find processor main class for {}",
processor.jar
))
})?,
)
.args(args::get_processor_arguments(
&state.directories.libraries_dir(),
&processor.args,
data,
)?)
.output()
.await
.map_err(|err| {
crate::ErrorKind::LauncherError(format!(
"Error running processor: {err}",
))
})?;
if !child.status.success() {
return Err(crate::ErrorKind::LauncherError(format!(
"Processor error: {}",
String::from_utf8_lossy(&child.stderr)
))
.as_error());
}
}
}
}
let args = version_info.arguments.clone().unwrap_or_default();
let mut command = match wrapper {
Some(hook) => {
wrap_ref_builder!(it = Command::new(hook) => {it.arg(java_install)})
}
None => Command::new(String::from(java_install.to_string_lossy())),
};
let env_args = Vec::from(env_args);
command
.args(
args::get_jvm_arguments(
args.get(&d::minecraft::ArgumentType::Jvm)
.map(|x| x.as_slice()),
&state.directories.version_natives_dir(&version.id),
&state.directories.libraries_dir(),
&args::get_class_paths(
&state.directories.libraries_dir(),
version_info.libraries.as_slice(),
&client_path,
)?,
&version_jar,
*memory,
Vec::from(java_args),
)?
.into_iter()
.map(|r| r.replace(' ', r"\ "))
.collect::<Vec<_>>(),
)
.arg(version_info.main_class.clone())
.args(
args::get_minecraft_arguments(
args.get(&d::minecraft::ArgumentType::Game)
.map(|x| x.as_slice()),
version_info.minecraft_arguments.as_deref(),
credentials,
&version.id,
&version_info.asset_index.id,
instance_path,
&state.directories.assets_dir(),
&version.type_,
*resolution,
)?
.into_iter()
.map(|r| r.replace(' ', r"\ "))
.collect::<Vec<_>>(),
)
.current_dir(instance_path.clone())
.env_clear()
.envs(env_args)
.stdout(Stdio::inherit())
.stderr(Stdio::inherit());
command.spawn().map_err(|err| {
crate::ErrorKind::LauncherError(format!(
"Error running Minecraft (minecraft-{} @ {}): {err}",
&version.id,
instance_path.display()
))
.as_error()
})
}