feat: 跨实例同步移植 WIP 检查点(第1轮+2a+2b+synced_servers)
Sync source to CNB / Sync Git ref (push) Has been cancelled
Sync source to CNB / Sync Git ref (push) Has been cancelled
从 Axolotl 移植跨实例同步子系统,当前进度: - 第1轮:synced_options/files.rs + mod.rs 骨架,state 层补齐 SyncedOption/InstanceSyncedOptions/CanonicalValue/GameOptionKind 等 - 2a:game_options 的 api_types/options_file/catalog(10文件) - 2b:game_options 除 resource_pack_options/local_settings_editor 外的全部(settings_editor/write_shared_settings/fullscreen/launch_overrides/instance_support/read_instance_changes/pack_updates/source_selection + locales/5文件);State 补 synced_options_lock/game_locale_indexer/lock_synced_options - synced_servers:6 文件全部搬入 - 35+20 处 sqlx::query! 宏改写为非宏 query+bind 已知未完成:synced_servers::operations 依赖 synced_options::instance_option_supported(属 orchestration,第5轮),当前编译不通。
This commit is contained in:
1 parent
5a289c3a15
commit
ed61a5942c
43 files changed
+10607
No files matched your search
@@ -0,0 +1,104 @@
|
||||
use super::files::synced_options_path;
|
||||
use crate::State;
|
||||
use crate::state::SyncedOption;
|
||||
use crate::util::io;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum SyncedOptionJoinResolution {
|
||||
UseSynced,
|
||||
UseInstance,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(tag = "action", rename_all = "snake_case")]
|
||||
pub(super) enum PendingAction {
|
||||
SelectSource,
|
||||
Join {
|
||||
resolution: Option<SyncedOptionJoinResolution>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub(super) struct PendingChange {
|
||||
pub instance_id: String,
|
||||
pub option: SyncedOption,
|
||||
pub action: PendingAction,
|
||||
}
|
||||
|
||||
pub(super) async fn read(state: &State) -> crate::Result<Vec<PendingChange>> {
|
||||
let path = synced_options_path(state).join("pending-changes.json");
|
||||
match tokio::fs::read(path).await {
|
||||
Ok(bytes) => Ok(serde_json::from_slice(&bytes)?),
|
||||
Err(error) if error.kind() == std::io::ErrorKind::NotFound => {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
Err(error) => Err(error.into()),
|
||||
}
|
||||
}
|
||||
|
||||
async fn write(changes: &[PendingChange], state: &State) -> crate::Result<()> {
|
||||
let directory = synced_options_path(state);
|
||||
io::create_dir_all(&directory).await?;
|
||||
let temporary = directory.join("pending-changes.json.tmp");
|
||||
io::write(&temporary, serde_json::to_vec(changes)?).await?;
|
||||
tokio::fs::rename(temporary, directory.join("pending-changes.json"))
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Callers hold the synced-options lock while updating or replaying requests.
|
||||
pub(super) async fn save(
|
||||
change: PendingChange,
|
||||
state: &State,
|
||||
) -> crate::Result<()> {
|
||||
let mut changes = read(state).await?;
|
||||
changes.retain(|previous| {
|
||||
previous.option != change.option
|
||||
|| (change.action != PendingAction::SelectSource
|
||||
&& previous.instance_id != change.instance_id)
|
||||
});
|
||||
changes.push(change);
|
||||
write(&changes, state).await
|
||||
}
|
||||
|
||||
pub(super) async fn remove(
|
||||
change: &PendingChange,
|
||||
state: &State,
|
||||
) -> crate::Result<()> {
|
||||
let mut changes = read(state).await?;
|
||||
let previous_len = changes.len();
|
||||
changes.retain(|previous| previous != change);
|
||||
if changes.len() != previous_len {
|
||||
write(&changes, state).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) async fn cancel(
|
||||
option: SyncedOption,
|
||||
instance_id: Option<&str>,
|
||||
state: &State,
|
||||
) -> crate::Result<()> {
|
||||
let mut changes = read(state).await?;
|
||||
let previous_len = changes.len();
|
||||
changes.retain(|change| {
|
||||
change.option != option
|
||||
|| instance_id.is_some_and(|id| change.instance_id != id)
|
||||
});
|
||||
if changes.len() != previous_len {
|
||||
write(&changes, state).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(in crate::api::instance) async fn contains(
|
||||
instance_id: &str,
|
||||
option: SyncedOption,
|
||||
state: &State,
|
||||
) -> crate::Result<bool> {
|
||||
Ok(read(state).await?.iter().any(|change| {
|
||||
change.instance_id == instance_id && change.option == option
|
||||
}))
|
||||
}
|
||||
Reference in new issue
Block a user