Request + queue logic.
| 367 | |
| 368 | // Request + queue logic. |
| 369 | static void RequestSchemaForApp(uint32_t appId) { |
| 370 | if (!MetadataSync::SchemaFetchEnabled()) return; |
| 371 | if (appId == 0) return; |
| 372 | if (g_shuttingDown.load(std::memory_order_acquire)) return; |
| 373 | if (g_connHandle.load(std::memory_order_relaxed) == 0) return; |
| 374 | |
| 375 | { |
| 376 | std::lock_guard<std::mutex> lock(g_fetchMutex); |
| 377 | if (!g_fetchAttempted.insert(appId).second) return; |
| 378 | } |
| 379 | |
| 380 | std::string steamPath = CloudIntercept::GetSteamPath(); |
| 381 | std::string schemaPath = steamPath + "appcache/stats/UserGameStatsSchema_" |
| 382 | + std::to_string(appId) + ".bin"; |
| 383 | |
| 384 | struct stat st; |
| 385 | if (stat(schemaPath.c_str(), &st) == 0 && st.st_size > 0) { |
| 386 | LOG("[SchemaFetch] app %u: schema already on disk (%ld bytes), skipping", |
| 387 | appId, (long)st.st_size); |
| 388 | return; |
| 389 | } |
| 390 | LOG("[SchemaFetch] app %u: needs fetch", appId); |
| 391 | |
| 392 | // Phase 1: discover owners from reviews + verify public stats |
| 393 | std::vector<uint64_t> owners = FetchReviewOwnerIds(appId); |
| 394 | std::vector<uint64_t> verified; |
| 395 | for (uint64_t sid : owners) { |
| 396 | if (g_shuttingDown.load(std::memory_order_acquire)) return; |
| 397 | if (HasPublicStats(appId, sid)) { |
| 398 | verified.push_back(sid); |
| 399 | if (verified.size() >= 3) break; |
| 400 | } |
| 401 | } |
| 402 | |
| 403 | // Phase 2: fallback owners if review discovery found nothing |
| 404 | bool usingFallback = verified.empty(); |
| 405 | if (usingFallback) { |
| 406 | for (uint64_t id : kFallbackOwnerIds) |
| 407 | verified.push_back(id); |
| 408 | } |
| 409 | if (verified.empty()) return; |
| 410 | |
| 411 | // Enqueue sends |
| 412 | { |
| 413 | std::lock_guard<std::mutex> lock(g_sendMutex); |
| 414 | for (uint64_t owner : verified) |
| 415 | g_sendQueue.push({appId, owner}); |
| 416 | } |
| 417 | LOG("[SchemaFetch] app %u: queued %zu request(s) via %s", |
| 418 | appId, verified.size(), usingFallback ? "fallback" : "review-owner discovery"); |
| 419 | } |
| 420 | |
| 421 | // Drain on net thread. |
| 422 | void DrainOnNetThread() { |
no test coverage detected