Skip to content

Implement velocity handling and BDD tests#68

Open
leynos wants to merge 2 commits intomainfrom
codex/update-bevy-systems-and-implement-bdd-tests
Open

Implement velocity handling and BDD tests#68
leynos wants to merge 2 commits intomainfrom
codex/update-bevy-systems-and-implement-bdd-tests

Conversation

@leynos
Copy link
Copy Markdown
Owner

@leynos leynos commented Jun 18, 2025

Summary

  • sync NewVelocity back into Bevy components
  • extend DdlogHandle with velocity tracking and force input
  • spawn entities with Velocity components
  • add BDD tests for force and friction behaviour

Testing

  • cargo clippy -- -D warnings
  • cargo test
  • cargo test --test physics_bdd

https://chatgpt.com/codex/tasks/task_e_6852a8754ea88322b8ac6a5497fff470

Summary by Sourcery

Implement per-entity velocity tracking and physics forces in the DDlog backend, and add BDD tests for gravity, force application, and friction behavior.

New Features:

  • Introduce a Velocity component and NewVelocity record to track entity velocities.
  • Add apply_force API on DdlogHandle for injecting forces per tick.

Enhancements:

  • Compute entity acceleration and integrate gravity, ground/air friction, and terminal velocity in DdlogHandle.step().
  • Extend push_state_to_ddlog_system and apply_ddlog_deltas_system to synchronize velocity between ECS and DDlog.
  • Automatically attach default Velocity to spawned entities in the world initialization.

Tests:

  • Add BDD-style tests with RON snapshots for falling, force application, and ground friction scenarios.

Summary by CodeRabbit

  • New Features

    • Introduced a velocity system for entities, enabling physics-based movement with support for forces, gravity, and friction.
    • Entities now store and synchronise their velocity as a 3D vector.
  • Bug Fixes

    • Ensured synchronisation of velocity data between systems, maintaining consistent entity movement.
  • Tests

    • Added and updated tests to verify correct velocity handling, force application, and friction effects on entities.

@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented Jun 18, 2025

Reviewer's Guide

This PR implements full velocity-based physics by adding a Velocity component, extending DdlogHandle to track forces and emit velocity deltas, enhancing the physics pipeline to compute acceleration (from external forces, gravity, friction, and terminal velocity), and updating sync/spawn systems accordingly, with new BDD tests validating force application and ground friction behavior.

Sequence diagram for ECS-DDlog velocity synchronization

sequenceDiagram
    participant ECS as ECS Entity
    participant Sync as push_state_to_ddlog_system
    participant DDlog as DdlogHandle
    participant Apply as apply_ddlog_deltas_system

    ECS->>Sync: Entity state (Transform, Velocity, etc.)
    Sync->>DDlog: Update DdlogEntity (position, velocity)
    DDlog->>DDlog: step() (physics update)
    DDlog-->>Apply: velocity_deltas, deltas
    Apply->>ECS: Update Transform, Velocity component
Loading

Class diagram for new and updated physics components

classDiagram
    class DdlogEntity {
        Vec3 position
        Vec3 velocity
        UnitType unit
        i32 health
        Option<Vec2> target
    }
    class NewVelocity {
        i64 entity
        f32 vx
        f32 vy
        f32 vz
    }
    class DdlogHandle {
        Vec<Block> blocks
        HashMap<i64, BlockSlope> slopes
        HashMap<i64, DdlogEntity> entities
        Vec<NewPosition> deltas
        Vec<NewVelocity> velocity_deltas
        HashMap<i64, Vec3> forces
        apply_force(id: i64, force: Vec3)
        step()
    }
    class Velocity {
        Vec3 0
    }
    DdlogEntity --> NewVelocity : emits
    DdlogHandle "1" o-- "*" DdlogEntity : manages
    DdlogHandle --> NewVelocity : emits
    DdlogEntity --> Velocity : ECS sync
    Velocity <|-- NewVelocity : data transfer
Loading

File-Level Changes

Change Details Files
Introduce Velocity component and integrate into ECS state sync and spawning
  • Define new Velocity component in components module
  • Export Velocity in the library API
  • Spawn entities with default Velocity in spawn_world_system
  • Include Velocity in push_state_to_ddlog_system and apply_ddlog_deltas_system
  • Update existing DDlog tests to initialize velocity field
src/components.rs
src/lib.rs
src/spawn_world.rs
src/ddlog_sync.rs
tests/ddlog.rs
Extend DdlogHandle to support force inputs and velocity deltas
  • Add forces map and velocity_deltas vector to DdlogHandle
  • Define NewVelocity delta struct and collect it in step()
  • Implement apply_force() method and clear forces after each tick
  • Sync initial velocity in DdlogEntity default and delta generation
src/ddlog_handle.rs
Enhance physics computation with forces, friction, gravity, and terminal velocity
  • Refactor compute_entity_update to calculate acceleration from force, gravity, and friction
  • Clamp vertical velocity to terminal limits when airborne
  • Combine velocity and movement logic to update position
  • Remove legacy apply_gravity helper
src/ddlog_handle.rs
Add BDD tests for force application and ground friction
  • Write BDD scenarios for applying force and verifying velocity/position snapshots
  • Write BDD scenarios for ground friction slowing entities over time
  • Add corresponding snapshot files for new tests
tests/physics_bdd.rs
tests/snapshots/physics_bdd__force_position.snap
tests/snapshots/physics_bdd__force_velocity.snap
tests/snapshots/physics_bdd__friction_position.snap
tests/snapshots/physics_bdd__friction_velocity.snap

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 18, 2025

Walkthrough

A new Velocity component was introduced and integrated throughout the ECS and DDlog synchronisation system. Entities now store and synchronise their velocity, which is updated using a physics-based system with force application, gravity, and friction. Tests and entity spawns were updated to initialise and verify velocity behaviour.

Changes

File(s) Change Summary
src/components.rs Added Velocity component as a tuple struct with Vec3, deriving Component, Debug, Default, Serialize.
src/ddlog_handle.rs Extended DdlogEntity with velocity. Added NewVelocity struct. DdlogHandle tracks velocity deltas and forces. Integrated physics (forces, gravity, friction) into entity updates.
src/ddlog_sync.rs Synchronisation functions now handle the Velocity component, updating and inserting as needed during sync.
src/lib.rs Publicly re-exported the Velocity component.
src/spawn_world.rs Entities now spawn with a default Velocity component.
tests/ddlog.rs Tests initialise the velocity field for DdlogEntity instances.
tests/physics_bdd.rs Added and updated tests to cover velocity, force application, and friction effects.

Sequence Diagram(s)

sequenceDiagram
    participant ECS
    participant DDlogHandle
    participant DDlog
    participant Commands

    Note over ECS, DDlogHandle: Push ECS state to DDlog
    ECS->>DDlogHandle: Query (DdlogId, Transform, ..., Velocity)
    DDlogHandle->>DDlog: Update DdlogEntity with velocity

    Note over DDlog, ECS: Apply DDlog deltas to ECS
    DDlog->>DDlogHandle: Provide position & velocity deltas
    DDlogHandle->>ECS: Query (Entity, DdlogId, Transform, Velocity)
    alt Velocity exists
        DDlogHandle->>ECS: Update Velocity component
    else Velocity missing
        DDlogHandle->>Commands: Insert Velocity component
    end
    DDlogHandle->>ECS: Update Transform (position)
Loading
sequenceDiagram
    participant GameLogic
    participant DdlogHandle

    GameLogic->>DdlogHandle: apply_force(entity_id, force)
    DdlogHandle->>DdlogHandle: Store force for entity

    Note over DdlogHandle: On tick/update
    DdlogHandle->>DdlogHandle: Compute acceleration, gravity, friction
    DdlogHandle->>DdlogHandle: Update velocity and position
    DdlogHandle->>DDlog: Push velocity and position deltas
Loading

Possibly related PRs

  • Implement velocity rules #23: Implements velocity-related rules and constants within the DDlog schema and logic, complementing this PR’s ECS and Rust-side velocity integration.

Poem

A hop, a skip, a leap through space,
Now velocity gives each entity grace!
Forces and friction, gravity’s might,
Entities whizzing from morning to night.
In tests and in code, the new speed is seen—
The physics are lively, the world feels keen!
🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate Unit Tests
  • Create PR with Unit Tests
  • Post Copyable Unit Tests in Comment
  • Commit Unit Tests in branch codex/update-bevy-systems-and-implement-bdd-tests

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai auto-generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown

@codescene-delta-analysis codescene-delta-analysis Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gates Passed
6 Quality Gates Passed

See analysis details in CodeScene

Absence of Expected Change Pattern

  • lille/src/spawn_world.rs is usually changed with: lille/tests/spawn.rs
  • lille/src/lib.rs is usually changed with: lille/src/main.rs

Quality Gate Profile: Pay Down Tech Debt
Want more control? Customize Code Health rules or catch issues early with our IDE extension and CLI tool.

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @leynos - I've reviewed your changes - here's some feedback:

  • The apply_force method currently overwrites any existing force for the same entity—if you ever need to combine multiple force sources in a single tick, you may want to accumulate forces (e.g. via entry API) instead of replacing them.
  • The compute_entity_update method mixes gravity, friction, input forces, walking logic, velocity clamping, and position updates; consider splitting it into smaller helper functions (e.g. apply_gravity, apply_friction, integrate_motion) to improve readability and facilitate targeted unit testing.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The `apply_force` method currently overwrites any existing force for the same entity—if you ever need to combine multiple force sources in a single tick, you may want to accumulate forces (e.g. via entry API) instead of replacing them.
- The `compute_entity_update` method mixes gravity, friction, input forces, walking logic, velocity clamping, and position updates; consider splitting it into smaller helper functions (e.g. apply_gravity, apply_friction, integrate_motion) to improve readability and facilitate targeted unit testing.

## Individual Comments

### Comment 1
<location> `src/ddlog_handle.rs:151` </location>
<code_context>
+        let unsupported = ent.position.z > floor + GRACE_DISTANCE_F32;
+
+        let mut acceleration = Vec3::ZERO;
+        if let Some(force) = self.forces.get(&id) {
+            acceleration += *force / DEFAULT_MASS_F32;
+        }
+
</code_context>

<issue_to_address>
Force application overwrites previous force rather than accumulating.

Since `apply_force` overwrites previous values, only the last force per entity is used each tick. If multiple forces should be combined, consider accumulating them instead.

Suggested implementation:

```rust
// In the struct definition (likely above, not shown here):
// forces: HashMap<i64, Vec3>,

// In the apply_force method:
pub fn apply_force(&mut self, id: i64, force: Vec3) {
    self.forces
        .entry(id)
        .and_modify(|f| *f += force)
        .or_insert(force);
}

```

If there are any places in the codebase that rely on the old "overwrite" behavior of `apply_force`, they may need to be updated to clear or reset the force before applying a new one, if that is still desired in those contexts.
</issue_to_address>

### Comment 2
<location> `src/ddlog_handle.rs:232` </location>
<code_context>
                 }
             }
         }
+        self.forces.clear();
     }
 }
</code_context>

<issue_to_address>
Forces are cleared at the end of each step, which may cause missed forces if step is not called every tick.

This approach relies on `step` being called exactly once per tick; otherwise, force application may be inconsistent. Please confirm that this scheduling is enforced.
</issue_to_address>

### Comment 3
<location> `src/ddlog_handle.rs:21` </location>
<code_context>
+const AIR_FRICTION_F32: f32 = AIR_FRICTION as f32;
+const DEFAULT_MASS_F32: f32 = DEFAULT_MASS as f32;

 #[derive(Clone, Serialize)]
 pub struct DdlogEntity {
     pub position: Vec3,
</code_context>

<issue_to_address>
Missing module-level item comment (`//!`) at the top of the file.

Please add a module-level item comment at the top of this file using `//!` to describe the purpose of the module.
</issue_to_address>

### Comment 4
<location> `src/ddlog_sync.rs:6` </location>
<code_context>
 use hashbrown::HashMap;

-use crate::components::{Block, BlockSlope, DdlogId, Health, Target, UnitType};
+use crate::components::{Block, BlockSlope, DdlogId, Health, Target, UnitType, Velocity};
 use crate::ddlog_handle::{DdlogEntity, DdlogHandle};

</code_context>

<issue_to_address>
Missing module-level item comment (`//!`) at the top of the file.

Please add a module-level item comment at the top of this file using `//!` to describe the purpose of the module.
</issue_to_address>

### Comment 5
<location> `src/components.rs:36` </location>
<code_context>
     pub grad_y: f32,
 }
+
+#[derive(Component, Debug, Default, Serialize)]
+pub struct Velocity(pub Vec3);
--- a/src/lib.rs
</code_context>

<issue_to_address>
Missing module-level item comment (`//!`) at the top of the file.

Please add a module-level item comment at the top of this file using `//!` to describe the purpose of the module.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/ddlog_handle.rs
Comment on lines +151 to +152
if let Some(force) = self.forces.get(&id) {
acceleration += *force / DEFAULT_MASS_F32;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Force application overwrites previous force rather than accumulating.

Since apply_force overwrites previous values, only the last force per entity is used each tick. If multiple forces should be combined, consider accumulating them instead.

Suggested implementation:

// In the struct definition (likely above, not shown here):
// forces: HashMap<i64, Vec3>,

// In the apply_force method:
pub fn apply_force(&mut self, id: i64, force: Vec3) {
    self.forces
        .entry(id)
        .and_modify(|f| *f += force)
        .or_insert(force);
}

If there are any places in the codebase that rely on the old "overwrite" behavior of apply_force, they may need to be updated to clear or reset the force before applying a new one, if that is still desired in those contexts.

Comment thread src/ddlog_handle.rs
}
}
}
self.forces.clear();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question (bug_risk): Forces are cleared at the end of each step, which may cause missed forces if step is not called every tick.

This approach relies on step being called exactly once per tick; otherwise, force application may be inconsistent. Please confirm that this scheduling is enforced.

Comment thread src/ddlog_handle.rs
const AIR_FRICTION_F32: f32 = AIR_FRICTION as f32;
const DEFAULT_MASS_F32: f32 = DEFAULT_MASS as f32;

#[derive(Clone, Serialize)]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (review_instructions): Missing module-level item comment (//!) at the top of the file.

Please add a module-level item comment at the top of this file using //! to describe the purpose of the module.

Review instructions:

Path patterns: **/*.rs

Instructions:
All modules MUST have a containing item comment (//!)

Comment thread src/ddlog_sync.rs
use hashbrown::HashMap;

use crate::components::{Block, BlockSlope, DdlogId, Health, Target, UnitType};
use crate::components::{Block, BlockSlope, DdlogId, Health, Target, UnitType, Velocity};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (review_instructions): Missing module-level item comment (//!) at the top of the file.

Please add a module-level item comment at the top of this file using //! to describe the purpose of the module.

Review instructions:

Path patterns: **/*.rs

Instructions:
All modules MUST have a containing item comment (//!)

Comment thread src/components.rs
pub grad_y: f32,
}

#[derive(Component, Debug, Default, Serialize)]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (review_instructions): Missing module-level item comment (//!) at the top of the file.

Please add a module-level item comment at the top of this file using //! to describe the purpose of the module.

Review instructions:

Path patterns: **/*.rs

Instructions:
All modules MUST have a containing item comment (//!)

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🔭 Outside diff range comments (1)
src/components.rs (1)

36-38: Derive Copy/Clone & Deref for Velocity for ergonomic use

Vec3 is Copy, so the wrapper can also be Copy + Clone.
Adding Deref/DerefMut, as done for Target, keeps the API consistent and lets systems treat Velocity transparently as a Vec3.

-#[derive(Component, Debug, Default, Serialize)]
-pub struct Velocity(pub Vec3);
+#[derive(Component, Debug, Default, Copy, Clone, Deref, DerefMut, Serialize)]
+pub struct Velocity(pub Vec3);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a1850e8 and e2023ce.

⛔ Files ignored due to path filters (4)
  • tests/snapshots/physics_bdd__force_position.snap is excluded by !**/*.snap
  • tests/snapshots/physics_bdd__force_velocity.snap is excluded by !**/*.snap
  • tests/snapshots/physics_bdd__friction_position.snap is excluded by !**/*.snap
  • tests/snapshots/physics_bdd__friction_velocity.snap is excluded by !**/*.snap
📒 Files selected for processing (7)
  • src/components.rs (1 hunks)
  • src/ddlog_handle.rs (6 hunks)
  • src/ddlog_sync.rs (4 hunks)
  • src/lib.rs (1 hunks)
  • src/spawn_world.rs (4 hunks)
  • tests/ddlog.rs (3 hunks)
  • tests/physics_bdd.rs (3 hunks)
🔇 Additional comments (5)
tests/ddlog.rs (1)

53-56: Velocity initialisation aligns tests with the new component

All three entities now start with Vec3::ZERO; this keeps the tests deterministic and reflects the updated schema.
Nothing else to flag.

Also applies to: 85-88, 95-98

src/lib.rs (1)

15-15: Re-export completes the public surface

This makes Velocity available to crates that depend on lille; good addition.

src/ddlog_sync.rs (1)

50-50: Well-implemented velocity synchronization.

The velocity handling correctly defaults to Vec3::ZERO when absent and properly synchronizes between ECS and DDlog. The dynamic component insertion ensures backward compatibility.

Also applies to: 74-79

src/ddlog_handle.rs (2)

159-171: Robust friction implementation.

The friction calculation correctly prevents velocity reversal by clamping deceleration magnitude to current velocity magnitude, and properly handles the zero velocity case.


146-192: Well-structured physics simulation.

The physics implementation properly handles:

  • Force-based acceleration with configurable mass
  • Gravity application for unsupported entities
  • Separate air and ground friction coefficients
  • Terminal velocity clamping
  • Combined velocity and AI-driven movement for ground entities

Comment thread src/spawn_world.rs
use bevy::prelude::*;

use crate::components::{DdlogId, Health, Target, UnitType};
use crate::components::{DdlogId, Health, Target, UnitType, Velocity};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Consider skipping Velocity on truly static entities

Assigning Velocity::default() to the landmark is harmless, but also unused and slightly noisy.
If the landmark will never move, omitting the component avoids an unnecessary sync to DDlog every frame.

No action required if you plan on animating landmarks later.

Also applies to: 54-55, 65-66, 76-77

🤖 Prompt for AI Agents
In src/spawn_world.rs around lines 54-55, 65-66, and 76-77, the Velocity
component is assigned to static entities like landmarks where it is unused. To
reduce unnecessary updates and syncing to DDlog, remove the Velocity::default()
assignment for these truly static entities. Only add Velocity if the entity is
expected to move or animate later.

Comment thread tests/physics_bdd.rs
Comment thread src/ddlog_sync.rs
Comment on lines +11 to +21
#[allow(clippy::type_complexity)]
pub fn push_state_to_ddlog_system(
mut ddlog: ResMut<DdlogHandle>,
entity_query: Query<(&DdlogId, &Transform, &Health, &UnitType, Option<&Target>)>,
entity_query: Query<(
&DdlogId,
&Transform,
&Health,
&UnitType,
Option<&Target>,
Option<&Velocity>,
)>,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Consider refactoring the complex query tuple.

The query tuple has grown to 6 components, necessitating the clippy::type_complexity suppression. This could indicate a design issue that would benefit from refactoring.

Consider bundling related components into a single component or using a query filter pattern to reduce complexity:

-#[allow(clippy::type_complexity)]
 pub fn push_state_to_ddlog_system(
     mut ddlog: ResMut<DdlogHandle>,
-    entity_query: Query<(
-        &DdlogId,
-        &Transform,
-        &Health,
-        &UnitType,
-        Option<&Target>,
-        Option<&Velocity>,
-    )>,
+    entity_query: Query<(&DdlogId, &Transform, &Health, &UnitType), With<DdlogId>>,
+    target_query: Query<&Target>,
+    velocity_query: Query<&Velocity>,
     block_query: Query<(&Block, Option<&BlockSlope>)>,
 ) {

Then query the optional components separately within the loop.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/ddlog_sync.rs around lines 11 to 21, the Query tuple has six components
causing high type complexity and requiring a clippy suppression. Refactor by
grouping related components into a single struct component or by splitting the
query into multiple queries, one for mandatory components and separate queries
or filters for optional components. Then, inside the loop, query the optional
components separately to reduce the tuple size and eliminate the need for the
clippy suppression.

Comment thread src/ddlog_handle.rs Outdated
Comment thread src/ddlog_handle.rs
Copy link
Copy Markdown

@codescene-delta-analysis codescene-delta-analysis Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gates Passed
6 Quality Gates Passed

See analysis details in CodeScene

Absence of Expected Change Pattern

  • lille/src/spawn_world.rs is usually changed with: lille/tests/spawn.rs
  • lille/src/lib.rs is usually changed with: lille/src/main.rs

Quality Gate Profile: Pay Down Tech Debt
Want more control? Customize Code Health rules or catch issues early with our IDE extension and CLI tool.

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Jun 18, 2025

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 18, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e2023ce and 950b516.

⛔ Files ignored due to path filters (2)
  • tests/snapshots/physics_bdd__friction_position.snap is excluded by !**/*.snap
  • tests/snapshots/physics_bdd__friction_velocity.snap is excluded by !**/*.snap
📒 Files selected for processing (3)
  • src/ddlog_handle.rs (6 hunks)
  • src/spawn_world.rs (3 hunks)
  • tests/physics_bdd.rs (3 hunks)
🔇 Additional comments (2)
src/spawn_world.rs (1)

65-66: Good use of Velocity on dynamic entities

Adding Velocity::default() only to the units that will actually move keeps the component set minimal and avoids needless DDlog sync noise.
Looks clean.

Also applies to: 76-77

src/ddlog_handle.rs (1)

190-195: ```shell
#!/bin/bash
set -e
echo "Extracting full civvy_move function body"
rg -n "fn civvy_move" -C50 src/ddlog_handle.rs


</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Comment thread tests/physics_bdd.rs
Comment on lines +69 to +79
let rounded: Vec<NewPosition> = ddlog
.deltas
.iter()
.map(|d| NewPosition {
entity: d.entity,
x: (d.x * 1e4).round() / 1e4,
y: (d.y * 1e4).round() / 1e4,
z: (d.z * 1e4).round() / 1e4,
})
.collect();
assert_ron_snapshot!("falling_delta", &rounded);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Factor-out duplicate rounding/snapshot helper

Each test re-implements the same “round to 1e-4 then snapshot” mapping for NewPosition / NewVelocity.
Extract a small helper (or use approx assertions) to keep tests concise and reduce copy-paste risk.

Also applies to: 104-127, 147-169

🤖 Prompt for AI Agents
In tests/physics_bdd.rs around lines 69 to 79, the rounding and snapshot logic
for NewPosition is duplicated across multiple tests. To fix this, create a
helper function that takes a slice of NewPosition or NewVelocity and returns a
Vec with each element rounded to 1e-4 precision. Replace the inline mapping in
each test with calls to this helper, then perform the snapshot assertion on the
helper's output. Apply the same refactoring to the code in lines 104-127 and
147-169 to eliminate duplication and improve test clarity.

Comment thread src/ddlog_handle.rs
Comment on lines +165 to +177
let vel_xy = ent.velocity.truncate();
let h_mag = vel_xy.length();
if h_mag > 0.0 {
let coeff = if unsupported {
AIR_FRICTION_F32
} else {
GROUND_FRICTION_F32
};
let decel_mag = h_mag.min(coeff);
let dir = vel_xy / h_mag;
acceleration.x -= dir.x * decel_mag;
acceleration.y -= dir.y * decel_mag;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Friction formula may over-decelerate slow movers

decel_mag = h_mag.min(coeff) applies the full coeff even when velocity is tiny (e.g. 0.01 with coeff 0.1 → decel 0.1, reversing direction).
Consider scaling by velocity magnitude or using a clamp that can’t exceed h_mag after the * DELTA_TIME step.

🤖 Prompt for AI Agents
In src/ddlog_handle.rs around lines 165 to 177, the friction deceleration
calculation uses decel_mag = h_mag.min(coeff), which can cause over-deceleration
and reverse velocity direction for very small speeds. To fix this, modify the
deceleration magnitude to scale with velocity and delta time, ensuring it never
exceeds the current velocity magnitude after applying the time step. This
prevents reversing direction by clamping deceleration appropriately.

Comment thread src/ddlog_handle.rs
Comment on lines +206 to 214
let updates: Vec<(i64, Vec3, Vec3)> = self
.entities
.iter()
.map(|(&id, ent)| (id, self.compute_entity_update(id, ent)))
.map(|(&id, ent)| {
let (pos, vel) = self.compute_entity_update(id, ent);
(id, pos, vel)
})
.collect();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Avoid temporary allocation in step

Collecting updates into a Vec then iterating creates an unnecessary allocation each tick.
Iterate mutably in place:

for (&id, ent) in self.entities.iter_mut() {
    let (pos, vel) = self.compute_entity_update(id, ent);
    ...
}

This removes the heap hit and a copy of each Vec3.

🤖 Prompt for AI Agents
In src/ddlog_handle.rs around lines 206 to 214, the current code collects entity
updates into a Vec, causing unnecessary heap allocation and copying each tick.
To fix this, replace the collection step with a mutable iteration over
self.entities using iter_mut(), compute updates in place, and apply changes
directly within the loop to avoid temporary allocation and copying of Vec3
values.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant