Compare commits

..
Author SHA1 Message Date
AxTps 8b94e7dbbe fix: 更新渠道默认值跟随安装包版本
Sync source to CNB / Sync Git ref (push) Skipped
从 GitHub 下载 Beta 安装包安装后,更新通道默认选中 Release,
因为 update-channel.json 状态文件不存在时会硬编码回退到 release。

新增 theseus::default_update_channel(),按编译期版本号判断:含预发布
后缀(如 1.0.2-beta7)默认 Beta,否则 Release。数据库渠道与设置页
渠道两处默认值统一改为调用它,保证两者一致。
2026-10-13 22:11:06 +08:00
5 changed files with 47 additions and 82 deletions

No files matched your search

+1 -1
View File
@@ -339,7 +339,7 @@ fn get_launcher_root_dir() -> api::Result<String> {
fn get_update_channel(app: tauri::AppHandle) -> api::Result<String> { fn get_update_channel(app: tauri::AppHandle) -> api::Result<String> {
let channel = read_update_channel_state(&app)? let channel = read_update_channel_state(&app)?
.active_channel .active_channel
.unwrap_or_else(|| "release".to_string()); .unwrap_or_else(|| theseus::default_update_channel().to_string());
match channel.as_str() { match channel.as_str() {
"release" | "beta" => Ok(channel), "release" | "beta" => Ok(channel),
@@ -109,7 +109,6 @@ pub struct CrashAnalysisMod {
struct SourceCandidate { struct SourceCandidate {
path: PathBuf, path: PathBuf,
filename: String, filename: String,
upload_name: String,
source_type: &'static str, source_type: &'static str,
modified: SystemTime, modified: SystemTime,
} }
@@ -117,25 +116,10 @@ struct SourceCandidate {
#[derive(Debug)] #[derive(Debug)]
struct SourceText { struct SourceText {
filename: String, filename: String,
upload_name: String,
source_type: &'static str, source_type: &'static str,
modified: SystemTime, modified: SystemTime,
content: String, content: String,
} }
pub(super) struct CollectedCrashLog {
pub name: String,
pub content: CensoredString,
}
pub(super) struct CollectedCrashLogs {
pub combined_log: CensoredString,
pub sources: Vec<CollectedCrashLog>,
}
struct CrashContext {
sources: Vec<SourceText>,
credentials: Vec<Credentials>,
}
struct Rule { struct Rule {
id: &'static str, id: &'static str,
@@ -1201,25 +1185,20 @@ async fn collect_candidates(
) -> crate::Result<Vec<SourceCandidate>> { ) -> crate::Result<Vec<SourceCandidate>> {
let mut candidates = Vec::new(); let mut candidates = Vec::new();
let mut locations = vec![ let mut locations = vec![
( (directories.game_logs_dir(instance_root), "minecraft_log"),
directories.game_logs_dir(instance_root),
"minecraft_log",
None,
),
( (
directories.game_crash_reports_dir(instance_root), directories.game_crash_reports_dir(instance_root),
"crash_report", "crash_report",
Some("crash-reports"),
), ),
(instance_root.to_path_buf(), "instance_log", None), (instance_root.to_path_buf(), "instance_log"),
]; ];
if let Some(dot_minecraft) = isolated_minecraft_root(instance_root) { if let Some(dot_minecraft) = isolated_minecraft_root(instance_root) {
// PCL-CE also searches the shared `.minecraft` root for JVM crash // PCL-CE also searches the shared `.minecraft` root for JVM crash
// logs when a version-isolated instance is active. // logs when a version-isolated instance is active.
locations.push((dot_minecraft, "minecraft_root_log", None)); locations.push((dot_minecraft, "minecraft_root_log"));
} }
for (directory, default_type, upload_prefix) in locations { for (directory, default_type) in locations {
if !tokio::fs::try_exists(&directory).await? { if !tokio::fs::try_exists(&directory).await? {
continue; continue;
} }
@@ -1269,9 +1248,6 @@ async fn collect_candidates(
}; };
candidates.push(SourceCandidate { candidates.push(SourceCandidate {
path: entry.path(), path: entry.path(),
upload_name: upload_prefix
.map(|prefix| format!("{prefix}/{filename}"))
.unwrap_or_else(|| filename.clone()),
filename, filename,
source_type, source_type,
modified: metadata.modified().unwrap_or(SystemTime::UNIX_EPOCH), modified: metadata.modified().unwrap_or(SystemTime::UNIX_EPOCH),
@@ -1281,53 +1257,6 @@ async fn collect_candidates(
Ok(candidates) Ok(candidates)
} }
async fn collect_crash_context(
instance_id: &str,
) -> crate::Result<CrashContext> {
let state = State::get().await?;
let (instance_path, game_dir_override) =
resolve_instance_path(instance_id, &state).await?;
let instance_root = state
.directories
.resolve_game_dir(&instance_path, game_dir_override.as_deref());
let candidates =
collect_candidates(&instance_root, &state.directories).await?;
let selected = select_run_candidates(candidates);
let sources = read_sources(selected).await;
let credentials = Credentials::get_all(&state.pool)
.await?
.into_iter()
.map(|credential| credential.1)
.collect::<Vec<_>>();
Ok(CrashContext {
sources,
credentials,
})
}
pub(super) async fn collect_crash_logs(
instance_id: &str,
) -> crate::Result<CollectedCrashLogs> {
let CrashContext {
sources,
credentials,
..
} = collect_crash_context(instance_id).await?;
let combined_log =
CensoredString::censor(build_combined_log(&sources), &credentials);
let sources = sources
.into_iter()
.map(|source| CollectedCrashLog {
name: source.upload_name,
content: CensoredString::censor(source.content, &credentials),
})
.collect();
Ok(CollectedCrashLogs {
combined_log,
sources,
})
}
fn isolated_minecraft_root(instance_root: &Path) -> Option<PathBuf> { fn isolated_minecraft_root(instance_root: &Path) -> Option<PathBuf> {
let versions_dir = instance_root.parent()?; let versions_dir = instance_root.parent()?;
if versions_dir if versions_dir
@@ -1391,7 +1320,6 @@ async fn read_sources(candidates: Vec<SourceCandidate>) -> Vec<SourceText> {
}; };
sources.push(SourceText { sources.push(SourceText {
filename: candidate.filename, filename: candidate.filename,
upload_name: candidate.upload_name,
source_type: candidate.source_type, source_type: candidate.source_type,
modified: candidate.modified, modified: candidate.modified,
content: String::from_utf8_lossy(bytes).into_owned(), content: String::from_utf8_lossy(bytes).into_owned(),
@@ -1851,7 +1779,6 @@ mod tests {
fn source(filename: &str, content: &str) -> SourceText { fn source(filename: &str, content: &str) -> SourceText {
SourceText { SourceText {
filename: filename.to_string(), filename: filename.to_string(),
upload_name: filename.to_string(),
source_type: "minecraft_log", source_type: "minecraft_log",
modified: SystemTime::now(), modified: SystemTime::now(),
content: content.to_string(), content: content.to_string(),
@@ -1875,7 +1802,6 @@ mod tests {
let candidates = vec![SourceCandidate { let candidates = vec![SourceCandidate {
path: PathBuf::from("launcher_log.txt"), path: PathBuf::from("launcher_log.txt"),
filename: "launcher_log.txt".to_string(), filename: "launcher_log.txt".to_string(),
upload_name: "launcher_log.txt".to_string(),
source_type: "minecraft_log", source_type: "minecraft_log",
modified: SystemTime::now() - Duration::from_secs(181), modified: SystemTime::now() - Duration::from_secs(181),
}]; }];
+28 -1
View File
@@ -4,7 +4,8 @@ use futures::StreamExt;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::{Value, json}; use serde_json::{Value, json};
use super::crash_analysis::collect_crash_logs; use super::CensoredString;
use super::crash_analysis::analyze_crash;
use crate::State; use crate::State;
use crate::emit_logshare_ai_event; use crate::emit_logshare_ai_event;
@@ -199,6 +200,32 @@ fn source_identifier() -> String {
format!("{LOGSHARE_SOURCE_PREFIX}/{}", env!("CARGO_PKG_VERSION")) format!("{LOGSHARE_SOURCE_PREFIX}/{}", env!("CARGO_PKG_VERSION"))
} }
struct CollectedCrashLog {
name: String,
content: CensoredString,
}
struct CollectedCrashLogs {
combined_log: CensoredString,
sources: Vec<CollectedCrashLog>,
}
async fn collect_crash_logs(
instance_id: &str,
) -> crate::Result<CollectedCrashLogs> {
let analysis = analyze_crash(instance_id).await?;
Ok(CollectedCrashLogs {
combined_log: analysis.combined_log,
sources: analysis
.sources
.into_iter()
.map(|source| CollectedCrashLog {
name: source.filename,
content: source.content,
})
.collect(),
})
}
async fn prepare_upload(instance_id: &str) -> crate::Result<UploadBody> { async fn prepare_upload(instance_id: &str) -> crate::Result<UploadBody> {
let settings = get_log_share_settings().await?; let settings = get_log_share_settings().await?;
let logs = collect_crash_logs(instance_id).await?; let logs = collect_crash_logs(instance_id).await?;
+1 -1
View File
@@ -33,7 +33,7 @@ pub use logger::start_logger;
pub use state::db::{ pub use state::db::{
backup_current_app_db_for_update, beta_database_exists, backup_current_app_db_for_update, beta_database_exists,
copy_database_between_channels, copy_release_database_to_beta, copy_database_between_channels, copy_release_database_to_beta,
current_app_database_path, current_app_database_path, default_update_channel,
}; };
pub use state::{DirectoryInfo, State}; pub use state::{DirectoryInfo, State};
pub use storage::*; pub use storage::*;
+13 -1
View File
@@ -304,6 +304,18 @@ async fn app_db_path(settings_dir: &Path) -> crate::Result<PathBuf> {
Ok(settings_dir.join(channel).join(LEGACY_APP_DB_FILE)) Ok(settings_dir.join(channel).join(LEGACY_APP_DB_FILE))
} }
/// The update channel a fresh install should default to. Beta builds carry a
/// pre-release version (e.g. `1.0.2-beta7`), so their installers should start
/// on the Beta channel instead of silently falling back to Release.
pub fn default_update_channel() -> &'static str {
let version = env!("CARGO_PKG_VERSION");
if version.contains('-') {
"beta"
} else {
"release"
}
}
async fn read_update_channel( async fn read_update_channel(
settings_dir: &Path, settings_dir: &Path,
) -> crate::Result<&'static str> { ) -> crate::Result<&'static str> {
@@ -311,7 +323,7 @@ async fn read_update_channel(
let contents = match tokio::fs::read_to_string(&path).await { let contents = match tokio::fs::read_to_string(&path).await {
Ok(contents) => contents, Ok(contents) => contents,
Err(error) if error.kind() == std::io::ErrorKind::NotFound => { Err(error) if error.kind() == std::io::ErrorKind::NotFound => {
return Ok("release"); return Ok(default_update_channel());
} }
Err(error) => return Err(error.into()), Err(error) => return Err(error.into()),
}; };