[UNTESTED] fix linux apps/prefix sync

This commit is contained in:
olcxja 2026-10-07 14:56:12 +02:00
commit 4680d7f014
6 changed files with 24 additions and 25 deletions

View file

@ -6,7 +6,6 @@
const path = require('path')
const fs = require('fs')
const fsp = fs.promises
const { isLocalOnlyRel } = require('./prefixes')
function createManifestBuilder({ filesPath, filesRoot, hash, mapLimit, concurrency }) {
// Targets inside the launcher data travel relative (the mirrored layout is
@ -39,8 +38,6 @@ function createManifestBuilder({ filesPath, filesRoot, hash, mapLimit, concurren
const leaves = []
for (const entry of entries) {
const relChild = `${dirRel}/${entry.name}`
// native Linux app data never leaves this machine
if (isLocalOnlyRel(relChild)) continue
if (entry.isDirectory()) {
const record = { path: relChild, type: 'dir' }
out.push(record)

View file

@ -21,7 +21,7 @@ const { createDiscovery } = require('./lansync-discovery')
const library = require('./library')
const wines = require('./wines')
const prefixes = require('./prefixes')
const { isPrefixRel, isLocalOnlyRel } = prefixes
const { isPrefixRel } = prefixes
const CONFIG = path.join(DIRS.DATA, 'lansync.json')
@ -432,8 +432,6 @@ async function pruneTree(abs, rel, keep) {
for (const entry of entries) {
const childAbs = path.join(abs, entry.name)
const childRel = `${rel}/${entry.name}`
// a mirror must not delete this machine's native Linux app data
if (isLocalOnlyRel(childRel)) continue
if (!keep.has(childRel)) {
if (removeAbs(childAbs)) removed++
continue
@ -623,6 +621,9 @@ function applyAppSettings(payload) {
if (from.env && typeof from.env === 'object' && JSON.stringify(from.env) !== JSON.stringify(game.env || {})) patch.env = from.env
if (Array.isArray(from.dllOverrides) && JSON.stringify(from.dllOverrides) !== JSON.stringify(game.dllOverrides || [])) patch.dllOverrides = from.dllOverrides
if (from.disableUmu !== undefined && !!from.disableUmu !== !!game.disableUmu) patch.disableUmu = !!from.disableUmu
// Linux-only app config travels with the app's configuration too
if (from.isolation !== undefined && from.isolation !== game.isolation) patch.isolation = from.isolation
if (from.forceX11 !== undefined && !!from.forceX11 !== !!game.forceX11) patch.forceX11 = !!from.forceX11
if (Object.keys(patch).length) {
library.updateGame(game.id, patch)
patched++
@ -991,7 +992,8 @@ async function handlePlan(state, session, url, req, res, hooks) {
const PUT_IDLE_MS = Number(process.env.PLWL_LANSYNC_PUT_IDLE) || 60000
async function handlePut(session, url, req, res, hooks) {
// unit and path travel as headers: constant per request
// current senders put the pair in the query (percent-encoded); 0.5.x used
// headers, which cannot carry a code above U+00FF at all
const key = req.headers['x-plwl-unit'] || url.searchParams.get('unit') || ''
const rel = req.headers['x-plwl-path'] || url.searchParams.get('path') || ''
const unit = session.units.get(key)

View file

@ -118,18 +118,20 @@ function createPusher(api) {
clearTimeout(timer)
fn(value)
}
// unit and path travel as headers: constant per request
// Unit and path travel in the query, percent-encoded: Node refuses any
// header value holding a code above U+00FF (ERR_INVALID_CHAR), which a
// real library has (Minecraft pack names, Polish file names), and that
// refusal used to kill the sync mid-unit.
const query = `token=${encodeURIComponent(token)}&unit=${encodeURIComponent(unitKey)}&path=${encodeURIComponent(rel)}`
const req = http.request(
{
host: device.address,
port: device.port,
path: `/plwl/unit/file?token=${encodeURIComponent(token)}`,
path: `/plwl/unit/file?${query}`,
method: 'PUT',
headers: {
'content-type': 'application/octet-stream',
'content-length': fs.statSync(abs).size,
'x-plwl-unit': unitKey,
'x-plwl-path': rel
'content-length': fs.statSync(abs).size
},
timeout: 0
},
@ -254,7 +256,10 @@ function createPusher(api) {
args: g.args || '',
env: g.env || {},
dllOverrides: g.dllOverrides || [],
disableUmu: !!g.disableUmu
disableUmu: !!g.disableUmu,
// Linux-only app config, part of the app's configuration as well
isolation: g.isolation,
forceX11: !!g.forceX11
}))
payload.appOrder = games.map(g => g.id)
}

View file

@ -46,12 +46,6 @@ function prefixLinuxRoot(name) {
return path.join(prefixPath(name), LINUX_PFX)
}
// Native Linux app data is machine-local (sockets, caches, machine-id, the
// sandbox's home): a sync neither sends it nor deletes it as an extra.
function isLocalOnlyRel(rel) {
return String(rel || '').split('/').includes(LINUX_PFX)
}
function prefixExists(name) {
try {
fs.accessSync(prefixPath(name))
@ -188,4 +182,4 @@ function setDefaultPrefix(name) {
return { ok: true }
}
module.exports = { isPrefixRel, PREFIX_REL_RE, LINUX_PFX, isLocalOnlyRel, listPrefixes, savePrefixes, createPrefix, deletePrefix, renamePrefix, setDefaultPrefix, prefixPath, prefixLinuxRoot, prefixExists, prefixDriveRoots, isPrefixRoot, healPrefix, ensureUnifiedPrefix, MAIN }
module.exports = { isPrefixRel, PREFIX_REL_RE, LINUX_PFX, listPrefixes, savePrefixes, createPrefix, deletePrefix, renamePrefix, setDefaultPrefix, prefixPath, prefixLinuxRoot, prefixExists, prefixDriveRoots, isPrefixRoot, healPrefix, ensureUnifiedPrefix, MAIN }