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
+49 -24
View File
@@ -108,7 +108,15 @@ fn apply_safe_area_anchors(
// expects logical pixels (≈ dp). Divide by the window scale factor so
// the HUD band shifts by the correct number of dp on high-DPI devices.
let scale = windows.iter().next().map_or(1.0, |w| w.scale_factor());
let top_logical = insets.top / scale;
let window_height = windows.iter().next().map_or(800.0, |w| w.height());
let max_inset = window_height * 0.25;
let raw_top = insets.top / scale;
if raw_top > max_inset {
warn!(
"safe_area: top inset {raw_top:.0}px exceeds 25% of window height ({max_inset:.0}px); clamping"
);
}
let top_logical = raw_top.min(max_inset);
for (anchor, mut node) in &mut q {
node.top = Val::Px(anchor.base_top + top_logical);
}
@@ -125,7 +133,15 @@ fn apply_safe_area_bottom_anchors(
return;
}
let scale = windows.iter().next().map_or(1.0, |w| w.scale_factor());
let bottom_logical = insets.bottom / scale;
let window_height = windows.iter().next().map_or(800.0, |w| w.height());
let max_inset = window_height * 0.25;
let raw_bottom = insets.bottom / scale;
if raw_bottom > max_inset {
warn!(
"safe_area: bottom inset {raw_bottom:.0}px exceeds 25% of window height ({max_inset:.0}px); clamping"
);
}
let bottom_logical = raw_bottom.min(max_inset);
for (anchor, mut node) in &mut q {
node.bottom = Val::Px(anchor.base_bottom + bottom_logical);
}
@@ -148,7 +164,8 @@ fn apply_safe_area_to_modal_scrims(
return;
}
let scale = windows.iter().next().map_or(1.0, |w| w.scale_factor());
let bottom_logical = insets.bottom / scale;
let window_height = windows.iter().next().map_or(800.0, |w| w.height());
let bottom_logical = (insets.bottom / scale).min(window_height * 0.25);
for mut node in &mut scrims {
node.padding.bottom = Val::Px(bottom_logical);
}
@@ -260,7 +277,7 @@ mod android {
fn query_insets() -> Result<SafeAreaInsets, String> {
use bevy::android::ANDROID_APP;
use jni::{objects::JObject, JavaVM};
use jni::{JavaVM, objects::JObject};
let app = ANDROID_APP
.get()
@@ -353,25 +370,33 @@ mod tests {
#[test]
fn is_populated_returns_true_for_any_nonzero_edge() {
assert!(SafeAreaInsets {
top: 24.0,
..Default::default()
}
.is_populated());
assert!(SafeAreaInsets {
bottom: 16.0,
..Default::default()
}
.is_populated());
assert!(SafeAreaInsets {
left: 8.0,
..Default::default()
}
.is_populated());
assert!(SafeAreaInsets {
right: 8.0,
..Default::default()
}
.is_populated());
assert!(
SafeAreaInsets {
top: 24.0,
..Default::default()
}
.is_populated()
);
assert!(
SafeAreaInsets {
bottom: 16.0,
..Default::default()
}
.is_populated()
);
assert!(
SafeAreaInsets {
left: 8.0,
..Default::default()
}
.is_populated()
);
assert!(
SafeAreaInsets {
right: 8.0,
..Default::default()
}
.is_populated()
);
}
}