mirror of
https://github.com/Xevion/Pac-Man.git
synced 2026-01-30 22:24:58 -06:00
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:
@@ -40,6 +40,14 @@ web *args:
|
|||||||
bun run --cwd web build
|
bun run --cwd web build
|
||||||
caddy file-server --root web/dist/client --listen :8547
|
caddy file-server --root web/dist/client --listen :8547
|
||||||
|
|
||||||
|
# Run strict multi-platform lints (desktop + wasm)
|
||||||
|
lint:
|
||||||
|
@echo "Running clippy for desktop target..."
|
||||||
|
@cargo clippy --all-targets --all-features --quiet -- -D warnings
|
||||||
|
@echo "Running clippy for wasm target..."
|
||||||
|
@cargo clippy -p pacman --target wasm32-unknown-emscripten --all-features --quiet -- -D warnings
|
||||||
|
@echo "All lints passed!"
|
||||||
|
|
||||||
# Fix linting errors & formatting
|
# Fix linting errors & formatting
|
||||||
fix:
|
fix:
|
||||||
cargo fix --workspace --lib --allow-dirty
|
cargo fix --workspace --lib --allow-dirty
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ impl AppState {
|
|||||||
sessions: Arc::new(DashMap::new()),
|
sessions: Arc::new(DashMap::new()),
|
||||||
jwt_encoding_key: Arc::new(EncodingKey::from_secret(jwt_secret.as_bytes())),
|
jwt_encoding_key: Arc::new(EncodingKey::from_secret(jwt_secret.as_bytes())),
|
||||||
jwt_decoding_key: Arc::new(DecodingKey::from_secret(jwt_secret.as_bytes())),
|
jwt_decoding_key: Arc::new(DecodingKey::from_secret(jwt_secret.as_bytes())),
|
||||||
db: db,
|
db,
|
||||||
health: Arc::new(RwLock::new(Health::default())),
|
health: Arc::new(RwLock::new(Health::default())),
|
||||||
image_storage,
|
image_storage,
|
||||||
healthchecker_task: Arc::new(RwLock::new(None)),
|
healthchecker_task: Arc::new(RwLock::new(None)),
|
||||||
|
|||||||
+15
-3
@@ -56,11 +56,18 @@ pub struct Audio {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
enum AudioState {
|
enum AudioState {
|
||||||
Enabled { volume: u8 },
|
Enabled {
|
||||||
Muted { previous_volume: u8 },
|
volume: u8,
|
||||||
|
},
|
||||||
|
Muted {
|
||||||
|
previous_volume: u8,
|
||||||
|
},
|
||||||
/// Audio is suspended until user interaction unlocks it (browser autoplay policy).
|
/// 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.
|
/// 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,
|
Disabled,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,6 +271,7 @@ impl Audio {
|
|||||||
///
|
///
|
||||||
/// Transitions from Suspended to Enabled state, allowing audio to play.
|
/// Transitions from Suspended to Enabled state, allowing audio to play.
|
||||||
/// Called when the user clicks or presses a key to satisfy browser autoplay policy.
|
/// Called when the user clicks or presses a key to satisfy browser autoplay policy.
|
||||||
|
#[cfg(target_os = "emscripten")]
|
||||||
pub fn unlock(&mut self) {
|
pub fn unlock(&mut self) {
|
||||||
if let AudioState::Suspended { volume } = self.state {
|
if let AudioState::Suspended { volume } = self.state {
|
||||||
tracing::info!("Audio unlocked after user interaction");
|
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,
|
/// Returns `true` if audio is in the Enabled state, `false` if suspended,
|
||||||
/// muted, or disabled.
|
/// muted, or disabled.
|
||||||
|
#[cfg(target_os = "emscripten")]
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn is_ready(&self) -> bool {
|
pub fn is_ready(&self) -> bool {
|
||||||
matches!(self.state, AudioState::Enabled { .. })
|
matches!(self.state, AudioState::Enabled { .. })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether audio is suspended waiting for user interaction.
|
/// Returns whether audio is suspended waiting for user interaction.
|
||||||
|
#[cfg(target_os = "emscripten")]
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn is_suspended(&self) -> bool {
|
pub fn is_suspended(&self) -> bool {
|
||||||
matches!(self.state, AudioState::Suspended { .. })
|
matches!(self.state, AudioState::Suspended { .. })
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-2
@@ -22,9 +22,12 @@ use crate::systems::{
|
|||||||
EntityType, Frozen, FruitSprites, GameStage, Ghost, GhostAnimation, GhostAnimations, GhostBundle, GhostCollider, GhostState,
|
EntityType, Frozen, FruitSprites, GameStage, Ghost, GhostAnimation, GhostAnimations, GhostBundle, GhostCollider, GhostState,
|
||||||
GlobalState, ItemBundle, ItemCollider, LastAnimationState, LinearAnimation, MapTextureResource, MovementModifiers, NodeId,
|
GlobalState, ItemBundle, ItemCollider, LastAnimationState, LinearAnimation, MapTextureResource, MovementModifiers, NodeId,
|
||||||
PacmanCollider, PlayerAnimation, PlayerBundle, PlayerControlled, PlayerDeathAnimation, PlayerLives, Position, RenderDirty,
|
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::animated::{DirectionalTiles, TileSequence};
|
||||||
use crate::texture::sprite::AtlasTile;
|
use crate::texture::sprite::AtlasTile;
|
||||||
use crate::texture::sprites::{FrightenedColor, GameSprite, GhostSprite, MazeSprite, PacmanSprite};
|
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
|
// 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
|
// Desktop uses LOOP_TIME (~16.67ms), WebAssembly adapts to requestAnimationFrame timing
|
||||||
let frame_budget_ms = (dt * 1000.0 * 1.2) as u128;
|
let frame_budget_ms = (dt * 1000.0 * 1.2) as u128;
|
||||||
|
|
||||||
// Log performance warnings for slow frames
|
// Log performance warnings for slow frames
|
||||||
if total_duration.as_millis() > frame_budget_ms {
|
if total_duration.as_millis() > frame_budget_ms {
|
||||||
let slowest_systems = timings.get_slowest_systems();
|
let slowest_systems = timings.get_slowest_systems();
|
||||||
|
|||||||
@@ -23,12 +23,7 @@ extern "C" {
|
|||||||
/// - `arg`: user data pointer passed to callback
|
/// - `arg`: user data pointer passed to callback
|
||||||
/// - `fps`: target FPS (0 = use requestAnimationFrame)
|
/// - `fps`: target FPS (0 = use requestAnimationFrame)
|
||||||
/// - `simulate_infinite_loop`: if 1, never returns (standard for games)
|
/// - `simulate_infinite_loop`: if 1, never returns (standard for games)
|
||||||
pub fn emscripten_set_main_loop_arg(
|
pub fn emscripten_set_main_loop_arg(func: EmMainLoopCallback, arg: *mut c_void, fps: c_int, simulate_infinite_loop: c_int);
|
||||||
func: EmMainLoopCallback,
|
|
||||||
arg: *mut c_void,
|
|
||||||
fps: c_int,
|
|
||||||
simulate_infinite_loop: c_int,
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Execute JavaScript code from Rust
|
/// Execute JavaScript code from Rust
|
||||||
fn emscripten_run_script(script: *const i8);
|
fn emscripten_run_script(script: *const i8);
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ pub fn hud_render_system(
|
|||||||
if pause_state.active() {
|
if pause_state.active() {
|
||||||
// Enable blending for transparency
|
// Enable blending for transparency
|
||||||
canvas.set_blend_mode(sdl2::render::BlendMode::Blend);
|
canvas.set_blend_mode(sdl2::render::BlendMode::Blend);
|
||||||
|
|
||||||
// Draw semi-transparent black overlay
|
// Draw semi-transparent black overlay
|
||||||
canvas.set_draw_color(Color::RGBA(0, 0, 0, 160));
|
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));
|
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_height = paused_renderer.text_height();
|
||||||
let paused_position = glam::UVec2::new(
|
let paused_position = glam::UVec2::new(
|
||||||
(constants::CANVAS_SIZE.x - paused_width) / 2,
|
(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) {
|
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());
|
errors.write(TextureError::RenderFailed(format!("Failed to render PAUSED text: {}", e)).into());
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ pub struct IntroPlayed(pub bool);
|
|||||||
pub enum GameStage {
|
pub enum GameStage {
|
||||||
/// Waiting for user interaction before starting (Emscripten only).
|
/// Waiting for user interaction before starting (Emscripten only).
|
||||||
/// Game is rendered but audio/gameplay are paused until the user clicks or presses a key.
|
/// Game is rendered but audio/gameplay are paused until the user clicks or presses a key.
|
||||||
|
#[cfg(target_os = "emscripten")]
|
||||||
WaitingForInteraction,
|
WaitingForInteraction,
|
||||||
Starting(StartupSequence),
|
Starting(StartupSequence),
|
||||||
/// The main gameplay loop is active.
|
/// The main gameplay loop is active.
|
||||||
@@ -186,7 +187,15 @@ impl TooSimilar for GameStage {
|
|||||||
fn too_similar(&self, other: &Self) -> bool {
|
fn too_similar(&self, other: &Self) -> bool {
|
||||||
discriminant(self) == discriminant(other) && {
|
discriminant(self) == discriminant(other) && {
|
||||||
// These states are very simple, so they're 'too similar' automatically
|
// 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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,7 +219,10 @@ impl TooSimilar for GameStage {
|
|||||||
},
|
},
|
||||||
) => ghost_entity == other_ghost_entity && ghost_type == other_ghost_type && node == other_node,
|
) => ghost_entity == other_ghost_entity && ghost_type == other_ghost_type && node == other_node,
|
||||||
// Already handled, but kept to properly exhaust the match
|
// Already handled, but kept to properly exhaust the match
|
||||||
|
#[cfg(target_os = "emscripten")]
|
||||||
(GameStage::Playing, _) | (GameStage::GameOver, _) | (GameStage::WaitingForInteraction, _) => unreachable!(),
|
(GameStage::Playing, _) | (GameStage::GameOver, _) | (GameStage::WaitingForInteraction, _) => unreachable!(),
|
||||||
|
#[cfg(not(target_os = "emscripten"))]
|
||||||
|
(GameStage::Playing, _) | (GameStage::GameOver, _) => unreachable!(),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -315,6 +327,7 @@ pub fn stage_system(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let new_state: GameStage = new_state_opt.unwrap_or_else(|| match *game_state {
|
let new_state: GameStage = new_state_opt.unwrap_or_else(|| match *game_state {
|
||||||
|
#[cfg(target_os = "emscripten")]
|
||||||
GameStage::WaitingForInteraction => {
|
GameStage::WaitingForInteraction => {
|
||||||
// Stay in this state until JS calls start_game()
|
// Stay in this state until JS calls start_game()
|
||||||
*game_state
|
*game_state
|
||||||
|
|||||||
Reference in New Issue
Block a user