use std::borrow::Cow; use std::ffi::{OsStr, OsString}; use std::iter; use std::process::Command; use crate::util::AsJavaPath; use crate::version::{CompleteVersion, FeatureMatcher, OperatingSystem}; use super::rules::CompatCheck; use super::strsub::{self, SubFunc}; use super::{Launch, LaunchInfo}; #[derive(Clone, Copy)] struct LaunchArgSub<'a, 'l, F: FeatureMatcher>(&'a LaunchInfo<'l, F>); impl<'rep, 'l, F: FeatureMatcher> SubFunc<'rep> for LaunchArgSub<'rep, 'l, F> { fn substitute(&self, key: &str) -> Option> { match key { "assets_index_name" => self.0.asset_index_name.as_ref().map(|s| Cow::Borrowed(s.as_str())), "assets_root" => Some(self.0.launcher.assets.get_home().as_java_path().to_string_lossy()), "auth_access_token" => Some(Cow::Borrowed("-")), // TODO "auth_player_name" => Some(Cow::Borrowed("Player")), // TODO "auth_session" => Some(Cow::Borrowed("-")), // TODO "auth_uuid" => Some(Cow::Borrowed("00000000-0000-0000-0000-000000000000")), // TODO "auth_xuid" => Some(Cow::Borrowed("00000000-0000-0000-0000-000000000000")), // TODO "classpath" => Some(Cow::Borrowed(self.0.classpath.as_str())), // TODO "classpath_separator" => None, // FIXME "game_assets" => self.0.virtual_assets_path.as_ref() .map(|s| s.as_path().as_java_path().to_string_lossy()), "game_directory" => Some(self.0.instance_home.as_java_path().to_string_lossy()), "language" => Some(Cow::Borrowed("en-us")), // ??? "launcher_name" => Some(Cow::Borrowed("ozone (olauncher 3)")), // TODO "launcher_version" => Some(Cow::Borrowed("yeah")), // TODO "library_directory" => Some(self.0.launcher.libraries.home.as_java_path().to_string_lossy()), "natives_directory" => Some(self.0.natives_path.as_java_path().to_string_lossy()), "primary_jar" => self.0.client_jar.as_ref().map(|p| p.as_path().as_java_path().to_string_lossy()), "quickPlayMultiplayer" => None, // TODO "quickPlayPath" => None, // TODO "quickPlayRealms" => None, // TODO "quickPlaySingleplayer" => None, // TODO "resolution_height" => None, // TODO "resolution_width" => None, // TODO "user_properties" => Some(Cow::Borrowed("{}")), // TODO "user_property_map" => Some(Cow::Borrowed("[]")), // TODO "user_type" => Some(Cow::Borrowed("legacy")), // TODO "version_name" => Some(Cow::Borrowed(&self.0.version_id.as_ref())), "version_type" => self.0.version_type.as_ref().map(|s| Cow::Borrowed(s.to_str())), _ => { if let Some(asset_key) = key.strip_prefix("asset=") { return self.0.asset_index.as_ref() .map_or(None, |idx| idx.objects.get(asset_key)) .map(|obj| Cow::Owned(self.0.launcher.assets.get_object_path(obj).as_java_path().to_string_lossy().into_owned())) } None } } } } #[derive(Clone, Copy)] pub enum ArgumentType { JVM, Game } pub fn build_arguments<'l, F: FeatureMatcher>(launch: &LaunchInfo<'l, F>, version: &CompleteVersion, arg_type: ArgumentType) -> Vec { let sub = LaunchArgSub(launch); let system_info = &launch.launcher.system_info; if let Some(arguments) = version.arguments.as_ref().map_or(None, |args| match arg_type { ArgumentType::JVM => args.jvm.as_ref(), ArgumentType::Game => args.game.as_ref() }) { arguments.iter() .filter(|wa| wa.rules_apply(system_info, launch.feature_matcher).is_ok()) .flat_map(|wa| &wa.value) .map(|s| OsString::from(strsub::replace_string(s, &sub).into_owned())).collect() } else if let Some(arguments) = version.minecraft_arguments.as_ref() { match arg_type { ArgumentType::JVM => { [ "-Djava.library.path=${natives_directory}", "-Dminecraft.launcher.brand=${launcher_name}", "-Dminecraft.launcher.version=${launcher_version}", "-Dminecraft.client.jar=${primary_jar}", "-cp", "${classpath}" ].into_iter() .chain(iter::once("-XX:HeapDumpPath=MojangTricksIntelDriversForPerformance_javaw.exe_minecraft.exe.heapdump") .take_while(|_| system_info.os == OperatingSystem::Windows)) .chain(iter::once(["-Dos.name=Windows 10", "-Dos.version=10.0"]) .take_while(|_| launch.feature_matcher.matches("__ozone_win10_hack")) .flatten()) .chain(iter::once(["-Xdock:icon=${asset=icons/minecraft.icns}", "-Xdock:name=Minecraft"]) .take_while(|_| system_info.os == OperatingSystem::MacOS) .flatten()) .map(|s| OsString::from(strsub::replace_string(s, &sub).into_owned())) .collect() }, ArgumentType::Game => { arguments.split(' ') .chain(iter::once("--demo") .take_while(|_| launch.feature_matcher.matches("is_demo_user"))) .chain(iter::once(["--width", "${resolution_width}", "--height", "${resolution_height}"]) .take_while(|_| launch.feature_matcher.matches("has_custom_resolution")) .flatten()) .map(|s| OsString::from(strsub::replace_string(s, &sub).into_owned())) .collect() } } } else { Vec::default() } } pub fn run_the_game(launch: &Launch) -> Result<(), Box> { Command::new("java") .args(launch.jvm_args.iter() .map(|o| o.as_os_str()) .chain(iter::once(OsStr::new(launch.main_class.as_str()))) .chain(launch.game_args.iter().map(|o| o.as_os_str()))) .current_dir(launch.instance_path.as_path().as_java_path()).spawn()?.wait()?; Ok(()) }