Skip to content

Commit 1105dd6

Browse files
Investigate Batman early startup failure independently of graphics backend selection
- Enhance `LaunchVerification` with Windows user and path diagnostics - Implement dynamic Windows username detection in `check_prefix_health` - Add verification for Steam client and PhysX middleware in the prefix - Capture first 50 and last 100 lines of Wine log for early exit analysis - Expand `classify_graphics_evidence` to detect middleware and bootstrap failures - Include `STEAM_COMPAT_APP_ID` in launch environment - Update concise launch summary with new diagnostic fields Co-authored-by: weter11 <14630689+weter11@users.noreply.github.com>
1 parent ad65563 commit 1105dd6

4 files changed

Lines changed: 79 additions & 6 deletions

File tree

src/infra/logging/session.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@ pub struct LaunchVerification {
5050
pub process_lifetime_ms: Option<u64>,
5151
pub exit_code: Option<i32>,
5252
pub log_growth_observed: bool,
53+
pub windows_username: Option<String>,
54+
pub windows_user_path: Option<String>,
55+
pub key_paths_detected: HashMap<String, bool>,
56+
pub steam_client_exposed: bool,
57+
pub log_head: Vec<String>,
58+
pub log_tail: Vec<String>,
5359
}
5460

5561
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq)]

src/infra/logging/wine_capture.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,24 @@ pub fn classify_graphics_evidence(log_line: &str) -> Option<String> {
3232
if line_lower.contains("winemac.drv") {
3333
return None;
3434
}
35+
36+
// Specific middleware/dependency failure patterns
37+
if line_lower.contains("physx") {
38+
return Some(format!("PhysX/Middleware failure: {}", log_line.trim()));
39+
}
40+
3541
return Some(format!("DLL Load Failure: {}", log_line.trim()));
3642
}
3743
if line_lower.contains("not found") && line_lower.contains("which is needed by") {
3844
return Some(format!("DLL Dependency Missing: {}", log_line.trim()));
3945
}
4046

47+
// Steam/Client patterns
48+
if line_lower.contains("failed to create steam.exe") ||
49+
line_lower.contains("cannot find 'steam.exe'") ||
50+
(line_lower.contains("steam.exe") && (line_lower.contains("not found") || line_lower.contains("failed"))) {
51+
return Some(format!("Steam Client/Environment Failure: {}", log_line.trim()));
52+
}
53+
4154
None
4255
}

src/infra/runners/wine_tkg.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,8 @@ impl Runner for WineTkgRunner {
299299
);
300300

301301
env.insert("SteamAppId".to_string(), app_id_str.clone());
302-
env.insert("SteamGameId".to_string(), app_id_str);
302+
env.insert("SteamGameId".to_string(), app_id_str.clone());
303+
env.insert("STEAM_COMPAT_APP_ID".to_string(), app_id_str);
303304
env.insert("WINEPREFIX".to_string(), effective_game_prefix.to_string_lossy().to_string());
304305
env.insert("STEAM_COMPAT_DATA_PATH".to_string(), compat_data_path.to_string_lossy().to_string());
305306

src/launch/pipeline.rs

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,14 @@ impl LaunchPipeline {
391391

392392
let mut fatal_error = None;
393393
for evidence in &ctx.graphics_stack.graphics_stack_evidence {
394+
if evidence.contains("Steam Client/Environment Failure") {
395+
fatal_error = Some("steam_environment_incomplete");
396+
break;
397+
}
398+
if evidence.contains("PhysX/Middleware failure") {
399+
fatal_error = Some("middleware_dependency_failure");
400+
break;
401+
}
394402
if evidence.contains("DLL Load Failure") {
395403
fatal_error = Some("missing_required_module");
396404
break;
@@ -756,6 +764,12 @@ impl LaunchPipeline {
756764
let lines: Vec<&str> = content.lines().collect();
757765
ctx.graphics_stack.runtime_evidence.scan_metadata.line_count = lines.len();
758766

767+
// Capture Log Head/Tail
768+
ctx.verification.log_head = lines.iter().take(50).map(|s| s.to_string()).collect();
769+
if lines.len() > 50 {
770+
ctx.verification.log_tail = lines.iter().rev().take(100).rev().map(|s| s.to_string()).collect();
771+
}
772+
759773
// Derive component paths from dll resolutions
760774
let mut component_paths = HashMap::new();
761775
for res in &ctx.dll_resolutions {
@@ -964,17 +978,51 @@ impl LaunchPipeline {
964978
let mut metadata = HashMap::new();
965979
metadata.insert("prefix_path".to_string(), prefix.clone());
966980

981+
// Detect Windows username
982+
let users_dir = prefix_path.join("drive_c/users");
983+
if users_dir.exists() {
984+
if let Ok(entries) = std::fs::read_dir(&users_dir) {
985+
let mut usernames = Vec::new();
986+
for entry in entries.flatten() {
987+
if entry.file_type().map(|t| t.is_dir()).unwrap_or(false) {
988+
let name = entry.file_name().to_string_lossy().to_string();
989+
if name != "Public" && name != "All Users" && name != "Default User" {
990+
usernames.push(name);
991+
}
992+
}
993+
}
994+
if !usernames.is_empty() {
995+
// Sort and pick first (most likely primary)
996+
usernames.sort();
997+
let primary = usernames[0].clone();
998+
ctx.verification.windows_username = Some(primary.clone());
999+
ctx.verification.windows_user_path = Some(format!("C:\\users\\{}", primary));
1000+
metadata.insert("detected_windows_username".to_string(), primary);
1001+
}
1002+
}
1003+
}
1004+
1005+
let username = ctx.verification.windows_username.as_deref().unwrap_or("steamuser");
9671006
let common_dirs = [
968-
"drive_c/users/steamuser/Documents",
969-
"drive_c/users/steamuser/AppData/Local",
970-
"drive_c/users/steamuser/AppData/Roaming",
1007+
format!("drive_c/users/{}/Documents", username),
1008+
format!("drive_c/users/{}/AppData/Local", username),
1009+
format!("drive_c/users/{}/AppData/Roaming", username),
1010+
"drive_c/Program Files (x86)/Steam/steam.exe".to_string(),
1011+
"drive_c/windows/system32/PhysXLoader.dll".to_string(),
1012+
"drive_c/windows/syswow64/PhysXLoader.dll".to_string(),
9711013
];
9721014

9731015
for dir in common_dirs {
974-
let full_path = prefix_path.join(dir);
975-
metadata.insert(format!("dir_exists:{}", dir), full_path.exists().to_string());
1016+
let full_path = prefix_path.join(&dir);
1017+
let exists = full_path.exists();
1018+
metadata.insert(format!("path_exists:{}", dir), exists.to_string());
1019+
ctx.verification.key_paths_detected.insert(dir, exists);
9761020
}
9771021

1022+
// Check steam client exposure
1023+
ctx.verification.steam_client_exposed = spec.env.contains_key("STEAM_COMPAT_CLIENT_INSTALL_PATH") ||
1024+
spec.env.get("WINEPATH").map(|wp| wp.contains("Steam")).unwrap_or(false);
1025+
9781026
if let Some(logger) = &ctx.logger {
9791027
let _ = logger.info("prefix_health_check", "WINEPREFIX sanity check complete".to_string(), None, metadata);
9801028
}
@@ -1148,6 +1196,11 @@ impl LaunchPipeline {
11481196
metadata.insert("verification_detailed".to_string(), detailed.clone());
11491197
}
11501198

1199+
if let Some(ref username) = ctx.verification.windows_username {
1200+
metadata.insert("windows_user".to_string(), username.clone());
1201+
}
1202+
metadata.insert("steam_client_exposed".to_string(), ctx.verification.steam_client_exposed.to_string());
1203+
11511204
let _ = logger.info("launch_summary_concise", "Concise launch summary recorded".to_string(), None, metadata);
11521205
}
11531206
}

0 commit comments

Comments
 (0)