fix: 修复自选目录实例的模组同步路径
Axolotl desktop CI / guardrails (push) Has been cancelled
Axolotl desktop CI / desktop (macos-latest) (push) Has been cancelled
Axolotl desktop CI / desktop (ubuntu-latest) (push) Has been cancelled
Axolotl desktop CI / desktop (windows-latest) (push) Has been cancelled
Axolotl desktop CI / website (push) Has been cancelled
Repository checks / typos (push) Has been cancelled
Repository checks / tombi (push) Has been cancelled
Rust checks / shear (push) Has been cancelled
Sync source to CNB / Sync Git ref (push) Has been cancelled
Sync LobeHub models / sync (push) Has been cancelled
Axolotl desktop CI / guardrails (push) Has been cancelled
Axolotl desktop CI / desktop (macos-latest) (push) Has been cancelled
Axolotl desktop CI / desktop (ubuntu-latest) (push) Has been cancelled
Axolotl desktop CI / desktop (windows-latest) (push) Has been cancelled
Axolotl desktop CI / website (push) Has been cancelled
Repository checks / typos (push) Has been cancelled
Repository checks / tombi (push) Has been cancelled
Rust checks / shear (push) Has been cancelled
Sync source to CNB / Sync Git ref (push) Has been cancelled
Sync LobeHub models / sync (push) Has been cancelled
This commit is contained in:
1 parent
c273734aec
commit
2359b80250
1 file changed
+101
-8
@@ -87,11 +87,6 @@ pub(crate) async fn sync_instance_content_files(
|
|||||||
.as_deref()
|
.as_deref()
|
||||||
.map(str::trim)
|
.map(str::trim)
|
||||||
.is_some_and(|linked| !linked.is_empty());
|
.is_some_and(|linked| !linked.is_empty());
|
||||||
let instance_files_root = if is_direct_linked {
|
|
||||||
content_root.clone()
|
|
||||||
} else {
|
|
||||||
state.directories.instances_dir().join(&instance.path)
|
|
||||||
};
|
|
||||||
let cache_key_path = if is_direct_linked {
|
let cache_key_path = if is_direct_linked {
|
||||||
content_root.to_string_lossy().into_owned()
|
content_root.to_string_lossy().into_owned()
|
||||||
} else {
|
} else {
|
||||||
@@ -168,8 +163,9 @@ pub(crate) async fn sync_instance_content_files(
|
|||||||
let hash_key = file.hash_cache_key.trim_end_matches(".disabled");
|
let hash_key = file.hash_cache_key.trim_end_matches(".disabled");
|
||||||
let existing_file = existing_files_by_path.get(&file.relative_path);
|
let existing_file = existing_files_by_path.get(&file.relative_path);
|
||||||
let (scanned_sha1, scanned_size) = if existing_file.is_some() {
|
let (scanned_sha1, scanned_size) = if existing_file.is_some() {
|
||||||
let path =
|
// Read from the resolved game directory. Managed StarLight
|
||||||
join_content_path(&instance_files_root, &file.relative_path);
|
// instances may keep their files outside the launcher data root.
|
||||||
|
let path = join_content_path(&content_root, &file.relative_path);
|
||||||
let (_, sha1) = fetch::sha1_file_async(&path).await?;
|
let (_, sha1) = fetch::sha1_file_async(&path).await?;
|
||||||
(sha1, file.size)
|
(sha1, file.size)
|
||||||
} else {
|
} else {
|
||||||
@@ -748,7 +744,9 @@ mod tests {
|
|||||||
base_path: minecraft.path().to_path_buf(),
|
base_path: minecraft.path().to_path_buf(),
|
||||||
instance_folder: "versions/1.12.2-linked".to_string(),
|
instance_folder: "versions/1.12.2-linked".to_string(),
|
||||||
instance_path: None,
|
instance_path: None,
|
||||||
game_dir_mode: None,
|
game_dir_mode: Some(
|
||||||
|
crate::launcher::ExternalGameDirMode::Shared,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
&state,
|
&state,
|
||||||
)
|
)
|
||||||
@@ -875,6 +873,59 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn refresh_reconciles_externally_replaced_profile_mods() {
|
||||||
|
let state = global_state().await;
|
||||||
|
let metadata = crate::api::instance::create(
|
||||||
|
format!("sync-external {}", uuid::Uuid::new_v4()),
|
||||||
|
"1.20.1".to_string(),
|
||||||
|
crate::state::ModLoader::Vanilla,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
crate::state::InstanceLink::Unmanaged,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let instance = &metadata.instance;
|
||||||
|
let mods = state.directories.instance_game_dir(instance).join("mods");
|
||||||
|
fs::create_dir_all(&mods).unwrap();
|
||||||
|
fs::write(mods.join("old-version.jar"), "old").unwrap();
|
||||||
|
fs::write(mods.join("same-name.jar"), "old").unwrap();
|
||||||
|
let initial =
|
||||||
|
sync_instance_content_files(instance, &state).await.unwrap();
|
||||||
|
assert_eq!(initial.len(), 2);
|
||||||
|
|
||||||
|
fs::remove_file(mods.join("old-version.jar")).unwrap();
|
||||||
|
fs::write(mods.join("new-version.jar"), "new version").unwrap();
|
||||||
|
fs::write(mods.join("same-name.jar"), "new").unwrap();
|
||||||
|
let refreshed =
|
||||||
|
sync_instance_content_files(instance, &state).await.unwrap();
|
||||||
|
assert_eq!(refreshed.len(), 2);
|
||||||
|
assert!(
|
||||||
|
refreshed
|
||||||
|
.iter()
|
||||||
|
.any(|file| file.relative_path == "mods/new-version.jar")
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!refreshed
|
||||||
|
.iter()
|
||||||
|
.any(|file| file.relative_path == "mods/old-version.jar")
|
||||||
|
);
|
||||||
|
let before = initial
|
||||||
|
.iter()
|
||||||
|
.find(|file| file.relative_path == "mods/same-name.jar")
|
||||||
|
.unwrap();
|
||||||
|
let after = refreshed
|
||||||
|
.iter()
|
||||||
|
.find(|file| file.relative_path == "mods/same-name.jar")
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(before.id, after.id);
|
||||||
|
assert_ne!(before.sha1, after.sha1);
|
||||||
|
assert_eq!(before.size, after.size);
|
||||||
|
}
|
||||||
|
|
||||||
/// Regression probe: an instance whose `game_dir_override` points to an
|
/// Regression probe: an instance whose `game_dir_override` points to an
|
||||||
/// external `.minecraft` root must have its content scanned from that root,
|
/// external `.minecraft` root must have its content scanned from that root,
|
||||||
/// not from the (empty) managed instance folder. This is the split-brain
|
/// not from the (empty) managed instance folder. This is the split-brain
|
||||||
@@ -947,6 +998,48 @@ mod tests {
|
|||||||
"expected the override-root mod to be scanned"
|
"expected the override-root mod to be scanned"
|
||||||
);
|
);
|
||||||
assert!(files[0].relative_path.ends_with("mods/my-mod.jar"));
|
assert!(files[0].relative_path.ends_with("mods/my-mod.jar"));
|
||||||
|
|
||||||
|
let refreshed =
|
||||||
|
sync_instance_content_files(&instance, &state).await.expect(
|
||||||
|
"repeat refresh must hash files in the actual game directory",
|
||||||
|
);
|
||||||
|
assert_eq!(refreshed[0].sha1, files[0].sha1);
|
||||||
|
|
||||||
|
fs::write(mods_dir.join("my-mod.jar"), "new").unwrap();
|
||||||
|
fs::write(mods_dir.join("added.jar"), "added mod").unwrap();
|
||||||
|
let replaced = sync_instance_content_files(&instance, &state)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(replaced.len(), 2);
|
||||||
|
let updated = replaced
|
||||||
|
.iter()
|
||||||
|
.find(|file| file.relative_path == "mods/my-mod.jar")
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(updated.id, files[0].id);
|
||||||
|
assert_ne!(updated.sha1, files[0].sha1);
|
||||||
|
assert_eq!(updated.size, files[0].size);
|
||||||
|
|
||||||
|
fs::remove_file(mods_dir.join("my-mod.jar")).unwrap();
|
||||||
|
fs::rename(
|
||||||
|
mods_dir.join("added.jar"),
|
||||||
|
mods_dir.join("added.jar.disabled"),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let removed = sync_instance_content_files(&instance, &state)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(removed.len(), 1);
|
||||||
|
assert_eq!(removed[0].relative_path, "mods/added.jar.disabled");
|
||||||
|
assert!(!removed[0].enabled);
|
||||||
|
let stored =
|
||||||
|
sqlite::content_rows::get_instance_files(&instance.id, &state.pool)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert!(
|
||||||
|
stored
|
||||||
|
.iter()
|
||||||
|
.any(|file| file.id == files[0].id && file.missing)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in new issue
Block a user