I've been managing multiple instances of factorio for a while (for multiple servers with different versions and mod packs), and recently discovered this tool. While super-awesome (and having quite high-quality code, kudos to @mickael9), it doesn't quite fit my use case. I want to write a PR to manage multiple instances of factorio (call it prefixes, environments, modpacks, whatever), but anything this sweeping should involve some input from the original devs and community, not just having me vomit code everywhere (and having this wall of text under a comment seemed bad). If the response here is negative, I will try to oblige users' opinions or else keep a separate fork under a different name.
Desired features:
Support switching prefixes, which each have a completely different set of mods, but may or may not share game versions. This is selected by one of eg. fac activate <prefix> or fac -p <prefix> <command> (discussed below). The specification of a game version is optional, so prefixes can follow the default version to update many prefixes at once.
Some automated way of creating prefixes, including potentially downloading a specific version of factorio.
(Pipe dream) fac clone <server-url> to automatically generate or update a prefix and download the server's version of factorio and all mods that it is running. I've reverse-engineered enough of the factorio wire protocol to actually make this feasible, but the previous points must be addressed before this.
Points of Debate:
Selecting a Prefix:
Command line flag fac -p <prefix> <command>
This is the easiest to implement by far, but involves using a flag for every invocation of fac. This must be added every time, and missing the flag will result in applying the command to the default prefix. It is, however, very obvious and requires no magic or bash/zsh wizardry.
Prefix activation via shell fac activate <prefix>
This is the most convenient, but relies on the user having a compatible shell and involves additional setup. We'd need to force users to include the zsh script (and probably introduce a bash equivalent for compatibility). The active prefix is stored in an environment variable, and is unique per shell session. We also lose all hope of including windows users in a convenient fashion.
Prefix activation via file fac activate <prefix>
Like 2, but doesn't use shell functions but rather saves the current prefix to the config file. Far more compatible, but it introduces some non-obvious behavior since the prefix is global (per-user) rather than session-specific.
I am personally in favor of implementing both 1 and 2. Comments welcome!
Storing prefix configuration
Python's ConfigParser (what's currently used) has some really nice features for default and fallback values that can be used very well for prefixes..... but the current config file layout won't work well for this, since it wasn't designed with this in mind.
Note: things of the syntax ${foo} are left as-is in the config.ini and are handled automatically since Python is awesome.
Current Layout
[mods]
hold =
[paths]
data-path =
write-path =
Tack it on and handle defaults manually
[mods]
hold =
[paths]
data-path =
write-path =
[some_prefix]
write-path =
hold =
data-path is noted to be missing, and manually read from [paths]
Each prefix gets a new section, and the old sections (mods/paths) stay. We'd need to manually handle fallbacks to default, though, which means special-casing each current variable. Alternatively, require all variables in every prefix (defaults looking like `data-path=${paths:data-path}`)
2. Change to let ConfigParser handle everything
New:
auto-generated during first run.
[DEFAULTS]
hold =
data-path =
write-path =
[some_prefix]
write-path =
hold =
data-path falls back to DEFAULTS
Migrated:
From previous version
[mods]
hold =
[paths]
data-path =
write-path =
auto-generated during migration.
[DEFAULTS]
hold = ${mods:hold}
data-path = ${paths:data-path}
write-path = ${paths:write-path}
[some_prefix]
write-path =
hold =
data-path falls back to DEFAULTS, which points to [paths]
Each prefix gets a new section, and the old sections (mods/paths) stay. More future-proof and less code bloat, but we'd need to add the DEFAULT section with pointers to migrate existing config files.
## What should be shared between prefixes?
Should settings be different between them (ie, separate write-path?). If factorio introduces another breaking change in config/ (last was 0.14 -> 0.15, last I checked), things will get migrated without warning and older versions won't be happy. Factorio is backwards-compatible, but not always forwards-compatible. Ditto for save files, although those should only occur on opening the save. Presumably, this should be able to be manually selected (ie. separate mod-path from write-path), but should the default be separate or combined?
## New variables and defaults
No matter the defaults above, some new variables should be included:
- `executable-path`: Path to factorio binary (since we have to have a run-from-prefix option, yeah?). Optionally overridden per prefix. Default: `${data-path}/../bin/x64/factorio[.exe]`
- `mod-path`: Path to mods directory. Should definitely be overridden per prefix. Default: `${prefix-base-path}/[prefix-name]/mods`
- `prefix-base-path`: Prefix storage directory, relative to fac config directory. Global. Default `prefixes/`
- `game-base-path`: Factorio version storage directory, relative to fac config directory. Useful if you want to store it on a different drive. Global. Default `game_versions/`
- `username`: Factorio credentials, used for game downloads. Global and optional.
- `password`: Factorio credentials, used for game downloads. Global and optional. Also insecure, but hey, it's convenient.
- `password_cmd`: Command to obtain the factorio password, presumably through some password manager or the system keychain. Mac users may get a default using `/usr/bin/security`, but someone will have to help testing there, since I don't own one.
This leaves the following directory layout. Thoughts?
## Commands
The PR is going to add a lot of subcommands:
- Creating prefixes
- Listing prefixes
- Removing prefixes
- Switching to prefixes (depending on answers above)
- Running the game in a prefix
- Downloading/Updating current factorio version
- Listing installed factorio versions
- Removing a factorio version (or all unused)
- (Eventually) syncing prefix from multiplayer server
That leaves a whopping 23 subcommands, and things like 'install' will get pretty ambiguous pretty quick. Is a restructure necessary ("fac mods enable modname", "fac game update 0.14", "fac prefix create prefixname", etc?). If so, this is a very breaking change. I'm going to use commands assuming no restructure like "fac makeprefix" in the meantime, but this should be considered.
I've been managing multiple instances of factorio for a while (for multiple servers with different versions and mod packs), and recently discovered this tool. While super-awesome (and having quite high-quality code, kudos to @mickael9), it doesn't quite fit my use case. I want to write a PR to manage multiple instances of factorio (call it prefixes, environments, modpacks, whatever), but anything this sweeping should involve some input from the original devs and community, not just having me vomit code everywhere (and having this wall of text under a comment seemed bad). If the response here is negative, I will try to oblige users' opinions or else keep a separate fork under a different name.
Desired features:
fac activate <prefix>
orfac -p <prefix> <command>
(discussed below). The specification of a game version is optional, so prefixes can follow the default version to update many prefixes at once.fac clone <server-url>
to automatically generate or update a prefix and download the server's version of factorio and all mods that it is running. I've reverse-engineered enough of the factorio wire protocol to actually make this feasible, but the previous points must be addressed before this.Points of Debate:
Selecting a Prefix:
Command line flag
fac -p <prefix> <command>
This is the easiest to implement by far, but involves using a flag for every invocation offac
. This must be added every time, and missing the flag will result in applying the command to the default prefix. It is, however, very obvious and requires no magic or bash/zsh wizardry.Prefix activation via shell
fac activate <prefix>
This is the most convenient, but relies on the user having a compatible shell and involves additional setup. We'd need to force users to include the zsh script (and probably introduce a bash equivalent for compatibility). The active prefix is stored in an environment variable, and is unique per shell session. We also lose all hope of including windows users in a convenient fashion.fac activate <prefix>
Like 2, but doesn't use shell functions but rather saves the current prefix to the config file. Far more compatible, but it introduces some non-obvious behavior since the prefix is global (per-user) rather than session-specific.I am personally in favor of implementing both 1 and 2. Comments welcome!
Storing prefix configuration
Python's ConfigParser (what's currently used) has some really nice features for default and fallback values that can be used very well for prefixes..... but the current config file layout won't work well for this, since it wasn't designed with this in mind.
Note: things of the syntax
${foo}
are left as-is in the config.ini and are handled automatically since Python is awesome.Current Layout
[paths] data-path = write-path =
[some_prefix] write-path = hold =
data-path is noted to be missing, and manually read from [paths]
auto-generated during first run.
[DEFAULTS] hold = data-path = write-path =
[some_prefix] write-path = hold =
data-path falls back to DEFAULTS
From previous version
[mods] hold =
[paths] data-path = write-path =
auto-generated during migration.
[DEFAULTS] hold = ${mods:hold} data-path = ${paths:data-path} write-path = ${paths:write-path}
[some_prefix] write-path = hold =
data-path falls back to DEFAULTS, which points to [paths]
FAC_DIR/ |--- config.ini |--- prefixes/ (default prefix-base-path) | |--- prefix_1/ (separate game config) | | |--- mods/ | | |--- player_data.json | | |--- config/ | |--- prefix_2/ (combined game config) | |--- mods/ |--- game_versions/ (default game-base-path) |--- 0.15.4/ |--- 0.16.20/ |--- 0.16.20-headless/