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:
@@ -41,7 +41,7 @@ use solitaire_core::rules::{can_place_on_foundation, can_place_on_tableau};
|
||||
use crate::card_plugin::RightClickHighlight;
|
||||
use crate::layout::{Layout, LayoutResource};
|
||||
use crate::resources::{DragState, GameStateResource};
|
||||
use crate::table_plugin::{PileMarker, PILE_MARKER_DEFAULT_COLOUR};
|
||||
use crate::table_plugin::{PILE_MARKER_DEFAULT_COLOUR, PileMarker};
|
||||
use crate::ui_theme::{
|
||||
DROP_TARGET_FILL, DROP_TARGET_OUTLINE, DROP_TARGET_OUTLINE_PX, Z_DROP_OVERLAY,
|
||||
};
|
||||
@@ -126,7 +126,9 @@ fn update_cursor_icon(
|
||||
button_q: Query<&Interaction, With<Button>>,
|
||||
mut commands: Commands,
|
||||
) {
|
||||
let Ok((win_entity, window)) = windows.single() else { return };
|
||||
let Ok((win_entity, window)) = windows.single() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let is_dragging = !drag.is_idle();
|
||||
|
||||
@@ -225,7 +227,9 @@ fn update_drop_highlights(
|
||||
let Some(game) = game else { return };
|
||||
|
||||
// The first element of drag.cards is the bottom card that lands on the target.
|
||||
let Some(&bottom_id) = drag.cards.first() else { return };
|
||||
let Some(&bottom_id) = drag.cards.first() else {
|
||||
return;
|
||||
};
|
||||
let bottom_card = game
|
||||
.0
|
||||
.piles
|
||||
@@ -233,7 +237,9 @@ fn update_drop_highlights(
|
||||
.flat_map(|p| p.cards.iter())
|
||||
.find(|c| c.id == bottom_id)
|
||||
.cloned();
|
||||
let Some(bottom_card) = bottom_card else { return };
|
||||
let Some(bottom_card) = bottom_card else {
|
||||
return;
|
||||
};
|
||||
let drag_count = drag.cards.len();
|
||||
|
||||
for (marker, mut sprite, _rch) in &mut markers {
|
||||
@@ -532,10 +538,7 @@ mod tests {
|
||||
fn marker_valid_and_default_colours_are_distinct() {
|
||||
// Regression guard — ensure these constants haven't been accidentally
|
||||
// set to the same value.
|
||||
assert_ne!(
|
||||
format!("{MARKER_VALID:?}"),
|
||||
format!("{MARKER_DEFAULT:?}")
|
||||
);
|
||||
assert_ne!(format!("{MARKER_VALID:?}"), format!("{MARKER_DEFAULT:?}"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -603,13 +606,17 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn cursor_over_draggable_returns_false_for_empty_game() {
|
||||
use solitaire_core::game_state::{DrawMode, GameState};
|
||||
use crate::layout::compute_layout;
|
||||
use solitaire_core::game_state::{DrawMode, GameState};
|
||||
|
||||
let game = GameState::new(42, DrawMode::DrawOne);
|
||||
let layout = compute_layout(Vec2::new(1280.0, 800.0), 0.0, 0.0, true);
|
||||
// A cursor far off-screen should never hit anything.
|
||||
assert!(!cursor_over_draggable(Vec2::new(-9999.0, -9999.0), &game, &layout));
|
||||
assert!(!cursor_over_draggable(
|
||||
Vec2::new(-9999.0, -9999.0),
|
||||
&game,
|
||||
&layout
|
||||
));
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
@@ -627,7 +634,12 @@ mod tests {
|
||||
let mut app = App::new();
|
||||
app.add_plugins(MinimalPlugins)
|
||||
.insert_resource(GameStateResource(game))
|
||||
.insert_resource(LayoutResource(compute_layout(Vec2::new(1280.0, 800.0), 0.0, 0.0, true)))
|
||||
.insert_resource(LayoutResource(compute_layout(
|
||||
Vec2::new(1280.0, 800.0),
|
||||
0.0,
|
||||
0.0,
|
||||
true,
|
||||
)))
|
||||
.insert_resource(DragState::default())
|
||||
.add_systems(Update, update_drop_target_overlays);
|
||||
app
|
||||
@@ -674,9 +686,19 @@ mod tests {
|
||||
set_tableau_top(
|
||||
&mut game,
|
||||
2,
|
||||
Card { id: 9001, suit: Suit::Spades, rank: Rank::Six, face_up: true },
|
||||
Card {
|
||||
id: 9001,
|
||||
suit: Suit::Spades,
|
||||
rank: Rank::Six,
|
||||
face_up: true,
|
||||
},
|
||||
);
|
||||
let dragged = Card { id: 9002, suit: Suit::Hearts, rank: Rank::Five, face_up: true };
|
||||
let dragged = Card {
|
||||
id: 9002,
|
||||
suit: Suit::Hearts,
|
||||
rank: Rank::Five,
|
||||
face_up: true,
|
||||
};
|
||||
|
||||
let mut app = overlay_test_app(game);
|
||||
begin_drag_with(&mut app, dragged);
|
||||
@@ -704,9 +726,19 @@ mod tests {
|
||||
set_tableau_top(
|
||||
&mut game,
|
||||
2,
|
||||
Card { id: 9101, suit: Suit::Clubs, rank: Rank::Six, face_up: true },
|
||||
Card {
|
||||
id: 9101,
|
||||
suit: Suit::Clubs,
|
||||
rank: Rank::Six,
|
||||
face_up: true,
|
||||
},
|
||||
);
|
||||
let dragged = Card { id: 9102, suit: Suit::Spades, rank: Rank::Five, face_up: true };
|
||||
let dragged = Card {
|
||||
id: 9102,
|
||||
suit: Suit::Spades,
|
||||
rank: Rank::Five,
|
||||
face_up: true,
|
||||
};
|
||||
|
||||
let mut app = overlay_test_app(game);
|
||||
begin_drag_with(&mut app, dragged);
|
||||
@@ -734,9 +766,19 @@ mod tests {
|
||||
set_tableau_top(
|
||||
&mut game,
|
||||
2,
|
||||
Card { id: 9201, suit: Suit::Spades, rank: Rank::Six, face_up: true },
|
||||
Card {
|
||||
id: 9201,
|
||||
suit: Suit::Spades,
|
||||
rank: Rank::Six,
|
||||
face_up: true,
|
||||
},
|
||||
);
|
||||
let dragged = Card { id: 9202, suit: Suit::Hearts, rank: Rank::Five, face_up: true };
|
||||
let dragged = Card {
|
||||
id: 9202,
|
||||
suit: Suit::Hearts,
|
||||
rank: Rank::Five,
|
||||
face_up: true,
|
||||
};
|
||||
|
||||
let mut app = overlay_test_app(game);
|
||||
begin_drag_with(&mut app, dragged);
|
||||
|
||||
Reference in New Issue
Block a user