1use lighty_core::time_it;
2use lighty_java::jre_downloader::jre_download;
3use lighty_java::{JavaDistribution, JreError};
4use lighty_loaders::types::version_metadata::Version;
5use crate::errors::{InstallerError, InstallerResult};
6use crate::installer::Installer;
7use super::builder::LaunchBuilder;
8use lighty_loaders::types::{Loader, LoaderExtensions, VersionInfo};
9use lighty_auth::UserProfile;
10use std::sync::Arc;
11use std::path::PathBuf;
12use lighty_loaders::types::version_metadata::VersionMetaData;
13use tokio::sync::oneshot;
14use lighty_java::jre_downloader::find_java_binary;
15use lighty_java::runtime::JavaRuntime;
16use crate::arguments::Arguments;
17use std::collections::{HashMap,HashSet};
18
19#[cfg(feature = "events")]
20use lighty_event::EventBus;
21
22pub trait Launch {
23 fn launch<'a>(&'a mut self, profile: &'a UserProfile, java_distribution: JavaDistribution) -> LaunchBuilder<'a, Self>
48 where
49 Self: Sized;
50}
51
52impl<T> Launch for T
54where
55 T: VersionInfo<LoaderType = Loader> + LoaderExtensions + Arguments + Installer,
56{
57 fn launch<'a>(&'a mut self, profile: &'a UserProfile, java_distribution: JavaDistribution) -> LaunchBuilder<'a, Self> {
58 LaunchBuilder::new(self, profile, java_distribution)
59 }
60}
61
62pub(crate) async fn execute_launch<T>(
64 version: &mut T,
65 profile: &UserProfile,
66 java_distribution: JavaDistribution,
67 jvm_overrides: &std::collections::HashMap<String, String>,
68 jvm_removals: &std::collections::HashSet<String>,
69 arg_overrides: &std::collections::HashMap<String, String>,
70 arg_removals: &std::collections::HashSet<String>,
71 raw_args: &[String],
72 #[cfg(feature = "events")] event_bus: Option<&EventBus>,
73) -> InstallerResult<()>
74where
75 T: VersionInfo<LoaderType = Loader> + LoaderExtensions + Arguments + Installer,
76{
77 let username = &profile.username;
78 let uuid = &profile.uuid;
79 let metadata = prepare_metadata(
81 version,
82 #[cfg(feature = "events")]
83 event_bus,
84 ).await?;
85
86 let version_data = extract_version(&metadata)?;
87
88 let java_path = ensure_java_installed(
90 version,
91 version_data,
92 &java_distribution,
93 #[cfg(feature = "events")]
94 event_bus,
95 ).await?;
96
97 time_it!("Install delay", version.install(
99 version_data,
100 #[cfg(feature = "events")]
101 event_bus,
102 ).await?);
103
104 execute_game(version, version_data, username, uuid, java_path, arg_overrides, arg_removals, jvm_overrides, jvm_removals, raw_args).await
106}
107
108async fn prepare_metadata<T>(
110 builder: &mut T,
111 #[cfg(feature = "events")] event_bus: Option<&EventBus>,
112) -> InstallerResult<Arc<VersionMetaData>>
113where
114 T: VersionInfo<LoaderType = Loader> + LoaderExtensions,
115{
116 lighty_core::trace_debug!("[Launch] Fetching metadata for loader: {:?}", builder.loader());
117
118
119 let loader_name = format!("{:?}", builder.loader());
120
121 #[cfg(feature = "events")]
122 if let Some(bus) = event_bus {
123 bus.emit(lighty_event::Event::Loader(lighty_event::LoaderEvent::FetchingData {
124 loader: loader_name.clone(),
125 minecraft_version: builder.minecraft_version().to_string(),
126 loader_version: builder.loader_version().to_string(),
127 }));
128 }
129
130 let metadata = match builder.loader() {
131 Loader::Vanilla => builder.get_complete().await?,
132 Loader::Fabric => builder.get_fabric_complete().await?,
133 Loader::Quilt => builder.get_quilt_complete().await?,
134 Loader::NeoForge => builder.get_neoforge_complete().await?,
135 Loader::LightyUpdater => builder.get_lighty_updater_complete().await?,
136 _ => return Err(InstallerError::UnsupportedLoader(format!("{:?}", builder.loader()))),
137 };
138
139 #[cfg(feature = "events")]
140 if let Some(bus) = event_bus {
141 bus.emit(lighty_event::Event::Loader(lighty_event::LoaderEvent::DataFetched {
142 loader: loader_name,
143 minecraft_version: builder.minecraft_version().to_string(),
144 loader_version: builder.loader_version().to_string(),
145 }));
146 }
147
148 lighty_core::trace_info!("[Launch] Metadata fetched successfully for {:?}", builder.loader());
149 Ok(metadata)
150}
151
152async fn ensure_java_installed<T>(
154 builder: &T,
155 version: &Version,
156 java_distribution: &JavaDistribution,
157 #[cfg(feature = "events")] event_bus: Option<&EventBus>,
158) -> InstallerResult<PathBuf>
159where
160 T: VersionInfo,
161{
162 let java_version = version.java_version.major_version;
163
164 match find_java_binary(builder.java_dirs(), java_distribution, &java_version).await {
166 Ok(path) => {
167 lighty_core::trace_info!("[Java] Java {} already installed at: {:?}", java_version, path);
168
169 #[cfg(feature = "events")]
170 if let Some(bus) = event_bus {
171 bus.emit(lighty_event::Event::Java(lighty_event::JavaEvent::JavaAlreadyInstalled {
172 distribution: java_distribution.get_name().to_string(),
173 version: java_version,
174 binary_path: path.to_string_lossy().to_string(),
175 }));
176 }
177
178 Ok(path)
179 }
180 Err(_) => {
181 lighty_core::trace_info!("[Java] Java {} not found, downloading...", java_version);
182
183 #[cfg(feature = "events")]
184 if let Some(bus) = event_bus {
185 bus.emit(lighty_event::Event::Java(lighty_event::JavaEvent::JavaNotFound {
186 distribution: java_distribution.get_name().to_string(),
187 version: java_version,
188 }));
189 }
190
191 #[cfg(feature = "events")]
192 let path = jre_download(
193 builder.java_dirs(),
194 java_distribution,
195 &java_version,
196 |current, total| {
197 lighty_core::trace_debug!("[Java] Download progress: {}/{}", current, total);
198 },
199 event_bus,
200 ).await.map_err(|e| InstallerError::DownloadFailed(format!("JRE download failed: {}", e)))?;
201
202 #[cfg(not(feature = "events"))]
203 let path = jre_download(
204 builder.java_dirs(),
205 java_distribution,
206 &java_version,
207 |current, total| {
208 lighty_core::trace_debug!("[Java] Download progress: {}/{}", current, total);
209 },
210 ).await.map_err(|e : JreError | InstallerError::DownloadFailed(format!("JRE download failed: {}", e)))?;
211
212 lighty_core::trace_info!("[Java] Java {} installed successfully", java_version);
213 Ok(path)
214 }
215 }
216}
217
218async fn execute_game<T>(
220 builder: &T,
221 version: &Version,
222 username: &str,
223 uuid: &str,
224 java_path: PathBuf,
225 arg_overrides: &HashMap<String, String>,
226 arg_removals: &HashSet<String>,
227 jvm_overrides: &HashMap<String, String>,
228 jvm_removals: &HashSet<String>,
229 raw_args: &[String],
230) -> InstallerResult<()>
231where
232 T: VersionInfo + Arguments,
233{
234 let arguments = builder.build_arguments(version, username, uuid, arg_overrides, arg_removals, jvm_overrides, jvm_removals, raw_args);
236
237 lighty_core::trace_debug!("[Launch] Launch arguments: {:?}", arguments);
238
239 let java_runtime = JavaRuntime::new(java_path);
241 lighty_core::trace_info!("[Launch] Executing game...");
242
243 match java_runtime.execute(arguments, builder.game_dirs()).await {
244 Ok(mut child) => {
245 let (_tx, rx) = oneshot::channel::<()>();
246
247 if let Some(pid) = child.id() {
248 lighty_core::trace_info!("[Launch] Game launched successfully, PID: {}", pid);
249 } else {
250 lighty_core::trace_info!("[Launch] Game launched successfully, PID unavailable");
251 }
252
253 let print_output = |_: &(), buf: &[u8]| -> lighty_java::JavaRuntimeResult<()> {
255 print!("{}", String::from_utf8_lossy(buf));
256 Ok(())
257 };
258
259 if let Err(e) = java_runtime
260 .handle_io(&mut child, print_output, print_output, rx, &())
261 .await
262 {
263 lighty_core::trace_error!("[Launch] IO error: {}", e);
264 }
265
266
267
268 Ok(())
270 }
271 Err(e) => {
272 lighty_core::trace_error!("[Launch] Failed to launch game: {}", e);
273 Err(InstallerError::DownloadFailed(format!("Launch failed: {}", e)))
274 }
275 }
276}
277
278fn extract_version(metadata: &VersionMetaData) -> InstallerResult<&Version> {
280 match metadata {
281 VersionMetaData::Version(v) => Ok(v),
282 _ => Err(InstallerError::InvalidMetadata),
283 }
284}