Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
15d74f1
fix: recursive delete in AdrenotoolsManager.extractDriverFromResources
TideGear Apr 23, 2026
e963f13
fix: defensive BadIdChoice on null Drawable/Pixmap in DRI3 pixmap cre…
TideGear Apr 23, 2026
215b5b3
fix: cap leaked GLX client windows to prevent OOM
TideGear Apr 23, 2026
8d9ab47
fix: skip orphaned Wine GLX leak chain in compositor
TideGear Apr 23, 2026
05f303e
fix: guard SysVSharedMemory.delete with the shmemories lock
TideGear Apr 21, 2026
bff36e5
fix: real Steam client launch reliability + overlay toggle
TideGear Apr 19, 2026
963d799
fix: suppress per-launch vcredist reinstall + DLL marker false desyncs
TideGear Apr 19, 2026
cdc1d80
refactor: PICS-driven shared depots + retire SteamFix diagnostics
TideGear Apr 21, 2026
5a17944
fix: real-Steam cloud sync race, graceful exit, pending-upload filter
TideGear Apr 21, 2026
711a050
fix: graceful-exit job leak, unowned SharedDepots, narrow shim catch
TideGear Apr 21, 2026
f392600
fix: clear lingering localhost session on real-Steam exit
TideGear Apr 21, 2026
e91f950
diag: log Steam binary fingerprint at launch, write steam.cfg in both…
TideGear Apr 22, 2026
4dea44a
fix: skip localhost AppLaunchIntent in real-Steam mode
TideGear Apr 22, 2026
bf759e5
fix: harden beginLaunchApp against leaked AppSessionActive phantoms
TideGear Apr 22, 2026
0324c16
fix: auto-clear same-device phantom pending ops in beginLaunchApp
TideGear Apr 23, 2026
06501d8
fix: clear cloud phantoms before real-Steam launch, drop -silent
TideGear Apr 23, 2026
5e50e0e
fix: probe for cloud phantoms before dismissing, don't corrupt Change…
TideGear Apr 23, 2026
2072890
fix: graceful Steam shutdown on red exit in real-Steam mode
TideGear Apr 23, 2026
5bf8459
fix: bridge SDK-cloud saves to Wine-Steam for Dead Cells
TideGear Apr 24, 2026
9c0c5f2
feat: SDK cloud save bridge with Ludusavi + launch-time prompt
TideGear Apr 24, 2026
c0da85f
fix: complete imageFs.wineprefix migration in SteamUtils.kt
TideGear Apr 27, 2026
cf7b57c
fix: Steam-only per-container path migrations not covered by the gene…
TideGear Apr 28, 2026
0c03f10
fix: post-9.1-merge container API migration in SteamUtils
TideGear Apr 29, 2026
4cfe6a6
fix: overlay disable also blocks gameoverlayrenderer at Wine's loader
TideGear Apr 29, 2026
9d2ac05
fix: address PR review findings
TideGear May 2, 2026
82dddd9
fix: address second round of PR review findings
TideGear May 2, 2026
6be9db1
fix: address third round of PR review findings
TideGear May 2, 2026
ae9653d
fix: ignore working-tree dirtiness in lsfg-vk-android submodule
TideGear May 3, 2026
ffcac75
fix: address fourth round of PR review findings
TideGear May 3, 2026
41edf45
Merge upstream/master into Fix-Steam-Client
TideGear May 4, 2026
2347384
fix: address fifth round of PR review findings
TideGear May 4, 2026
b233279
fix: address sixth round of PR review findings
TideGear May 4, 2026
300fbf7
Merge upstream/master (PR #1430 bionic Steam + PR #1440 bionic Steam …
TideGear May 14, 2026
b19efda
fix: surface Cloud Save Bridge UI + launch-time prompt in Bionic Stea…
TideGear May 14, 2026
9106054
fix: address PR review feedback on exit ordering + recursive SDK clou…
TideGear May 14, 2026
f105fd5
fix: route Bionic Steam exit through Steam's graceful shutdown path
TideGear May 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
[submodule "app/src/main/cpp/lsfg-vk-android"]
path = app/src/main/cpp/lsfg-vk-android
url = https://github.com/GameNative/lsfg-vk-android.git
ignore = dirty
2 changes: 1 addition & 1 deletion app/src/main/assets/box86_64/lightsteam.box64rc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[steam.exe]
BOX64_ENV=WINEARGS=-silent -no-browser -noreactlogin -no-dwrite -no-cef-sandbox -nooverlay -nobigpicture -nofriendsui -noshaders -novid -noverifyfiles -nointro -skipstreamingdrivers -norepairfiles -nohltv -nofasthtml -nocrashmonitor -no-shared-textures -disablehighdpi -cef-single-process -cef-in-process-gpu -single_core -cef-disable-d3d11 -cef-disable-sandbox -disable-winh264 -vrdisable -cef-disable-breakpad -cef-disable-gpu -cef-disable-hang-timeouts -cef-disable-seccomp-sandbox -cef-disable-extensions -cef-disable-remote-fonts -cef-enable-media-stream -cef-disable-accelerated-video-decode
BOX64_ENV=WINEARGS=-no-browser -noreactlogin -no-dwrite -no-cef-sandbox -nooverlay -nobigpicture -nofriendsui -noshaders -novid -noverifyfiles -nointro -skipstreamingdrivers -norepairfiles -nohltv -nofasthtml -nocrashmonitor -no-shared-textures -disablehighdpi -cef-single-process -cef-in-process-gpu -single_core -cef-disable-d3d11 -cef-disable-sandbox -disable-winh264 -vrdisable -cef-disable-breakpad -cef-disable-gpu -cef-disable-hang-timeouts -cef-disable-seccomp-sandbox -cef-disable-extensions -cef-disable-remote-fonts -cef-enable-media-stream -cef-disable-accelerated-video-decode
BOX64_ENV1=WINEDLLOVERRIDES=gameoverlayui,gameoverlayui64.exe=d

[steamwebhelper.exe]
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/assets/box86_64/ultralightsteam.box64rc
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[steam.exe]
BOX64_ENV=WINEARGS=-no-browser -noreactlogin -silent -no-dwrite -no-cef-sandbox -nooverlay -nofriendsui -nobigpicture -noshaders -novid -noverifyfiles -nointro -skipstreamingdrivers -norepairfiles -nohltv -nofasthtml -nocrashmonitor -no-shared-textures -disablehighdpi -cef-single-process -cef-in-process-gpu -single_core -cef-disable-d3d11 -cef-disable-sandbox -disable-winh264 -vrdisable -cef-disable-breakpad -cef-disable-gpu -cef-disable-hang-timeouts -cef-disable-seccomp-sandbox -cef-disable-gpu-compositing -cef-disable-extensions -cef-disable-remote-fonts -cef-enable-media-stream -cef-disable-accelerated-video-decode
BOX64_ENV=WINEARGS=-no-browser -noreactlogin -no-dwrite -no-cef-sandbox -nooverlay -nofriendsui -nobigpicture -noshaders -novid -noverifyfiles -nointro -skipstreamingdrivers -norepairfiles -nohltv -nofasthtml -nocrashmonitor -no-shared-textures -disablehighdpi -cef-single-process -cef-in-process-gpu -single_core -cef-disable-d3d11 -cef-disable-sandbox -disable-winh264 -vrdisable -cef-disable-breakpad -cef-disable-gpu -cef-disable-hang-timeouts -cef-disable-seccomp-sandbox -cef-disable-gpu-compositing -cef-disable-extensions -cef-disable-remote-fonts -cef-enable-media-stream -cef-disable-accelerated-video-decode
BOX64_ENV1=WINEDLLOVERRIDES=gameoverlayui,gameoverlayui64.exe=d

[steamwebhelper.exe]
Expand Down
10 changes: 10 additions & 0 deletions app/src/main/java/app/gamenative/PluviaApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import app.gamenative.service.DownloadService
import app.gamenative.service.SteamService
import app.gamenative.utils.ContainerMigrator
import app.gamenative.utils.IntentLaunchManager
import app.gamenative.utils.LudusaviRegistry
import app.gamenative.utils.PlayIntegrity
import java.io.File
import javax.inject.Inject
Expand Down Expand Up @@ -89,6 +90,15 @@ class PluviaApp : SplitCompatApplication() {
)
}

// Prime the Ludusavi save-path registry in the background. The loader itself
// checks cache freshness (7-day TTL) and only hits the network when stale — so
// the SDK Cloud Save Bridge "Use Recommended" button and the launch-time prompt
// are both instant for users once the cache is populated.
appScope.launch {
runCatching { LudusaviRegistry.primeCache(applicationContext) }
.onFailure { Timber.w(it, "Background Ludusavi registry refresh failed") }
}

// Clear any stale temporary config overrides from previous app sessions
try {
IntentLaunchManager.clearAllTemporaryOverrides()
Expand Down
7 changes: 7 additions & 0 deletions app/src/main/java/app/gamenative/PrefManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,13 @@ object PrefManager {
setPref(LAUNCH_REAL_STEAM, value)
}

private val DISABLE_STEAM_OVERLAY = booleanPreferencesKey("disable_steam_overlay")
var disableSteamOverlay: Boolean
get() = getPref(DISABLE_STEAM_OVERLAY, true)
set(value) {
setPref(DISABLE_STEAM_OVERLAY, value)
}

private val LAUNCH_BIONIC_STEAM = booleanPreferencesKey("launch_bionic_steam")
var launchBionicSteam: Boolean
get() = getPref(LAUNCH_BIONIC_STEAM, false)
Expand Down
214 changes: 156 additions & 58 deletions app/src/main/java/app/gamenative/service/SteamAutoCloud.kt

Large diffs are not rendered by default.

305 changes: 283 additions & 22 deletions app/src/main/java/app/gamenative/service/SteamService.kt

Large diffs are not rendered by default.

110 changes: 109 additions & 1 deletion app/src/main/java/app/gamenative/ui/PluviaMain.kt
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,12 @@ import app.gamenative.ui.theme.PluviaTheme
import app.gamenative.ui.util.SnackbarManager
import app.gamenative.utils.BestConfigService
import app.gamenative.utils.ContainerUtils
import app.gamenative.utils.SteamUtils
import app.gamenative.utils.PlatformAuthUtils
import app.gamenative.utils.CustomGameScanner
import app.gamenative.utils.ManifestInstaller
import app.gamenative.utils.GameFeedbackUtils
import app.gamenative.utils.IntentLaunchManager
import app.gamenative.utils.SteamUtils
import app.gamenative.utils.UpdateChecker
import app.gamenative.utils.UpdateInfo
import app.gamenative.utils.UpdateInstaller
Expand Down Expand Up @@ -796,6 +796,66 @@ fun PluviaMain(
}
}

DialogType.SDK_CLOUD_BRIDGE_SUGGESTION -> {
val relaunch = { suppressPrompt: Boolean ->
preLaunchApp(
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
context = context,
appId = state.launchedAppId,
// Always suppress for the current relaunch so the dialog doesn't immediately
// resurface and loop; persistence (sdkCloudSaveSubdir / sdkCloudBridgePromptDismissed)
// is what gates future launches.
skipBridgePrompt = suppressPrompt,
setLoadingDialogVisible = viewModel::setLoadingDialogVisible,
setLoadingProgress = viewModel::setLoadingDialogProgress,
setLoadingMessage = viewModel::setLoadingDialogMessage,
setMessageDialogState = setMessageDialogState,
onSuccess = viewModel::launchApp,
isOffline = viewModel.isOffline.value,
)
}
onConfirmClick = {
// Enable: write Ludusavi's suggested subdir, then continue the launch.
msgDialogState = MessageDialogState(false)
CoroutineScope(Dispatchers.IO).launch {
val gameId = ContainerUtils.extractGameIdFromContainerId(state.launchedAppId)
val rec = runCatching {
SteamUtils.getRecommendedSdkCloudSaveSubdirAsync(context, gameId)
}.getOrNull()
if (rec != null) {
runCatching {
val container = ContainerUtils.getContainer(context, state.launchedAppId)
container.sdkCloudSaveSubdir = rec.subdir
container.saveData()
}.onFailure { Timber.w(it, "Failed to persist sdkCloudSaveSubdir=${rec.subdir}") }
}
withContext(Dispatchers.Main) { relaunch(true) }
}
}
onDismissClick = {
// Skip this time — continue launch without setting the field. Always suppress
// for the current relaunch so the dialog doesn't reappear; since nothing was
// persisted, future launches will still re-prompt.
msgDialogState = MessageDialogState(false)
relaunch(true)
}
onActionClick = {
// Don't ask again for this game.
msgDialogState = MessageDialogState(false)
CoroutineScope(Dispatchers.IO).launch {
runCatching {
val container = ContainerUtils.getContainer(context, state.launchedAppId)
container.putExtra("sdkCloudBridgePromptDismissed", "1")
container.saveData()
}.onFailure { Timber.w(it, "Failed to persist sdkCloudBridgePromptDismissed") }
withContext(Dispatchers.Main) { relaunch(true) }
}
}
onDismissRequest = {
msgDialogState = MessageDialogState(false)
relaunch(true)
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

DialogType.SYNC_CONFLICT -> {
onConfirmClick = {
preLaunchApp(
Expand Down Expand Up @@ -1525,6 +1585,7 @@ fun preLaunchApp(
preferredSave: SaveLocation = SaveLocation.None,
useTemporaryOverride: Boolean = false,
skipCloudSync: Boolean = false,
skipBridgePrompt: Boolean = false,
setLoadingDialogVisible: (Boolean) -> Unit,
setLoadingProgress: (Float) -> Unit,
setLoadingMessage: (String) -> Unit,
Expand Down Expand Up @@ -1581,6 +1642,52 @@ fun preLaunchApp(
return@launch
}

// First-launch suggestion for Pattern B SDK-cloud games (e.g. Dead Cells).
// Fires only when real-Steam is on, the subdir is blank, the user hasn't dismissed,
// Ludusavi knows this game, AND it has no Auto-Cloud saveFilePatterns in PICS UFS.
// That intersection is a reliable Pattern B signal and keeps false positives low.
//
// Skip the prompt for non-game launches (Open Container / temporary override / explicit
// skipCloudSync) and for re-entries from conflict resolution (preferredSave set, or
// ignorePendingOperations true). The post-prompt `relaunch` lambda doesn't carry those
// flags forward, so firing the prompt for them would silently drop the user's
// conflict-resolution choice.
if (!skipBridgePrompt &&
!bootToContainer &&
!useTemporaryOverride &&
!skipCloudSync &&
!ignorePendingOperations &&
preferredSave == SaveLocation.None &&
gameSource == GameSource.STEAM &&
Comment thread
coderabbitai[bot] marked this conversation as resolved.
(container.isLaunchRealSteam || container.isLaunchBionicSteam) &&
container.sdkCloudSaveSubdir.isBlank() &&
container.getExtra("sdkCloudBridgePromptDismissed", "") != "1"
) {
Comment thread
coderabbitai[bot] marked this conversation as resolved.
val rec = runCatching {
SteamUtils.shouldSuggestSdkCloudBridge(context, gameId)
}.getOrNull()
if (rec != null) {
Timber.i("Pattern B bridge prompt for appId=$gameId (\"${rec.name}\" -> \"${rec.subdir}\")")
setLoadingDialogVisible(false)
setMessageDialogState(
MessageDialogState(
visible = true,
type = DialogType.SDK_CLOUD_BRIDGE_SUGGESTION,
title = context.getString(R.string.sdk_cloud_bridge_prompt_title),
message = context.getString(
R.string.sdk_cloud_bridge_prompt_message,
rec.name.ifEmpty { gameId.toString() },
rec.subdir,
),
confirmBtnText = context.getString(R.string.sdk_cloud_bridge_prompt_enable),
dismissBtnText = context.getString(R.string.sdk_cloud_bridge_prompt_skip),
actionBtnText = context.getString(R.string.sdk_cloud_bridge_prompt_dont_ask),
),
)
return@launch
}
}

// When "Open container" is used we boot to desktop/file manager only — skip executable check
if (!bootToContainer) {
// Verify we have a launch executable for all platforms before proceeding (fail fast, avoid black screen)
Expand Down Expand Up @@ -2032,6 +2139,7 @@ fun preLaunchApp(
preferredSave = preferredSave,
parentScope = this,
isOffline = isOffline,
isLaunchRealSteam = container.isLaunchRealSteam,
onProgress = { message, progress ->
setLoadingMessage(message)
setLoadingProgress(if (progress < 0) -1f else progress)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ fun ContainerConfigDialog(
default: Boolean = false,
title: String,
initialConfig: ContainerData = ContainerData(),
appId: Int? = null,
onDismissRequest: () -> Unit,
onSave: (ContainerData) -> Unit,
) {
Expand Down Expand Up @@ -1070,6 +1071,7 @@ fun ContainerConfigDialog(
applyScreenSizeToConfig = applyScreenSizeToConfig,
vkd3dForcedVersion = { vkd3dForcedVersion() },
currentDxvkContext = { currentDxvkContext() },
appId = appId,
)

LoadingDialog(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,4 +136,7 @@ class ContainerConfigState(
val applyScreenSizeToConfig: () -> Unit,
val vkd3dForcedVersion: () -> String,
val currentDxvkContext: () -> ManifestComponentHelper.DxvkContext,
/** Steam appId for this container, or null if not a Steam container. Used by
* per-game UI like the SDK-cloud save subdir detect button. */
val appId: Int? = null,
)
Loading