Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
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
11 changes: 11 additions & 0 deletions app/src/main/java/app/gamenative/PluviaApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.navigation.NavController
import app.gamenative.db.dao.AmazonGameDao
import app.gamenative.db.dao.AppInfoDao
import app.gamenative.db.dao.EpicGameDao
import app.gamenative.db.dao.GOGGameDao
import app.gamenative.display.GamescopeCommandQueue
import app.gamenative.events.EventDispatcher
Expand All @@ -18,6 +20,7 @@ import app.gamenative.powercontrol.PowerManager
import app.gamenative.service.ActiveGameRegistry
import app.gamenative.service.DownloadService
import app.gamenative.service.SteamService
import app.gamenative.shadercache.FossilizeManager
import app.gamenative.sync.FrontendSyncManager
import app.gamenative.ui.screen.xserver.RadialMenuCoordinator
import app.gamenative.utils.AppExitRecorder
Expand Down Expand Up @@ -60,6 +63,8 @@ class PluviaApp : SplitCompatApplication() {

@Inject lateinit var gogGameDao: GOGGameDao
@Inject lateinit var amazonGameDao: AmazonGameDao
@Inject lateinit var appInfoDao: AppInfoDao
@Inject lateinit var epicGameDao: EpicGameDao

private val appScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)

Expand Down Expand Up @@ -131,6 +136,12 @@ class PluviaApp : SplitCompatApplication() {
}
}

if (PrefManager.sharedShaderCache && Build.FINGERPRINT != "robolectric") {
appScope.launch {
FossilizeManager.checkPoolsAtStart(applicationContext, appInfoDao, gogGameDao, epicGameDao, amazonGameDao)
}
}

// Clear any stale temporary config overrides from previous app sessions
try {
IntentLaunchManager.clearAllTemporaryOverrides()
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/app/gamenative/PrefManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -1968,7 +1968,7 @@ object PrefManager {
get() = getPref(USAGE_ANALYTICS_ENABLED, true)
set(value) { setPref(USAGE_ANALYTICS_ENABLED, value) }

// FEX code caches: share which code FEX translated and download prebuilt translations (FexCodeMaps).
// FEX code caches: share which code FEX translated and compile it into caches on the device (FexCodeMaps, FexCodeCaches).
private val FEX_CODE_CACHE_ENABLED = booleanPreferencesKey("fex_code_cache_enabled")
var fexCodeCacheEnabled: Boolean
get() = getPref(FEX_CODE_CACHE_ENABLED, true)
Expand Down
124 changes: 0 additions & 124 deletions app/src/main/java/app/gamenative/api/CodeCacheApi.kt

This file was deleted.

38 changes: 34 additions & 4 deletions app/src/main/java/app/gamenative/api/ShaderCacheApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@ import okhttp3.MediaType.Companion.toMediaType
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import org.json.JSONArray
import org.json.JSONObject
import timber.log.Timber

object ShaderCacheApi {

data class Pool(val version: Long, val url: String, val sha256: String, val size: Long, val entries: Long)

data class PoolVersion(val version: Long, val size: Long, val entries: Long)

data class UploadSlot(val key: String, val putUrl: String)

data class UploadMeta(
Expand All @@ -31,8 +34,13 @@ object ShaderCacheApi {
val gpu: String,
val driver: String,
val displayServer: String,
val dxvk: String?,
val vkd3d: String?,
)

/** The most games one [checkPools] request may ask about. */
const val MAX_CHECK_GAMES = 500

private const val TAG = "ShaderCacheApi"
private const val BASE_URL = "https://api.gamenative.app/v1/shadercache"

Expand All @@ -41,8 +49,6 @@ object ShaderCacheApi {
suspend fun fetchPool(key: ShaderCacheKey): ApiResult<Pool?> = call("pool") {
val url = "$BASE_URL/pool".toHttpUrl().newBuilder()
.addQueryParameter("game", key.game)
.addQueryParameter("dxvk", key.dxvk)
.addQueryParameter("vkd3d", key.vkd3d)
.build()
it.url(url).get()
}.map { raw ->
Expand All @@ -60,17 +66,41 @@ object ShaderCacheApi {
)
}

/**
* The current pool version of each of [games] (at most [MAX_CHECK_GAMES]) that has a pool; games without one are
* absent from the result.
*/
suspend fun checkPools(games: List<String>): ApiResult<Map<String, PoolVersion>> = call("pools/check") {
it.url("$BASE_URL/pools/check").post(jsonBody(JSONObject().put("games", JSONArray(games))))
}.map { raw ->
// Another service on this host can answer an unknown path with a 200 of its own.
val pools = JSONObject(raw.body).optJSONObject("pools") ?: throw IOException("pools/check: no pools in the response")
buildMap<String, PoolVersion> {
for (game in pools.keys()) {
val pool = pools.optJSONObject(game) ?: continue
put(
game,
PoolVersion(
version = pool.getLong("version"),
size = pool.optLong("size", -1L),
entries = pool.optLong("entries", 0L),
),
)
}
}
}

suspend fun beginUpload(key: ShaderCacheKey, meta: UploadMeta): ApiResult<UploadSlot> {
val body = JSONObject()
.put("game", key.game)
.put("dxvk", key.dxvk)
.put("vkd3d", key.vkd3d)
.put("sha256", meta.sha256)
.put("size", meta.size)
.put("appVersion", meta.appVersion)
.put("gpu", meta.gpu)
.put("driver", meta.driver)
.put("displayServer", meta.displayServer)
meta.dxvk?.let { body.put("dxvk", it) }
meta.vkd3d?.let { body.put("vkd3d", it) }
return call("upload/begin") { it.url("$BASE_URL/upload/begin").post(jsonBody(body)) }.map { raw ->
val json = JSONObject(raw.body)
UploadSlot(key = json.getString("key"), putUrl = json.getString("putUrl"))
Expand Down
7 changes: 5 additions & 2 deletions app/src/main/java/app/gamenative/api/SupportApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package app.gamenative.api

import android.os.SystemClock
import app.gamenative.utils.DebugReportUtils
import app.gamenative.utils.executeCancellable
import androidx.compose.runtime.mutableStateOf
import app.gamenative.PrefManager
import kotlinx.coroutines.CancellationException
Expand All @@ -13,6 +14,7 @@ import kotlinx.coroutines.withContext
import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MultipartBody
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.asRequestBody
Expand Down Expand Up @@ -910,15 +912,16 @@ object SupportApi {
url: String,
destination: File,
maxBytes: Long = Long.MAX_VALUE,
client: OkHttpClient = transferClient,
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
onProgress: ((Float) -> Unit)? = null,
): ApiResult<String> = withContext(Dispatchers.IO) {
if (!url.startsWith("https://")) return@withContext ApiResult.HttpError(400, "bad_url")
try {
destination.parentFile?.mkdirs()
val job = coroutineContext.job
val request = Request.Builder().url(url).get().build()
val result: Pair<String?, Int> = transferClient.newCall(request).execute().use { response ->
if (!response.isSuccessful) return@use null to response.code
val result: Pair<String?, Int> = client.newCall(request).executeCancellable { response ->
if (!response.isSuccessful) return@executeCancellable null to response.code
val total = response.body.contentLength()
if (total > maxBytes) throw IOException("download of $total bytes exceeds $maxBytes")
val digest = MessageDigest.getInstance("SHA-256")
Expand Down
Loading
Loading