Skip to content

Commit c5849dc

Browse files
PJB3005jbuehler23
authored andcommitted
Make game processes exit if editor crashes on Windows
Uses job objects with JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE.
1 parent 1eac6b5 commit c5849dc

3 files changed

Lines changed: 65 additions & 0 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,9 @@ jackdaw_localization = { version = "0.19.0", path = "crates/jackdaw_localization
189189
jackdaw_env.workspace = true
190190
webbrowser.workspace = true
191191

192+
[target.'cfg(windows)'.dependencies]
193+
windows = { workspace = true, features = ["Win32_System_JobObjects"] }
194+
192195
# Powers the Linux `PR_SET_PDEATHSIG` hook that takes PIE game processes down
193196
# with the editor when it is killed (`src/pie.rs`).
194197
[target.'cfg(target_os = "linux")'.dependencies]
@@ -300,6 +303,7 @@ webbrowser = "1"
300303
fluent_content = "0.0.5"
301304
unic-langid = { version = "0.9", features = ["macros"] }
302305
sys-locale = "0.2"
306+
windows = "0.61.3"
303307

304308
# Idiomatic Bevy code often triggers these lints, and the CI workflow treats them as errors.
305309
[workspace.lints.clippy]

src/pie.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1549,6 +1549,61 @@ fn reconcile_play_state(world: &mut World) {
15491549
}
15501550
}
15511551

1552+
#[cfg(target_os = "windows")]
1553+
mod pie_windows {
1554+
use std::{
1555+
os::{
1556+
raw::c_void,
1557+
windows::io::{AsRawHandle, FromRawHandle, OwnedHandle},
1558+
},
1559+
process::Child,
1560+
sync::LazyLock,
1561+
};
1562+
use windows::Win32::Foundation::HANDLE;
1563+
use windows::Win32::System::JobObjects::*;
1564+
use windows::core::PCWSTR;
1565+
1566+
pub static PIE_JOB: LazyLock<Job> = LazyLock::new(|| unsafe {
1567+
let handle = OwnedHandle::from_raw_handle(
1568+
CreateJobObjectW(None, PCWSTR::null())
1569+
.expect("Failed to create job object for PIE")
1570+
.0,
1571+
);
1572+
1573+
let info = JOBOBJECT_EXTENDED_LIMIT_INFORMATION {
1574+
BasicLimitInformation: JOBOBJECT_BASIC_LIMIT_INFORMATION {
1575+
LimitFlags: JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
1576+
| JOB_OBJECT_LIMIT_BREAKAWAY_OK
1577+
| JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK,
1578+
..Default::default()
1579+
},
1580+
..Default::default()
1581+
};
1582+
1583+
SetInformationJobObject(
1584+
HANDLE(handle.as_raw_handle()),
1585+
JobObjectExtendedLimitInformation,
1586+
&raw const info as *const c_void,
1587+
std::mem::size_of::<JOBOBJECT_EXTENDED_LIMIT_INFORMATION>() as u32,
1588+
)
1589+
.expect("Failed to set job object info for PIE");
1590+
1591+
Job(handle)
1592+
});
1593+
1594+
pub struct Job(pub OwnedHandle);
1595+
1596+
impl Job {
1597+
pub fn assign_to_process(&self, process: &Child) {
1598+
let handle = process.as_raw_handle();
1599+
unsafe {
1600+
AssignProcessToJobObject(HANDLE(self.0.as_raw_handle()), HANDLE(handle))
1601+
.expect("Failed to assign process to job for PIE")
1602+
};
1603+
}
1604+
}
1605+
}
1606+
15521607
/// Launch the project's game binary for one instance, point it at a
15531608
/// fresh rendezvous, start draining its stderr, and begin awaiting its
15541609
/// connection on a task pool. Returns the `Connecting` stage; on
@@ -1641,6 +1696,11 @@ fn spawn_instance(
16411696
}
16421697
};
16431698

1699+
// On Windows, we use a job object with JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE set.
1700+
// Because we own the only handle to the object, if Jackdaw crashes, the job object will be closed and the game killed.
1701+
#[cfg(target_os = "windows")]
1702+
pie_windows::PIE_JOB.assign_to_process(&child);
1703+
16441704
let stderr_tail: StderrTail = Arc::new(Mutex::new(VecDeque::with_capacity(STDERR_TAIL_LINES)));
16451705
if let Some(stderr) = child.stderr.take() {
16461706
let tail = Arc::clone(&stderr_tail);

0 commit comments

Comments
 (0)