diff --git a/AGENTS.md b/AGENTS.md index 8ad98ea..ec51064 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -588,14 +588,17 @@ Estado atual: - O servico `game-save-ludusavi-backup.service` executa Ludusavi em modo CLI. - Os backups locais ficam em `~/Backups/game-saves/ludusavi`. - O formato usado e `simple`, com retencao de 14 backups completos e 14 diferenciais por jogo. -- O backup roda uma segunda passada do Ludusavi com o Wine prefix `~/Games/none` apenas para jogos selecionados do Lutris: `Cloudpunk`, `Cyberpunk 2077`, `Dreamcore` e `Outer Wilds`. +- Para jogos do Lutris no prefix `~/Games/none`, o servico tambem cria snapshots locais de `~/Games/none/drive_c/users/steamuser`. +- Esses snapshots ficam em `~/Backups/game-saves/lutris-wine-userdata/snapshots`, com `latest` apontando para o snapshot mais recente. +- Os snapshots do Lutris usam hardlinks via `rsync --link-dest` e mantem os 14 mais recentes. Cuidados: - Este fluxo e apenas local; nao ha Restic, OCI ou outro destino remoto para saves de jogos no estado atual. - Se adicionar backup remoto depois, mantenha segredos fora do repo e prefira um repo separado dos backups do VPS. - Nao faca backup bruto de `~/.local/share/Steam/steamapps`; prefira Ludusavi e excecoes pontuais para jogos nao detectados. -- Nao use `--wine-prefix ~/Games/none` em uma varredura geral sem nomes de jogos; isso gera muitos falsos positivos por causa dos arquivos de registro do prefix. +- Nao use `--wine-prefix ~/Games/none` em uma varredura geral do Ludusavi sem nomes de jogos; isso gera muitos falsos positivos por causa dos arquivos de registro do prefix. +- Se adicionar jogos novos ao Lutris usando o mesmo prefix `~/Games/none`, eles entram automaticamente no snapshot local do Lutris, mesmo que o Ludusavi nao conheca o jogo. ## Secrets e variaveis de ambiente diff --git a/home/ltadeu6.nix b/home/ltadeu6.nix index 6845a19..fbb294d 100644 --- a/home/ltadeu6.nix +++ b/home/ltadeu6.nix @@ -3,12 +3,9 @@ let gameSaveBackupRoot = "${config.home.homeDirectory}/Backups/game-saves"; gameSaveLudusaviPath = "${gameSaveBackupRoot}/ludusavi"; - gameSaveLutrisGames = [ - "Cloudpunk" - "Cyberpunk 2077" - "Dreamcore" - "Outer Wilds" - ]; + gameSaveLutrisWinePrefix = "${config.home.homeDirectory}/Games/none"; + gameSaveLutrisUserPath = "${gameSaveLutrisWinePrefix}/drive_c/users/steamuser"; + gameSaveLutrisBackupPath = "${gameSaveBackupRoot}/lutris-wine-userdata"; gameSaveLudusaviBackup = pkgs.writeShellScript "game-save-ludusavi-backup" '' set -eu @@ -23,16 +20,37 @@ let --differential-limit 14 \ --force - exec ${pkgs.ludusavi}/bin/ludusavi \ - --try-manifest-update \ - backup \ - --path ${lib.escapeShellArg gameSaveLudusaviPath} \ - --wine-prefix ${lib.escapeShellArg "${config.home.homeDirectory}/Games/none"} \ - --format simple \ - --full-limit 14 \ - --differential-limit 14 \ - --force \ - ${lib.concatMapStringsSep " " lib.escapeShellArg gameSaveLutrisGames} + if [ ! -d ${lib.escapeShellArg gameSaveLutrisUserPath} ]; then + echo "Skipping Lutris Wine user-data snapshot: missing ${gameSaveLutrisUserPath}" >&2 + exit 0 + fi + + snapshot_root=${lib.escapeShellArg "${gameSaveLutrisBackupPath}/snapshots"} + snapshot="$snapshot_root/backup-$(${pkgs.coreutils}/bin/date -u +%Y%m%dT%H%M%SZ)" + latest_link=${lib.escapeShellArg "${gameSaveLutrisBackupPath}/latest"} + link_dest_args=() + + ${pkgs.coreutils}/bin/mkdir -p "$snapshot_root" + + if [ -L "$latest_link" ]; then + latest_target="$(${pkgs.coreutils}/bin/readlink -f "$latest_link" || true)" + if [ -n "$latest_target" ] && [ -d "$latest_target" ]; then + link_dest_args=(--link-dest "$latest_target") + fi + fi + + ${pkgs.rsync}/bin/rsync -a --delete \ + "''${link_dest_args[@]}" \ + ${lib.escapeShellArg "${gameSaveLutrisUserPath}/"} \ + "$snapshot/" + ${pkgs.coreutils}/bin/ln -sfn "$snapshot" "$latest_link" + + ${pkgs.findutils}/bin/find "$snapshot_root" -mindepth 1 -maxdepth 1 -type d \ + -printf '%T@ %p\n' \ + | ${pkgs.coreutils}/bin/sort -rn \ + | ${pkgs.coreutils}/bin/tail -n +15 \ + | ${pkgs.gnused}/bin/sed 's/^[^ ]* //' \ + | ${pkgs.findutils}/bin/xargs -r ${pkgs.coreutils}/bin/rm -rf ''; opencodeWithLibstdcxx = pkgs.symlinkJoin { name = "opencode";