mod client;
mod configuration;
mod process;
mod server;
use std::path::Path;
use anyhow::{bail, Context};
use mcvm_shared::output::{MCVMOutput, MessageContents, MessageLevel};
use mcvm_shared::{translate, Side};
use self::client::create_quick_play_args;
use self::process::{launch_game_process, LaunchGameProcessParameters};
use crate::config::BrandingProperties;
use crate::instance::InstanceKind;
use crate::io::files::paths::Paths;
use crate::io::java::args::MemoryArg;
use crate::io::java::classpath::Classpath;
use crate::io::java::install::JavaInstallation;
use crate::net::game_files::client_meta::ClientMeta;
use crate::net::game_files::version_manifest::VersionManifestAndList;
use crate::user::auth::check_game_ownership;
use crate::user::UserManager;
use crate::util::versions::VersionName;
pub use self::configuration::{
LaunchConfigBuilder, LaunchConfiguration, QuickPlayType, WrapperCommand,
};
pub use self::process::launch_process;
pub use self::process::{LaunchProcessParameters, LaunchProcessProperties};
pub(crate) async fn launch(
params: LaunchParameters<'_>,
o: &mut impl MCVMOutput,
) -> anyhow::Result<InstanceHandle> {
let command = params.java.get_jvm_path();
if let InstanceKind::Client { .. } = ¶ms.side {
o.display(
MessageContents::StartProcess(translate!(o, StartAuthenticating)),
MessageLevel::Important,
);
params
.users
.authenticate(params.paths, params.req_client, o)
.await
.context("Failed to ensure authentication")?;
let owns_game =
check_game_ownership(params.paths).context("Failed to check for game ownership")?;
if !owns_game {
bail!("Could not prove game ownership. If using an alternative auth system, like from a plugin, you must login with a Microsoft account that owns Minecraft first.");
}
}
let props = match params.side.get_side() {
Side::Client => self::client::get_launch_props(¶ms).await,
Side::Server => self::server::get_launch_props(¶ms),
}
.context("Failed to generate side-specific launch properties")?;
let user_access_token = params
.users
.get_chosen_user()
.and_then(|x| x.get_access_token());
let proc_params = LaunchGameProcessParameters {
command: command.as_os_str(),
cwd: params.launch_dir,
main_class: Some(params.main_class),
props,
launch_config: params.launch_config,
version: params.version,
version_list: ¶ms.version_manifest.list,
side: params.side,
user_access_token,
censor_secrets: params.censor_secrets,
};
let child = launch_game_process(proc_params, o).context("Failed to launch game process")?;
let handle = InstanceHandle::new(child);
Ok(handle)
}
pub(crate) struct LaunchParameters<'a> {
pub version: &'a VersionName,
pub version_manifest: &'a VersionManifestAndList,
pub side: &'a InstanceKind,
pub launch_dir: &'a Path,
pub java: &'a JavaInstallation,
pub classpath: &'a Classpath,
pub main_class: &'a str,
pub launch_config: &'a LaunchConfiguration,
pub paths: &'a Paths,
pub req_client: &'a reqwest::Client,
pub client_meta: &'a ClientMeta,
pub users: &'a mut UserManager,
pub censor_secrets: bool,
pub branding: &'a BrandingProperties,
}
impl LaunchConfiguration {
pub fn generate_jvm_args(&self) -> Vec<String> {
let mut out = self.jvm_args.clone();
if let Some(n) = &self.min_mem {
out.push(MemoryArg::Min.to_string(n));
}
if let Some(n) = &self.max_mem {
out.push(MemoryArg::Max.to_string(n));
}
out
}
pub fn generate_game_args(
&self,
version: &str,
version_list: &[String],
side: Side,
o: &mut impl MCVMOutput,
) -> Vec<String> {
let mut out = self.game_args.clone();
out.extend(self.generate_additional_game_args(version, version_list, side, o));
out
}
pub fn generate_additional_game_args(
&self,
version: &str,
version_list: &[String],
side: Side,
o: &mut impl MCVMOutput,
) -> Vec<String> {
let mut out = Vec::new();
if let Side::Client = side {
out.extend(create_quick_play_args(
&self.quick_play,
version,
version_list,
o,
));
}
out
}
}
#[derive(Debug)]
pub struct InstanceHandle {
process: std::process::Child,
}
impl InstanceHandle {
fn new(process: std::process::Child) -> Self {
Self { process }
}
pub fn wait(&mut self) -> std::io::Result<std::process::ExitStatus> {
self.process.wait()
}
pub fn kill(&mut self) -> std::io::Result<()> {
self.process.kill()
}
pub fn get_process(self) -> std::process::Child {
self.process
}
pub fn get_pid(&self) -> u32 {
self.process.id()
}
}