GameHub icon indicating copy to clipboard operation
GameHub copied to clipboard

Implement itch.io as a new game service

Open neuromancer opened this issue 5 years ago • 44 comments

Reference implementations:

  • https://github.com/itchio/itch
  • https://github.com/itchio/butler

neuromancer avatar Sep 09 '18 16:09 neuromancer

It doesn't look like they have required functionality in their API. Also OAuth applications can only request profile (https://itch.io/docs/api/oauth#the-authorization-step/scopes).

tkashkin avatar Sep 09 '18 22:09 tkashkin

Can you just use butler to download and update games? (I know it is not the best solution..)

neuromancer avatar Sep 10 '18 09:09 neuromancer

An interesting feature to consider from the itch.io client is the use of a sandbox.

neuromancer avatar Sep 15 '18 11:09 neuromancer

It looks like the official client uses a different API, used and sort-of-documented here. Specific endpoints of interest might be /login, /profile/owned-keys, /games/<gameid>/uploads, /games/<gameid>/download-sessions, and /profile/game-sessions/summaries/<gameid>. Additionally, it seems like Playnite was able to get started by contacting the itch devs directly.

impiaaa avatar Jan 04 '19 00:01 impiaaa

Hi, I have made some progress on this. https://github.com/hagabaka/GameHub/tree/itch

Currently I'm able to login with an API key, and fetch a list of games, although I haven't figured out why they're not showing up in the GameHub UI (it's able to print a list of their names in console).

This is much more complicated than other data sources, because butler daemon uses a flavor of JSON-RPC 2.0 over TCP which doesn't seem to be supported by jsonrpc-glib, so I had to make my own implementation. The documentation for this API is currently here: http://docs.itch.ovh/butlerd/master

The code is missing a lot of error checking, and I'm probably doing a lot of things the wrong way. I'd appreciate any help and directions.

hagabaka avatar Aug 23 '19 02:08 hagabaka

@hagabaka thanks. I have fetched your branch to https://github.com/tkashkin/GameHub/tree/itch.

I have added some error checks and improved butler detection. By default butler is not in $PATH if it was installed with itch desktop app. This case should be handled correctly now.

I can't actually test this since I don't have anything on itch.io. I assume Fetch.ProfileOwnedKeys should only return bought items.

Run GameHub with --debug --verbose arguments to see logs.

tkashkin avatar Aug 23 '19 08:08 tkashkin

Thanks. I've merged the commits and will keep working on it. ~Fetch.ProfileOwnedKeys returns free games as well as purchased games (). It should be printing a list of game names in the console even if you haven't bought anything...~ I think it returns all the games listed in https://itch.io/my-purchases . My list definitely shows a lot more games than I actively bought though.

~The games are actually displayed in the "all games" tab now, but not the Itch.io games tab. Any idea why?~ Now the app shows the list of itch games correctly.

hagabaka avatar Aug 23 '19 18:08 hagabaka

I've added the functionality to list, install, and run itch.io games. Here are some of the remaining issues:

  • Installing a game uses the first version of game offered by butler, which I think is the latest version for your platform. There is no functionality to install an alternative version, and installing games with no Linux support will just fail. Butler does provide the information needed to implement this, but I'm not sure how to implement the UI for it. We just need to display a dialog with a few choices, and return the choice the user selected. InstallDialog seems interesting, but it takes a list of Runnable.Installer which I think need to have URLs, and butler doesn't provide them.

  • There is no indication of "downloading" or "running". Butler provides these through JSON-RPC notifications. However they are not automatically associated with tasks. To properly handle them, it recommends opening dedicated connections for longer running tasks like installations and launches, which will only receive notifications for the tasks they start.

  • Updating game information on demand isn't implemented yet, so the information is only loaded from butler when GameHub starts.

  • Uninstalling a game isn't implemented yet.

  • Running with compatibility tool isn't implemented. Currently we use Butler's Launch request to run games, which doesn't support compatibility tools. I'm not sure what the data source needs to do to support this actually.

hagabaka avatar Aug 24 '19 23:08 hagabaka

Yes, Runnable.Installer should be implemented for itch.io games. It may require some changes but should be possible.

Maybe Runnable.Installer should be abstracted more and split into

  • Installer that is something more abstract that does nothing and only has metadata like id, name, size. ItchGame.Installer should inherit Installer and interact with butler.
  • DownloadableInstaller which will be same as Installer is now. Anything that inherits current Installer should inherit DownloadableInstaller instead.

Downloader and Download/PausableDownload are abstract classes. Probably there should be itch-specific Download implementation which will manage butler connection, listen to events and update download status.


Running with compatibility tool isn't implemented.

Is it possible to get game's executable path from butler? It should be possible to use compatibility layers in that case.

tkashkin avatar Aug 25 '19 05:08 tkashkin

@hagabaka I have refactored Runnable.Installer in 38f7c2b. Now there are Installer, FileInstaller and DownloadableInstaller base abstract classes.

Now GameHub should load installed free itch games in addition to owned games.

Also some GameHub features are implemented for itch games.

tkashkin avatar Aug 26 '19 10:08 tkashkin

@tkashkin Thanks. To update status during game install and launch, I think we need to split ButlerDaemon into two classes, one which starts the daemon and gets the address and secret from output, and one (ButlerConnection) which maintains a connection to the daemon and sends/receives messages. This way we can use a main ButlerConnection for general tasks, and create a temporary ButlerConnection when starting an installation or launch, and handle notifications in it which will be related to that task. This is basically how itch.io recommends using the daemon API. I know you simplified the ButlerConnection away by combining it with ButlerDaemon before, but that makes it hard to use multiple connections to the daemon. What do you think?

hagabaka avatar Aug 26 '19 19:08 hagabaka

Summoning @fasterthanlime, he may be able to help, though his status say's he's on break.

aaronfranke avatar Aug 27 '19 11:08 aaronfranke

@hagabaka Using separate connections for tasks like launching/installing/etc. is a good idea indeed! Closing the connection will "cancel" these operations. This is the concept of "conversations" as implemented in https://github.com/itchio/node-butlerd

Here's an example usage of node-butlerd:

https://github.com/itchio/itch/blob/308582de070376fb017a4f2917e42a6d8116cdeb/src/main/reactors/updater.ts#L73-L109

Having a separate connection lets you route logging (the Log message) elsewhere, cancel separately, not mix up server requests (that should show dialogs, etc.). It's low-tech, but it works.

Like @aaronfranke said, I'm on semi-break, but happy to review butlerd client implementation before it goes live (I will be afk Sep 1-9 though).

fasterthanlime avatar Aug 27 '19 11:08 fasterthanlime

I've added status update during game download. There are a couple ugly details:

  • To get a "downloading" Game.Status, it needs to be constructed with a Downloader.Download. And this class requires remote, local, and local_tmp files/urls to be set. The actual download is performed by butler daemon, which doesn't provide all this information, so I just set them all to "/dev/null", and that seems to cause no problems. I think Game.Status should only require a Downloader.DownloadStatus, which contains the download progress and speed.
  • Speeking of which, Downloader.DownloadStatus requires the currently downloaded size and total size. Butler daemon's Progress notifications only give a progress percentage. So I resorted to using the totalSize in the TaskStarted notification to calculate the downloaded size. If for any reason we fail to get that notification, it would display "0/0 0%", even if we do have the current percentage. Maybe the status should only require a progress, and allow you to display any other text information. For example, butler provides an "eta" field too.

hagabaka avatar Aug 28 '19 04:08 hagabaka

@hagabaka I have refactored download status so it doesn't need workarounds like this. There's now ItchDownloader which implements Downloader and has signals to update UI. That allows to integrate downloads into UI. Installer is also implemented and now InstallDialog is used to select version to install.

tkashkin avatar Aug 28 '19 20:08 tkashkin

@tkashkin Cool! That works great for me.

I added handling of ShellLaunch, HTMLLaunch, and URLLaunch server requests. These are used when butler tries to open an HTML game. We're just using xdg-open right now, and some games don't work because they need to be served in a local HTTP server, but I don't think this would be a high priority issue for most people. I implemented this just so that GameHub isn't stuck in "running" when I click on such games, because the Launch call waits for our response to return.

It seems to me that the only main missing feature is running with compatibility layer. Butler doesn't have an API to tell us the name of the game executable. We could try to guess it, but there would be risk of running "uninstall.exe" etc. Butler has code to handle this (through dash), but it doesn't tell us the result, and it filters by platform by default. I created an issue to ask for the butler API to provide this information, which will make launching with compatibility much easier to implement. There's also an issue requesting for itch/butler itself to support compatibility tools.

hagabaka avatar Aug 28 '19 23:08 hagabaka

@tkashkin What other missing features or implements do you think we need to implement before merging this?

~One possibility is updating games. Butler has an CheckUpdates API, which returns a list of Uploads that can be installed with similar calls as installing games. However GameHub doesn't seem to have UI for updating games. Should it automatically check for and install any updates before running a game?~

I implemented game updates before launching. It works, but the UI isn't very intuitive as it looks the same as freshly installing a game. Is there a way to make the InstallDialog display "Install update"?

hagabaka avatar Sep 04 '19 05:09 hagabaka

Looking forward to using this! What else needs to be done before we merge it in? If you need me to test anything, I can give it a shot.

Thanks for the work on this, and for gamehub in general, I really like the project.

spinningD20 avatar Dec 08 '19 17:12 spinningD20

@spinningD20 testing would be nice. If it works and it's stable I can probably find some free time and merge it. Some kind of game updates UI for this and #323 would be nice, but meanwhile updates on game launch are fine.

tkashkin avatar Dec 08 '19 19:12 tkashkin

[ERROR]  [GLib-GIO] Settings schema 'com.github.tkashkin.gamehub.paths' does not contain a key named 'itch-home'
Trace/breakpoint trap (core dumped)

There might be a previous settings file somewhere, but the above is what I'm getting when I do ninja in the build directory after doing a meson in the base directory, and attempt to run the compiled binary.

I'm on ArchLinux fwiw.

EDIT: I removed .cache/com.github.tkashkin.gamehub files and tried it again, got the same result.

spinningD20 avatar Dec 19 '19 14:12 spinningD20

@spinningD20 You need to build package from this branch or run sudo ninja install to install updated settings schema. Alternatively you can try to set schema directory before running compiled binary:

export GSETTINGS_SCHEMA_DIR="$APPDIR/usr/share/glib-2.0/schemas/:$GSETTINGS_SCHEMA_DIR"

tkashkin avatar Dec 19 '19 16:12 tkashkin

https://gist.github.com/spinningD20/f4e20c16b4cdf14e784d0eab67bd5a97

Should I try it on a fresh system that hasn't seen gamehub yet?

I also tried running gamehub after exporting the env vars above as you had specified, still a seg fault.

spinningD20 avatar Dec 21 '19 02:12 spinningD20

Thanks for helping with testing.

I also get a segfault if butler isn't installed. hagabaka@af359d9a362790accd661d508d33c95d0ba8ec44 fixes this. In the mean time you can also try if installing butler (available on AUR) works.

hagabaka avatar Dec 21 '19 04:12 hagabaka

https://gist.github.com/spinningD20/db4a7da0472a68c08c21c6a27bc7e96e

I am beyond the segfault now, thanks for the pointer about butler.

Observations so far:

  • clicking on the itch.io "authentication is required" in the list of services when gamehub initially opens, nothing happens.
  • I navigated to the options, found the itch.io section, clicked "Generate Key" and went through that process.
  • Copy pasted the api key into the itch.io settings. status of service changed to Authenticated, and I saw my itch.io titles show up in the list of games.
  • Right clicked "Baba Is You" (great game) and pressed Install. Install popup appeared, picked linux version, pushed Install.
  • Baba Is You tile text displays "Starting Download", and noticed [FATAL] [Json] json_object_get_int_member: assertion 'node != NULL' failed in the terminal log. Nothing happens beyond this point.

spinningD20 avatar Dec 21 '19 16:12 spinningD20

I noticed a few different flags for running gamehub, and got it to give me some DEBUG level logging in the terminal. after the FATAL json null error, I got this json response:

https://gist.github.com/spinningD20/0411ec16bd1751f15dcf38d9b041a63f

The only thing I noticed that was null is the build value? Or perhaps this is some other (unrelated) response, I am not sure. Figured I would mention it!

EDIT: Also, I am running this directly from your fork's itch branch, hagabaka.

spinningD20 avatar Dec 22 '19 02:12 spinningD20

I reverted the commit which added game updates but unfortunately also causes this problem with some games. Can you try if the latest commit works?

Also, it might help with testing to know that after you've installed the itch branch one time, the setting schema will be updated, and you don't need to install it every time after rebuilding, but can just run the newly built binary directly.

hagabaka avatar Dec 22 '19 03:12 hagabaka

we're getting further! :)

With the latest commit, I am able to download and install Baba Is You. I went and installed Celeste as well.

So with the happy path out of the way, I started going down some other scenarios... one of them is canceling the download after it is started. When I do this, it does technically stop the Download, but the UI state of the tile text is not updated, still shows me the (last update of) the download progress. If I close gamehub and reopen it, it correctly shows not installed, and will let me go install it.

The only other side-effect of starting the download, canceling, closing gamehub, and reopening and reinstalling, is that it won't clean up the canceled download:

image

Not sure what the priority is, if we want any of the above fixed before it is merged in or not? Testing this same scenario with GOG games, it uses a file in the GOG collection directory to store the downloaded data, and when canceled and re-started, it uses the same file (canceling the download does not make gamehub delete the progress it made in a previous download attempt).

More just letting you guys know this detail I noticed. Otherwise, it seems to work as intended!

(disclaimer, I've only installed/uninstalled/downloaded/cancelled download, I am sure there are other things to test too :)

spinningD20 avatar Dec 22 '19 14:12 spinningD20

With the latest commit on my branch, status should be updated correctly when cancelling an installation.

For clearing or reusing files from cancelled installations, I think there should be an easier way to let butler handle it, but I'm still trying to get the information from the Itch developer. I feel this could be done in a separate issue after the main features have been merged.

hagabaka avatar Dec 28 '19 02:12 hagabaka

Thanks @hagabaka ! While I only have a few games to test with from itch.io (most purchases I've done through them are for art assets), I would say it's ready to merge, and we'll tackle more undiscovered, involved issues as they arise.

@tkashkin or hagabaka, just let me know if you'd like me to do anything further, if there's anything more I can do to help out.

spinningD20 avatar Dec 28 '19 03:12 spinningD20

I have merged it into dev branch. It may need more tests covering various cases (with/without butler installed, authenticated/not authenticated, free/paid games, etc.).

tkashkin avatar Dec 28 '19 05:12 tkashkin