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:
@@ -5,8 +5,8 @@
|
||||
use bevy::prelude::*;
|
||||
use chrono::Local;
|
||||
use solitaire_data::{
|
||||
current_iso_week_key, save_progress_to, weekly_goal_by_id, WeeklyGoalContext, WEEKLY_GOALS,
|
||||
WEEKLY_GOAL_XP,
|
||||
WEEKLY_GOAL_XP, WEEKLY_GOALS, WeeklyGoalContext, current_iso_week_key, save_progress_to,
|
||||
weekly_goal_by_id,
|
||||
};
|
||||
|
||||
use crate::events::{GameWonEvent, XpAwardedEvent};
|
||||
@@ -51,9 +51,10 @@ fn roll_weekly_goals_on_startup(
|
||||
let week_key = current_iso_week_key(Local::now().date_naive());
|
||||
if progress.0.roll_weekly_goals_if_new_week(&week_key)
|
||||
&& let Some(target) = &path.0
|
||||
&& let Err(e) = save_progress_to(target, &progress.0) {
|
||||
warn!("failed to save progress after weekly reset on startup: {e}");
|
||||
}
|
||||
&& let Err(e) = save_progress_to(target, &progress.0)
|
||||
{
|
||||
warn!("failed to save progress after weekly reset on startup: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
fn evaluate_weekly_goals(
|
||||
@@ -114,9 +115,10 @@ fn evaluate_weekly_goals(
|
||||
|
||||
if any_change
|
||||
&& let Some(target) = &path.0
|
||||
&& let Err(e) = save_progress_to(target, &progress.0) {
|
||||
warn!("failed to save progress after weekly goal update: {e}");
|
||||
}
|
||||
&& let Err(e) = save_progress_to(target, &progress.0)
|
||||
{
|
||||
warn!("failed to save progress after weekly goal update: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve a goal id to its description (used for toasts).
|
||||
@@ -196,10 +198,14 @@ mod tests {
|
||||
// keeping the XP delta predictable.
|
||||
{
|
||||
let mut p = app.world_mut().resource_mut::<ProgressResource>();
|
||||
p.0.weekly_goal_progress.insert("weekly_3_fast".to_string(), 2);
|
||||
p.0.weekly_goal_progress.insert("weekly_1_under_five".to_string(), 1);
|
||||
p.0.weekly_goal_progress.insert("weekly_5_wins".to_string(), 5);
|
||||
p.0.weekly_goal_progress.insert("weekly_3_no_undo".to_string(), 3);
|
||||
p.0.weekly_goal_progress
|
||||
.insert("weekly_3_fast".to_string(), 2);
|
||||
p.0.weekly_goal_progress
|
||||
.insert("weekly_1_under_five".to_string(), 1);
|
||||
p.0.weekly_goal_progress
|
||||
.insert("weekly_5_wins".to_string(), 5);
|
||||
p.0.weekly_goal_progress
|
||||
.insert("weekly_3_no_undo".to_string(), 3);
|
||||
}
|
||||
// Match the current ISO week key so roll_weekly_goals doesn't clear it.
|
||||
let key = current_iso_week_key(Local::now().date_naive());
|
||||
@@ -263,7 +269,10 @@ mod tests {
|
||||
fn weekly_bonus_xp_fires_levelup_when_threshold_crossed() {
|
||||
let mut app = headless_app();
|
||||
// Set XP just below the first level boundary (500) so the 75-XP bonus crosses it.
|
||||
app.world_mut().resource_mut::<ProgressResource>().0.total_xp = 430;
|
||||
app.world_mut()
|
||||
.resource_mut::<ProgressResource>()
|
||||
.0
|
||||
.total_xp = 430;
|
||||
// Pre-set goal to 2/3 so the next fast win completes it.
|
||||
app.world_mut()
|
||||
.resource_mut::<ProgressResource>()
|
||||
@@ -285,7 +294,10 @@ mod tests {
|
||||
let events = app.world().resource::<Messages<LevelUpEvent>>();
|
||||
let mut cursor = events.get_cursor();
|
||||
let fired: Vec<_> = cursor.read(events).copied().collect();
|
||||
assert!(!fired.is_empty(), "LevelUpEvent must fire when weekly bonus pushes past a level threshold");
|
||||
assert!(
|
||||
!fired.is_empty(),
|
||||
"LevelUpEvent must fire when weekly bonus pushes past a level threshold"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user