mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-10-14 19:27:09 -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(",\"dir\":").append(quote(g.guestDir)).append(",\"appid\":").append(g.appId)
|
||||||
.append(",\"seen\":").append(SessionPrefs.addedGameExeSeen(context, g.folder.path))
|
.append(",\"seen\":").append(SessionPrefs.addedGameExeSeen(context, g.folder.path))
|
||||||
if (g.launchOptions.isNotEmpty()) json.append(",\"launch\":").append(quote(g.launchOptions))
|
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 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)
|
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 } } }
|
.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()
|
val listing = AddedGames.writeListing(app, games).readText()
|
||||||
assertTrue(listing.contains("\"name\":\"Celeste\""))
|
assertTrue(listing.contains("\"name\":\"Celeste\""))
|
||||||
assertTrue(listing.contains(StoreLaunch.LAUNCHER))
|
assertTrue(listing.contains(StoreLaunch.LAUNCHER))
|
||||||
|
assertTrue(listing.contains("\"store\":\"${Store.EPIC.id}\""))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -62,6 +62,12 @@ def source_stamp(game):
|
|||||||
return None
|
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):
|
def manifest_ids(steamapps, name):
|
||||||
matches = []
|
matches = []
|
||||||
for manifest in Path(steamapps).glob("appmanifest_*.acf"):
|
for manifest in Path(steamapps).glob("appmanifest_*.acf"):
|
||||||
@@ -178,7 +184,7 @@ def unimported(steam_root, snapshot, candidates):
|
|||||||
|
|
||||||
|
|
||||||
def watch(steam_root, listing):
|
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:
|
if os.environ.get("BL_STEAM_OFFLINE") == "1" or not games:
|
||||||
return
|
return
|
||||||
candidates, sources = {}, {}
|
candidates, sources = {}, {}
|
||||||
@@ -363,6 +369,22 @@ def seed_install(steam_root, game, appid):
|
|||||||
return True
|
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):
|
def route(steam_root, acct, games):
|
||||||
snapshot = read_json(Path(acct) / "config" / STATE, {})
|
snapshot = read_json(Path(acct) / "config" / STATE, {})
|
||||||
if not isinstance(snapshot, dict):
|
if not isinstance(snapshot, dict):
|
||||||
@@ -379,6 +401,13 @@ def route(steam_root, acct, games):
|
|||||||
except OSError as error:
|
except OSError as error:
|
||||||
print(f"droiddeck-steam-games: set-aside installs not restored ({error})")
|
print(f"droiddeck-steam-games: set-aside installs not restored ({error})")
|
||||||
for game in games:
|
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", "")
|
folder = game.get("folder", "")
|
||||||
appid = candidates.get(folder, candidates.get(legacy_path(folder)))
|
appid = candidates.get(folder, candidates.get(legacy_path(folder)))
|
||||||
stamp = source_stamp(game)
|
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(self.folder, (self.steam / 'steamapps/common/DroidDeck-42').resolve())
|
||||||
self.assertEqual(b'game', self.exe.read_bytes())
|
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):
|
def test_migrates_only_unchanged_legacy_import_manifests(self):
|
||||||
self.snapshot()
|
self.snapshot()
|
||||||
imports.route(self.steam, self.acct, [self.game])
|
imports.route(self.steam, self.acct, [self.game])
|
||||||
|
|||||||
Reference in new issue
Block a user