mcfunction_debug_adapter/
installer.rs

1// McFunction-Debugger is a debugger for Minecraft's *.mcfunction files that does not require any
2// Minecraft mods.
3//
4// © Copyright (C) 2021-2023 Adrodoc <adrodoc55@googlemail.com> & skess42 <skagaros@gmail.com>
5//
6// This file is part of McFunction-Debugger.
7//
8// McFunction-Debugger is free software: you can redistribute it and/or modify it under the terms of
9// the GNU General Public License as published by the Free Software Foundation, either version 3 of
10// the License, or (at your option) any later version.
11//
12// McFunction-Debugger is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
13// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15//
16// You should have received a copy of the GNU General Public License along with McFunction-Debugger.
17// If not, see <http://www.gnu.org/licenses/>.
18
19use crate::{
20    api::ProgressContext,
21    error::{PartialErrorResponse, RequestError},
22    DebugAdapterContext,
23};
24use futures::{
25    future::{select, Either},
26    pin_mut,
27};
28use minect::MinecraftConnection;
29use std::{io, path::Path};
30
31pub async fn establish_connection(
32    minecraft_world_dir: impl AsRef<Path>,
33    minecraft_log_file: impl AsRef<Path>,
34    mut context: impl DebugAdapterContext,
35) -> Result<MinecraftConnection, RequestError<io::Error>> {
36    let mut progress = context.start_cancellable_progress(
37        "Connecting to Minecraft".to_string(),
38        Some(
39            "If you are connecting for the first time please execute /reload in Minecraft."
40                .to_string(),
41        ),
42    );
43
44    let mut connection =
45        MinecraftConnection::builder("mcfunction-debugger", minecraft_world_dir.as_ref())
46            .log_file(minecraft_log_file.as_ref())
47            .build();
48    let result = connect(&mut connection, &mut progress).await;
49
50    let progress_id = progress.progress_id.to_string();
51    let progress_end_message = match &result {
52        Ok(()) => "Successfully connected to Minecraft".to_string(),
53        Err(ConnectError::Cancelled) => "Cancelled connecting to Minecraft".to_string(),
54        Err(ConnectError::Failed(error)) => format!("Failed to connect to Minecraft: {}", error),
55    };
56    context.end_cancellable_progress(progress_id, Some(progress_end_message));
57
58    result
59        .map_err(|e| match e {
60            ConnectError::Cancelled => "Launch was cancelled.".to_string(),
61            ConnectError::Failed(error) => format!("Failed to connect to Minecraft: {}", error),
62        })
63        .map_err(PartialErrorResponse::new)?;
64
65    Ok(connection)
66}
67
68enum ConnectError {
69    Cancelled,
70    Failed(minect::ConnectError),
71}
72impl From<minect::ConnectError> for ConnectError {
73    fn from(error: minect::ConnectError) -> Self {
74        if error.is_cancelled() {
75            ConnectError::Cancelled
76        } else {
77            ConnectError::Failed(error)
78        }
79    }
80}
81async fn connect(
82    connection: &mut MinecraftConnection,
83    progress: &mut ProgressContext,
84) -> Result<(), ConnectError> {
85    let connect = connection.connect();
86    pin_mut!(connect);
87    let cancel = progress.next_cancel_request();
88    pin_mut!(cancel);
89    match select(connect, cancel).await {
90        Either::Left((result, _)) => {
91            result?;
92            Ok(())
93        }
94        Either::Right(_) => return Err(ConnectError::Cancelled),
95    }
96}