Skip to content
GarulfPublic

About

Steam Cloud style save sync for EmuDeck, through a server you host yourself

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

emusync

Steam Cloud style save sync for EmuDeck.

emusync keeps the emulator saves of your Linux machines in step through a small server you host yourself. It pulls the latest saves before a game starts and pushes yours after the game exits. If two machines changed the same saves, it asks which copy to keep. The server keeps the last 100 versions of each slot, so the copy you don't keep stays in history.

It covers the save folders EmuDeck links under ~/Emulation/saves/. It was built for a Steam Deck and a desktop, but any number of machines work.

Server

You need Docker on the machine that will host the saves.

git clone https://github.com/Garulf/emusync.git
cd emusync
docker compose up -d --build
docker compose exec emusync emusync admin device add steamdeck

The second command prints a token for the device. It is shown once, so copy it now. Run it once per machine, with a different name each time. emusync admin device list shows the registered devices and emusync admin device revoke <name> cuts one off.

Everything the server stores lives in the emusync-data Docker volume, mounted at /data in the container. Back that volume up. To use a host folder instead, swap in a bind mount, but create it first and give it to uid 1000 with mkdir data && sudo chown 1000:1000 data, because the container doesn't run as root.

The server speaks plain HTTP on port 8080. If the machines reach it over anything but a trusted LAN, put a reverse proxy in front for HTTPS.

Client

Install uv on each machine. On SteamOS the installer works as is, because it only writes to ~/.local.

curl -LsSf https://astral.sh/uv/install.sh | sh

Then install emusync from this repository:

uv tool install git+https://github.com/Garulf/emusync
emusync login http://your-server:8080 <token>
emusync sync
emusync hook install

login writes ~/.config/emusync/config.toml and checks the connection. The first sync pushes your saves, or pulls the server's copy if the local folder is empty. hook install adds sync calls to the EmuDeck launchers, so games sync on their own from then on.

EmuDeck rewrites its launchers when it updates. After an update, run emusync hook status and, if it reports a launcher as not hooked, run emusync hook install again.

Turn off EmuDeck CloudSync before you use this. The two would fight over the same folders, and hook install warns you if CloudSync is still enabled.

Configuration

Login is enough to get going. To change what gets synced, edit ~/.config/emusync/config.toml.

server_url = "http://saves.lan:8080"
token = "..."

emudeck_root = "~/Emulation"

[emulators.retroarch]
states = true

[emulators.dolphin]
exclude = ["Wii"]

[emulators.citra]
enabled = false

[slots."custom/some-emu"]
path = "~/somewhere/saves"

Each folder under <emudeck_root>/saves/<emulator>/ is a slot, named <emulator>/<folder>, for example pcsx2/saves. Folders named states or StateSaves are skipped unless the emulator has states = true. exclude drops folders by name, enabled = false drops the whole emulator, and a [slots."name"] section adds a folder that EmuDeck doesn't manage.

xemu and xenia are off by default, since EmuDeck links their save folders to the whole xemu disk image and to Xbox 360 DLC and title updates; set enabled = true under [emulators.xemu] or [emulators.xenia] to sync them anyway.

Commands

Command What it does
emusync login <url> <token> Save the server and token, then check the connection.
emusync status Show whether each slot is in sync, has local changes, is behind the server, or conflicts.
emusync sync [slot...] Sync every slot, or only the ones you name. --emulator <name> limits it to one emulator.
emusync history <slot> List the versions of a slot with device, time and label.
emusync restore <slot> <version> Make an old version the new head and pull it.
emusync run <emulator> -- <command...> Sync, run the command, sync again, and exit with the command's exit code.
emusync hook install|remove|status Add, remove or check the sync calls in the EmuDeck launchers.

hook install finds the emulator name in each launcher, including the ones that call emulatorInit "$emuName" like Citron and Eden. It skips launchers whose name it can't work out. Installing twice changes nothing.

run is for launch setups outside EmuDeck. It always starts the command, even when the sync before it fails, so a down server never stops you from playing.

Conflicts

A conflict means the saves changed on this machine and on the server since the last time they matched. emusync never picks a side by itself. In a terminal it asks on stdin. In Game Mode it opens a zenity dialog showing when each copy changed and which device pushed the server's. With neither available it skips the slot and leaves both copies alone.

There are three answers.

  • Keep this device. The local saves become the new server version.
  • Keep server. The local saves are stored in history as conflict-lost, the old server version becomes the new head, and this machine pulls it. This happens as one step on the server, so a crash can't leave half of it done.
  • Skip. Nothing changes, and the question comes back on the next sync.

Whichever copy loses is still in history. To get it back:

emusync history pcsx2/saves
emusync restore pcsx2/saves 7

If the server is unreachable, emusync warns and lets the game start with the local saves. The next successful sync pushes whatever changed.

About

Steam Cloud style save sync for EmuDeck, through a server you host yourself

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages