guard prefix and wine edits while their apps run

This commit is contained in:
olcxja 2026-10-02 14:46:21 +02:00
commit 38eea05789
9 changed files with 170 additions and 39 deletions

View file

@ -82,6 +82,53 @@ function getGame(id) {
return loadGames().find(g => g.id === id) || null
}
// Which prefix a game's runs land in: an app without one follows the launcher
// default. The single place for that fallback (runner.buildEnv and the in-use
// guards both use it, so a rename/delete can never blame the wrong folder).
function prefixNameForGame(game) {
if (game && game.prefix) return game.prefix
const defs = readJson(path.join(DIRS.DATA, 'prefixes.json'), { default: 'Main', prefixes: [] })
return defs.default || 'Main'
}
// Apps whose runs land in this prefix - used to keep a prefix that any app
// still references from being deleted out from under it.
function gamesUsingPrefix(name) {
if (!name) return []
return loadGames().filter(g => prefixNameForGame(g) === name)
}
// In-use guards for registry edits: a prefix folder being renamed or deleted
// (or a wine build deleted) while a game runs in it would leave that process
// without its registry/files - the folder move/removal has to wait until the
// game stops. `running` is the games service's list of live runs
// ([{ name, prefixes, wine }]); prefixes lists every folder a run holds (its
// snapshot plus a new one it was pointed at mid-run).
function appsUsingPrefix(name, running) {
if (!name) return []
return (running || []).filter(r => (r.prefixes || []).includes(name))
}
function isPrefixInUse(name, running) {
return appsUsingPrefix(name, running).length > 0
}
// A run with no explicit wine falls back to System Wine, which cannot be
// deleted anyway - only an exact match on a registered build counts.
function isWineInUse(id, running) {
if (!id || id === 'system') return false
return (running || []).some(r => r.wine === id)
}
// Switching the default prefix would silently move the next launch of every
// app that has no prefix of its own - so it waits while any of them runs.
function gamesFollowingDefault(running) {
return (running || []).filter(r => {
const game = getGame(r.id)
return !game || !game.prefix
})
}
function addGame(game) {
const games = loadGames()
games.push(game)
@ -207,9 +254,7 @@ function renamePrefixRefs(oldName, newName) {
}
function resolvePrefixDir(game) {
if (game.prefix) return prefixPath(game.prefix)
const defs = readJson(path.join(DIRS.DATA, 'prefixes.json'), { default: 'default', prefixes: [] })
return prefixPath(defs.default || 'default')
return prefixPath(prefixNameForGame(game))
}
module.exports = { loadGames, saveGames, portableGame, getGame, addGame, updateGame, removeGame, reorderGames, renamePrefixRefs, loadSettings, saveSettings, newGame, newGameId, resolvePrefixDir, clearPlaytime, readPlaySeconds, writePlaySeconds, playtimeFile }
module.exports = { loadGames, saveGames, portableGame, getGame, addGame, updateGame, removeGame, reorderGames, renamePrefixRefs, prefixNameForGame, gamesUsingPrefix, appsUsingPrefix, isPrefixInUse, isWineInUse, gamesFollowingDefault, loadSettings, saveSettings, newGame, newGameId, resolvePrefixDir, clearPlaytime, readPlaySeconds, writePlaySeconds, playtimeFile }