mmatyas / pegasus-frontend

A cross platform, customizable graphical frontend for launching emulators and managing your game collection.
http://pegasus-frontend.org
Other
1.28k stars 115 forks source link

Bug: Steam games relaunches the frontend while launching game #442

Open PaddyCo opened 5 years ago

PaddyCo commented 5 years ago

When I start a game from Steam through the frontend (default settings) it seems to trigger the exit game event while the game is still launching, causing some issues like the frontend running in the background (with videos still playing etc)

I think the reason is probably because when running steam.exe with the steam://rungameid/XXXXX parameter it simply returns exit code 0 once it has finished launching the game, which makes the frontend think the game has exited?

Here are the relevant lines from my lastrun.log:

2019-04-06T21:40:58 [i] Executing command: "c:/program files (x86)/steam/steam.exe" steam://rungameid/801630 2019-04-06T21:40:58 [i] Process 2872 started 2019-04-06T21:40:58 [i] ---------------------------------------- 2019-04-06T21:40:58 [i] ---------------------------------------- 2019-04-06T21:40:58 [i] The external program has finished cleanly, with exit code 0

Same problem on two different computers (Both running Windows 10) with all games I've tested so far

mmatyas commented 5 years ago

Yes, unfortunately there's no way to detect when the game launched by Steam starts on closes -- frontends can only access Steam itself and ask it to launch the game. When Steam is already open this completes instantly, causing the issue you've seen.

Sadly starting from a closed-Steam state won't entirely solve the problem, because Steam keeps running after a game closes, meaning you can't properly return to Pegasus either.

These issues have been reported to Valve already (eg. Valve/steam-for-linux repo, issue 1721), but there was no interest in the past few years to improve it.

PaddyCo commented 5 years ago

That's a bummer since the Steam integration in Pegasus is extremely good except for this one issue

Since it looks like Valve won't be fixing it on their end anytime soon I started looking into some other way of launching steam games reliably, there was a workaround posted in the the issue you referred to by Github user ejthill which I liked the sound of so I adapted it a little and made a very quick & simple proof-of-concept (Windows only)

Basically what this launcher does is: 1) Runs steam.exe steam://rungameid/APPID 2) Keep checking the foreground window, as long as it is Steam (or the launcher itself) it will keep checking. When it detects another window it will mark that as the game window 3) Now we keep checking that the game window is open, the second it closes we simply exit the launcher

I would be willing to look into implementing something like this (ofcourse way more robust and cross-platform) directly into the frontend, or alternatively add some optional configuration option that lets the user configure what command is used to launch steam games if either of those sounds like a good idea to you?

mmatyas commented 5 years ago

Yes I've seen these workarounds, but I had some issues:

I was also thinking about another approach, that we should check the process tree and see if Steam has any non-built-in child processes running. In theory that should be reliable, but it also have most of the same problems as above.

Another idea, perhaps a Steam wrapper script could be set, which Pegasus would call instead of the Steam binary. One could set it to a script like the one you linked, but if that itself has bugs, there might be no way to return to the frontend. I'm also not sure if it'd work on macOS as things are launched differently there.

Now these are only how I see things at the moment. However if you have a better idea or a working implementation I'd be always happy to take a look on it.

PaddyCo commented 5 years ago

After reading your reply and looking at it a little closer, it's safe to say I underestimated the complexity of the task 😅

But yes, the idea of letting the user set their own Steam wrapper script sounds like the best way forward, I'll try my hand at implementing it and submitting a PR and you can decide if it makes sense to merge or not 🙂

LightningXCE commented 4 years ago

@PaddyCo apologies for the ping, but I'm hitting this issue now and was wondering if you had a binary built anywhere for your launcher?

Cheers, thanks!

ToxicCrack commented 4 years ago

As this is still a problem, how about that: If one starts a steam game, the video stops playing as long as no other game gets selected. This would easily work on all platforms.

mmatyas commented 4 years ago

Yes, that might work for now.

ScottESanDiego commented 3 years ago

+1 to the @ToxicCrack idea. I finally got around to configuring Pegasus on my gaming VM, and this issue makes Stream integration effectively unusable, since Pegasus does a great job finding videos for everything!

MrMajeeka commented 2 years ago

steam-launcherv1.0.zip

I fixed this issue by writing this launcher for steam.

Instructions are in the zip file.

You can add individual games and it will launch them, monitor them to allow Pegasus to go back to fullscreen when you are finished with the game.

This works whether Steam is open or not.

DJ.

MrMajeeka commented 2 years ago

I also have a working Battle.net system too. I'll upload that when I have made the code look prettier.

ThaC2027 commented 2 years ago

steam-launcherv1.0.zip

I fixed this issue by writing this launcher for steam.

Instructions are in the zip file.

You can add individual games and it will launch them, monitor them to allow Pegasus to go back to fullscreen when you are finished with the game.

This works whether Steam is open or not.

DJ.

Hi DJ,

Tried your launcher but received some errors even after using the correct version of the .net framework. Do you have any updates using the latest framework?

In general, the issue remains - I can still hear the video for the Steam game playing in the background after launch. Any updates on this issue?

inqalab commented 1 year ago

nice post like it

LightningXCE commented 1 year ago

"If you have purchased a game on Steam and found that it doesn't meet your expectations, you can request a refund. Steam offers a simple refund process that allows users to get their money back for any game they have purchased within a certain time frame. Here's how to refund a game on Steam:

1. Open the Steam client and go to the ""Help"" section.

2. Click on ""Steam Support.""

3. Click on the game that you want to refund.

4. Select the reason for the refund and provide additional information (if required).

5. Click on ""Submit Request.""

If your refund request is approved, the game's purchase price will be refunded to your Steam wallet or the original payment method, depending on your preference.

It's important to note that there are certain conditions that must be met for a refund to be approved. For example, you must have purchased the game within the last 14 days, and you must have played it for less than 2 hours. Additionally, some games may have specific refund policies that differ from the standard Steam policy.

For more information on how to refund a game on Steam, visit https://appuals.com/how-to-refund-a-game-on-steam/. By following these steps, you can get a refund for any game that doesn't meet your expectations and use the funds to purchase another game that you'll enjoy."

Not sure if you're in the right place here, or an AI bot mass replying to tickets, but this has nothing to do with what you are describing.

jahtim commented 8 months ago

i just found a slight workaround, use the launch line in some metadata file: launch: cmd /c start /b metal.bat steam://rungameid/123456789

metal.bat: cmd /c %1 taskkill /IM Pegasus_latest.exe

it just lauches the steam game and kill pegasus process, so you need to relauches pegasus manually when needed. ps , is it good option to have some hotkeys/cmd/url to freeze/unfreeze pegasus?

ianrispin commented 7 months ago

Is there a fix for this on macOS?

Lokito23 commented 4 months ago

I might have found a workaround, atleast on Linux, not sure if Windows or MacOS have it.

Steam starts a process called reaper when it starts a game, as such, I reckon we can use it to see if the game is running, and if it isn't, reopen pegasus.

Lokito23 commented 4 months ago

It's within the steam process tree, but I don't know if it exists beyond Linux.

Lokito23 commented 4 months ago

I just tried with a Windows VM, seems like reaper is Linux exclusive. However in Windows it just opens the exe file of the game, and is considered to be inside the steam.exe process tree, not sure if that can help though.

No clue on MacOS, someone would need to test it there.

EDIT: Just found out that Steam creates a registry on Windows, inside HKEY_CURRENT_USER\Software\Valve\Steam, the item RunningAppID reports the ID of the game currently running.

As such, if said item has a value of 0, or it doesn't exist, it means that the game was closed, or there was no game running to begin with.

It does work. Tested on a Windows 10 VM.

Lokito23 commented 3 months ago

It seems like both options could be plausible solutions, though I am unsure of the Windows one, since I don't know how to read the registry on a program, or if it's even possible.

As for Linux, it's just a matter of tracking if the reaper process is open, which shouldn't be that hard.

As for MacOS, someone would need to test if reaper exists there too, and if not, I am unsure as to how it could be done.

TL;DR: I highly doubt there's a way to detect it on all three at once. As such, the focus should be on a way to detect it on each OS.

Lokito23 commented 3 months ago

@mmatyas Looking at the code, it seems like the registry read that I have mentioned above might be viable, after seeing that there's seems to be a registry item being read already in the backend code.

As for Linux, it might be of interest to find the PID of reaper, and using waitpid in order to wait until it finishes. I am unsure if there's a better way to do it in C++, or in QT though...

For MacOS, I don't have a machine that I could use to test, so I can't say about how to fix it there.

edermats32 commented 2 months ago

As for Linux, it might be of interest to find the PID of reaper, and using waitpid in order to wait until it finishes. I am unsure if there's a better way to do it in C++, or in QT though...

This works. However the funny thing is that (at least with the game i tested) the PID for reaper changes at least once. To get around this i found you can either use waitpid twice:

#!/bin/bash
steam steam://rungameid/2231450
sleep 2 # wait for reaper to start
waitpid $(pgrep "^reaper$")
sleep 2
waitpid $(pgrep "^reaper$")

This is not ideal since now where dealing with two arbitrary times, and it also doesn't work if reaper restarts again. The second solution is to use a while loop instead of whaitpid:

#!/bin/bash
steam steam://rungameid/2231450
sleep 2
while pgrep "^reaper$" > /dev/null; do
  sleep 5
done

The problem now is that (for some reason) it has to sleep longer, at least 5 seconds. That means every time we exit a game it will take around that time to get to pegasus again. Now we at least doesn't need to worry about reaper restarting. However we're still guessing how long processes takes to start witch is, again, not ideal...

Edit:

Ended up just doing this:

steam steam://rungameid/2231450
sleep 10 # wait for reaper to start (and restart)
waitpid $(pgrep "^reaper$")

This should give reaper enough time to start and restart. Pegasus also reopens instantly on game exit, unless you exit within the first 10 seconds.

Lokito23 commented 2 months ago

This works. However the funny thing is that (at least with the game i tested) the PID for reaper changes at least once.

I have done some testing, but it didn't happen for me, so it might be related to anticheats/DRM or launchers. (For context, I tried Celeste and Vampire Survivors, both kept the reaper PID that launched with them)

However, I also found a theoretical shortcoming of this solution: Opening Pegasus on Steam would make Pegasus think a game is running.

Technically you could detect if Pegasus was opened from Steam, by detecting if reaper is already running when launched, but that makes it possible to have false positives, for instance, if a Steam game is running before Pegasus was launched.

Lokito23 commented 2 months ago

Also the timing would be off, since you have to wait for Steam to load, if it isn't already open. (EDIT: This is even worse taking into consideration automatic updates)

Either way, not much else that can be done. Previously Linux also had RunningAppID, within the .steam/registry.vdf file, but they got rid of it on the GUI update, for some reason.

Lokito23 commented 2 months ago

If it were to be implemented into Pegasus, it would have to:

  1. Detect that Steam has loaded
  2. Wait for an undetermined amount of time for the game to start loading (Precompiling shaders, etc...)
  3. Detect that process reaper exists
  4. Wait for process reaper to close
  5. Show Pegasus

Might be possible, but I am uncertain. Either way, this is just the Linux solution, and while Windows has RunningAppID, I can't say about MacOS.

Lokito23 commented 2 months ago

From what I've seen from the logs of running the steam command, it does not look like there's a log that indicates that Steam has loaded, beyond Steam is already running, exiting. when it finds that Steam is already open. So that makes step 1 harder.

As for step 2, Steam uses a process called fossilize_replay to prepare Vulkan, but beyond that, I couldn't find much.

The rest of the steps should be easier, the problem's with the steps 1 and 2.

EDIT: Technically you are able to brute force both of the steps, by having a while loop that checks if reaper has opened, and only proceeds once it has opened, or a time limit is reached (making it infinite is not a good idea).

edermats32 commented 2 months ago

Yeah. I'm on the same track. This works, but again there is the issue of waiting 5 seconds when closing the game:

while true; do
  # Wait for reaper to start  
  while ! pgrep -x "reaper" > /dev/null; do sleep 1; done

  # Wait for reaper to stop 
  while pgrep -x "reaper" > /dev/null; do sleep 1; done

  # Sleep in case reaper restarts
  sleep 5

  # Check if reaper restarted; if not, break outer loop
  if ! pgrep -x "reaper" > /dev/null; then
    break
  fi
done

But in cases where reaper doesn't restart you should only need this:

  while ! pgrep -x "reaper" > /dev/null; do sleep 1; done
  while pgrep -x "reaper" > /dev/null; do sleep 1; done

Btw. I used forkstat -s to track what processes open when starting a game and found steam-launch-wrapper. But i can't find it with pgrep for some reason.

Lokito23 commented 2 months ago

I found it to be more efficient to use until, like so:

#!/bin/bash

steam steam://rungameid/<Insert ID here>

until pgrep "^reaper$"  > /dev/null
do
    sleep 1
done

waitpid $(pgrep "^reaper$")
Lokito23 commented 2 months ago

This is still scuffed, granted. There can be another process called reaper which interferes with this script, or another Steam game is open on the background and, since it's has it's own reaper, it tricks the script into waiting for the background game (or Pegasus opened with Steam) to stop running.

EDIT: This might get fixed if the script filtered out reaper processes that were running before the steam command is run. This still has the side effect of allowing another reaper process to appear just before the game loads, so it's still not that viable.

Lokito23 commented 2 months ago

Found out that doing pgrep -P $(pgrep "^steam$") "^reaper$" eliminates the possibility of non Steam reaper processes of interrumping it.

Combined with filtering the already running reaper processes, it should allow it to be consistent, but I have no clue how to filter it.

edermats32 commented 2 months ago

I might have found a solution, at least for Linux. Was looking through some logs and found that content_log.txt writes a line when a game closes. By reading this file we can successfully check if a game is running:

#!/bin/bash

id="2231450"

steam steam://rungameid/"$id"

while ! tail -f -n 0 "$HOME/.local/share/Steam/logs/content_log.txt" | grep -q "Remove ${id} from running list"
do
  sleep 1
done

I haven't tried this with other games, but I don't see why it wouldn't work.

Lokito23 commented 2 months ago

The solution I was trying failed when Steam wasn't open, unsure why, here it is incase someone wants to debug it:

#!/bin/bash

if pgrep -P $(pgrep "^steam$") "^reaper$"; then
        exclude=$(pgrep -P $(pgrep "^steam$") "^reaper$")
else
        exclude="none"
fi

steam steam://rungameid/504230 &

until pgrep -P $(pgrep "^steam$") "^reaper$" | grep -v "$exclude"
do
        sleep 1
done

waitpid $(pgrep -P $(pgrep "^steam$") "^reaper$" | grep -v "$exclude")
Lokito23 commented 2 months ago

Probably.

Lokito23 commented 2 months ago

The objective of that is to filter currently running reaper processes, though I guess the steam check is irrelevant.

Lokito23 commented 2 months ago

Anyways:

I might have found a solution, at least for Linux. Was looking through some logs and found that content_log.txt writes a line when a game closes. By reading this file we can successfully check if a game is running:

#!/bin/bash

id="2231450"

steam steam://rungameid/"$id"

while ! tail -f -n 0 "$HOME/.local/share/Steam/logs/content_log.txt" | grep -q "Remove ${id} from running list"
do
  sleep 1
done

I haven't tried this with other games, but I don't see why it wouldn't work.

This works with other games, but can be simplified to:

#!/bin/bash

id="2231450"

steam steam://rungameid/"$id"

tail -f -n 0 "$HOME/.local/share/Steam/logs/content_log.txt" | grep -q "Remove ${id} from running list"

Since the tail keeps checking the log, and grep -q exits the moment it finds the match.

Lokito23 commented 2 months ago

Anyways, found the fix for my solution:

#!/bin/bash

if pgrep "^reaper$"; then
        exclude=$(pgrep "^reaper$")
else
        exclude="none"
fi

steam steam://rungameid/504230 &

until pgrep "^steam$" > /dev/null && pgrep -P $(pgrep "^steam$") "^reaper$" | grep -v -q "$exclude"
do
        sleep 1
done

waitpid $(pgrep -P $(pgrep "^steam$") "^reaper$" | grep -v "$exclude")
Lokito23 commented 2 months ago

However yours should be easier to implement into the code, since it's just watching a file for a specific string, compared to watching processes.

Lokito23 commented 2 months ago

Seems like this is the line that declares the launch command, I am unsure if it can be done, I'll have to investigate more.

Looks like these are the lines that control what command is run.

And these are the lines that launch the commands.

Lokito23 commented 2 months ago

Judging by the structure of the code, it might be hard to specify commands specifically for Steam.

Might work if some sort of special attribute was added here, so that the function that runs the games can differentiate between normal games and Steam games.

In that way the fix could be implemented, though I am unsure, as I don't have that much experience.

Lokito23 commented 2 months ago

I might have found a solution, at least for Linux. Was looking through some logs and found that content_log.txt writes a line when a game closes. By reading this file we can successfully check if a game is running:

...

BTW, this works on Windows too, just checked with a VM, the log exists.

Lokito23 commented 2 months ago

The logs should be located in these locations:

Windows: C:\Program Files (x86)\Steam\logs MacOS: ~/Library/Application Support/Steam/logs Linux: ~/.local/share/Steam/logs

Source: Steam Cloud FAQ

As for the Steam Flatpak, it's ~/.var/app/com.valvesoftware.Steam/.local/share/Steam/logs

edermats32 commented 2 months ago

Judging by the structure of the code, it might be hard to specify commands specifically for Steam.

Might work if some sort of special attribute was added here, so that the function that runs the games can differentiate between normal games and Steam games.

In that way the fix could be implemented, though I am unsure, as I don't have that much experience.

Yeah. I wouldn't know how to implement it in the actual pegasus source code. However the way i have my library set up is by having the startup script steam-rungameid.sh:

#!/bin/bash

id=$1

steam steam://rungameid/"$id"

while ! tail -f -n 0 "$HOME/.local/share/Steam/logs/content_log.txt" | grep -q "Remove $id from running list"
do
  sleep 1
done

Then for every game i create a game.sh file that includes:

#!/bin/bash

steam-rungameid.sh <game-id>

And then in the Pegasus metadata.txt i link to the script:

launch: bash {file.path}

game: Game 1
file: /path/to/game1.sh

game. Game 2
file: /path/to/game2.sh
Lokito23 commented 2 months ago

Judging by the structure of the code, it might be hard to specify commands specifically for Steam. Might work if some sort of special attribute was added here, so that the function that runs the games can differentiate between normal games and Steam games. In that way the fix could be implemented, though I am unsure, as I don't have that much experience.

Yeah. I wouldn't know how to implement it in the actual pegasus source code. However the way i have my library set up is by having the startup script steam-rungameid.sh:

#!/bin/bash

id=$1

steam steam://rungameid/"$id"

while ! tail -f -n 0 "$HOME/.local/share/Steam/logs/content_log.txt" | grep -q "Remove $id from running list"
do
  sleep 1
done

Then for every game i create a game.sh file that includes:

#!/bin/bash

steam-rungameid.sh <game-id>

And then in the Pegasus metadata.txt i link to the script:

launch: bash {file.path}

game: Game 1
file: /path/to/game1.sh

game. Game 2
file: /path/to/game2.sh

That is a way to do it, though in order to close this issue, it would need to be added into mainstream.

That or Valve would have to add a way to start a Steam game without keeping the client open.

edermats32 commented 2 months ago

though in order to close this issue, it would need to be added into mainstream.

Right. I might take a look at the source code tomorrow and see if i figure something out.

Lokito23 commented 2 months ago

I'll see if I find something.

Lokito23 commented 2 months ago

Beyond the Steam game declaration and the launcher of the commands, I've also found the declaration of the game class, along side it's header

edermats32 commented 2 months ago

I give up at trying to understanding the code, not good enough at C++

Anyway if someone picks this up, to summarize:

We figured out you can check if steam has closed the game by monitoring the log content_log.txt and waiting for the line "Remove <game-id> from running list"

This should be enough as we can just keep a dummy processes running from when pegasus launches the game until the log prints the line.

The logs should be located in these locations:

Windows: C:\Program Files (x86)\Steam\logs MacOS: ~/Library/Application Support/Steam/logs Linux: ~/.local/share/Steam/logs

Source: Steam Cloud FAQ

As for the Steam Flatpak, it's ~/.var/app/com.valvesoftware.Steam/.local/share/Steam/logs