Skip to content

Commit 1b25838

Browse files
utkarshdalalUtkarsh Dalal
andauthored
don't bug out if initial files can't be downloaded (utkarshdalal#819)
* don't bug out if initial files can't be downloaded * added implementation of okhttp dnsoverhttps to make code compile * added fallback DNS for doh --------- Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>
1 parent ea4ffc9 commit 1b25838

22 files changed

Lines changed: 228 additions & 84 deletions

File tree

CONTRIBUTING.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Contributing to GameNative
2+
3+
Thanks for your interest in contributing! We welcome pull requests, bug reports, and feedback.
4+
5+
## Getting Started
6+
7+
1. Join our [Discord server](https://discord.gg/2hKv4VfZfE) to discuss what you're working on
8+
2. Fork the repo and create a branch for your changes
9+
3. See the **Building** section in the [README](README.md) for setup instructions
10+
4. Submit a pull request with a clear description of what your change does and why
11+
12+
## Reporting Issues
13+
14+
Please report bugs and request support through our [Discord server](https://discord.gg/2hKv4VfZfE). GitHub issues will be automatically closed.
15+
16+
## Contribution Licensing
17+
18+
By submitting a contribution to GameNative, you agree that your contribution shall be licensed under the same license(s) as the project at the time of contribution.
19+
20+
You also grant the project maintainers the right to relicense the project, including your contribution, under any future version of those license(s), or under any other license that is free and open source software (FOSS) and compatible with the current license(s).
21+
22+
You retain full copyright ownership of your contributions. You confirm that your contributions are your original work and that you have the right to submit them under these terms.

app/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ dependencies {
223223
}
224224
}
225225
implementation(libs.spongycastle)
226+
implementation(libs.okhttp.dnsoverhttps)
226227

227228
// Split Modules
228229
implementation(libs.bundles.google)

app/src/main/java/app/gamenative/service/SteamService.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ import `in`.dragonbra.javasteam.util.log.LogListener
113113
import `in`.dragonbra.javasteam.util.log.LogManager
114114
import java.io.Closeable
115115
import java.io.File
116+
import java.io.IOException
116117
import java.io.InputStream
117118
import java.io.OutputStream
118119
import java.lang.NullPointerException
@@ -1123,10 +1124,11 @@ class SteamService : Service(), IChallengeUrlChanged {
11231124
try {
11241125
fetchFile(fallbackUrl, dest, onProgress)
11251126
} catch (e2: Exception) {
1126-
withContext(Dispatchers.Main) {
1127-
val msg = "Download failed with ${e2.message ?: e2.toString()}. Please disable VPN or try a different network."
1128-
android.widget.Toast.makeText(context.applicationContext, msg, android.widget.Toast.LENGTH_LONG).show()
1129-
}
1127+
dest.delete()
1128+
throw IOException(
1129+
"Failed to download $fileName. Please check your network connection or try a VPN.",
1130+
e2,
1131+
)
11301132
}
11311133
}
11321134
}

app/src/main/java/app/gamenative/ui/PluviaMain.kt

Lines changed: 105 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1402,65 +1402,109 @@ fun preLaunchApp(
14021402
// Skip the check if booting to container (Open Container menu option)
14031403
val isCustomGame = gameSource == GameSource.CUSTOM_GAME
14041404

1405-
// set up Ubuntu file system
1405+
// set up Ubuntu file system — download required files and install
14061406
SplitCompat.install(context)
1407-
if (!SteamService.isImageFsInstallable(context, container.containerVariant)) {
1408-
setLoadingMessage("Downloading first-time files")
1409-
SteamService.downloadImageFs(
1410-
onDownloadProgress = { setLoadingProgress(it / 1.0f) },
1411-
this,
1412-
variant = container.containerVariant,
1413-
context = context,
1414-
).await()
1415-
}
1416-
if (container.containerVariant.equals(Container.GLIBC) &&
1417-
!SteamService.isFileInstallable(context, "imagefs_patches_gamenative.tzst")
1418-
) {
1419-
setLoadingMessage("Downloading Wine")
1420-
SteamService.downloadImageFsPatches(
1421-
onDownloadProgress = { setLoadingProgress(it / 1.0f) },
1422-
this,
1423-
context = context,
1424-
).await()
1425-
} else {
1426-
if (container.wineVersion.contains("proton-9.0-arm64ec") &&
1427-
!SteamService.isFileInstallable(context, "proton-9.0-arm64ec.txz")
1407+
try {
1408+
if (!SteamService.isImageFsInstallable(context, container.containerVariant)) {
1409+
setLoadingMessage("Downloading first-time files")
1410+
SteamService.downloadImageFs(
1411+
onDownloadProgress = { setLoadingProgress(it / 1.0f) },
1412+
this,
1413+
variant = container.containerVariant,
1414+
context = context,
1415+
).await()
1416+
}
1417+
if (container.containerVariant.equals(Container.GLIBC) &&
1418+
!SteamService.isFileInstallable(context, "imagefs_patches_gamenative.tzst")
14281419
) {
1429-
setLoadingMessage("Downloading arm64ec Proton")
1430-
SteamService.downloadFile(
1420+
setLoadingMessage("Downloading Wine")
1421+
SteamService.downloadImageFsPatches(
14311422
onDownloadProgress = { setLoadingProgress(it / 1.0f) },
14321423
this,
14331424
context = context,
1434-
"proton-9.0-arm64ec.txz",
14351425
).await()
1436-
} else if (container.wineVersion.contains("proton-9.0-x86_64") &&
1437-
!SteamService.isFileInstallable(context, "proton-9.0-x86_64.txz")
1426+
} else {
1427+
if (container.wineVersion.contains("proton-9.0-arm64ec") &&
1428+
!SteamService.isFileInstallable(context, "proton-9.0-arm64ec.txz")
1429+
) {
1430+
setLoadingMessage("Downloading arm64ec Proton")
1431+
SteamService.downloadFile(
1432+
onDownloadProgress = { setLoadingProgress(it / 1.0f) },
1433+
this,
1434+
context = context,
1435+
"proton-9.0-arm64ec.txz",
1436+
).await()
1437+
} else if (container.wineVersion.contains("proton-9.0-x86_64") &&
1438+
!SteamService.isFileInstallable(context, "proton-9.0-x86_64.txz")
1439+
) {
1440+
setLoadingMessage("Downloading x86_64 Proton")
1441+
SteamService.downloadFile(
1442+
onDownloadProgress = { setLoadingProgress(it / 1.0f) },
1443+
this,
1444+
context = context,
1445+
"proton-9.0-x86_64.txz",
1446+
).await()
1447+
}
1448+
if (container.wineVersion.contains("proton-9.0-x86_64") || container.wineVersion.contains("proton-9.0-arm64ec")) {
1449+
val protonVersion = container.wineVersion
1450+
val imageFs = ImageFs.find(context)
1451+
val outFile = File(imageFs.rootDir, "/opt/$protonVersion")
1452+
val binDir = File(outFile, "bin")
1453+
if (!binDir.exists() || !binDir.isDirectory) {
1454+
Timber.i("Extracting $protonVersion to /opt/")
1455+
setLoadingMessage("Extracting $protonVersion")
1456+
setLoadingProgress(-1f)
1457+
val downloaded = File(imageFs.getFilesDir(), "$protonVersion.txz")
1458+
TarCompressorUtils.extract(
1459+
TarCompressorUtils.Type.XZ,
1460+
downloaded,
1461+
outFile,
1462+
)
1463+
}
1464+
}
1465+
}
1466+
1467+
if (!container.isUseLegacyDRM && !container.isLaunchRealSteam &&
1468+
!SteamService.isFileInstallable(context, "experimental-drm-20260116.tzst")
14381469
) {
1439-
setLoadingMessage("Downloading x86_64 Proton")
1470+
setLoadingMessage("Downloading extras")
14401471
SteamService.downloadFile(
14411472
onDownloadProgress = { setLoadingProgress(it / 1.0f) },
14421473
this,
14431474
context = context,
1444-
"proton-9.0-x86_64.txz",
1475+
"experimental-drm-20260116.tzst",
14451476
).await()
14461477
}
1447-
if (container.wineVersion.contains("proton-9.0-x86_64") || container.wineVersion.contains("proton-9.0-arm64ec")) {
1448-
val protonVersion = container.wineVersion
1449-
val imageFs = ImageFs.find(context)
1450-
val outFile = File(imageFs.rootDir, "/opt/$protonVersion")
1451-
val binDir = File(outFile, "bin")
1452-
if (!binDir.exists() || !binDir.isDirectory) {
1453-
Timber.i("Extracting $protonVersion to /opt/")
1454-
setLoadingMessage("Extracting $protonVersion")
1455-
setLoadingProgress(-1f)
1456-
val downloaded = File(imageFs.getFilesDir(), "$protonVersion.txz")
1457-
TarCompressorUtils.extract(
1458-
TarCompressorUtils.Type.XZ,
1459-
downloaded,
1460-
outFile,
1461-
)
1462-
}
1478+
if (container.isLaunchRealSteam && !SteamService.isFileInstallable(context, "steam.tzst")) {
1479+
setLoadingMessage(context.getString(R.string.main_downloading_steam))
1480+
SteamService.downloadSteam(
1481+
onDownloadProgress = { setLoadingProgress(it / 1.0f) },
1482+
this,
1483+
context = context,
1484+
).await()
1485+
}
1486+
if (container.isLaunchRealSteam && !SteamService.isFileInstallable(context, "steam-token.tzst")) {
1487+
setLoadingMessage("Downloading steam-token")
1488+
SteamService.downloadFile(
1489+
onDownloadProgress = { setLoadingProgress(it / 1.0f) },
1490+
this,
1491+
context = context,
1492+
"steam-token.tzst",
1493+
).await()
14631494
}
1495+
} catch (e: Exception) {
1496+
Timber.tag("preLaunchApp").e(e, "File download failed")
1497+
setLoadingDialogVisible(false)
1498+
setMessageDialogState(
1499+
MessageDialogState(
1500+
visible = true,
1501+
type = DialogType.SYNC_FAIL,
1502+
title = context.getString(R.string.download_failed_title),
1503+
message = e.message ?: context.getString(R.string.download_failed_message),
1504+
dismissBtnText = context.getString(R.string.ok),
1505+
),
1506+
)
1507+
return@launch
14641508
}
14651509

14661510
if (!isOffline) {
@@ -1491,35 +1535,6 @@ fun preLaunchApp(
14911535
Timber.tag("preLaunchApp").e("Offline mode, skipping launch dependencies")
14921536
}
14931537

1494-
if (!container.isUseLegacyDRM && !container.isLaunchRealSteam &&
1495-
!SteamService.isFileInstallable(context, "experimental-drm-20260116.tzst")
1496-
) {
1497-
setLoadingMessage("Downloading extras")
1498-
SteamService.downloadFile(
1499-
onDownloadProgress = { setLoadingProgress(it / 1.0f) },
1500-
this,
1501-
context = context,
1502-
"experimental-drm-20260116.tzst",
1503-
).await()
1504-
}
1505-
if (container.isLaunchRealSteam && !SteamService.isFileInstallable(context, "steam.tzst")) {
1506-
setLoadingMessage(context.getString(R.string.main_downloading_steam))
1507-
SteamService.downloadSteam(
1508-
onDownloadProgress = { setLoadingProgress(it / 1.0f) },
1509-
this,
1510-
context = context,
1511-
).await()
1512-
}
1513-
if (container.isLaunchRealSteam && !SteamService.isFileInstallable(context, "steam-token.tzst")) {
1514-
setLoadingMessage("Downloading steam-token")
1515-
SteamService.downloadFile(
1516-
onDownloadProgress = { setLoadingProgress(it / 1.0f) },
1517-
this,
1518-
context = context,
1519-
"steam-token.tzst",
1520-
).await()
1521-
}
1522-
15231538
val loadingMessage = if (container.containerVariant.equals(Container.GLIBC)) {
15241539
context.getString(R.string.main_installing_glibc)
15251540
} else {
@@ -1528,9 +1543,24 @@ fun preLaunchApp(
15281543
setLoadingMessage(loadingMessage)
15291544
val imageFsInstallSuccess =
15301545
ImageFsInstaller.installIfNeededFuture(context, context.assets, container) { progress ->
1531-
// Log.d("XServerScreen", "$progress")
15321546
setLoadingProgress(progress / 100f)
15331547
}.get()
1548+
1549+
if (!imageFsInstallSuccess) {
1550+
Timber.tag("preLaunchApp").e("ImageFS installation failed")
1551+
setLoadingDialogVisible(false)
1552+
setMessageDialogState(
1553+
MessageDialogState(
1554+
visible = true,
1555+
type = DialogType.SYNC_FAIL,
1556+
title = context.getString(R.string.install_failed_title),
1557+
message = context.getString(R.string.install_failed_message),
1558+
dismissBtnText = context.getString(R.string.ok),
1559+
),
1560+
)
1561+
return@launch
1562+
}
1563+
15341564
setLoadingMessage(context.getString(R.string.main_loading))
15351565
setLoadingProgress(-1f)
15361566

app/src/main/java/app/gamenative/utils/BestConfigService.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import java.util.concurrent.ConcurrentHashMap
3131
* Service for fetching best configurations for games from GameNative API.
3232
*/
3333
object BestConfigService {
34-
private const val API_BASE_URL = "https://gamenative-best-config-worker.gamenative.workers.dev/api/best-config"
34+
private const val API_BASE_URL = "https://api.gamenative.app/api/best-config"
3535
private const val TIMEOUT_SECONDS = 10L
3636

3737
private val httpClient = OkHttpClient.Builder()

app/src/main/java/app/gamenative/utils/SteamUtils.kt

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,43 @@ import kotlin.io.path.absolutePathString
3232
import kotlin.io.path.name
3333
import timber.log.Timber
3434
import okhttp3.*
35+
import okhttp3.HttpUrl.Companion.toHttpUrl
36+
import okhttp3.dnsoverhttps.DnsOverHttps
3537
import org.json.JSONObject
38+
import java.net.InetAddress
3639
import java.net.URLEncoder
3740
import java.util.concurrent.TimeUnit
3841

3942
object SteamUtils {
4043

44+
private val bootstrapClient = OkHttpClient.Builder()
45+
.protocols(listOf(Protocol.HTTP_2, Protocol.HTTP_1_1))
46+
.build()
47+
48+
private val doh: DnsOverHttps = DnsOverHttps.Builder()
49+
.client(bootstrapClient)
50+
.url("https://dns.google/dns-query".toHttpUrl())
51+
.bootstrapDnsHosts(
52+
InetAddress.getByName("8.8.8.8"),
53+
InetAddress.getByName("8.8.4.4"),
54+
)
55+
.build()
56+
57+
private val fallbackDns = object : Dns {
58+
override fun lookup(hostname: String): List<InetAddress> {
59+
return try {
60+
doh.lookup(hostname)
61+
} catch (e: Exception) {
62+
Timber.w(e, "DoH lookup failed for $hostname, falling back to system DNS")
63+
Dns.SYSTEM.lookup(hostname)
64+
}
65+
}
66+
}
67+
4168
internal val http = OkHttpClient.Builder()
42-
.readTimeout(5, TimeUnit.MINUTES) // from 2 min → 5 min
43-
.protocols(listOf(Protocol.HTTP_1_1)) // skip HTTP/2 stream stalls
69+
.dns(fallbackDns)
70+
.readTimeout(5, TimeUnit.MINUTES)
71+
.protocols(listOf(Protocol.HTTP_1_1))
4472
.retryOnConnectionFailure(true)
4573
.build()
4674

app/src/main/java/com/winlator/xenvironment/ImageFsInstaller.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,10 @@ else if (downloaded.exists()){
151151
}
152152
else {
153153
Log.e("ImageFsInstaller", "Failed to install system files");
154-
// AppUtils.showToast(context, R.string.unable_to_install_system_files);
154+
if (downloaded.exists()) {
155+
Log.w("ImageFsInstaller", "Deleting corrupt archive so next attempt re-downloads: " + downloaded.getPath());
156+
downloaded.delete();
157+
}
155158
}
156159
return success;
157160
// dialog.closeOnUiThread();

app/src/main/res/values-da/strings.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,4 +1088,8 @@
10881088
<string name="manifest_content_untrusted">Indholdet er ikke betroet og blev ikke installeret.</string>
10891089
<string name="manifest_type_mismatch">Downloadet indhold matcher ikke den forventede type.</string>
10901090
<string name="manifest_glibc_not_supported">GLIBC-builds understøttes ikke.</string>
1091+
<string name="download_failed_title">Download mislykkedes</string>
1092+
<string name="download_failed_message">En nødvendig fil kunne ikke downloades. Kontrollér din netværksforbindelse, og prøv igen.</string>
1093+
<string name="install_failed_title">Installation mislykkedes</string>
1094+
<string name="install_failed_message">Systemfiler kunne ikke installeres. Prøv igen. Hvis problemet fortsætter, prøv et andet netværk eller brug en VPN.</string>
10911095
</resources>

app/src/main/res/values-de/strings.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1158,4 +1158,8 @@
11581158
<string name="manifest_content_untrusted">Inhalt ist nicht vertrauenswürdig und wurde nicht installiert.</string>
11591159
<string name="manifest_type_mismatch">Heruntergeladener Inhalt entspricht nicht dem erwarteten Typ.</string>
11601160
<string name="manifest_glibc_not_supported">GLIBC-Builds werden nicht unterstützt.</string>
1161+
<string name="download_failed_title">Download fehlgeschlagen</string>
1162+
<string name="download_failed_message">Eine erforderliche Datei konnte nicht heruntergeladen werden. Bitte überprüfe deine Netzwerkverbindung und versuche es erneut.</string>
1163+
<string name="install_failed_title">Installation fehlgeschlagen</string>
1164+
<string name="install_failed_message">Systemdateien konnten nicht installiert werden. Bitte versuche es erneut. Falls das Problem weiterhin besteht, versuche ein anderes Netzwerk oder verwende ein VPN.</string>
11611165
</resources>

app/src/main/res/values-es/strings.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1223,4 +1223,8 @@
12231223
<string name="manifest_content_untrusted">El contenido no es de confianza y no se instaló.</string>
12241224
<string name="manifest_type_mismatch">El contenido descargado no coincide con el tipo esperado.</string>
12251225
<string name="manifest_glibc_not_supported">Las versiones GLIBC no son compatibles.</string>
1226+
<string name="download_failed_title">Error de descarga</string>
1227+
<string name="download_failed_message">No se pudo descargar un archivo necesario. Comprueba tu conexión de red e inténtalo de nuevo.</string>
1228+
<string name="install_failed_title">Error de instalación</string>
1229+
<string name="install_failed_message">No se pudieron instalar los archivos del sistema. Inténtalo de nuevo. Si el problema persiste, prueba con una red diferente o usa una VPN.</string>
12261230
</resources>

0 commit comments

Comments
 (0)