summaryrefslogtreecommitdiffstats
path: root/src/launcher/runner.rs
blob: 41b3ed15460ca3a965ff1081944d5f5369723d4e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
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<Cow<'rep, str>> {
        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<OsString> {
    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<dyn std::error::Error>> {
    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(())
}