DavidoTek / ProtonUp-Qt

Install and manage GE-Proton, Luxtorpeda & more for Steam and Wine-GE & more for Lutris with this graphical user interface.
https://davidotek.github.io/protonup-qt
GNU General Public License v3.0
1.22k stars 40 forks source link

Heroic: Add Basic Games List #199

Closed sonic2kk closed 1 year ago

sonic2kk commented 1 year ago

This PR implements #168, at least in a basic capacity :-) There is also some slight refactoring and additions to the fields stored in HeroicGame.

Games List

Layout

Here's the layout I came up with:

image

Note: There are no Legendary games in the screenshot as my test installed.json games don't have corresponding GamesConfig/<app_name>.json files, so they were showing up as native games, and so I decided to exclude them as this would not normally happen in practice.

Implementation

The data for this list is fetched from a list of HeroicGames. The Heroic Wine and Heroic Proton install locations share a gamelist, I felt that from a user perspective there is not much reason to split them up. In my mind I think it would be more frustrating to have to switch between each install directory just to see the games used. It makes sense for individual compatibility tools but in my mind it makes a bit less sense for viewing a list of games. But that's also just my opinion :-)

Plus, if they were split, native games would be shown on both, leading to inconsistencies there as well.

However if it is preferred to split them, it shouldn't be too hard of a change. We could just set another filter condition for the heroic games list fetching to only show those with the relevant type in their wine_info based on the self.launcher value.

Like with the Steam and Lutris game lists, I have implemented some tooltip information and double-click functionality where I thought it made sense to do so.

Considerations

The data I chose to include here is purely based on my own initial thoughts of what would be good to include. Please let me know if there are better choices or more preferable choices. Of course, users will also be a good way to gauge what should be shown, so I didn't spend too long deliberating and just tried to show what I felt would be the most useful and what matched the other game lists best.

I don't own any Legendary games to test this with, so please do test with some Legendary games installed!

Speaking of Legendary, I couldn't find out if those games have a store_url. Heroic has a button in its hamburger menu for GOG games which stores lets you view the store page. I would be a bit surprised if it didn't have this for Legendary. In the example in #168 I did see a base_urls, but this seems to be for downloading and not for a store page. Perhaps this is stored elsewhere, and if so we can store this on legendary HeroicGames, and then the double-click functionality will work for Legendary games too :smile:

Code Refactoring

HeroicGame data

I updated the HeroicGame datastructure a little bit. The main change was that I removed the install dict, which is based on the install dict for Heroic games. The issue is that this can store varying information. Instead, I decided to remove that and store the fields we would be mostly interested in as separate attributes. Not all install sections of a Heroic Game JSON will store the same data, so it didn't make much sense looking back to try and shoehorn our HeroicGame class to do this.

Now, we have three new fields on the HeroicGame:

Figuring out how to get the executable for GOG games was a bit of a (heroic) adventure. In the end I found out that the executable is stored in a rather roundabout way for Wine/Proton games. In a Wine/Proton game's folder, there will be a file named goggame-<app_name>.info, which is a file with a JSON file structure. Here is an example of this file:

DOOM I Enhanced (goggame-2015545325.info) ```json { "buildId": "56055485420912305", "clientId": "55704669042708932", "gameId": "2015545325", "language": "English", "languages": [ "en-US" ], "name": "DOOM I Enhanced", "osBitness": [ "64" ], "playTasks": [ { "arguments": "--bnet-language en", "category": "game", "isPrimary": true, "languages": [ "en-US" ], "name": "DOOM I Enhanced", "osBitness": [ "64" ], "path": "DOOM.exe", "type": "FileTask" } ], "rootGameId": "2015545325", "version": 1 } ```

The executable path (which afaict is the relative path from the install folder) is always stored in the playTasks list of objects, and it's always stored on the object whose name attribute matches the root name attribute -- I checked this with 5 different GOG games and it seemed to be the case. From reading through a few of these files, I would guess that playTasks is used for things like alternative game executables that you can use, such as multiplayer servers. Some files like Dungeon Keeper for example had several of the playTasks, and we couldn't guarantee that the first entry would be the correct one, so we had to resort to searching the file for the correct path to use.

I created a separate utility function in heroicutil to get this value: get_gog_game_executable which manages doing this. It defaults to start.sh for native GOG games (instead of just an empty string). For Wine/Proton, it searches for this goggame info file and attempts to extract the relevant path attribute from the correct object.

For the game I tested this seemed to work but as always, the more testing, the better! :smile:

Games List Dialog changes

I created a utility method in the games dialog, set_item_data_directory, which manages attaching a path to a QTableWidgetItem's data. This logic was duplicated between Lutris and Heroic for the Install Path double-click functionality, so I created a helper method for this.

If we do want to add a double-click action for compat tools which opens their folder, this method could be used.


The changes here turned out to be a little bigger than anticipated in the end, with the HeroicGame refactor. Hopefully I justified the changes I made there well enough. Other fields could potentially be removed and that is certainly up for discussion, maybe storing the art paths is not really that useful. Some of the fields stored are not explicitly used at least not yet, but I thought they were good to store as it's good general information to store about a game. Likewise if there is some data that was missed we can always expand the HeroicGame.

Thanks! :-)

DavidoTek commented 1 year ago

The Heroic Wine and Heroic Proton install locations share a gamelist, I felt that from a user perspective there is not much reason to split them up

Agreed. The only reasons they are split up in the first place are that it is easier to handle internally and most users probably only want to use Wine anyway and Proton is more of a bonus.

Tooltip shows Game Name, and appends (executable_name) if the game executable can be found. Game name is shown in tooltip in case the game name is too long.

That's useful.

Double click opens the store_url using xdg-open, if the store_url is valid (not set for sideloaded games, should be set for GOG games, unsure about legendary?)

I think Epic Games URLs are using the name, but lower-case and spaces and special characters replaced with -. Though I'm not sure if that is always the case. Example: Destiny 2: Legacy Collection -> https://store.epicgames.com/p/destiny-2--legacy-collection

though perhaps we could bind this to opening the compatibility tool path (if present)?

Why not.

Of course, users will also be a good way to gauge what should be shown, so I didn't spend too long deliberating and just tried to show what I felt would be the most useful and what matched the other game lists best.

Looks good to me. If users want anything else, they will probably open an issue. I also don't think we need something like the AreWeAntiCheatYet.com status as Heroic should already include this (at least it includes an areweanticheatyet.json file).

I decided to remove that and store the fields we would be mostly interested in as separate attributes.

Looks very clean now.


In my testing, all columns populated correctly for gog, sideloaded and legendary games. Double-clicking also opened the URL/folder just fine (tested 7 games). Detection of the executable also worked fine even when they are in a subdirectory.


Thanks!

sonic2kk commented 1 year ago

I think Epic Games URLs are using the name, but lower-case and spaces and special characters replaced with -. Though I'm not sure if that is always the case.

We could add build store_url for legendary games like this then, storing the Epic Games base store URL as a constant: lg.store_url = f'{EPIC_GAMES_STORE_URL}{re.sub("[^a-zA-Z0-9]", "-", lg.title.lower())}' where the regex matches any character that isn't alphanumeric (case insensitive) with a -.

Indeed this may not always be the case, I'm not sure if there's a good way that we could sanity check a URL. I haven't checked the code to see how Heroic gets the Epic store URL, if at all. Based on the URL you gave though it seems like it could be a safe assumption, though I wonder what happens in cases where a name changes. This is likely a hardcore edge-case but: Say "Destiny 2" gets renamed to "Destiny Two" and the Epic store URL updates, but it's already been downloaded from Heroic by a user. Maybe Heroic refreshes the downloaded names but if not, ProtonUp-Qt may build the wrong URL (using destiny-2 instead of destiny-two, if the Epic URL updates at all that is).

Again that's very likely an extreme edge-case, and Heroic may refresh the next time it's opened, but it's something that came into my mind. Maybe having something that's generally correct (or maybe even very often correct) is better than not having anything :sweat_smile:

Looks good to me. If users want anything else, they will probably open an issue. I also don't think we need something like the AreWeAntiCheatYet.com status as Heroic should already include this (at least it includes an areweanticheatyet.json file).

Yeah Heroic lists this along with some info from howlongtobeat.com, and possibly other info I haven't seen. It also lets you view a game's compatibility I think in the hamburger menu, which redirects to ProtonDB - Though this may not always be totally accurate as there are cases where builds of games from GOG/Epic don't work the same as Steam builds and vice versa (can't find the issue now, but I remember the GOG build of Cyberpunk 2077 didn't work initially at launch, but the Steam version did). It may not be worth showing this either as, once again, Heroic itself can show this information to users :-)

In my testing, all columns populated correctly for gog, sideloaded and legendary games. Double-clicking also opened the URL/folder just fine (tested 7 games). Detection of the executable also worked fine even when they are in a subdirectory.

Woohoo :partying_face: Thank you for testing.


I'll push up a couple more changes :-)

sonic2kk commented 1 year ago

Pushed a change that:

Not sure how many people will use the double-click functionality but I think it's nice to have :-)

DavidoTek commented 1 year ago

Thanks!

attempts to build a base Epic Games URL, may not always be valid but could be better than no URL. We can remove it if preferred

Good. I cannot remember a game where they changed its name, but that's really an edge case and Heroic should deal with, i.e. they probably want the name to change in Heroic as well so they would add a mechanism for that if it ever happens.

adds a double-click action for Wine/Proton compatibility tool cells which opens their base directory

Great, even works fine when the display name doesn't match.

Not sure how many people will use the double-click functionality but I think it's nice to have :-)

Yeah, I'm wondering if we should document the "hidden functionality" somewhere or leave it as an easter egg :)

PS: Regarding the translations, there need to be some changes. I will create a commit...

sonic2kk commented 1 year ago

Yeah, I'm wondering if we should document the "hidden functionality" somewhere or leave it as an easter egg :)

Hehe, some of it is """documented""" in that the game install path cells for Heroic and Lutris both have a tooltip that says you can double click to browse. Though then there's also the case of how many users would actually read docs :wink: No objections from me either way!

PS: Regarding the translations, there need to be some changes. I will create a commit...

Thanks for always being able to help with that :smile: