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

- #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:
funman300
2026-05-28 13:07:22 -07:00
parent 8cb4c9808e
commit 6e407a3ea7
104 changed files with 6356 additions and 3092 deletions
+32 -12
View File
@@ -18,7 +18,7 @@
//! --help Print this message
use solitaire_core::game_state::DrawMode;
use solitaire_core::solver::{try_solve, SolverConfig, SolverResult};
use solitaire_core::solver::{SolverConfig, SolverResult, try_solve};
fn main() {
let mut args = std::env::args().skip(1).peekable();
@@ -45,7 +45,14 @@ fn main() {
});
}
"--help" | "-h" => {
eprintln!("{}", include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/bin/gen_seeds.rs")).lines().take(20).collect::<Vec<_>>().join("\n"));
eprintln!(
"{}",
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/bin/gen_seeds.rs"))
.lines()
.take(20)
.collect::<Vec<_>>()
.join("\n")
);
return;
}
other => {
@@ -66,16 +73,11 @@ fn main() {
let mut tried: u64 = 0;
let mut seed = start;
eprintln!(
"gen_seeds: finding {count} Winnable seeds from 0x{start:016X} (DrawOne) …"
);
eprintln!("gen_seeds: finding {count} Winnable seeds from 0x{start:016X} (DrawOne) …");
while found.len() < count {
tried += 1;
if matches!(
try_solve(seed, draw_mode, &cfg),
SolverResult::Winnable
) {
if matches!(try_solve(seed, draw_mode, &cfg), SolverResult::Winnable) {
found.push(seed);
eprintln!(
" [{:>3}/{}] 0x{:016X} ({} tried so far)",
@@ -88,7 +90,9 @@ fn main() {
seed = seed.wrapping_add(1);
}
eprintln!("\nDone. Paste the block below into CHALLENGE_SEEDS in solitaire_data/src/challenge.rs:\n");
eprintln!(
"\nDone. Paste the block below into CHALLENGE_SEEDS in solitaire_data/src/challenge.rs:\n"
);
println!(
" // Generated by solitaire_assetgen::gen_seeds \
@@ -111,7 +115,10 @@ fn main() {
fn parse_u64(s: &str) -> u64 {
let cleaned = s.replace('_', "");
if let Some(hex) = cleaned.strip_prefix("0x").or_else(|| cleaned.strip_prefix("0X")) {
if let Some(hex) = cleaned
.strip_prefix("0x")
.or_else(|| cleaned.strip_prefix("0X"))
{
u64::from_str_radix(hex, 16).unwrap_or_else(|_| {
eprintln!("error: could not parse '{s}' as a hex u64");
std::process::exit(1);
@@ -144,7 +151,20 @@ fn current_date() -> String {
y += 1;
}
let leap = (y.is_multiple_of(4) && !y.is_multiple_of(100)) || y.is_multiple_of(400);
let month_days: [u64; 12] = [31, if leap { 29 } else { 28 }, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
let month_days: [u64; 12] = [
31,
if leap { 29 } else { 28 },
31,
30,
31,
30,
31,
31,
30,
31,
30,
31,
];
let mut m = 0usize;
for &md in &month_days {
if d < md {