fix(core): disallow launching multiple instances so fast
This commit is contained in:
parent
44c153dd91
commit
038dc94725
3 changed files with 38 additions and 2 deletions
|
|
@ -33,6 +33,11 @@ pub enum GameError {
|
|||
#[error("another cluster ({0}) is already running in the same directory")]
|
||||
DirectoryInUse(i64),
|
||||
|
||||
#[error(
|
||||
"{0} is already running in the shared game directory; close it before launching another cluster"
|
||||
)]
|
||||
SharedDirectoryBusy(String),
|
||||
|
||||
#[error("failed to spawn the game process: {0}")]
|
||||
Spawn(String),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,12 +60,22 @@ pub async fn launch_cluster(
|
|||
let existing = state.clusters.get(cluster_id).await?;
|
||||
|
||||
let game_dir = existing.game_dir()?;
|
||||
let dedicated = existing.uses_dedicated_dir();
|
||||
|
||||
// Every cluster without a dedicated folder plays out of the same shared
|
||||
// directory, so a second game there would materialize its content over the
|
||||
// running one's. `dir_in_use_by` waves a cluster past its own session, which
|
||||
// parallel launches make reachable, so the directory is checked outright.
|
||||
if !dedicated && let Some(other) = state.games.dir_in_use(&game_dir) {
|
||||
tracing::warn!(cluster_id, other, "shared game dir busy; refusing launch");
|
||||
let name = running_cluster_name(state, other).await;
|
||||
return Err(GameError::SharedDirectoryBusy(name).into());
|
||||
}
|
||||
|
||||
if let Some(other) = state.games.dir_in_use_by(&game_dir, cluster_id) {
|
||||
return Err(GameError::DirectoryInUse(other).into());
|
||||
}
|
||||
|
||||
let dedicated = existing.uses_dedicated_dir();
|
||||
|
||||
let progress = GroupedProgressSession::start(
|
||||
&state.services.events,
|
||||
format!("Launching {}", existing.name),
|
||||
|
|
@ -406,6 +416,16 @@ pub async fn launch_cluster(
|
|||
Ok(LaunchedGame { cluster_id, pid })
|
||||
}
|
||||
|
||||
/// Names the cluster in the user's way for a refusal they have to act on.
|
||||
/// Falls back to the id, which is still better than no answer at all.
|
||||
async fn running_cluster_name(state: &Arc<LauncherState>, cluster_id: i64) -> String {
|
||||
state
|
||||
.clusters
|
||||
.get(cluster_id)
|
||||
.await
|
||||
.map_or_else(|_| format!("Cluster {cluster_id}"), |cluster| cluster.name)
|
||||
}
|
||||
|
||||
/// Cut the game loose from the launcher's process group / console, so signals
|
||||
/// aimed at the launcher (a terminal Ctrl-C, a console window closing) don't
|
||||
/// reach the game as collateral.
|
||||
|
|
|
|||
|
|
@ -118,6 +118,17 @@ impl GameProcessManager {
|
|||
self.dirs.lock().unwrap().insert(cluster_id, dir);
|
||||
}
|
||||
|
||||
/// Like [`Self::dir_in_use_by`], but counts a cluster's own session too. The
|
||||
/// shared game directory holds one game at a time, whoever it belongs to.
|
||||
pub fn dir_in_use(&self, dir: &Path) -> Option<i64> {
|
||||
self.dirs
|
||||
.lock()
|
||||
.unwrap()
|
||||
.iter()
|
||||
.find(|(_, d)| d.as_path() == dir)
|
||||
.map(|(id, _)| *id)
|
||||
}
|
||||
|
||||
pub fn dir_in_use_by(&self, dir: &Path, exclude: i64) -> Option<i64> {
|
||||
self.dirs
|
||||
.lock()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue