From c3addde9b11b20cf45882980014f77f0c77fc110 Mon Sep 17 00:00:00 2001 From: funman300 Date: Tue, 18 Aug 2026 20:59:07 +0000 Subject: [PATCH] Correct FIFA17 SBC dispatch notifier lifecycle guard to post-exit invariant --- openfut-hook/src/sbc_dispatch.rs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/openfut-hook/src/sbc_dispatch.rs b/openfut-hook/src/sbc_dispatch.rs index f10638b..c2838d7 100644 --- a/openfut-hook/src/sbc_dispatch.rs +++ b/openfut-hook/src/sbc_dispatch.rs @@ -180,9 +180,15 @@ fn decide(input: DecisionInput) -> Result { if input.live_category_count != input.category_count { return Err(Rejection::ModelChanged); } + // The category-success notifier for this generation must have entered and + // fully returned before the SBC completion runs. On the pinned CardsDLL the + // completion fires immediately after the notifier unwinds (measured: notifier + // entries == exits == generation at completion), not nested inside it, so we + // bind both notifier counts to the current generation rather than requiring + // an in-flight notifier. if input.notifier_entries != generation || input.notifier_entries == 0 - || input.notifier_exits.checked_add(1) != Some(input.notifier_entries) + || input.notifier_exits != generation { return Err(Rejection::NotifierNotCurrent); } @@ -730,7 +736,7 @@ mod tests { live_category_count: 2, category_count: 2, notifier_entries: generation, - notifier_exits: generation - 1, + notifier_exits: generation, controller_matches: true, controller_model_matches: true, last_repaired_generation: generation - 1, @@ -776,8 +782,16 @@ mod tests { input.status = None; assert_eq!(decide(input), Err(Rejection::StatusUnreadable)); + // Notifier still in flight for this generation (has not returned) is rejected: + // on the pinned build the completion only runs after the notifier unwinds. let mut input = valid_input(1); - input.notifier_exits = 1; + input.notifier_exits = 0; + assert_eq!(decide(input), Err(Rejection::NotifierNotCurrent)); + + // A notifier count that does not match the current generation is rejected. + let mut input = valid_input(1); + input.notifier_entries = 2; + input.notifier_exits = 2; assert_eq!(decide(input), Err(Rejection::NotifierNotCurrent)); let mut input = valid_input(1);