Skip to content

fix(client): refactor event handling and stabilize release workflow - #51

Merged
garnizeh-labs merged 6 commits into
mainfrom
fix/client-hardening-and-release-plz-stability
Apr 26, 2026
Merged

garnizeh-labs merged 6 commits into
mainfrom
fix/client-hardening-and-release-plz-stability

Conversation

@garnizeh

@garnizeh garnizeh commented Apr 26, 2026

Copy link
Copy Markdown
Collaborator

Changes

  • WASM Refactor: Extracted helper in to deduplicate logic and improve maintainability.
  • Vitals Visibility: Updated and to expose dynamic and derived from entity type defaults.
  • UI Improvements: Fixed health and shield bars to calculate percentages using actual max values instead of hardcoded defaults.
  • CI/CD Fix: Simplified by removing nested checkouts and complex wrappers that were causing duplicate release triggers. Matches the standard working config from the protocol repository.
  • Versioning: Maintained the requested (v6.0.2) version.

Summary by CodeRabbit

  • New Features

    • Entity status now includes max HP and max shield for complete vitals.
  • Bug Fixes

    • Game events are buffered during processing and dispatched afterward to preserve correct state updates.
    • Vitals calculations normalize hull and shield by their actual maxima and handle missing max shield safely.
  • Documentation

    • Added client design notes on using protocol defaults to initialize visuals.
  • Chores

    • Simplified CI/release workflow and cache behavior; bumped protocol-related dependency versions.

…orkflow

- Deduplicated GameEvent logic in lib.rs using dispatch_game_event helper
- Updated EntityStatus to include dynamic max_hp/max_shield from type defaults
- Fixed health bar calculations in playground.ts to use actual max vitals
- Simplified release-plz.yml to resolve double release issue and match workspace standards
@coderabbitai

coderabbitai Bot commented Apr 26, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@garnizeh has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 9 minutes and 0 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 9 minutes and 0 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 73d03304-8b70-46f7-b4ea-dc9c7f27e3fe

📥 Commits

Reviewing files that changed from the base of the PR and between feed63f and 3898040.

📒 Files selected for processing (1)
  • crates/aetheris-client-wasm/src/lib.rs
📝 Walkthrough

Walkthrough

Centralizes WASM client GameEvent handling into a deferred dispatcher, enriches exported entity statuses with protocol-derived max_hp/max_shield, updates frontend vitals normalization, bumps protocol/encoder versions, and simplifies the release CI by removing the external protocol checkout and custom Cargo wrapper/cache behavior.

Changes

Cohort / File(s) Summary
CI / Release Workflow
.github/workflows/release-plz.yml
Removes dedicated checkout of external aetheris-protocol, drops custom Cargo environment/wrapper and explicit project_manifest, changes caching to only target and computes cache keys from all **/Cargo.lock files.
WASM client — event dispatch
crates/aetheris-client-wasm/src/lib.rs
Reworks tick() to buffer decoded GameEvents into collected_game_events and adds dispatch_game_event(&mut, &GameEvent) to apply side effects after borrow scopes. Preserves prior behaviors for AsteroidDepleted, SystemManifest, and forwards other variants to world_state.handle_game_event.
Entity status export & UI
crates/aetheris-client-wasm/src/lib.rs, playground/src/playground.ts
wasm_get_entity_statuses payload now includes max_hp: u16 and max_shield: u16 (sourced from protocol defaults). Frontend updateEntityStatuses normalizes hull/shield percentages by these maxima and guards against non-positive maxima. Playground spawn now initializes vitals from protocol defaults.
Versioning / manifests
Cargo.toml
Bumps workspace versions for aetheris-protocol, aetheris-encoder-serde, and aetheris-encoder-bitpack to 0.2.22.
Docs / changelog
docs/CLIENT_DESIGN.md, crates/aetheris-client-wasm/CHANGELOG.md
Adds client design section describing protocol-driven defaults and zero-latency visuals; updates changelog to record deferred event queue and status export changes.

Sequence Diagram(s)

sequenceDiagram
    rect rgba(200,220,255,0.5)
    participant Transport
    participant Decoder
    participant WASM_Tick as "WASM tick"
    participant Dispatcher
    participant WorldState
    end

    Transport->>Decoder: poll & read bytes
    Decoder->>WASM_Tick: decode NetworkEvent(s)
    WASM_Tick->>WASM_Tick: buffer GameEvent(s) into collected_game_events
    WASM_Tick->>Dispatcher: iterate collected_game_events
    Dispatcher->>WorldState: dispatch_game_event(event)
    WorldState-->>Dispatcher: apply side effects (despawn, manifest update, handlers)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐇 I buffered hops until the coast was clear,
Deferred each bounce so borrows won't fear.
Max HP and shields now gleam in the light,
CI trimmed its checkout and danced out of sight.
Hooray — I nibble bugs and bound through the night!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title covers the two main change categories: client event handling refactoring (WASM logic deduplication, vitals normalization) and release workflow stabilization (CI/CD simplification, removed nested checkouts).
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/client-hardening-and-release-plz-stability

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/aetheris-client-wasm/src/lib.rs (1)

659-815: ⚠️ Potential issue | 🔴 Critical

Defer game-event dispatch until the transport borrow is released.

This loop holds &mut self.transport from line 659 to 852, so the new calls on lines 700 and 809 to self.dispatch_game_event(...) (which takes &mut self) create an E0499 borrow conflict and prevent compilation. Queue the decoded GameEvents during polling and dispatch them after the if let Some(transport) = &mut self.transport scope ends.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/aetheris-client-wasm/src/lib.rs` around lines 659 - 815, The borrow
error is caused by calling self.dispatch_game_event(&game_event) while holding
&mut self.transport in the poll loop; change the poll block to collect decoded
GameEvent instances (e.g., push game_event into a local Vec<GameEvent> like
collected_game_events) instead of calling self.dispatch_game_event inside the
match arms (references: transport.poll_events, NetworkEvent::GameEvent,
self.dispatch_game_event); after the if let Some(transport) = &mut
self.transport { ... } scope ends, iterate the collected_game_events and call
self.dispatch_game_event for each to release the transport borrow before mutably
borrowing self again.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/aetheris-client-wasm/src/lib.rs`:
- Around line 1017-1039: The hard-coded match that derives max_hp/max_shield
inside the entities.into_iter().map producing EntityStatus is out-of-sync and
must be replaced with a lookup from the authoritative entity definitions; remove
the local match on slot.entity_type and instead fetch the canonical stats
(max_hp/max_shield) from the central entity definitions API/registry (use the
module/function that exposes the authoritative definitions) using
slot.entity_type as the key, fall back to a sensible default if the lookup is
missing, and keep the rest of the EntityStatus construction (network_id, hp,
shield, entity_type, is_player) unchanged so exported maxima reflect the real
entity definitions.

---

Outside diff comments:
In `@crates/aetheris-client-wasm/src/lib.rs`:
- Around line 659-815: The borrow error is caused by calling
self.dispatch_game_event(&game_event) while holding &mut self.transport in the
poll loop; change the poll block to collect decoded GameEvent instances (e.g.,
push game_event into a local Vec<GameEvent> like collected_game_events) instead
of calling self.dispatch_game_event inside the match arms (references:
transport.poll_events, NetworkEvent::GameEvent, self.dispatch_game_event); after
the if let Some(transport) = &mut self.transport { ... } scope ends, iterate the
collected_game_events and call self.dispatch_game_event for each to release the
transport borrow before mutably borrowing self again.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 6bbd9bd2-76d4-465d-bbf2-3375632b5116

📥 Commits

Reviewing files that changed from the base of the PR and between 8ab98f8 and 9407a54.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • .github/workflows/release-plz.yml
  • crates/aetheris-client-wasm/src/lib.rs
  • playground/src/playground.ts

Comment thread crates/aetheris-client-wasm/src/lib.rs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@crates/aetheris-client-wasm/src/lib.rs`:
- Line 669: collected_game_events is declared inside the if-let transport block
but later referenced at dispatch time, so move its declaration to an outer scope
so it lives across the transport block and the dispatch use; specifically,
declare let mut collected_game_events: Vec<...> (use the existing GameEvent
type) before the if let Some(transport) { ... } block and remove the inner
declaration so the same mutable vector is populated inside the block and still
available later when dispatching.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e11160ec-7487-4607-879f-e68148502e9e

📥 Commits

Reviewing files that changed from the base of the PR and between 9407a54 and 1ac1043.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • Cargo.toml
  • crates/aetheris-client-wasm/CHANGELOG.md
  • crates/aetheris-client-wasm/src/lib.rs
  • docs/CLIENT_DESIGN.md
✅ Files skipped from review due to trivial changes (2)
  • crates/aetheris-client-wasm/CHANGELOG.md
  • docs/CLIENT_DESIGN.md

Comment thread crates/aetheris-client-wasm/src/lib.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
crates/aetheris-client-wasm/src/lib.rs (1)

659-669: ⚠️ Potential issue | 🔴 Critical

Move collected_game_events outside the transport block.

Line 669 declares the buffer inside if let Some(transport), but Lines 857-859 consume it after that scope ends. That makes the deferred-dispatch refactor a compile blocker.

🐛 Proposed fix
+            let mut collected_game_events = Vec::new();
             if let Some(transport) = &mut self.transport {
                 let events = match transport.poll_events().await {
                     Ok(e) => e,
                     Err(e) => {
                         tracing::error!("Transport poll failure: {:?}", e);
@@
                 };
                 let mut updates: Vec<(ClientId, aetheris_protocol::events::ComponentUpdate)> =
                     Vec::new();
-                let mut collected_game_events = Vec::new();

Also applies to: 855-859

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/aetheris-client-wasm/src/lib.rs` around lines 659 - 669, The buffer
variable collected_game_events is declared inside the if let Some(transport) {
... } scope but later used after that block (in the deferred-dispatch code),
causing a borrow/ownership error; move the declaration of collected_game_events
(and any other buffers that must survive beyond the transport block) so it is
created before the if let Some(transport) = &mut self.transport { ... } block,
then push into it from inside the transport.poll_events() handling
(functions/methods to look at: transport.poll_events, the surrounding async
method in lib.rs, and any code that consumes collected_game_events around lines
855–859), ensuring the variable is in the outer scope and not dropped when the
transport block ends.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@crates/aetheris-client-wasm/src/lib.rs`:
- Around line 659-669: The buffer variable collected_game_events is declared
inside the if let Some(transport) { ... } scope but later used after that block
(in the deferred-dispatch code), causing a borrow/ownership error; move the
declaration of collected_game_events (and any other buffers that must survive
beyond the transport block) so it is created before the if let Some(transport) =
&mut self.transport { ... } block, then push into it from inside the
transport.poll_events() handling (functions/methods to look at:
transport.poll_events, the surrounding async method in lib.rs, and any code that
consumes collected_game_events around lines 855–859), ensuring the variable is
in the outer scope and not dropped when the transport block ends.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a0d13e4a-07d3-4dd1-a6c4-4518fafd9199

📥 Commits

Reviewing files that changed from the base of the PR and between f91cce1 and feed63f.

📒 Files selected for processing (1)
  • crates/aetheris-client-wasm/src/lib.rs

@garnizeh-labs
garnizeh-labs merged commit 9764f26 into main Apr 26, 2026
10 checks passed
@garnizeh-labs
garnizeh-labs deleted the fix/client-hardening-and-release-plz-stability branch April 26, 2026 16:02
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.

2 participants