Files
launcher/src-tauri/src/features/instance_sync.rs
T
2026-04-06 22:05:27 +02:00

124 lines
3.2 KiB
Rust

use std::{collections::HashMap, fs, path::PathBuf};
use lyceris::auth::microsoft::MinecraftAccount;
use tauri::State;
use crate::{features::Feature, launcher::launcher_dir, GameVersion, SharedState};
pub struct InstanceSync {}
impl Default for InstanceSync {
fn default() -> Self {
Self {}
}
}
impl Feature for InstanceSync {
fn launch(
&mut self,
state: &State<'_, SharedState>,
version: &GameVersion,
_account: &MinecraftAccount,
) -> super::FeatureResult {
if state.lock().unwrap().settings.features.sync_options {
sync_to_instance(version).map_err(|e| e.to_string())?;
}
Ok(())
}
fn after_launch(
&mut self,
state: &State<'_, SharedState>,
version: &GameVersion,
_account: &MinecraftAccount,
) -> super::FeatureResult {
if state.lock().unwrap().settings.features.sync_options {
sync_back_from_instance(version).map_err(|e| e.to_string())?;
}
Ok(())
}
}
fn sync_to_instance(version: &GameVersion) -> std::io::Result<()> {
let global_path = options_path_global();
let instance_path = options_path_instance(version);
if !global_path.exists() {
if let Ok(instance_content) = fs::read_to_string(instance_path) {
fs::write(global_path, instance_content)?;
}
return Ok(());
}
let global_content = fs::read_to_string(global_path)?;
// Ensure instance dir exists
if let Some(parent) = instance_path.parent() {
fs::create_dir_all(parent)?;
}
fs::write(instance_path, global_content)?;
Ok(())
}
fn sync_back_from_instance(version: &GameVersion) -> std::io::Result<()> {
let global_path = options_path_global();
let instance_path = options_path_instance(version);
let global_content = fs::read_to_string(&global_path)?;
let instance_content = fs::read_to_string(&instance_path)?;
let mut global_map = parse_options(&global_content);
let instance_map = parse_options(&instance_content);
// Only apply changes (ignore deletions)
for (key, instance_value) in instance_map {
match global_map.get(&key) {
Some(global_value) if global_value == &instance_value => {}
_ => {
// changed or new → update global
global_map.insert(key, instance_value);
}
}
}
fs::write(global_path, serialize_options(&global_map))?;
Ok(())
}
fn options_path_global() -> PathBuf {
launcher_dir().join("options.txt")
}
fn options_path_instance(version: &GameVersion) -> PathBuf {
launcher_dir().join(&version.id).join("options.txt")
}
fn parse_options(content: &str) -> HashMap<String, String> {
let mut map = HashMap::new();
for line in content.lines() {
if line.trim().is_empty() {
continue;
}
if let Some((key, value)) = line.split_once(':') {
map.insert(key.to_string(), value.to_string());
}
}
map
}
fn serialize_options(map: &HashMap<String, String>) -> String {
map.iter()
.map(|(k, v)| format!("{k}:{v}"))
.collect::<Vec<_>>()
.join("\n")
}