Files
enricobuehler 5fb6946aa1
CI / build (push) Failing after 15s
CI / publish (push) Skipped
feat: the Heroic library source
The host's in-tree Heroic scanner, lifted out into its own installable plugin
(design/library-scanner-plugins.md M5/WP5.2). One source covers all three of
Heroic's backends — legendary (Epic), gog, nile (Amazon) — because that is how
Heroic presents them and how the built-in scanner did.

The package is a config schema, a detect, a scan and a watchDirs. The store
claim, sync engine, __config surface, console registration and the
detect/scan/parity/uninstall CLI verbs all come from
@punktfunk/plugin-kit/library.

Claiming the `heroic` store keeps the entry ids the built-in scanner produced
(`heroic:<runner>:<appName>`), so GameStream app ids, Moonlight pins, cached art
and the operator's enabled/disabled state all survive the migration.

Ported behaviours worth naming, each covered by a test:

  * a title counts as installed only if Heroic says so AND its install directory
    still exists. The second check is the workaround for Heroic's GOG
    is_installed bug (#2691), without which uninstalled games linger.
  * only http(s) art is emitted. A sideloaded title can carry a local file://
    path, which — unlike a path this plugin resolved itself — is not one the host
    should proxy, and the client cannot fetch either. It degrades to the title
    card, as in-host.
  * both detect signals ride along: the install dir and the HEROIC_APP_NAME env
    marker. Heroic hands off to legendary/gogdl/nile, so the host never sees the
    game's own process any other way, and the env marker is what works under
    Proton (verified on-glass 2026-07-27). They are a union, so a Heroic version
    that stops setting it degrades rather than breaking.

The launch VALUE and the `heroic` launch kind are unchanged — the host still owns
turning `<runner>:<appName>` into a heroic://launch command, so this plugin never
constructs a command line.

Verified locally against the kit: tsc clean, biome clean, 5 tests, bundle builds,
CLI runs (detect -> absent, scan -> 0 games).

As with lutris: no lockfile and CI cannot be green until
@punktfunk/plugin-kit@0.3.0 is published. The parity gate has NOT been run — that
needs a box with Heroic installed.
2026-08-05 19:17:02 +02:00

82 lines
3.4 KiB
Markdown

# @punktfunk/plugin-heroic
Adds your installed **Heroic Games Launcher** titles to a Punktfunk host's library — Epic, GOG and
Amazon in one source, with their cover art, and with the session ending when you quit the game.
Install it from the host's plugin store (**Library → Game sources → Add a source**), or:
```sh
punktfunk-host plugins add heroic
```
Nothing to configure: it finds Heroic's config folder in the usual places (`$XDG_CONFIG_HOME/heroic`,
`~/.config/heroic`, Flatpak) and reads the library caches from there. If yours lives somewhere else,
set the path in **Library → Game sources → Heroic → Settings**.
## What it replaces
Punktfunk used to scan Heroic from inside the host. This plugin is that scanner, lifted out — so you
install it only if you use Heroic, and it can be updated without updating the host.
It claims the `heroic` store, which means your games keep **exactly** the ids they had before
(`heroic:legendary:<appName>`): Moonlight pins, cached box art, and your enabled/disabled state all
carry over, and the host's built-in scanner switches itself off while this is installed. Remove the
plugin and the built-in comes straight back.
## What gets listed
Only titles Heroic reports as installed **and** whose install directory still exists — the second
check works around Heroic's GOG `is_installed` bug
([#2691](https://github.com/Heroic-Games-Launcher/HeroicGamesLauncher/issues/2691)), which otherwise
leaves uninstalled games sitting in your library.
Art comes straight from Heroic's cached CDN URLs, so the client fetches it directly. A sideloaded
title whose art is a local `file://` path is listed without art rather than with a picture nothing
can load.
## Commands
```sh
punktfunk-plugin-heroic detect # is Heroic installed on this host?
punktfunk-plugin-heroic scan --preview # what WOULD be synced, as JSON (works with no host)
punktfunk-plugin-heroic uninstall # drop the games and release the store claim
```
## Parity — the release gate
Unit tests pin the parsing. What actually has to hold is that this plugin reproduces the in-host
scanner it replaces, down to the ids — one that parses perfectly but emits a subtly different
`external_id` breaks every Moonlight pin on the host, and no unit test notices.
So, on a box with Heroic installed, **before** installing this plugin:
```sh
punktfunk-plugin-heroic parity --snapshot before.json # host still on its built-in scanner
punktfunk-plugin-heroic parity --compare before.json # offline; exits non-zero on any difference
```
`--compare` runs this plugin's own scan and diffs it: ids, titles, launch recipes, roles and
metadata exactly; art by **presence, not value**.
Worth re-checking on-glass specifically: a game launched under **Proton**. Heroic hands off to
legendary/gogdl/nile, so the `HEROIC_APP_NAME` environment marker is what lets the host recognise
the running game and end the session when you quit it.
Do not publish a version whose parity run is red.
## Development
```sh
bun install
bun run typecheck && bun test && bun run build
```
The framework — store claim, sync engine, `__config`, CLI verbs, and the launcher-file parsers —
lives in [`@punktfunk/plugin-kit/library`](https://git.unom.io/unom/punktfunk/src/branch/main/plugin-kit).
This package is the Heroic-specific part: find the config root, read the three `store_cache`
libraries, map entries. See `design/library-scanner-plugins.md` in the main repo.
## License
MIT OR Apache-2.0.