fix(lint): resolve clippy warnings and add cross-platform lint recipe

- Add `just lint` recipe for desktop + wasm clippy checks
- Fix clippy warnings: redundant field names, formatting, dead code
- Gate Emscripten-specific audio methods behind target_os cfg
This commit is contained in:
2025-12-29 01:15:03 -06:00
parent 6db061cc41
commit 791a0e48e3
7 changed files with 46 additions and 15 deletions
+15 -3
View File
@@ -56,11 +56,18 @@ pub struct Audio {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum AudioState {
Enabled { volume: u8 },
Muted { previous_volume: u8 },
Enabled {
volume: u8,
},
Muted {
previous_volume: u8,
},
/// Audio is suspended until user interaction unlocks it (browser autoplay policy).
/// On Emscripten, audio starts in this state and transitions to Enabled when unlock() is called.
Suspended { volume: u8 },
#[cfg(target_os = "emscripten")]
Suspended {
volume: u8,
},
Disabled,
}
@@ -264,6 +271,7 @@ impl Audio {
///
/// Transitions from Suspended to Enabled state, allowing audio to play.
/// Called when the user clicks or presses a key to satisfy browser autoplay policy.
#[cfg(target_os = "emscripten")]
pub fn unlock(&mut self) {
if let AudioState::Suspended { volume } = self.state {
tracing::info!("Audio unlocked after user interaction");
@@ -275,11 +283,15 @@ impl Audio {
///
/// Returns `true` if audio is in the Enabled state, `false` if suspended,
/// muted, or disabled.
#[cfg(target_os = "emscripten")]
#[allow(dead_code)]
pub fn is_ready(&self) -> bool {
matches!(self.state, AudioState::Enabled { .. })
}
/// Returns whether audio is suspended waiting for user interaction.
#[cfg(target_os = "emscripten")]
#[allow(dead_code)]
pub fn is_suspended(&self) -> bool {
matches!(self.state, AudioState::Suspended { .. })
}
+5 -2
View File
@@ -22,9 +22,12 @@ use crate::systems::{
EntityType, Frozen, FruitSprites, GameStage, Ghost, GhostAnimation, GhostAnimations, GhostBundle, GhostCollider, GhostState,
GlobalState, ItemBundle, ItemCollider, LastAnimationState, LinearAnimation, MapTextureResource, MovementModifiers, NodeId,
PacmanCollider, PlayerAnimation, PlayerBundle, PlayerControlled, PlayerDeathAnimation, PlayerLives, Position, RenderDirty,
Renderable, ScoreResource, StartupSequence, SystemId, SystemTimings, Timing, TouchState, Velocity, Visibility,
Renderable, ScoreResource, SystemId, SystemTimings, Timing, TouchState, Velocity, Visibility,
};
#[cfg(not(target_os = "emscripten"))]
use crate::systems::StartupSequence;
use crate::texture::animated::{DirectionalTiles, TileSequence};
use crate::texture::sprite::AtlasTile;
use crate::texture::sprites::{FrightenedColor, GameSprite, GhostSprite, MazeSprite, PacmanSprite};
@@ -787,7 +790,7 @@ impl Game {
// Use dt to determine expected frame time, with 80% as threshold to account for normal variance
// Desktop uses LOOP_TIME (~16.67ms), WebAssembly adapts to requestAnimationFrame timing
let frame_budget_ms = (dt * 1000.0 * 1.2) as u128;
// Log performance warnings for slow frames
if total_duration.as_millis() > frame_budget_ms {
let slowest_systems = timings.get_slowest_systems();
+1 -6
View File
@@ -23,12 +23,7 @@ extern "C" {
/// - `arg`: user data pointer passed to callback
/// - `fps`: target FPS (0 = use requestAnimationFrame)
/// - `simulate_infinite_loop`: if 1, never returns (standard for games)
pub fn emscripten_set_main_loop_arg(
func: EmMainLoopCallback,
arg: *mut c_void,
fps: c_int,
simulate_infinite_loop: c_int,
);
pub fn emscripten_set_main_loop_arg(func: EmMainLoopCallback, arg: *mut c_void, fps: c_int, simulate_infinite_loop: c_int);
/// Execute JavaScript code from Rust
fn emscripten_run_script(script: *const i8);
+2 -2
View File
@@ -89,7 +89,7 @@ pub fn hud_render_system(
if pause_state.active() {
// Enable blending for transparency
canvas.set_blend_mode(sdl2::render::BlendMode::Blend);
// Draw semi-transparent black overlay
canvas.set_draw_color(Color::RGBA(0, 0, 0, 160));
let _ = canvas.fill_rect(Rect::new(0, 0, constants::CANVAS_SIZE.x, constants::CANVAS_SIZE.y));
@@ -101,7 +101,7 @@ pub fn hud_render_system(
let paused_height = paused_renderer.text_height();
let paused_position = glam::UVec2::new(
(constants::CANVAS_SIZE.x - paused_width) / 2,
(constants::CANVAS_SIZE.y - paused_height) / 2
(constants::CANVAS_SIZE.y - paused_height) / 2,
);
if let Err(e) = paused_renderer.render_with_color(canvas, &mut atlas, paused_text, paused_position, Color::YELLOW) {
errors.write(TextureError::RenderFailed(format!("Failed to render PAUSED text: {}", e)).into());
+14 -1
View File
@@ -43,6 +43,7 @@ pub struct IntroPlayed(pub bool);
pub enum GameStage {
/// Waiting for user interaction before starting (Emscripten only).
/// Game is rendered but audio/gameplay are paused until the user clicks or presses a key.
#[cfg(target_os = "emscripten")]
WaitingForInteraction,
Starting(StartupSequence),
/// The main gameplay loop is active.
@@ -186,7 +187,15 @@ impl TooSimilar for GameStage {
fn too_similar(&self, other: &Self) -> bool {
discriminant(self) == discriminant(other) && {
// These states are very simple, so they're 'too similar' automatically
if matches!(self, GameStage::Playing | GameStage::GameOver | GameStage::WaitingForInteraction) {
#[cfg(target_os = "emscripten")]
if matches!(
self,
GameStage::Playing | GameStage::GameOver | GameStage::WaitingForInteraction
) {
return true;
}
#[cfg(not(target_os = "emscripten"))]
if matches!(self, GameStage::Playing | GameStage::GameOver) {
return true;
}
@@ -210,7 +219,10 @@ impl TooSimilar for GameStage {
},
) => ghost_entity == other_ghost_entity && ghost_type == other_ghost_type && node == other_node,
// Already handled, but kept to properly exhaust the match
#[cfg(target_os = "emscripten")]
(GameStage::Playing, _) | (GameStage::GameOver, _) | (GameStage::WaitingForInteraction, _) => unreachable!(),
#[cfg(not(target_os = "emscripten"))]
(GameStage::Playing, _) | (GameStage::GameOver, _) => unreachable!(),
_ => unreachable!(),
}
}
@@ -315,6 +327,7 @@ pub fn stage_system(
}
let new_state: GameStage = new_state_opt.unwrap_or_else(|| match *game_state {
#[cfg(target_os = "emscripten")]
GameStage::WaitingForInteraction => {
// Stay in this state until JS calls start_game()
*game_state