Skip to content

Instantly share code, notes, and snippets.

@BinToss
Last active April 22, 2026 05:03
Show Gist options
  • Select an option

  • Save BinToss/c7bf588f9ba31d16effbb6de8f082727 to your computer and use it in GitHub Desktop.

Select an option

Save BinToss/c7bf588f9ba31d16effbb6de8f082727 to your computer and use it in GitHub Desktop.
Rustique game-version wrappers

Background

When Vintage Story 1.22.0 released on April 21st, 2026, I realized my preferred mod manager, Rustique, had an inconvenient solution to switching between game versions e.g. rustique config set --pinned-game-version 1.22.0

I spent that afternoon and evening writing Fish functions to wrap rustique commands for Vintage Story 1.21.x and 1.22.x. Two scripts were the result, but it took only another hour to merge two and make the result argument-driven.

This time would have been better spent contributing a solution upstream so Rustique users can specify pinned_game_version on a per-mod-dir basis.

Examples

> cat ~/.config/rustique/config.toml | grep pinned_game_version
pinned_game_version = "1.21.7"
> rustique_vs 1.22 --mods-dir ~/Games/VintageStory/VSInstallations/1.22.x-latest/Mods/ install betterruins
╭────────────────────────────╮
  Installing..   betterruins  
╰────────────────────────────╯
  [00:00:01] [████████████████████] 1/1 Fetch Complete                                                                                        
  [00:00:05] [████████████████████] 1/1 Finished installing mods                                                                              ╭────────────────────────┬───────╮
│ Successfully Installed ┆       │
╞════════════════════════╪═══════╡
│ BetterRuins            ┆ 0.6.0 │
╰────────────────────────┴───────╯
╭──────────────────────────╮
│ Total mods Installed   1 │
╰──────────────────────────╯
╭────────────────────────────────────────────────────────────────────────────────╮
  Syncing...   /home/noah/Games/VintageStory/VSInstallations/1.22.x-latest/Mods/  
╰────────────────────────────────────────────────────────────────────────────────╯
  [00:00:01] [████████████████████] 1/1 Fetch Complete                                                                                        ╭────────────────────────────────────╮
  Sync operation completed:    1.91s  
╰────────────────────────────────────╯
╭───────────────────────────────────────╮
  Install operation completed:    8.94s  
╰───────────────────────────────────────╯

Changelog

1.0.2

Fixed rustique 1.22 assigning the full list of 1.22.x versions to pinned_game_version. __get_latest_for incorrectly return a list instead of a string because it echo two strings and both are "returned". The root cause was a leftover echo $allVersions which was used for troubleshooting.

1.0.1

Fixed a literal string "1.21" where $game_major_minor was supposed to be referenced. rustique_vs 1.22 ... no longer sets the game version to 1.21.x.

1.0.0

Initial Release. It's clunky, but functional. I haven't figured out how to properly add a named --game-version parameter. So, the first argument should always be MAJOR.MINOR unless you're setting the global --pinned-game-version. In that case, it can be omitted.

#!/bin/fish
# ~/.config/fish/functions/rustique_vs.fish
set configPath $(realpath $HOME/.config/rustique/config.toml)
set pinned_game_version $(pcregrep -oe '(?<=^pinned_game_version = ").+(?="$)' "$configPath")
function rustique_vs -w rustique -a game_major_minor
# don't parse args if the user is setting the global game version
if [ -n "$game_major_minor" ] && string match -qr -- "config set --pin-game-version" $argv
rustique $argv
exit $status
end
# Ensure arg --game_major_minor matches pattern MAJOR.MINOR
if not string match -qr '\d+\.\d+' $game_major_minor
echo 'Argument game_major_minor must be of pattern \'\d+\.\d+\' e.g. 1.22'
return 1
end
set argv $argv[2..]
__rustique_set_vs $game_major_minor &&
rustique $argv
set __status $status
__rustique_restore_pinned_game_version
return $(math max $__status,$status)
end
function __rustique_restore_pinned_game_version
sed -ri "s/pinned_game_version = \".+\"/pinned_game_version = \"$pinned_game_version\"/" -i "$configPath"
return $status
end
function __get_latest_for -a game_major_minor
if ! string match -qr '\d+\.\d+' $game_major_minor
echo 'Argument game_major_minor must be of pattern \'\d+\.\d+\' e.g. 1.22'
exit 1
end
set allVersions $(
jq -r -- '.game_versions[]' "$HOME/.config/rustique/game-versions.json" |
string match -aer -- $game_major_minor |
sort -V
)
set highestTriple $(
echo $allVersions[-1] |
string match --regex -- '\d+\.\d+\.\d+'
)
######
## Select the most stable release matching in the $highestTriple version's range.
## e.g. stable > rc > pre > dev
## alpha, beta, et cetera are not used.
######
# if a stable version is available, return it
if string match -qr -- "^$highestTriple\$" $allVersions
echo $highestTriple
return 0
end
if [ ! -n $allVersions[-1] ]
echo "Run `rustique sync -g` before trying again."
exit 1
end
# A stable release for the latest x.y channel is unavailable, so we
# return the latest prerelease. The `sort` command sorts "rc", "pre", and
# "dev" which allows us to simply return the last version.
echo $allVersions[-1]
return 0
end
function __rustique_set_vs -a game_major_minor
if ! string match -qr '\d+\.\d+' $game_major_minor
echo 'Argument game_major_minor must be of pattern \'\d+\.\d+\' e.g. 1.22'
exit 1
end
set -l latest $(__get_latest_for $game_major_minor)
if string match -q -- $latest $pinned_game_version
echo "Already managing VS $latest"
return 0
end
sed -ri "s/^pinned_game_version = \".+\"\$/pinned_game_version = \"$latest\"/" -i "$configPath"
return $status
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment