mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-10-14 11:17:16 -04:00
fix: store installs stay shortcuts instead of becoming Steam installs (#816)
This commit is contained in:
1 parent
799be037e5
commit
9838c83fc2
4 files changed
+61
-1
No files matched your search
@@ -392,6 +392,7 @@ object AddedGames {
|
||||
.append(",\"dir\":").append(quote(g.guestDir)).append(",\"appid\":").append(g.appId)
|
||||
.append(",\"seen\":").append(SessionPrefs.addedGameExeSeen(context, g.folder.path))
|
||||
if (g.launchOptions.isNotEmpty()) json.append(",\"launch\":").append(quote(g.launchOptions))
|
||||
if (g.source != Library.ADDED) json.append(",\"store\":").append(quote(g.source))
|
||||
val art = AddedGameArt.resolve(context, g)
|
||||
val pieces = listOf("p" to art.portrait, "header" to art.header, "hero" to art.hero, "logo" to art.logo, "icon" to art.icon)
|
||||
.mapNotNull { (k, f) -> f?.let { file -> artGuestPath(context, file)?.let { k to it } } }
|
||||
|
||||
@@ -62,5 +62,6 @@ class AddedGamesStoreScanTest {
|
||||
val listing = AddedGames.writeListing(app, games).readText()
|
||||
assertTrue(listing.contains("\"name\":\"Celeste\""))
|
||||
assertTrue(listing.contains(StoreLaunch.LAUNCHER))
|
||||
assertTrue(listing.contains("\"store\":\"${Store.EPIC.id}\""))
|
||||
}
|
||||
}
|
||||
@@ -62,6 +62,12 @@ def source_stamp(game):
|
||||
return None
|
||||
|
||||
|
||||
def store_game(game):
|
||||
"""Games a store client installed belong to that store: the Steam copy of the same title is
|
||||
another license with its own files, and an import would let Steam update over the store's."""
|
||||
return bool(game.get("store"))
|
||||
|
||||
|
||||
def manifest_ids(steamapps, name):
|
||||
matches = []
|
||||
for manifest in Path(steamapps).glob("appmanifest_*.acf"):
|
||||
@@ -178,7 +184,7 @@ def unimported(steam_root, snapshot, candidates):
|
||||
|
||||
|
||||
def watch(steam_root, listing):
|
||||
games = read_json(listing, [])
|
||||
games = [game for game in read_json(listing, []) if not store_game(game)]
|
||||
if os.environ.get("BL_STEAM_OFFLINE") == "1" or not games:
|
||||
return
|
||||
candidates, sources = {}, {}
|
||||
@@ -363,6 +369,22 @@ def seed_install(steam_root, game, appid):
|
||||
return True
|
||||
|
||||
|
||||
def release(steam_root, game):
|
||||
"""Undoes an import made before store games were skipped. Only our symlink and manifest go;
|
||||
the store's folder stays where it is."""
|
||||
steamapps = Path(steam_root) / "steamapps"
|
||||
sources = {game.get("folder", ""), legacy_path(game.get("folder", ""))}
|
||||
for target in sorted((steamapps / "common").glob("DroidDeck-*")):
|
||||
appid = target.name.removeprefix("DroidDeck-")
|
||||
if not (appid.isdigit() and target.is_symlink() and os.readlink(target) in sources):
|
||||
continue
|
||||
for manifest in (steamapps / f"appmanifest_{appid}.acf", steamapps / PARKED / f"appmanifest_{appid}.acf"):
|
||||
if manifest.is_file() and ours(manifest, appid):
|
||||
manifest.unlink()
|
||||
target.unlink()
|
||||
print(f"droiddeck-steam-games: {game.get('name', appid)} came from {game['store']}; it is a shortcut again, not Steam app {appid}")
|
||||
|
||||
|
||||
def route(steam_root, acct, games):
|
||||
snapshot = read_json(Path(acct) / "config" / STATE, {})
|
||||
if not isinstance(snapshot, dict):
|
||||
@@ -379,6 +401,13 @@ def route(steam_root, acct, games):
|
||||
except OSError as error:
|
||||
print(f"droiddeck-steam-games: set-aside installs not restored ({error})")
|
||||
for game in games:
|
||||
if store_game(game):
|
||||
try:
|
||||
release(steam_root, game)
|
||||
except OSError as error:
|
||||
print(f"droiddeck-steam-games: store import not undone ({error})")
|
||||
shortcuts.append(game)
|
||||
continue
|
||||
folder = game.get("folder", "")
|
||||
appid = candidates.get(folder, candidates.get(legacy_path(folder)))
|
||||
stamp = source_stamp(game)
|
||||
|
||||
@@ -55,6 +55,35 @@ class ImportsTest(unittest.TestCase):
|
||||
self.assertEqual(self.folder, (self.steam / 'steamapps/common/DroidDeck-42').resolve())
|
||||
self.assertEqual(b'game', self.exe.read_bytes())
|
||||
|
||||
def test_store_installs_stay_shortcuts_even_when_owned_on_steam(self):
|
||||
self.snapshot()
|
||||
game = dict(self.game, store='amazon')
|
||||
games, routes = imports.route(self.steam, self.acct, [game])
|
||||
self.assertEqual([game], games)
|
||||
self.assertEqual({}, routes)
|
||||
self.assertFalse((self.steam / 'steamapps/appmanifest_42.acf').exists())
|
||||
self.assertFalse(os.path.lexists(self.steam / 'steamapps/common/DroidDeck-42'))
|
||||
|
||||
def test_store_installs_imported_earlier_are_released(self):
|
||||
self.snapshot()
|
||||
imports.route(self.steam, self.acct, [self.game])
|
||||
unrelated = self.steam / 'steamapps/appmanifest_7.acf'
|
||||
unrelated.write_text('"AppState"\n{\n "appid" "7"\n "installdir" "Other"\n}\n')
|
||||
games, routes = imports.route(self.steam, self.acct, [dict(self.game, store='epic')])
|
||||
self.assertEqual({}, routes)
|
||||
self.assertEqual(1, len(games))
|
||||
self.assertFalse((self.steam / 'steamapps/appmanifest_42.acf').exists())
|
||||
self.assertFalse(os.path.lexists(self.steam / 'steamapps/common/DroidDeck-42'))
|
||||
self.assertTrue(unrelated.is_file())
|
||||
self.assertEqual(b'game', self.exe.read_bytes())
|
||||
|
||||
def test_watch_never_looks_up_store_installs(self):
|
||||
listing = self.root / 'games.json'
|
||||
listing.write_text(json.dumps([dict(self.game, store='gog')]))
|
||||
with patch.object(imports, 'identify') as identify:
|
||||
imports.watch(str(self.steam), str(listing))
|
||||
identify.assert_not_called()
|
||||
|
||||
def test_migrates_only_unchanged_legacy_import_manifests(self):
|
||||
self.snapshot()
|
||||
imports.route(self.steam, self.acct, [self.game])
|
||||
|
||||
Reference in new issue
Block a user