cobble-core 1.2.0

Library for managing, installing and launching Minecraft instances and more.
Documentation
use super::GameProcess;
use crate::error::LaunchResult;
use async_trait::async_trait;
use parking_lot::Mutex;
use std::os::windows::process::CommandExt;
use std::process::Child;
use std::process::Command;
use std::sync::Arc;
use std::thread::sleep;
use std::time::Duration;
use tokio::task;

/// Handle to the game process.
#[cfg_attr(doc_cfg, doc(cfg(windows)))]
#[derive(Clone, Debug)]
pub struct GameProcessHandle {
    child: Arc<Mutex<Child>>,
}

#[async_trait]
impl GameProcess for GameProcessHandle {
    /// Launches the game with the DETACHED_PROCESS and CREATE_NEW_PROCESS_GROUP flag.
    /// https://docs.microsoft.com/en-us/windows/win32/procthread/process-creation-flags
    fn launch(mut command: Command) -> LaunchResult<Self> {
        command.creation_flags(0x00000200 | 0x00000008);
        let child = command.spawn()?;

        Ok(Self {
            child: Arc::new(Mutex::new(child)),
        })
    }

    /// Kills the game process.
    async fn stop(&self) -> LaunchResult<()> {
        let child = self.child.clone();
        task::spawn_blocking(move || {
            let mut child = child.lock();
            child.kill()?;
            Ok(())
        })
        .await
        .unwrap()
    }

    /// Waits for the process to exit.
    async fn wait(&self) -> LaunchResult<()> {
        let handle = Self {
            child: self.child.clone(),
        };

        task::spawn_blocking(move || {
            // Wait for stopped
            loop {
                if handle.is_stopped_blocking()? {
                    break;
                }

                sleep(Duration::from_millis(500));
            }

            // Process exited
            let mut child = handle.child.lock();
            child.wait()?;

            Ok(())
        })
        .await
        .unwrap()
    }

    /// Checks whether the game is still running.
    async fn is_stopped(&self) -> LaunchResult<bool> {
        let child = self.child.clone();
        task::spawn_blocking(move || {
            let mut child = child.lock();
            match child.try_wait()? {
                Some(_) => Ok(true),
                None => Ok(false),
            }
        })
        .await
        .unwrap()
    }

    /// Checks whether the game is still running.
    fn is_stopped_blocking(&self) -> LaunchResult<bool> {
        let mut child = self.child.lock();

        match child.try_wait()? {
            Some(_) => Ok(true),
            None => Ok(false),
        }
    }
}