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
@@ -27,8 +27,8 @@
//! alongside the `card_plugin` constant migration.
use solitaire_engine::assets::card_face_svg::{
back_svg, face_svg, rank_filename, suit_filename, theme_rank_token, theme_suit_token,
ALL_RANKS, ALL_SUITS, BACK_ACCENTS, TARGET,
ALL_RANKS, ALL_SUITS, BACK_ACCENTS, TARGET, back_svg, face_svg, rank_filename, suit_filename,
theme_rank_token, theme_suit_token,
};
use solitaire_engine::assets::rasterize_svg;
use std::path::PathBuf;
+7 -5
View File
@@ -44,8 +44,8 @@ fn main() {
// 256×384 = 2:3 aspect at half the default svg_loader resolution.
// See migration plan § "Output format" for the rationale.
let target = UVec2::new(256, 384);
let image = rasterize_svg(svg.as_bytes(), target)
.expect("rasterising the PoC SVG should succeed");
let image =
rasterize_svg(svg.as_bytes(), target).expect("rasterising the PoC SVG should succeed");
let bytes = image
.data
@@ -61,11 +61,13 @@ fn main() {
// bytes from a Pixmap inside `svg_loader`; this round-trip is
// the cost of going through Bevy's `Image` shape.
let size = IntSize::from_wh(target.x, target.y).expect("target size is non-zero");
let pixmap = Pixmap::from_vec(bytes, size)
.expect("RGBA byte buffer should form a valid Pixmap");
let pixmap =
Pixmap::from_vec(bytes, size).expect("RGBA byte buffer should form a valid Pixmap");
let out = "/tmp/ace_spades_terminal.png";
pixmap.save_png(out).expect("writing the PNG should succeed");
pixmap
.save_png(out)
.expect("writing the PNG should succeed");
println!(
"Wrote {} ({}×{} RGBA8, {} bytes on disk)",
+1 -1
View File
@@ -18,7 +18,7 @@
//! pipeline already used by every other generated asset).
use bevy::math::UVec2;
use solitaire_engine::assets::icon_svg::{icon_svg, ICON_SIZES};
use solitaire_engine::assets::icon_svg::{ICON_SIZES, icon_svg};
use solitaire_engine::assets::rasterize_svg;
use std::path::PathBuf;
use tiny_skia::{IntSize, Pixmap};