a LOT sync fixes...
This commit is contained in:
parent
c0f6e802ed
commit
806e173e43
14 changed files with 1073 additions and 240 deletions
|
|
@ -20,16 +20,50 @@ function sendToWindow(win, channel, data) {
|
|||
// Game-process state: id -> pid (or null while the process is starting).
|
||||
// onGamesChanged fires in the MAIN process whenever the running-games set
|
||||
// changes (the tray menu rebuilds itself from it).
|
||||
const STARTUP_GRACE_MS = 60000
|
||||
|
||||
function createGamesService({ getWindow, onGamesChanged }) {
|
||||
const running = new Map()
|
||||
const startedAt = new Map()
|
||||
const killRequests = new Set()
|
||||
let onGameExit = null
|
||||
const notifyChanged = () => {
|
||||
if (typeof onGamesChanged === 'function') onGamesChanged()
|
||||
}
|
||||
|
||||
const isRunning = id => running.has(id)
|
||||
const listRunning = () => [...running.keys()]
|
||||
function alive(pid) {
|
||||
if (!pid) return false
|
||||
try {
|
||||
process.kill(pid, 0)
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// A game is "running" only while its process really is. An entry left behind
|
||||
// by a launch that never got a pid (the binary could not be executed, so the
|
||||
// spawn failed) or whose pid is gone (the game crashed, the launcher was
|
||||
// restarted) would otherwise answer every later Play with "already
|
||||
// running".
|
||||
function prune() {
|
||||
for (const [id, pid] of [...running]) {
|
||||
if (alive(pid)) continue
|
||||
const age = Date.now() - (startedAt.get(id) || 0)
|
||||
if (!pid && age < STARTUP_GRACE_MS) continue // may still be starting
|
||||
running.delete(id)
|
||||
startedAt.delete(id)
|
||||
}
|
||||
}
|
||||
|
||||
const isRunning = id => {
|
||||
prune()
|
||||
return running.has(id)
|
||||
}
|
||||
const listRunning = () => {
|
||||
prune()
|
||||
return [...running.keys()]
|
||||
}
|
||||
|
||||
// "Open with PLWL" handoff: queue the file for the renderer (picker popup
|
||||
// with wine version + prefix). Safe to call at any time - if the window is
|
||||
|
|
@ -45,6 +79,7 @@ function createGamesService({ getWindow, onGamesChanged }) {
|
|||
|
||||
function handleExit(id) {
|
||||
running.delete(id)
|
||||
startedAt.delete(id)
|
||||
sendToWindow(getWindow(), 'games:changed')
|
||||
notifyChanged()
|
||||
if (onGameExit) onGameExit(id)
|
||||
|
|
@ -53,42 +88,59 @@ function createGamesService({ getWindow, onGamesChanged }) {
|
|||
async function start(win, id) {
|
||||
const game = library.getGame(id)
|
||||
if (!game) return { ok: false, error: 'game-not-found' }
|
||||
prune()
|
||||
if (running.has(id)) return { ok: false, error: 'already-running' }
|
||||
running.set(id, null)
|
||||
startedAt.set(id, Date.now())
|
||||
let wine
|
||||
try {
|
||||
wine = resolveWine(game)
|
||||
} catch {
|
||||
running.delete(id)
|
||||
startedAt.delete(id)
|
||||
return { ok: false, error: 'wine-not-found' }
|
||||
}
|
||||
try {
|
||||
const res = await runGame({ game, wine, systemWine: wine.type === 'system', onExit: () => handleExit(id) })
|
||||
if (res.pid) running.set(id, res.pid)
|
||||
// stop was requested while the process was still starting
|
||||
// stop was requested while the process was still starting: kill it and
|
||||
// leave nothing in the running set
|
||||
if (killRequests.has(id)) {
|
||||
killRequests.delete(id)
|
||||
if (res.pid) killProcessGroup(res.pid)
|
||||
running.delete(id)
|
||||
startedAt.delete(id)
|
||||
sendToWindow(win, 'games:changed')
|
||||
notifyChanged()
|
||||
return { ok: true, pid: null, logFile: res.logFile || null }
|
||||
}
|
||||
// no pid means the process was not started after all - never keep such
|
||||
// an entry, it would block the next launch with "already running"
|
||||
if (!res.pid) {
|
||||
running.delete(id)
|
||||
startedAt.delete(id)
|
||||
return { ok: false, error: 'not-started' }
|
||||
}
|
||||
running.set(id, res.pid)
|
||||
sendToWindow(win, 'games:changed')
|
||||
notifyChanged()
|
||||
return { ok: true, pid: res.pid || null, logFile: res.logFile || null }
|
||||
return { ok: true, pid: res.pid, logFile: res.logFile || null }
|
||||
} catch (err) {
|
||||
running.delete(id)
|
||||
startedAt.delete(id)
|
||||
return { ok: false, error: String((err && err.message) || err) }
|
||||
}
|
||||
}
|
||||
|
||||
// Stop always clears the state: whether the process was running, still
|
||||
// starting (kill request for as soon as it has a pid) or already gone.
|
||||
async function kill(id) {
|
||||
const pid = running.get(id)
|
||||
if (pid) {
|
||||
killProcessGroup(pid)
|
||||
running.delete(id)
|
||||
sendToWindow(getWindow(), 'games:changed')
|
||||
notifyChanged()
|
||||
} else if (running.has(id)) {
|
||||
killRequests.add(id)
|
||||
}
|
||||
if (pid) killProcessGroup(pid)
|
||||
else if (running.has(id)) killRequests.add(id)
|
||||
running.delete(id)
|
||||
startedAt.delete(id)
|
||||
sendToWindow(getWindow(), 'games:changed')
|
||||
notifyChanged()
|
||||
return { ok: true }
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue