This commit is contained in:
2026-05-01 16:08:19 +08:00
parent dce84f677d
commit 14208ccb64
15 changed files with 752 additions and 175 deletions

View File

@@ -1396,9 +1396,13 @@ pub async fn use_puzzle_runtime_prop(
));
}
};
let should_sync_freeze_boundary = matches!(prop_kind.as_str(), "freezeTime" | "freeze_time");
let billing_asset_id = format!("{}:{}:{}", run_id, prop_kind, current_utc_micros());
let reducer_owner_user_id = owner_user_id.clone();
let run = execute_billable_asset_operation(
let reducer_run_id = run_id.clone();
let fallback_run_id = run_id.clone();
let fallback_owner_user_id = owner_user_id.clone();
let run_result = execute_billable_asset_operation(
&state,
&owner_user_id,
billing_asset_kind,
@@ -1407,7 +1411,7 @@ pub async fn use_puzzle_runtime_prop(
state
.spacetime_client()
.use_puzzle_runtime_prop(PuzzleRunPropRecordInput {
run_id,
run_id: reducer_run_id,
owner_user_id: reducer_owner_user_id,
prop_kind,
used_at_micros: current_utc_micros(),
@@ -1417,8 +1421,30 @@ pub async fn use_puzzle_runtime_prop(
.map_err(map_puzzle_client_error)
},
)
.await
.map_err(|error| puzzle_error_response(&request_context, PUZZLE_RUNTIME_PROVIDER, error))?;
.await;
let run = match run_result {
Ok(run) => run,
Err(error) if should_sync_puzzle_freeze_boundary(&error, should_sync_freeze_boundary) => {
// 中文注释:冻结确认窗打开时前端会暂停视觉计时,但正式 run 仍可能在服务端边界帧先结算失败。
// 这类情况已由扣费包装器退款,此处只同步失败态快照,避免玩家看到“操作不合法”。
state
.spacetime_client()
.get_puzzle_run(fallback_run_id, fallback_owner_user_id)
.await
.map_err(map_puzzle_client_error)
.map_err(|error| {
puzzle_error_response(&request_context, PUZZLE_RUNTIME_PROVIDER, error)
})?
}
Err(error) => {
return Err(puzzle_error_response(
&request_context,
PUZZLE_RUNTIME_PROVIDER,
error,
));
}
};
Ok(json_success_body(
Some(&request_context),
@@ -2503,6 +2529,10 @@ fn map_puzzle_client_error(error: SpacetimeClientError) -> AppError {
}))
}
fn should_sync_puzzle_freeze_boundary(error: &AppError, is_freeze_time: bool) -> bool {
is_freeze_time && error.body_text().contains("操作不合法")
}
fn is_missing_puzzle_form_draft_procedure_error(error: &SpacetimeClientError) -> bool {
matches!(error, SpacetimeClientError::Procedure(message) if
message.contains("save_puzzle_form_draft")
@@ -3580,6 +3610,26 @@ mod tests {
let response = error.into_response();
assert_eq!(response.status(), StatusCode::BAD_GATEWAY);
}
#[test]
fn freeze_boundary_sync_only_matches_freeze_invalid_operation() {
let invalid_operation =
AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
"provider": "spacetimedb",
"message": "操作不合法",
}));
let other_error = AppError::from_status(StatusCode::BAD_GATEWAY).with_details(json!({
"provider": "spacetimedb",
"message": "陶泥币余额不足",
}));
assert!(should_sync_puzzle_freeze_boundary(&invalid_operation, true));
assert!(!should_sync_puzzle_freeze_boundary(
&invalid_operation,
false
));
assert!(!should_sync_puzzle_freeze_boundary(&other_error, true));
}
}
struct PuzzleDashScopeSettings {