Creating a game extension for Vortex

From Nexus Mods Wiki
Revision as of 15:00, 1 August 2019 by Pickysaurus (talk | contribs) (Registering your game)
Jump to: navigation, search
Info.png
Notice
Work in progress

This guide will explain how to create a very basic game extension for Vortex, touching on where to start with some of the more advanced features. It requires a basic understanding of Javascript/programming. I also recommend you use an application with syntax highlighting, such as Visual Studio Code or Notepad++ for the coding sections of the guide.

Getting Set Up

To get started, you first need a version of Vortex to work with. I recommend using the current release of Vortex installed at the default location. If you're familiar with Github and other development tools you can also follow the instructions on the Vortex Github to clone the repository and build a development environment. To keep things simple, we'll be using the first option.

You'll also need to gather some information about the game you want to support before starting. This information will help us build our extension.

  • Which game store(s) are you able to get this game from?
  • Do the game stores have any useful meta-data we can use? (SteamApp ID, Epic store codename, GOG app ID, registry key etc)
  • What is the structure of the game directory? Where is the main EXE file?
  • Where should mods be installed? Are there multiple different ways to install mods?
  • How are mod archives usually structured? How consistent are they?

If you can't answer most of these questions, you may have problems creating a completely functional extension. For our example, we'll be using Bloodstained: Ritual of the Night. Below are example answers to the setup questions.


  • Which game store(s) are you able to get this game from?

The game is available on GOG.com and Steam.

  • Do the game stores have any useful meta-data we can use? (SteamApp ID, Epic store codename, GOG app ID, registry key etc)

Steam App ID: 692850, GOG App ID: 1133514031

  • What is the structure of the game directory? Where is the main EXE file?

The main game EXE is located at "BloodstainedROTN/Binaries/Win64/BloodstainedRotN-Win64-Shipping.exe" and there is also a launcher located at "BloodstainedRotN.exe".

  • Where should mods be installed? Are there multiple different ways to install mods?

Most mods for this game are presented as .pak files which are placed in the folder "BloodstainedRotN/Content/Paks/~mods" or "BloodstainedRotN/Content/Paks/~mod". The "~mods" option is the community standard.

  • How are mod archives usually structured? How consistent are they?

The majority of mods have the .pak files on the root level of the mod archive, however, some mod archives contain variants or are structured in different ways.

Creating your extension

A basic game extension consists of 3 files an info file, a game image and a javascript file. First, we will need a folder for our extension. If you're using a regular installation of Vortex, navigate to AppData\Roaming\Vortex\plugins. If you're using a Github repository, you can create this folder at vortex\extensions\games. For consistency, give your folder the same name as your game with no spaces, prefixed with "game-" e.g. "game-bloodstainedritualofthenight". Now, open this folder and we're ready to get set up.


For the game image, we recommend taking the game tile from Nexus Mods, however, if you're not able to do this you can use something else. Just ensure it's the same ratio as the other images inside Vortex. It's also important that you name it "gameart", as this will help us find it later.


For the info file, create a new JSON file called "info.json" and fill it with the following information:

{
  "name": "Game: Bloodstained: Ritual of the Night",
  "author": "Pickysaurus",
  "version": "0.0.1",
  "description": "Support for Bloodstained: Ritual of the Night"
}

Note: Using a version less than 1.0.0 will flag this extension as Beta in the Vortex UI.


Finally, we'll create the main javascript file called "index.js". Inside the file, add the following basic code.

//Import some assets from Vortex we'll need.
const path = require('path');
const { fs, log, util } = require('vortex-api');

function main(context) {
	//This is the main function Vortex will run when detecting the game extension. 
	
	return true
}

module.exports = {
    default: main,
  };

With this code in place, your extension will now be recognised by Vortex (a restart will be required) but does not do anything. You can check the "Extensions" tab in advanced mode to see that it is loading without errors.


Registering your game

Now we have the basic information in place, we need to tell Vortex that we're adding a new game. First, we'll want to add some additional constants to the top of the index.js.

// Nexus Mods domain for the game. e.g. nexusmods.com/bloodstainedritualofthenight
const GAME_ID = 'bloodstainedritualofthenight';

//Steam Application ID, you can get this from https://steamdb.info/apps/
const STEAMAPP_ID = 692850;

//GOG Application ID, you can get this from https://www.gogdb.org/
const GOGAPP_ID = 1133514031;

Next, add the following code to your index.js above the "return true" line inside the "main" function and change it according to the instructions below:

context.registerGame({
    id: GAME_ID,
    name: 'Bloodstained: Ritual of the Night',
    mergeMods: true,
    queryPath: findGame,
    supportedTools: [],
    queryModPath: () => 'BloodstainedRotN/Content/Paks/~mods',
    logo: 'gameart.jpg',
    executable: () => 'BloodstainedROTN.exe',
    requiredFiles: [
      'BloodstainedRotN.exe',
      'BloodstainedROTN/Binaries/Win64/BloodstainedRotN-Win64-Shipping.exe'
    ],
    setup: prepareForModding,
    environment: {
      SteamAPPId: STEAMAPP_ID.toString(),
    },
    details: {
      steamAppId: STEAMAPP_ID,
      gogAppId: GOGAPP_ID,
    },
  });


Property Description
id This can be filled with the constant we defined in the previous step.
name The full title of your game, this will be how it appears inside Vortex.
mergeMods This defines if mods will be installed to the same folder (merged) or installed to their own folders.
queryPath We want to fill this with the root directory of the game. In this example, we'll be using the "findGame" function (discussed later) to allow us to check for the correct folder.
supportedTools See: Defining tools
queryModPath This is where we tell Vortex how to find the mods folder.
logo Make sure this matches the name of your gameart file.
executable This is where we tell Vortex how to find the main game executable, so we'll be able to launch the game.
requiredFiles Fill this with an array of ket files that should be found in the game folder. Vortex will know that it has found the correct folder for the game if all requiredFiles are present.
setup This property is optional but is used if we need to make the game folder ready to accept mods. In this example, we need to create the "~mods" folder using the prepareForModding function.
environment If the game must be run through Steam you should include the SteamAppId property as shown above. This allows the game to be launched directly from the EXE file, rather than through the Steam Client. We have to convert our STEAMAPP_ID to a string as otherwise, it isn't a valid variable.
details We can store the SteamAppId/GOGAppId here in case we need it, you can also add other details which may be used by other extensions.

Game detection

Now we need to create the "findGame" function mentioned earlier. This will be what Vortex uses to discover the game during a search. You can use different methods (or a combination) to detect the game. The most common instances are SteamApp ID and registry key.

Find our game with Steam:

function findGame() {
  return util.steam.findByAppId('692850')
      .then(game => game.gamePath);
}

Find our game from the registry:

//Add this to the top of the file
const winapi = require('winapi-bindings');

function findGame() {
    const instPath = winapi.RegGetValue(
      'HKEY_LOCAL_MACHINE',
      'SOFTWARE\\WOW6432Node\\GOG.com\\Games\\' + GOGAPP_ID,
      'PATH');
    if (!instPath) {
      throw new Error('empty registry key');
    }
    return Promise.resolve(instPath.value);
}

Using both Steam and registry methods together:

//Add this to the top of the file
const winapi = require('winapi-bindings');

function findGame() {
  try {
    const instPath = winapi.RegGetValue(
      'HKEY_LOCAL_MACHINE',
      'SOFTWARE\\WOW6432Node\\GOG.com\\Games\\' + GOGAPP_ID,
      'PATH');
    if (!instPath) {
      throw new Error('empty registry key');
    }
    return Promise.resolve(instPath.value);
  } catch (err) {
    return util.steam.findByAppId('464920')
      .then(game => game.gamePath);
  }
}

Mod installation patterns

Mod installers

Publishing your extension

Advanced options

Defining tools

Requiring an external modding tool

Multiple install patterns