Jump to command-line mode or building with Docker.
gamatrix is a tool to compare the games owned by several users, and list all the games they have in common. It requires all users to use GOG Galaxy; since GOG Galaxy supports almost all major digital distribution platforms through integrations, it's a great service for aggregating your games in one place. gamatrix uses the sqlite database that GOG Galaxy stores locally to pull its data from; users can upload their DBs via the main page or with a provided script.
- compares the game libraries of an arbitrary number of users, with several filtering options
- multiplayer support and max players autopopulated from IGDB when available
- option to pick a random game
- configuration via YAML file and/or command-line options
- small (<150MB) Docker container
- IP whitelisting support
- ability to upload DBs
Select your search criteria from the front page:
Upload a new GOG DB. Only files with a .db
extension are allowed; the requesting IP is used to identify the uploader and name the target file correctly. To upload from a script, you can download curl for Windows and run the following batch script, replacing your-gamatrix-url
with your server's DNS name or IP address:
curl -F file=@"C:\ProgramData\GOG.com\Galaxy\storage\galaxy-2.0.db" http://<your-gamatrix-url>/compare?option=upload
pause
The Game list
option provides a list of games owned by the selected users:
- titles supporting fewer players than selected are greyed out
- under
Installed
, a check mark indicates all players have the game installed; otherwise the names (or profile pics, if available) of the users that have the game installed are shown
The Game grid option shows all games owned by the selected users
- green cells indicate the user owns the game, red indicates they don't
- a check mark means the user has the game installed
gamatrix
Show and compare between games owned by multiple users.
Usage:
gamatrix.py --help
gamatrix.py --version
gamatrix.py [--config-file=CFG] [--debug] [--all-games] [--interface=IFC] [--installed-only] [--include-single-player] [--port=PORT] [--server] [--update-cache] [--userid=UID ...] [<db> ... ]
Options:
-h, --help Show this help message and exit.
-v, --version Print version and exit.
-c CFG, --config-file=CFG The config file to use.
-d, --debug Print out verbose debug output.
-a, --all-games List all games owned by the selected users (doesn't include single player unless -S is used).
-i IFC, --interface=IFC The network interface to use if running in server mode; default is 0.0.0.0.
-I, --installed-only Only show games installed by all users.
-p PORT, --port=PORT The network port to use if running in server mode; default is 8080.
-s, --server Run in server mode.
-S, --include-single-player Include single player games.
-U, --update-cache Update cache entries that have incomplete info.
-u USERID, --userid=USERID The GOG user IDs to compare, there can be multiples of this switch.
Positional Arguments:
<db> The GOG DB for a user, multiple can be listed.
db
: a GOG database to use. You can usually find a user's DB in C:\ProgramData\GOG.com\Galaxy\storage\galaxy-2.0.db
. Multiple DBs can be listed. Not compatible with -u
.
-a/--all-games
: list all the multiplayer games owned by the selected users (user selection is covered below). Use with -I
to include single-player games too.
-c/--config-file
: the YAML config file to use. You don't need a config file, but you'll likely want one. See configuration for details.
-d/--debug
: enable debug messages.
-s/--server
: run in server mode. This will use Flask to serve a small web page where you can select the options you want, and will output the results there.
-S/--include-single-player
: include single-player games; by default, only multiplayer games are shown.
-u/--userid
: a list of GOG user IDs to compare. The IDs must be in the config file. You can find the user ID by running sqlite3 /path/to/galaxy-2.0.db "select * from Users;"
. If you use this option, you can't list DBs; they must be provided for the user IDs in the config file.
-U/--update-cache
: pull info from IGDB for titles that previously got incomplete data, e.g. no info on multiplayer support, or no max player data. For instance, if you contribute max player info for a title in IGDB, once it's approved you can use this to pull it into your cache.
Command-line mode lists the output to a terminal, and doesn't use Flask. It's a good way to get copy/pastable titles to put into the config file for titles you want to hide or add metadata for. This mode is not developed as heavily as server mode, so may lag behind in features. To run, clone this repo and:
1. Setup your virtual environment:
python3 -m venv venv
. venv/bin/activate
For our Windows friends:
py -3 -m venv venv
.venv\Scripts\Activate.ps1
2. Install dependencies:
python -m pip install -U pip
python -m pip install -r requirements.txt
./gamatrix.py
Note: Python 3.7+ is recommended. Dictionaries are assumed to be ordered, which is a 3.7+ feature.
If you use the -s
option or set mode: server
in the config file, a Flask web server is started and runs until the script is killed. This serves a web page with a check box for all users defined in the config file; users can select who they want to compare and a table is generated for the matching games. There is also a game grid layout that shows all games with color-coded cells indicating who owns which games.
Server mode is the intended use case, and supports all options, unlike CLI mode, which may not.
A YAML file provides the runtime configuration; by default, this is config.yaml
in the same directory as the script, but this can be overridden with the -c
option. See the annotated sample file or the Windows flavour of this file for an explanation of the format.
IGDB will be used to pull multiplayer info if you have the needed credentials. See this page for instructions on getting a client ID and secret, and put these in your config file as igdb_client_id
and igdb_client_secret
. Once this is set up, IGDB will be checked for all titles the first time they're processed, and if available, will categorize the title as multiplayer or single player, and set the maximum players. Note that this takes about a second per title, so the first time you use it, it can take a long time. The data is saved to disk in a cache file, which is read each time gamatrix is launched, so once the cache is populated it's quite fast.
gamatrix respects the IGDB rate limit and auto-renews your access token, so once you set your ID and secret in your config you should be good to go. If you have issues, debug by running in CLI mode with the -d
option.
A Dockerfile is provided for running gamatrix in a container. Build it with:
docker build -t gamatrix .
Then run it:
Linux/MacOS:
docker run -d --name gamatrix -p 8080:80/tcp \
-e TZ="America/Vancouver" \
-v /path/to/gog_dbs:/usr/src/app/gog_dbs \
--mount type=bind,source=/path/to/.cache.json,target=/usr/src/app/.cache.json \
--mount type=bind,source=/path/to/config.yaml,target=/usr/src/app/config/config.yaml,readonly \
gamatrix
The value of TZ
should be a valid time zone in /usr/share/zoneinfo
; this time zone will be used when showing the timestamps of the DBs on the main page. If it's unset or not set correctly, UTC will be used.
Windows:
C:\Users\me> docker --name gamatrix -p 8080:80/tcp -v C:\Users\me\dev\gamatrix-dbs:/usr/src/app/gog_dbs -v C:\Users\me\dev\gamatrix\.cache.json:/usr/src/app/.cache.json -v C:\Users\me\dev\gamatrix\derek-config.yaml:/usr/src/app/config/config.yaml gamatrix
Now you should be able to access the web page. If not, use docker logs
to see what went wrong. The DBs are read on every call, so you can update them and they'll be used immediately. If you change the config file you'll need to restart the container for it to take effect.
Flask is not a production-grade web server, and some people may not like their GOG DBs being exposed to the open Internet (I'm not aware of anything more personal in there than user IDs and game info, but I have not exhaustively checked by any means, so better safe than sorry). If you want to make the service only accessible to your friends, you have a couple options.
You can add CIDRs to allowed_cidrs
in the config file, as shown in the sample config. If this is used, any IP not in those CIDR blocks will get a 401 Unauthorized. If this is not defined or is empty, all IPs are allowed.
You can also block access with iptables. Network access is best handled at the network layer, not the application layer, so this is the more secure method, but more complicated. The example below is for Ubuntu 20.04, but should work for just about any Linux distribution.
Create a new chain called gamatrix:
# iptables -N gamatrix
Allow access to your friends' IPs, and your own internal network:
# for cidr in 192.168.0.0/24 1.2.3.4 5.6.7.8; do iptables -A gamatrix --src $cidr -j ACCEPT ; done
Reject everyone else that tries to reach gamatrix (be sure to use the right port):
# iptables -A gamatrix -m tcp -p tcp --dport 80 -j REJECT
Return to the calling chain if none of the rules applied:
# iptables -A gamatrix -j RETURN
Finally, insert your new chain into the DOCKER-USER
chain:
# iptables -I DOCKER-USER 1 -j gamatrix
You final configuration should look like this:
# iptables -L DOCKER-USER
Chain DOCKER-USER (1 references)
target prot opt source destination
gamatrix all -- anywhere anywhere
RETURN all -- anywhere anywhere
# iptables -L gamatrix
Chain gamatrix (1 references)
target prot opt source destination
ACCEPT all -- 192.168.0.0/24 anywhere
ACCEPT all -- 1.2.3.4 anywhere
ACCEPT all -- 5.6.7.8 anywhere
REJECT tcp -- anywhere anywhere tcp dpt:http
RETURN all -- anywhere anywhere
Save it so it persists after reboot:
# apt install iptables-persistent
# iptables-save > /etc/iptables/rules.v4
Now you can open the port on your router. For more information on using iptables with Docker, see here.
PR's welcome! If you're making nontrivial changes, please include test output if possible. Update version.py following SemVer conventions.