Skip to content

Conversation

@halgari
Copy link

@halgari halgari commented Jan 17, 2026

Rationale

Most of the games included with Vortex are pretty simple, just metadata, ids and registry keys. Many of these extensions are in JS not TS, and use bluebird and other outdated programming libraries. In addition these extensions don't properly detect Linux installs. This PR fixes this by creating a new YAML based game definition. This definition includes automatic game location, works with Steam on Linux, and provides a schema for the YAML (so VS Code type checks the data).

For now this creates a new game called "Skyrim YAML Demo". After this PR is reviewed and approved, I'll remove the old SkyrimSE instance, and rename this definition. We can then go through and clean out all the other games that follow these same patterns.

Summary

  • Add GameFinder extension for automatic game detection across multiple storefronts
  • Add game-definitions extension for YAML-based game definitions
  • Improve type safety for collection selectors

GameFinder Integration

A new internal extension that detects installed games across multiple storefronts:

  • Steam: Parses libraryfolders.vdf and appmanifest_*.acf files
  • GOG: Reads Galaxy database and registry entries
  • Epic: Parses manifest files from Epic Games Launcher
  • Xbox: Scans gaming roots and parses appxmanifest.xml

Game extensions can now opt-in to automatic detection by specifying store IDs:

gameFinder: {
  steam: '489830',
  gog: '1711230643',
  epic: 'ac82db5035584c7f8a2c548d98c86b2c',
  xbox: 'BethesdaSoftworks.SkyrimSE-PC'
}

This is checked during quickDiscovery() before falling back to queryPath/queryArgs.

YAML Game Definitions

A new extension that allows game definitions to be expressed in YAML instead of JavaScript, dramatically reducing boilerplate. Store IDs are defined once and auto-populated to queryArgs, gameFinder, details, environment, and requiresLauncher.

Example (games/skyrimse-yaml/game.yaml):

 id: skyrimse-yaml
 name: Skyrim Special Edition (YAML Demo)
 logo: gameart.webp
 executable: SkyrimSE.exe

 stores:
   steam: 489830
   gog: 1711230643

 modPath: Data

The extension transforms this into a full IGame registration with all the repetitive fields auto-generated.

Other Changes

  • Add IGameFinderIds interface to public API
  • Add gameFinder property to IGame interface
  • Improve collection selector types (any → IState)
  • Add neverthrow and fast-xml-parser dependencies
  • Add yarn detect-games CLI script for testing GameFinder

Test plan

  • Run yarn detect-games to verify GameFinder detects installed games
  • Start Vortex and verify "Skyrim Special Edition (YAML Demo)" appears in game list
  • Verify game discovery works for YAML-defined games
  • Verify existing JS game extensions still work unchanged

halgari and others added 9 commits January 16, 2026 16:33
Integrate GameFinder as an internal extension that detects games across
multiple store fronts (Steam, GOG, Epic, Xbox). Game extensions can now
opt-in to automatic detection by specifying store IDs in their registration.

Changes:
- Add IGameFinderIds interface and gameFinder property to IGame
- Create src/extensions/gamefinder/ with store handlers for Steam, GOG,
  Epic, and Xbox
- Add GameFinderCache for indexed lookup by store IDs
- Hook into quickDiscovery() to check gameFinder before queryPath/queryArgs
- Add yarn detect-games CLI script for testing
- Add neverthrow and fast-xml-parser dependencies

Game extensions can now use:
  gameFinder: { steam: '1086940', gog: '1456460669' }
instead of or in addition to queryPath/queryArgs.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Introduces a new extension that allows game definitions to be expressed
in YAML format, reducing boilerplate code duplication. Store IDs are
defined once and auto-populated to queryArgs, gameFinder, details,
environment, and requiresLauncher.

Includes JSON Schema validation, transformer logic, and a sample
Skyrim SE YAML definition for demonstration.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Change to subfolder structure: games/<game-id>/game.yaml with assets
- Remove redundant requiredFiles from tools (defaults to [executable])
- Update schema to document default behavior for requiredFiles
- extensionPath now points to game subfolder for logo resolution

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy logos and game art from existing game-skyrimse extension.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Renamed CK.png -> ck.png and SKSE.png -> skse.png to avoid
case-sensitivity issues on Linux filesystems.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Schema now accepts integers for steam/gog IDs (YAML parses unquoted
  numbers as integers)
- Transformer normalizes store IDs to strings before use
- Updated sample YAML to remove all unnecessary quotes
- Registry paths no longer need escaped backslashes without quotes

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Configure InstallAssets.json to copy YAML files, JSON schema, and
image assets from src/extensions/game-definitions/ to output.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Reduces total image size from ~79KB to ~39KB (~50% reduction).
Electron/Chromium has native WebP support.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add info.json at extension root (required by registerGame)
- Update transformer to prefix logo paths with relative game directory
- Replace Ajv validation with manual validation to avoid version conflicts
- Add game-definitions to staticExtensions for proper loading
- Remove unused ajv dependency from package.json

The registerGame function overwrites game.extensionPath with the
extension's directory, so logo paths must be relative to that root
(e.g., "games/skyrimse-yaml/gameart.webp" instead of "gameart.webp").

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@halgari halgari requested review from IDCs and insomnious January 17, 2026 05:38
@Pickysaurus
Copy link
Contributor

Doesn't this remove a lot of the utility game extensions have to include additional options and parameters? The only thing I would change with the current model personally is move things like INI and plugins txt path resolution into the JS file rather than keeping it duplicated in the 7-8 gamebyro extensions.

It also seems to remove the ability to register all the other features you might want with a game. New pages, tests, notifications, steps to prepare the game for modding, toolbar buttons, etc.

I have to be honest, I don't really see the problem this is solving? What's wrong with JSON or JS? Switching to a static Yaml file removes conditional logic. Like if you need to resolve a different EXE file based on game store, locale, etc. Same with launch arguments.

I don't want to be too critical as I'm sure there's something I've missed but it seems like a heavily simplified version of something that has been working well for years. "If it ain't broke" and all that...

Please take this with a bit of a pinch of salt as I'm not a professional developer but this is just my thoughts as a third party consumer of the Vortex API

Copy link
Member

@erri120 erri120 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High level feedback to discuss:

The PR should be split into two separate PRs to reduce review overhead. One to add the gamefinder extension and one for the game-definitions extension. While they might depend on each other, they don't need to be merges as one.

For the gamefinder extension specifically, I don't think it should exist in this form. It's currently a slimmed down AI-assisted TypeScript port of my GameFinder C# library. As the author of that library, I can tell you that the GameFinder code is not worth porting over. The only worthwhile code and information from my library that could be used here are all of the edge-cases that have accumulated over the years on where to find files and how to parse them. Different default paths for Steam on various Linux installations or information like manifest version changes that require different handling.

The only "special sauce" that my library has over other game detection systems is the tight integration with the NexusMods.Paths library, which I also authored. Various abstractions over the file system and the registry allowed the GameFinder library to be used seamlessly on Linux for Windows games, something that hasn't been carried over to this new extension because we don't have that paths library for Vortex.

I have many regrets for the code I've written for my GameFinder library. There are many things I'd do differently if I were to rework it, which I've tried to do a couple times with no progress. What pains me to see is that my arguably bad decisions have now propagated into this new extension with the port.

With Vortex being the tech-debt ridden mess it is, and with the new found intent to focus development on Vortex and bring the codebase up to stuff, I don't believe we should be porting my bad decisions from three years ago to Vortex, especially since Vortex already has game stores.

Steam, EGS, GOG, Xbox, Origin, UPlay are already supported by Vortex. Code for those game stores exists in src/util for Steam and EGS, and in extensions/gamestore-* for the others. There is no point in having the same functionality twice, especially since the old code is already used and battle-tested by all Vortex users.

I believe we should merge the existing game store extensions into one core extension and get the old code updated and modernized with maybe some new APIs.

Copy link
Contributor

@IDCs IDCs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm glad that I'm not the only reviewer to highlight this - why are we re-writing core functionality that has been functioning correctly for a decade?

Surely if we want to add new functionality such as the YAML integration, or fix linux issues, we can do so within the confines of GameStoreHelper without creating an entirely new plugin/extension. Alternatively if we want a single extension to cater for all stores, we can flatten the existing gamestore extensions into a single plugin/extension.

Re-writing the whole thing is bound to raise issues we have solved years ago in the previous system.

@ChemGuy1611
Copy link

ChemGuy1611 commented Jan 19, 2026

Specifically on Skyrim SE, I noticed this method does not seem to include the stopPatterns. Those are absolutely essential for properly installing many mods. An author simply including a "Data" folder would break the mod install entirely.

This is one example of critical functionality for games that's not available in this YAML format.

I like the idea behind this new method. But typically "stub" extensions are not sufficient to support most games. Bethesda games are built with modding in mind, so they are actually quite simple extensions (just for the actual game, not LOOT, etc).

This method wouldn't work for a Unity or Unreal Engine game, for example. Too many modTypes in different paths. Most games need multiple installers with careful indexing to fix packaging issues.

@halgari
Copy link
Author

halgari commented Jan 19, 2026

Specifically on Skyrim SE, I noticed this method does not seem to include the stopPatterns. Those are absolutely essential for properly installing many mods. An author simply including a "Data" folder would break the mod install entirely.

The original code doesn't include Stop Patterns either, those are in a different extension 🤦

@halgari
Copy link
Author

halgari commented Jan 19, 2026

I'm glad that I'm not the only reviewer to highlight this - why are we re-writing core functionality that has been functioning correctly for a decade?

Surely if we want to add new functionality such as the YAML integration, or fix linux issues, we can do so within the confines of GameStoreHelper without creating an entirely new plugin/extension. Alternatively if we want a single extension to cater for all stores, we can flatten the existing gamestore extensions into a single plugin/extension.

Re-writing the whole thing is bound to raise issues we have solved years ago in the previous system.

This is a good question, my main idea is this: the original Skyrim SE plugin for this behavior is Javascript, not TS, not even well defined Javascript. So the idea here, is instead of writing TS code for every bit of functionality, for every game, we implement it as data, then we can re-interpret the pattern as we rework parts of Vortex. For the most part this work is a prototype to discuss how we could implement this behavior.

I'd love in the future to implement stop patterns (which are also mostly data) as YAML. Code rots over time, and Vortex has a lot of rotted code. Data doesn't rot, or when it does we can simply write a data transformer to translate it to a new format.

@ChemGuy1611
Copy link

Correct. Stop patterns are in a different file. My mistake. But the point is that most games are not as simple as skyrimse.

You're not wrong about Vortex having some suboptimal code, but I think using hyperbolic words like "rotted" isn't really helpful.

The basic boilerplate code on game extensions is certainly not "rotted", so I'm not sure that characterization applies to the goals of this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants