sonic2kk / steamtinkerlaunch

Linux wrapper tool for use with the Steam client for custom launch options and 3rd party programs
GNU General Public License v3.0
2.19k stars 73 forks source link

Auto-close (kill) forked processes when main game closes #1025

Open Fmstrat opened 10 months ago

Fmstrat commented 10 months ago

System Information

Feature Description

If I use STL to run a game plugin, for instance, launching BakkesMod with Rocket League (IE, doing this: https://github.com/Fmstrat/linux-gaming/blob/main/rocketleague/BAKKESMOD.md), then when I exit Rocket League I also have to find the BakkesMod application and close it or the game is still running as far as Steam is concerned.

It would be great if there was a watcher that could identify when the main application exits, and send a close (then kill) signal to forked applications.

sonic2kk commented 10 months ago

You may be able to resolve this by disabling reaper, but this is only available on an up-to-date version of STL. Steam made some changes that STL had to update to adapt to, and this is not available in old versions. v12.12 is too out of date, it's a stable release. Try updating to the latest version from git and disabling reaper.

I think such an option like this is undesirable though, and should be a checkbox that is off by default. Something like "kill custom command when game exits". I have no idea how this would be implemented to be honest (we'd probably have to try and guess the process ID or something?), and is probably not something I'll work on much.

To me, keeping the game process running until all processes launched by SteamTinkerLaunch are closed, is the desirable and intended functionality. But if someone is motivated I would not oppose an option like this, so long as it is optional and off by default :-)

I will leave this open in case others want to contribute this feature, but it is not something I'll work on (at least anytime in the near future), so I'll attach a "Help wanted".

sonic2kk commented 10 months ago

Just tested, with a forked custom command, disabling reaper doesn't solve the issue. It also results in drastically increased start times, and Steam scrambling to find the process. The game is also not listed as closed, and ofc Steam can't force close, since it doesn't have reaper to kill the processes, since reaper is the one that functions as the process watcher (pressing "STOP" in Steam probably gets Steam to try and do something with reaper, but since reaper is killed when the option is disabled, it can't do anything).

To implement this feature, SteamTinkerLaunch would have to know if a custom process is still running. It would also need to account for cases where this program has child processes. Steam uses reaper as a way to make sure game processes and background launcher processes (i.e. Origin games) are closed correctly. It isn't always perfect, but this is one of the leading explanations I've seen for why reaper exists.

STL would essentially have to do something like reaper, tracking those child processes and killing them when a game is closed. But STL has no awareness of when forked processes are still running, they're forked into the background so that they run independently.

This seems like a lot of work for something that may not be reliable, if even Valve aren't getting it perfect...

Fmstrat commented 10 months ago

Maybe I'll look to do a PR, but I would think something like getting the PID with variable $? right after execution of the forked process (https://github.com/sonic2kk/steamtinkerlaunch/blob/6fc2a35a9661a2ecbeb6820a9573f769927bbde8/steamtinkerlaunch#L7112) would do.

The same thing for executing the game, then a loop watching for existance of the main PID before a kill on the forked PID and then a few seconds later if still alive, kill -9.

This is of course, total guesses, and I'll need to digest the bash script a bit more.

sonic2kk commented 10 months ago

Even if you don't find time for a PR yourself, $? is a good starting point (totally forgot about this).

This should be good information for anyone else that wants to look into this in future if you are unable to! This is the kind of info I like to see being left in issues :-)

Fmstrat commented 10 months ago

So game is running, I see:

$ ps -ef |grep /home/fmstrat/.steam/debian-installation/compatibilitytools.d/SteamTinkerLaunch/steamtinkerlaunch
...
fmstrat  3812743 3812742  0 21:50 ?        00:00:00 bash /home/fmstrat/.steam/debian-installation/compatibilitytools.d/SteamTinkerLaunch/steamtinkerlaunch waitforexitandrun /home/fmstrat/.steam/debian-installation/steamapps/common/rocketleague/Binaries/Win64/RocketLeague.exe
fmstrat  3813997 3812743  0 21:50 ?        00:00:00 bash /home/fmstrat/.steam/debian-installation/compatibilitytools.d/SteamTinkerLaunch/steamtinkerlaunch waitforexitandrun /home/fmstrat/.steam/debian-installation/steamapps/common/rocketleague/Binaries/Win64/RocketLeague.exe
...

These are the two processes of STL. Not sure why there's two, but there is. Let's look at the direct children:

$ ps -ef |grep -E "3812743|3813997"
...
fmstrat  3814378 3813997  0 21:50 ?        00:00:00 python3 /home/fmstrat/.steam/debian-installation/steamapps/common/Proton 7.0/proton run /home/fmstrat/.steam/debian-installation/steamapps/compatdata/252950/pfx/drive_c/Program Files/BakkesMod/BakkesMod.exe
...

There's the forked app. Since STL launched that, we should be able to get that PID at launch with $?. Or, since we know the command that was run, we could also parse for it. So let's look at it's children (Yes this could be done faster with --forest):

$ ps -ef |grep -E 3814378
...
fmstrat  3814380 3814378  0 21:50 ?        00:00:00 c:\windows\system32\steam.exe /home/fmstrat/.steam/debian-installation/steamapps/compatdata/252950/pfx/drive_c/Program Files/BakkesMod/BakkesMod.exe
...

And:

$ kill 3814380

Kills it. But it's still running. Where is it?

$ ps -ef |grep BakkesMod
...
fmstrat  3814410 3812742  0 21:50 ?        00:00:02 C:\Program Files\BakkesMod\BakkesMod.exe
...

$ ps -ef |grep 3812742
...
fmstrat  3812742 3812741  0 21:50 ?        00:00:00 /home/fmstrat/.steam/debian-installation/ubuntu12_32/reaper SteamLaunch AppId=252950 -- /home/fmstrat/.steam/debian-installation/ubuntu12_32/steam-launch-wrapper -- /home/fmstrat/.steam/debian-installation/compatibilitytools.d/SteamTinkerLaunch/steamtinkerlaunch waitforexitandrun /home/fmstrat/.steam/debian-installation/steamapps/common/rocketleague/Binaries/Win64/RocketLeague.exe
...
fmstrat  3812743 3812742  0 21:50 ?        00:00:00 bash /home/fmstrat/.steam/debian-installation/compatibilitytools.d/SteamTinkerLaunch/steamtinkerlaunch waitforexitandrun /home/fmstrat/.steam/debian-installation/steamapps/common/rocketleague/Binaries/Win64/RocketLeague.exe
...

Looks like I picked the wrong path down. Sooooo

$ kill 3814410

And BakkesMod is closed. So this is theoretically possible, just need to trace in the bash.

Fmstrat commented 10 months ago

Even if you don't find time for a PR yourself, $? is a good starting point (totally forgot about this).

This should be good information for anyone else that wants to look into this in future if you are unable to! This is the kind of info I like to see being left in issues :-)

Hah, then you probably like my last comment. I'll probably dive into this more this weekend/week.

sonic2kk commented 10 months ago

These are the two processes of STL. Not sure why there's two, but there is.

Ah, that is likely the tray icon.

Hah, then you probably like my last comment.

Yes, that is absolutely perfect! This has went from being a complete mess in my mind to something that might be tangible (and not a massive performance tax, not that STL is the most performant anyway but still).

I'll probably dive into this more this weekend/week.

I don't have any plans to get in your way with this one, so by all means look into it if you have some time! I'll be here to help if you need anything. I'm mostly on a bit of a break, but I'll answer any questions you might have. STL is a bit big, I've been trying to clean up mainly old areas from before I came around and to make new areas a bit more managable, but a lot of the game launch-related stuff is very old.

You seem to have a good idea on a solution, and you already found extProtonRun. You might want to look at launchCustomProg, as extProtonRun is called in a few different places but launchCustomProg is where it's called for custom command launches specifically.

I would still suggest this to be an option that is off by default, as this preserves existing behaviour and behaviour that I think is mostly desirable. Not to say your use-case is pointless, I just don't know that it should be the default. I'm still open to hearing what you think though if you want it as the default. :slightly_smiling_face: