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
@@ -71,7 +71,9 @@ pub struct HoverState {
/// Describes a user action that arrived while cards were still animating.
#[derive(Debug, Clone)]
pub enum BufferedInput {
Move { from: crate::events::MoveRequestEvent },
Move {
from: crate::events::MoveRequestEvent,
},
Draw,
Undo,
}
@@ -139,9 +141,7 @@ pub(crate) fn detect_hover(
let mut best: Option<(Entity, f32)> = None;
for (entity, transform) in &cards {
let pos = transform.translation.truncate();
if (cursor_world.x - pos.x).abs() < half_w
&& (cursor_world.y - pos.y).abs() < half_h
{
if (cursor_world.x - pos.x).abs() < half_w && (cursor_world.y - pos.y).abs() < half_h {
let z = transform.translation.z;
if best.is_none_or(|(_, bz)| z > bz) {
best = Some((entity, z));
@@ -187,9 +187,7 @@ pub(crate) fn apply_hover_scale(
// Update the tracked scale for external inspection.
hover_state.scale = if let Some(entity) = target_entity {
cards
.get(entity)
.map_or(hover_target, |(_, t)| t.scale.x)
cards.get(entity).map_or(hover_target, |(_, t)| t.scale.x)
} else {
1.0
};