Skip to content

Conversation

@PoulavBhowmick03
Copy link

@PoulavBhowmick03 PoulavBhowmick03 commented Sep 7, 2025

Fixes #199

Summary by CodeRabbit

  • Bug Fixes
    • Prevents invalid actions when using abilities, items, attacking enemies, or navigating paths through stricter validation.
    • Correctly applies mana costs, cooldowns, damage, and inventory updates, reducing edge-case failures.
  • Refactor
    • Standardized in-game error messages (e.g., invalid target, missing item/ability, not enough mana) for clearer player feedback.
    • Strengthened ID and bounds checks to avoid out-of-range and invalid path errors, improving overall stability.

@coderabbitai
Copy link

coderabbitai bot commented Sep 7, 2025

Walkthrough

Centralizes error handling by introducing error constants and replacing string literals. Adds input/state validation across abilities, items, attacks, damage, and path/index helpers. Refactors ability usage to compute effects/costs via a context, applies mana/cooldown, and hardens effect application with typed conversions and explicit panics.

Changes

Cohort / File(s) Summary
Error constants and validations
contract/src/systems/game.cairo
Added reusable error constants (e.g., INVALID_AMOUNT, PLAYER_NOT_FOUND). Replaced string literals in asserts/panics across functions. Implemented input validation (amount > 0, id checks, inventory presence). Replaced unwrap() with try/expect carrying constants.
Ability usage flow
contract/src/systems/game.cairo
Introduced AbilityUsageContext and process_usage to derive effects/costs. Enforced checks: player/ability existence, alive, equipped, cooldown, target validity, mana costs, cooldown application, and effect dispatch with typed conversions.
Damage and attack flow
contract/src/systems/game.cairo
take_damage now validates and safely converts amount. attack_enemy validates damage, enemy identity, and alive status; applies rewards on death.
Item usage flow
contract/src/systems/game.cairo
Validates item_id and inventory membership before applying item effects; updates inventory post-usage.
Path and index safety
contract/src/systems/game.cairo
get_path_step/is_path_completed now use INVALID_PATH_ID; index helper guards bounds with INDEX_OUT_OF_BOUNDS; replaced default cases with explicit panics.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant P as Player
  participant G as GameSystem
  participant A as Ability
  participant S as Store/State

  Note over G: use_ability(player_id, ability_id, target)

  G->>S: Fetch player, ability, target state
  S-->>G: Entities
  G->>G: Validate existence/alive/equipped/cooldown/target (panic on constants)
  G->>A: process_usage(context)
  A-->>G: usage_result (effects, mana_cost, cooldown)
  G->>S: Deduct mana, set cooldown
  loop For each effect
    G->>S: apply_ability_effect(effect)\n(convert, expect INVALID_AMOUNT)
  end
  G-->>P: Done or panic with specific constant
Loading
sequenceDiagram
  autonumber
  participant P as Player
  participant G as GameSystem
  participant E as Enemy
  participant S as Store/State

  Note over G: attack_enemy(player_id, enemy_id, damage)

  G->>G: Validate damage > 0
  G->>S: Load enemy by id
  S-->>G: Enemy
  G->>G: Validate id matches and alive
  G->>E: Apply damage
  E-->>G: New HP / dead?
  alt Enemy dead
    G->>S: Distribute rewards
  end
  G-->>P: Return / specific panic
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Assessment against linked issues

Objective Addressed Explanation
Add custom error definitions and meaningful error messages [#199]
Implement input validation for functions (amounts, IDs, targets) [#199]
Replace unwrap with proper error handling (try/expect) [#199]
Handle edge cases to prevent generic Cairo panics [#199]

Assessment against linked issues: Out-of-scope changes

(no out-of-scope changes found)

Poem

I thump my paws on tidy code, hooray!
Errors named, no mystery today.
Mana spent, cool-downs behave,
Paths won’t wander, indexes save.
With carrots bright and tests in sight—
This bun approves the safer bite! 🥕✨

✨ Finishing Touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@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: 2

🧹 Nitpick comments (3)
contract/src/systems/game.cairo (3)

30-46: Centralized error constants: good; minor polish.

Nice consolidation. Consider a consistent prefix (e.g., ERR_) and verify all literals remain <= 31 ASCII bytes (Cairo short-string limit) to avoid future surprises when editing. Also add an entry for “ABILITY_ALREADY_EXISTS” if you gate duplicate creation (see create_ability note).


234-236: Guard against duplicate ability ids.

Currently, create_ability can overwrite an existing ability. Add a preexistence check.

Apply these diffs:

Add a constant:

+const ABILITY_ALREADY_EXISTS: felt252 = 'Ability already exists';

And in create_ability:

             assert(ability_id != 0, INVALID_ABILITY_ID);
             assert(power > 0, INVALID_AMOUNT);
+            let existing: Ability = world.read_model(ability_id);
+            assert(existing.is_zero(), ABILITY_ALREADY_EXISTS);

371-392: Typed conversions are fine; ensure upstream caps effect_amount.

expect(INVALID_AMOUNT) is acceptable, but confirm Ability::process_usage never returns values exceeding u16/u8 to avoid surprising hard reverts at runtime. If necessary, add explicit caps or tighter validation there.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0de50aa and 5a33215.

📒 Files selected for processing (1)
  • contract/src/systems/game.cairo (10 hunks)
🔇 Additional comments (5)
contract/src/systems/game.cairo (5)

154-160: take_damage validation and narrowing look good.

Positive amount check plus u32→u16 conversion with a clear error is appropriate.


163-175: attack_enemy validations look solid.

Damage > 0, id consistency, and alive checks are all appropriate prior to mutation.


295-296: Consistent INVALID_PATH_ID panics look good.

Unified error signaling for invalid path ids improves debuggability.

Also applies to: 323-324


331-333: Bounds check for step index is correct.

Clear guard with INDEX_OUT_OF_BOUNDS before Span access.


207-223: Consume items after use – update inventory accordingly
After applying effects for Upgrade and Consumable, call inventory.remove_item(item_id) (or decrement stack) before world.write_model(@inventory); confirm whether Trap items should also be consumed.

Comment on lines +99 to 104
assert(!player.is_zero(), PLAYER_NOT_FOUND);

// Check if the ability exists
let ability: Ability = world.read_model(ability_id);
assert(ability.is_non_zero(), 'Ability not found');
assert(ability.is_non_zero(), ABILITY_NOT_FOUND);

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Ability lookup likely uses the wrong key width (u32 vs u256).

Abilities are stored with a u256 id, but this reads with a u32. This can return the zero-model and make ABILITY_NOT_FOUND trigger incorrectly.

Apply this diff:

-            let ability: Ability = world.read_model(ability_id);
+            let ability: Ability = world.read_model(ability_id.into());
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
assert(!player.is_zero(), PLAYER_NOT_FOUND);
// Check if the ability exists
let ability: Ability = world.read_model(ability_id);
assert(ability.is_non_zero(), 'Ability not found');
assert(ability.is_non_zero(), ABILITY_NOT_FOUND);
assert(!player.is_zero(), PLAYER_NOT_FOUND);
// Check if the ability exists
let ability: Ability = world.read_model(ability_id.into());
assert(ability.is_non_zero(), ABILITY_NOT_FOUND);
🤖 Prompt for AI Agents
In contract/src/systems/game.cairo around lines 99 to 104, the code reads an
Ability using a u32-width key which causes a mismatch with stored u256 IDs and
can return the zero-model; change the read to use a Uint256 key (either by
changing ability_id's type to Uint256 where defined or by constructing a Uint256
from the existing value before calling world.read_model), update the read_model
call to accept that Uint256, and add any necessary imports/constructors so the
ability lookup uses the correct 256-bit key width.

Comment on lines +119 to 123
assert(is_player_alive, PLAYER_DEAD);
assert(is_ability_equipped, ABILITY_NOT_EQUIPPED);
assert(current_timestamp >= cooldown_until, ABILITY_ON_COOLDOWN);
assert(is_target_valid, INVALID_TARGET);

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Non-player targets slip through validation and receive player-only effects.

validate_target returns true when the target Player model is zero, then apply_ability_effect executes player-system calls on a non-player address. This is unsafe and will misbehave/revert downstream.

Apply this diff to make only alive players valid targets:

-            assert(is_target_valid, INVALID_TARGET);
+            assert(is_target_valid, INVALID_TARGET);

And update validate_target:

-            if target_player.is_zero() {
-                true // TODO: Non-player targets are valid?
-            } else {
-                player_system_dispatcher.is_alive(target_id)
-            }
+            if target_player.is_zero() {
+                false
+            } else {
+                player_system_dispatcher.is_alive(target_id)
+            }

If you intend to support non-player targets later, thread a typed target enum and dispatch effects accordingly instead of calling into the player system.

Also applies to: 346-356

🤖 Prompt for AI Agents
In contract/src/systems/game.cairo around lines 119-123 (and similarly at
346-356), the current target validation allows a zero/empty Player model which
lets non-player addresses receive player-only effects; change the checks so
targets must be a non-zero player and alive before calling player-system: update
validate_target to return false for a zero/empty Player model (e.g., check
player_model.id or equivalent non-zero indicator) and that the player is alive,
and add/assert an explicit non-zero-player-address/valid-player check before
applying effects (replace the permissive check with an assert for non-zero
player and alive status); if non-player targets will be supported later,
introduce a typed target enum and dispatch effects based on target type instead
of directly calling player-system.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: Inconsistent Error Handling and Missing Error Messages

2 participants