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:
@@ -80,14 +80,14 @@ pub mod interaction;
|
||||
pub mod timing;
|
||||
pub mod tuning;
|
||||
|
||||
pub use animation::{retarget_animation, win_scatter_targets, CardAnimation};
|
||||
pub use animation::{CardAnimation, retarget_animation, win_scatter_targets};
|
||||
pub use chain::AnimationChain;
|
||||
pub use curves::{sample_curve, MotionCurve};
|
||||
pub use curves::{MotionCurve, sample_curve};
|
||||
pub use diagnostics::{FrameTimeDiagnostics, WINDOW_SIZE as DIAG_WINDOW_SIZE};
|
||||
pub use interaction::{BufferedInput, HoverState, InputBuffer};
|
||||
pub use timing::{
|
||||
cascade_delay, compute_duration, micro_vary, DEAL_INTERVAL_SECS, MAX_DURATION_SECS,
|
||||
MIN_DURATION_SECS, WIN_CASCADE_INTERVAL_SECS,
|
||||
DEAL_INTERVAL_SECS, MAX_DURATION_SECS, MIN_DURATION_SECS, WIN_CASCADE_INTERVAL_SECS,
|
||||
cascade_delay, compute_duration, micro_vary,
|
||||
};
|
||||
pub use tuning::{AnimationTuning, InputPlatform};
|
||||
|
||||
@@ -179,10 +179,7 @@ pub struct WinCascadePlugin;
|
||||
|
||||
impl Plugin for WinCascadePlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.add_systems(
|
||||
Update,
|
||||
trigger_expressive_win_cascade.after(GameMutation),
|
||||
);
|
||||
app.add_systems(Update, trigger_expressive_win_cascade.after(GameMutation));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,9 +197,7 @@ fn trigger_expressive_win_cascade(
|
||||
return;
|
||||
}
|
||||
|
||||
let radius = layout
|
||||
.as_ref()
|
||||
.map_or(800.0, |l| l.0.card_size.x * 8.0);
|
||||
let radius = layout.as_ref().map_or(800.0, |l| l.0.card_size.x * 8.0);
|
||||
|
||||
let targets = win_scatter_targets(radius);
|
||||
|
||||
@@ -212,10 +207,16 @@ fn trigger_expressive_win_cascade(
|
||||
let target = targets[index % targets.len()];
|
||||
|
||||
commands.entity(entity).insert(
|
||||
CardAnimation::slide(start_xy, start_z, target, start_z + 60.0, MotionCurve::Expressive)
|
||||
.with_delay(cascade_delay(index, WIN_CASCADE_INTERVAL_SECS))
|
||||
.with_duration(0.65)
|
||||
.with_z_lift(25.0),
|
||||
CardAnimation::slide(
|
||||
start_xy,
|
||||
start_z,
|
||||
target,
|
||||
start_z + 60.0,
|
||||
MotionCurve::Expressive,
|
||||
)
|
||||
.with_delay(cascade_delay(index, WIN_CASCADE_INTERVAL_SECS))
|
||||
.with_duration(0.65)
|
||||
.with_z_lift(25.0),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -265,7 +266,8 @@ mod tests {
|
||||
#[test]
|
||||
fn card_animation_advances_and_removes_itself() {
|
||||
let mut app = App::new();
|
||||
app.add_plugins(MinimalPlugins).add_plugins(CardAnimationPlugin);
|
||||
app.add_plugins(MinimalPlugins)
|
||||
.add_plugins(CardAnimationPlugin);
|
||||
|
||||
let start = Vec2::new(0.0, 0.0);
|
||||
let end = Vec2::new(100.0, 0.0);
|
||||
@@ -306,7 +308,8 @@ mod tests {
|
||||
#[test]
|
||||
fn card_animation_instant_snaps_on_zero_duration() {
|
||||
let mut app = App::new();
|
||||
app.add_plugins(MinimalPlugins).add_plugins(CardAnimationPlugin);
|
||||
app.add_plugins(MinimalPlugins)
|
||||
.add_plugins(CardAnimationPlugin);
|
||||
|
||||
let end = Vec2::new(200.0, 100.0);
|
||||
let entity = app
|
||||
@@ -353,7 +356,8 @@ mod tests {
|
||||
#[test]
|
||||
fn card_animation_respects_delay() {
|
||||
let mut app = App::new();
|
||||
app.add_plugins(MinimalPlugins).add_plugins(CardAnimationPlugin);
|
||||
app.add_plugins(MinimalPlugins)
|
||||
.add_plugins(CardAnimationPlugin);
|
||||
|
||||
let entity = app
|
||||
.world_mut()
|
||||
@@ -391,8 +395,14 @@ mod tests {
|
||||
buf.push(BufferedInput::Draw);
|
||||
buf.push(BufferedInput::Undo);
|
||||
// FIFO: Draw comes out first.
|
||||
assert!(matches!(buf.queue.pop_front().unwrap(), BufferedInput::Draw));
|
||||
assert!(matches!(buf.queue.pop_front().unwrap(), BufferedInput::Undo));
|
||||
assert!(matches!(
|
||||
buf.queue.pop_front().unwrap(),
|
||||
BufferedInput::Draw
|
||||
));
|
||||
assert!(matches!(
|
||||
buf.queue.pop_front().unwrap(),
|
||||
BufferedInput::Undo
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user