No description
Find a file
Jose Falanga 8b393090ca Fix stale cache launches, FNF runtime deps, and uninstall focus death (v0.1.20)
- Inject the user-level shared-libs dir into LD_LIBRARY_PATH for installed
  games so FNF finds libvlc.so.5 (idempotent, avoids duplicate entries)
- Reconcile cached installs against butlerd's live caves: normalize
  int/float ids, wait for butlerd when the cache claims installs, keep the
  cache when Fetch.Caves fails, and re-save the reconciled cache
- get_caves() returns null on RPC error (vs [] for genuinely nothing
  installed) so callers can tell a transient failure apart
- Patch freshly installed/updated items in place so the Play button launches
  the new binary immediately
- Drop and re-add items through the LibraryManager so the library menu
  re-renders after install/uninstall (core handler is a no-op upstream)
- Restore launch-page focus after uninstall by searching get_tree().root:
  get_tree().current_scene is null in OGPU, so the old fix silently no-oped
  and left the D-pad dead
2026-08-09 15:43:30 -03:00
assets Initial commit 2026-08-05 17:27:43 -03:00
core Fix stale cache launches, FNF runtime deps, and uninstall focus death (v0.1.20) 2026-08-09 15:43:30 -03:00
.gitignore Add option to filter games not available on the current platform 2026-08-07 18:41:39 -03:00
export_presets.cfg Initial commit 2026-08-05 17:27:43 -03:00
LICENSE Initial commit 2026-08-05 17:27:43 -03:00
Makefile Install games to persistent user://butler/games and reconcile orphaned caves 2026-08-07 22:48:17 -03:00
plugin.gd Initial commit 2026-08-05 17:27:43 -03:00
plugin.json Fix stale cache launches, FNF runtime deps, and uninstall focus death (v0.1.20) 2026-08-09 15:43:30 -03:00
README.md Initial commit 2026-08-05 17:27:43 -03:00

OpenGamepadUI-itch

An itch.io library plugin for OpenGamepadUI, built against itch's official butlerd JSON-RPC launcher daemon (the same integration path the official itch.io app uses under the hood) rather than screen-scraping the itch desktop app.

Owned games, install state, install/update/uninstall, and launching are all driven through butlerd. This plugin spawns butler daemon --json ... itself, so the itch.io desktop app doesn't need to be installed at all — only a butler binary, which this plugin downloads automatically on first run from itch's own broth distribution channel.

Setup

  1. Build and install the plugin (see below).
  2. Generate an API key at https://itch.io/user/settings/api-keys.
  3. Open the plugin's settings screen in OpenGamepadUI and paste it in.

API key login was chosen over the OAuth + PKCE flow butlerd also supports, because OAuth requires registering a redirect URI / custom URL scheme with itch.io for a specific app, which doesn't make sense for a self-built, unpublished plugin. If you're distributing this plugin publicly, switching to OAuth is worth doing — see Profile.LoginWithOAuthCode in the butlerd launcher guide.

Requirements

See the OpenGamepadUI requirements. This plugin additionally needs unzip on the host (used to unpack the downloaded butler binary).

Building

Same as any other OpenGamepadUI plugin:

make help
make build
make install

Known limitations / good next steps

This was put together from butlerd's public spec rather than against a running instance, so a few corners are simplifications rather than the "real" solution. If you pick this up:

  • Launch target discovery. butlerd's Launch call is the "correct" way to start a game (it handles prerequisites, manifest actions, sandboxing, etc.), but OpenGamepadUI's library items launch via a plain command + args OS exec, not an RPC call. This plugin currently side-steps that by scanning each installed game's folder for an executable file and pointing command directly at it (library_itch.gd::_find_executable). That works for straightforward native builds but skips prerequisite installation and manifest-declared launch actions. Parsing .itch/receipt.json.gz in the install folder would give the authoritative answer.
  • Field names. The JSON-RPC field names used in itch_client.gd follow butlerd's documented camelCase convention, cross-checked against the butlerd Go package docs for the calls that matter most (Install.Queue/Install.Perform, Uninstall.Perform). A couple of the less-central ones (Launch, Profile.LoginWithAPIKey, Fetch.ProfileOwnedKeys) were written from the documented naming convention rather than a byte-for-byte spec check — worth diffing against your butler's actual behavior (run with --log to see the raw JSON-RPC traffic) if a call comes back with an unexpected error shape.
  • Install location picker. _ensure_install_location() silently picks the first existing install location, or creates one under the plugin's own data directory. No UI for choosing/managing install locations yet.
  • Settings screen. Uses plain Godot Control nodes rather than OpenGamepadUI's themed widget set (the one the built-in Steam plugin's settings scene uses), since this was written without editor access to those theme resources.
  • has_update() always returns false — wiring up CheckUpdate (and periodic background polling) is a natural next step.
  • No progress→UI wiring. install_progressed is re-emitted from raw butlerd notifications but not yet mapped back to a LibraryLaunchItem through LibraryManager (see the TODO in library_itch.gd).

How it fits together

plugin.gd            entrypoint; wires client + library + settings menu
core/itch_client.gd   spawns butlerd, speaks JSON-RPC 2.0 over TCP to it
core/library_itch.gd  extends Library; turns butlerd data into LibraryLaunchItems
core/itch_settings.*  API key entry screen