Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,27 @@ repos:
- id: ruff
args: [--extend-select, I, --fix]
- id: ruff-format
- repo: https://github.com/python-poetry/poetry-plugin-export
rev: "1.9.0"
hooks:
- id: poetry-export
args:
[
"--without=dev",
"-f",
"requirements.txt",
"-o",
"plugin-requirements.txt",
]
- repo: local
hooks:
- id: update-readme-games
name: Update list of games in README.md
entry: python
language: python
# files: ^(.*/)?\.py$
args: [pre-commit-hooks/update-readme-games.py]
additional_dependencies: [jinja2==3.1.6]

ci:
autofix_commit_msg: "[pre-commit.ci] Auto fixes from pre-commit.com hooks."
Expand Down
152 changes: 152 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@

# ModOrganizer 2 - Basic Games - How to Contribute?

## How to add a new game?

You can create a plugin by providing a python class in the `games` folder.

**Note:** If your game plugin does not load properly, you should set the log level
to debug and look at the `mo_interface.log` file.

You need to create a class that inherits `BasicGame` and put it in a `game_XX.py` in `games`.
Below is an example for The Witcher 3 (see also [games/game_witcher3.py](games/game_witcher3.py)):

```python
from PyQt6.QtCore import QDir
from ..basic_game import BasicGame


class Witcher3Game(BasicGame):

Name = "Witcher 3 Support Plugin"
Author = "Holt59"
Version = "1.0.0a"

GameName = "The Witcher 3"
GameShortName = "witcher3"
GameBinary = "bin/x64/witcher3.exe"
GameDataPath = "Mods"
GameSaveExtension = "sav"
GameSteamId = 292030

def savesDirectory(self):
return QDir(self.documentsDirectory().absoluteFilePath("gamesaves"))
```

`BasicGame` inherits `IPluginGame` so you can override methods if you need to.
Each attribute you provide corresponds to a method (e.g., `Version` corresponds
to the `version` method, see the table below). If you override the method, you do
not have to provide the attribute:

```python
from PyQt6.QtCore import QDir
from ..basic_game import BasicGame

import mobase


class Witcher3Game(BasicGame):

Name = "Witcher 3 Support Plugin"
Author = "Holt59"

GameName = "The Witcher 3"
GameShortName = "witcher3"
GameBinary = "bin/x64/witcher3.exe"
GameDataPath = "Mods"
GameSaveExtension = "sav"
GameSteamId = 292030

def version(self):
# Don't forget to import mobase!
return mobase.VersionInfo(1, 0, 0, mobase.ReleaseType.final)

def savesDirectory(self):
return QDir(self.documentsDirectory().absoluteFilePath("gamesaves"))
```

### List of valid keys

| Name | Description | `IPluginGame` method | Python |
|------|-------------|----------------------|--------|
| Name | Name of the plugin | `name` | `str` |
| Author | Author of the plugin | `author` | `str` |
| Version | Version of the plugin | `version` | `str` or `mobase.VersionInfo` |
| Description| Description (Optional) | `description` | `str` |
| GameName | Name of the game, as displayed by MO2 | `gameName` | `str` |
| GameShortName | Short name of the game | `gameShortName` | `str` |
| GameNexusName| Nexus name of the game (Optional, default to `GameShortName`) | `gameNexusName` | `str` |
| GameValidShortNames | Other valid short names (Optional) | `validShortNames` | `List[str]` or comma-separated list of values |
| GameNexusId | Nexus ID of the game (Optional) | `nexusGameID` | `str` or `int` |
| GameBinary | Name of the game executable, relative to the game path | `binaryName` | `str` |
| GameLauncher | Name of the game launcher, relative to the game path (Optional) | `getLauncherName` | `str` |
| GameDataPath | Name of the folder containing mods, relative to game folder| `dataDirectory` | |
| GameDocumentsDirectory | Documents directory (Optional) | `documentsDirectory` | `str` or `QDir` |
| GameIniFiles | Config files in documents, for profile specific config (Optional) | `iniFiles` | `str` or `List[str]` |
| GameSavesDirectory | Directory containing saves (Optional, default to `GameDocumentsDirectory`) | `savesDirectory` | `str` or `QDir` |
| GameSaveExtension | Save file extension (Optional) `savegameExtension` | `str` |
| GameSteamId | Steam ID of the game (Optional) | `steamAPPId` | `List[str]` or `str` or `int` |
| GameGogId | GOG ID of the game (Optional) | `gogAPPId` | `List[str]` or `str` or `int` |
| GameOriginManifestIds | Origin Manifest ID of the game (Optional) | `originManifestIds` | `List[str]` or `str` |
| GameOriginWatcherExecutables | Executables to watch for Origin DRM (Optional) | `originWatcherExecutables` | `List[str]` or `str` |
| GameEpicId | Epic ID (`AppName`) of the game (Optional) | `epicAPPId` | `List[str]` or `str` |
| GameEaDesktopId | EA Desktop ID of the game (Optional) | `eaDesktopContentId` | `List[str]` or `str` or `int` |

You can use the following variables for `str`:

- `%DOCUMENTS%` will be replaced by the standard *Documents* folder.
- `%GAME_PATH%` will be replaced by the path to the game folder.
- `%GAME_DOCUMENTS%` will be replaced by the value of `GameDocumentsDirectory`.

## Extra features

The meta-plugin provides some useful extra feature:

1. **Automatic Steam, GOG, Origin, Epic Games and EA Desktop detection:** If you provide
Steam, GOG, Origin or Epic IDs for the game (via `GameSteamId`, `GameGogId`,
`GameOriginManifestIds`, `GameEpicId` or `GameEaDesktopId`), the game will be listed
in the list of available games when creating a new MO2 instance (if the game is
installed via Steam, GOG, Origin, Epic Games / Legendary or EA Desktop).
2. **Basic save game preview / metadata** (Python): If you can easily obtain a picture
(file) and/or metadata (like from json) for any saves, you can provide basic save-game
preview by using the `BasicGameSaveGameInfo`. See
[games/game_witcher3.py](games/game_witcher3.py) and
[games/game_bladeandsorcery.py](games/game_bladeandsorcery.py) for more details.
3. **Basic local save games** (Python): profile specific save games, as in [games/game_valheim.py](games/game_valheim.py).
4. **Basic mod data checker** (Python):
Check and fix different mod archive layouts for an automatic installation with the proper
file structure, using simple (glob) patterns via `BasicModDataChecker`.
See [games/game_valheim.py](games/game_valheim.py) and [game_subnautica.py](games/game_subnautica.py) for an example.

Game IDs can be found here:

- For Steam on [Steam Database](https://steamdb.info/)
- For GOG on [GOG Database](https://www.gogdb.org/)
- For Origin from `C:\ProgramData\Origin\LocalContent` (.mfst files)
- For Epic Games (`AppName`) from:
- `C:\ProgramData\Epic\EpicGamesLauncher\Data\Manifests\` (.item files)
- or `C:\ProgramData\Epic\EpicGamesLauncher\UnrealEngineLauncher\LauncherInstalled.dat`
- or [Unofficial EGS ID DB](https://erri120.github.io/egs-db/)
- For Legendary (alt. Epic launcher) via command `legendary list-games`
or from: `%USERPROFILE%\.config\legendary\installed.json`
- For EA Desktop from `<EA Games install location>\<game title>\__Installer\installerdata.xml`

## Contribute

We recommend using a dedicated Python environment to write a new basic game plugins.

1. Install the required version of Python - Currently Python 3.12 (MO2 2.5).
2. Remove the repository at `${MO2_INSTALL}/plugins/basic_games`.
3. Clone this repository at the location of the old plugin (
`${MO2_INSTALL}/plugins/basic_games`).
4. Place yourself inside the cloned folder and:

```bash
# create a virtual environment (recommended)
py -3.12 -m venv .\venv
.\venv\scripts\Activate.ps1

# "install" poetry and the development package
pip install poetry
poetry install
```
164 changes: 9 additions & 155 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,10 @@ providing a very simple python class.

## How to install?

Download the archive for your MO2 version and extract it directly into your MO2 `plugins` folder.

- Mod Organizer **2.3.2**: [Download](https://github.com/ModOrganizer2/modorganizer-basic_games/releases/download/v0.0.3/basic_games-0.0.3.zip)
and extract in your `plugins/` folder (see below).
- Mod Organizer **2.4**: Basic games is included in Mod Organizer 2.4.
- If you want to use new game plugins that have not been included in the
release, [download the latest archive](https://github.com/ModOrganizer2/modorganizer-basic_games/archive/master.zip) and extract the files
in the existing `basic_games` folder, overwriting existing files.
Basic games is included in Mod Organizer 2.4, if you want to use new game plugins that
have not been included in the release,
[download the latest archive](https://github.com/ModOrganizer2/modorganizer-basic_games/archive/master.zip)
and extract the files in the existing `basic_games` folder, overwriting existing files.

**Important:** Extract the *folder* in your `plugins` folder, not the individual files. Your
`plugins` folder should look like this:
Expand All @@ -45,6 +41,8 @@ You can rename `modorganizer-basic_games-xxx` to whatever you want (e.g., `basic

## Supported games

<!-- START: GAMES -->

| Game | Author | File | Extras |
|------|--------|------|--------|
| The Binding of Isaac: Rebirth — [STEAM](https://store.steampowered.com/app/250900/The_Binding_of_Isaac_Rebirth/) |[EzioTheDeadPoet](https://github.com/EzioTheDeadPoet)|[game_thebindingofisaacrebirth.py](games/game_thebindingofisaacrebirth.py)|<ul><li>profile specific ini file</li></ul>|
Expand Down Expand Up @@ -82,152 +80,8 @@ You can rename `modorganizer-basic_games-xxx` to whatever you want (e.g., `basic
| Yu-Gi-Oh! Master Duel — [STEAM](https://store.steampowered.com/app/1449850/) | [The Conceptionist](https://github.com/the-conceptionist) & [uwx](https://github.com/uwx) | [game_masterduel.py](games/game_masterduel.py) | |
| Zeus and Poseidon — [GOG](https://www.gog.com/game/zeus_poseidon) / [STEAM](https://store.steampowered.com/app/566050/Zeus__Poseidon/) | [Holt59](https://github.com/holt59/) | [game_zeusandpoiseidon.py](games/game_zeusandpoiseidon.py) | <ul><li>mod data checker</li></ul> |

## How to add a new game?

You can create a plugin by providing a python class in the `games` folder.

**Note:** If your game plugin does not load properly, you should set the log level
to debug and look at the `mo_interface.log` file.

You need to create a class that inherits `BasicGame` and put it in a `game_XX.py` in `games`.
Below is an example for The Witcher 3 (see also [games/game_witcher3.py](games/game_witcher3.py)):

```python
from PyQt6.QtCore import QDir
from ..basic_game import BasicGame


class Witcher3Game(BasicGame):

Name = "Witcher 3 Support Plugin"
Author = "Holt59"
Version = "1.0.0a"

GameName = "The Witcher 3"
GameShortName = "witcher3"
GameBinary = "bin/x64/witcher3.exe"
GameDataPath = "Mods"
GameSaveExtension = "sav"
GameSteamId = 292030

def savesDirectory(self):
return QDir(self.documentsDirectory().absoluteFilePath("gamesaves"))
```

`BasicGame` inherits `IPluginGame` so you can override methods if you need to.
Each attribute you provide corresponds to a method (e.g., `Version` corresponds
to the `version` method, see the table below). If you override the method, you do
not have to provide the attribute:

```python
from PyQt6.QtCore import QDir
from ..basic_game import BasicGame

import mobase


class Witcher3Game(BasicGame):
<!-- END: GAMES -->

Name = "Witcher 3 Support Plugin"
Author = "Holt59"

GameName = "The Witcher 3"
GameShortName = "witcher3"
GameBinary = "bin/x64/witcher3.exe"
GameDataPath = "Mods"
GameSaveExtension = "sav"
GameSteamId = 292030

def version(self):
# Don't forget to import mobase!
return mobase.VersionInfo(1, 0, 0, mobase.ReleaseType.final)

def savesDirectory(self):
return QDir(self.documentsDirectory().absoluteFilePath("gamesaves"))
```

### List of valid keys

| Name | Description | `IPluginGame` method | Python |
|------|-------------|----------------------|--------|
| Name | Name of the plugin | `name` | `str` |
| Author | Author of the plugin | `author` | `str` |
| Version | Version of the plugin | `version` | `str` or `mobase.VersionInfo` |
| Description| Description (Optional) | `description` | `str` |
| GameName | Name of the game, as displayed by MO2 | `gameName` | `str` |
| GameShortName | Short name of the game | `gameShortName` | `str` |
| GameNexusName| Nexus name of the game (Optional, default to `GameShortName`) | `gameNexusName` | `str` |
| GameValidShortNames | Other valid short names (Optional) | `validShortNames` | `List[str]` or comma-separated list of values |
| GameNexusId | Nexus ID of the game (Optional) | `nexusGameID` | `str` or `int` |
| GameBinary | Name of the game executable, relative to the game path | `binaryName` | `str` |
| GameLauncher | Name of the game launcher, relative to the game path (Optional) | `getLauncherName` | `str` |
| GameDataPath | Name of the folder containing mods, relative to game folder| `dataDirectory` | |
| GameDocumentsDirectory | Documents directory (Optional) | `documentsDirectory` | `str` or `QDir` |
| GameIniFiles | Config files in documents, for profile specific config (Optional) | `iniFiles` | `str` or `List[str]` |
| GameSavesDirectory | Directory containing saves (Optional, default to `GameDocumentsDirectory`) | `savesDirectory` | `str` or `QDir` |
| GameSaveExtension | Save file extension (Optional) `savegameExtension` | `str` |
| GameSteamId | Steam ID of the game (Optional) | `steamAPPId` | `List[str]` or `str` or `int` |
| GameGogId | GOG ID of the game (Optional) | `gogAPPId` | `List[str]` or `str` or `int` |
| GameOriginManifestIds | Origin Manifest ID of the game (Optional) | `originManifestIds` | `List[str]` or `str` |
| GameOriginWatcherExecutables | Executables to watch for Origin DRM (Optional) | `originWatcherExecutables` | `List[str]` or `str` |
| GameEpicId | Epic ID (`AppName`) of the game (Optional) | `epicAPPId` | `List[str]` or `str` |
| GameEaDesktopId | EA Desktop ID of the game (Optional) | `eaDesktopContentId` | `List[str]` or `str` or `int` |

You can use the following variables for `str`:

- `%DOCUMENTS%` will be replaced by the standard *Documents* folder.
- `%GAME_PATH%` will be replaced by the path to the game folder.
- `%GAME_DOCUMENTS%` will be replaced by the value of `GameDocumentsDirectory`.

## Extra features

The meta-plugin provides some useful extra feature:

1. **Automatic Steam, GOG, Origin, Epic Games and EA Desktop detection:** If you provide
Steam, GOG, Origin or Epic IDs for the game (via `GameSteamId`, `GameGogId`,
`GameOriginManifestIds`, `GameEpicId` or `GameEaDesktopId`), the game will be listed
in the list of available games when creating a new MO2 instance (if the game is
installed via Steam, GOG, Origin, Epic Games / Legendary or EA Desktop).
2. **Basic save game preview / metadata** (Python): If you can easily obtain a picture
(file) and/or metadata (like from json) for any saves, you can provide basic save-game
preview by using the `BasicGameSaveGameInfo`. See
[games/game_witcher3.py](games/game_witcher3.py) and
[games/game_bladeandsorcery.py](games/game_bladeandsorcery.py) for more details.
3. **Basic local save games** (Python): profile specific save games, as in [games/game_valheim.py](games/game_valheim.py).
4. **Basic mod data checker** (Python):
Check and fix different mod archive layouts for an automatic installation with the proper
file structure, using simple (glob) patterns via `BasicModDataChecker`.
See [games/game_valheim.py](games/game_valheim.py) and [game_subnautica.py](games/game_subnautica.py) for an example.

Game IDs can be found here:

- For Steam on [Steam Database](https://steamdb.info/)
- For GOG on [GOG Database](https://www.gogdb.org/)
- For Origin from `C:\ProgramData\Origin\LocalContent` (.mfst files)
- For Epic Games (`AppName`) from:
- `C:\ProgramData\Epic\EpicGamesLauncher\Data\Manifests\` (.item files)
- or `C:\ProgramData\Epic\EpicGamesLauncher\UnrealEngineLauncher\LauncherInstalled.dat`
- or [Unofficial EGS ID DB](https://erri120.github.io/egs-db/)
- For Legendary (alt. Epic launcher) via command `legendary list-games`
or from: `%USERPROFILE%\.config\legendary\installed.json`
- For EA Desktop from `<EA Games install location>\<game title>\__Installer\installerdata.xml`

## Contribute

We recommend using a dedicated Python environment to write a new basic game plugins.

1. Install the required version of Python --- Currently Python 3.11 (MO2 2.5).
2. Remove the repository at `${MO2_INSTALL}/plugins/basic_games`.
3. Clone this repository at the location of the old plugin (
`${MO2_INSTALL}/plugins/basic_games`).
4. Place yourself inside the cloned folder and:

```bash
# create a virtual environment (recommended)
py -3.11 -m venv .\venv
.\venv\scripts\Activate.ps1
## How to add a new game?

# "install" poetry and the development package
pip install poetry
poetry install
```
See [CONTRIBUTING](./CONTRIBUTING.md).
Loading