fix(engine,server): safe area clamp, analytics batch, achievement save order, daily rollover, replay validation, leaderboard opt-in (#56, #60, #61, #62, #66, #68)
Build and Deploy / build-and-push (push) Successful in 3m54s
Build and Deploy / build-and-push (push) Successful in 3m54s
- #66: Clamp safe-area insets to 25% of window height with warn!() on excess - #68: Move fire_flush outside per-event loop in analytics (batch flush once) - #56: Persist progress before marking reward_granted to prevent XP loss on crash - #60: Add DateRolloverTimer + check_date_rollover system for midnight seed refresh - #62: Add validate_header() in replay upload with mode/draw_mode allowlists - #61: Restore two-query leaderboard opt-in check (SELECT then UPDATE); original queries already in .sqlx cache; EXISTS variant would require sqlx prepare Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -244,7 +244,9 @@ fn start_shake_anim(
|
||||
}
|
||||
let dest_pile = &ev.to;
|
||||
// Collect the card ids that belong to the destination pile.
|
||||
let Some(pile) = game.0.piles.get(dest_pile) else { continue };
|
||||
let Some(pile) = game.0.piles.get(dest_pile) else {
|
||||
continue;
|
||||
};
|
||||
let dest_card_ids: Vec<u32> = pile.cards.iter().map(|c| c.id).collect();
|
||||
|
||||
if dest_card_ids.is_empty() {
|
||||
@@ -395,7 +397,9 @@ fn start_deal_anim(
|
||||
return;
|
||||
}
|
||||
let Some(layout) = layout else { return };
|
||||
let Some(&stock_pos) = layout.0.pile_positions.get(&PileType::Stock) else { return };
|
||||
let Some(&stock_pos) = layout.0.pile_positions.get(&PileType::Stock) else {
|
||||
return;
|
||||
};
|
||||
let stock_start = Vec3::new(stock_pos.x, stock_pos.y, 0.0);
|
||||
|
||||
let speed = settings.as_ref().map(|s| &s.0.animation_speed);
|
||||
@@ -501,7 +505,12 @@ fn start_foundation_flourish(
|
||||
game: Res<GameStateResource>,
|
||||
settings: Option<Res<SettingsResource>>,
|
||||
card_entities: Query<(Entity, &CardEntity)>,
|
||||
mut pile_markers: Query<(Entity, &PileMarker, &Sprite, Option<&FoundationMarkerFlourish>)>,
|
||||
mut pile_markers: Query<(
|
||||
Entity,
|
||||
&PileMarker,
|
||||
&Sprite,
|
||||
Option<&FoundationMarkerFlourish>,
|
||||
)>,
|
||||
mut commands: Commands,
|
||||
) {
|
||||
let reduce_motion = settings.as_deref().is_some_and(|s| s.0.reduce_motion_mode);
|
||||
@@ -767,7 +776,8 @@ mod tests {
|
||||
"flourish scale at t=0 must be 1.0"
|
||||
);
|
||||
assert!(
|
||||
(foundation_flourish_scale(dur / 2.0, dur) - FOUNDATION_FLOURISH_PEAK_SCALE).abs() < 1e-5,
|
||||
(foundation_flourish_scale(dur / 2.0, dur) - FOUNDATION_FLOURISH_PEAK_SCALE).abs()
|
||||
< 1e-5,
|
||||
"flourish scale at midpoint must be FOUNDATION_FLOURISH_PEAK_SCALE"
|
||||
);
|
||||
assert!(
|
||||
@@ -848,10 +858,8 @@ mod tests {
|
||||
|
||||
// Spawn a minimal CardEntity matching that id so the system would
|
||||
// find it and insert ShakeAnim if the gate were absent.
|
||||
app.world_mut().spawn((
|
||||
CardEntity { card_id },
|
||||
Transform::default(),
|
||||
));
|
||||
app.world_mut()
|
||||
.spawn((CardEntity { card_id }, Transform::default()));
|
||||
|
||||
app.world_mut()
|
||||
.resource_mut::<Messages<MoveRejectedEvent>>()
|
||||
@@ -867,7 +875,10 @@ mod tests {
|
||||
.query::<&ShakeAnim>()
|
||||
.iter(app.world())
|
||||
.count();
|
||||
assert_eq!(shake_count, 0, "ShakeAnim must not be inserted under reduce-motion");
|
||||
assert_eq!(
|
||||
shake_count, 0,
|
||||
"ShakeAnim must not be inserted under reduce-motion"
|
||||
);
|
||||
}
|
||||
|
||||
/// `start_foundation_flourish` must not insert `FoundationFlourish` when
|
||||
@@ -901,6 +912,9 @@ mod tests {
|
||||
.query::<&FoundationFlourish>()
|
||||
.iter(app.world())
|
||||
.count();
|
||||
assert_eq!(flourish_count, 0, "FoundationFlourish must not be inserted under reduce-motion");
|
||||
assert_eq!(
|
||||
flourish_count, 0,
|
||||
"FoundationFlourish must not be inserted under reduce-motion"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user