Audit quick wins: unexported imports, dt clamp, perf bracket, FFI/alloc hoists, mojibake repair - #28
Conversation
…nd-trip damage) Every non-ASCII char in these files' comments had been double-encoded (UTF-8 read as cp1252 and re-written: em dash -> 'â€"', x -> '×', pi -> 'Ï€'), and weapons.ts additionally carried the BOM the same round-trip adds. 87 sequences repaired by inverting the exact transformation (cp1252-encode each suspect run, require the byte image to decode as valid multi-byte UTF-8); clean text has no such image, so the repair cannot touch it by construction. Comment-only damage - a targeted regex sweep confirmed no string literal was affected. main.ts and director.ts had the same damage (a further ~170 sequences); their repair rides the next commit alongside functional changes to the same files.
Correctness: - director.ts exported countAlive/despawnAllEnemies/damageEnemy - all three were imported (hud, combat, main) and called every frame while never being exported; it worked only because Perry ignores export semantics, and would break under tsc, an LSP, or a stricter Perry. - dt is now clamped to 100 ms. A stalled frame (SDF-clipmap re-bake, window drag) hands back 300 ms+, and one Euler step that big walks a charging dragoon clean over a wall's repulsion ring into the house. - PERFTEST's perfTB was declared and never assigned, so phase B always reported a huge negative and phase C silently absorbed it - the perf instrument of record could not tell sim time from draw time. It now brackets at beginMode3D. - score.ts knew 5 of the 7 kinds: both SH-042 ranged upgrade kinds fell to the 10-point fallback (less than a mantis). Now 40/90 points. - commitRun/bestScore hardcoded arena slot 0 at five sites while the menu read the real ARENA_INDEX - per-arena bests would have silently merged the day a second arena ships. - removed the dead duplicate 'const moving' (same block, two meanings) and the never-implemented DIR.pending* accumulate-then-apply fields. Perf hygiene (perf-audit finding 11, quality-neutral): - one authoritative playerPosition() read per frame after stepPhysics; the per-projectile loop alone was re-crossing the FFI up to 24x/frame. - forest + static-mesh draw loops reuse hoisted position/tint objects built once at load (was ~180 fresh objects per frame for static data). - diag-bar strings are built inside the !MOBILE gate that draws them. - removed ~45 imports verified unused by word-boundary count (the SH-025 split left them stranded). Docs: perry-quirks.md 'Impact on the shooter's design' described the deleted build-world.ts bake step as the shipped answer; it now states the runtime-loadWorld reality and the rules that still bind. Also repairs the remaining ~170 mojibake sequences in main.ts and director.ts (see previous commit for the mechanism and verification). Verified: perry compile green; 25 s title-screen batch run boots clean (120k grass, nav flights found, no ReferenceError); 75 s AITEST run auto-plays a wave - enemies spawn, close 57 m to melee, cycle AI states, damage path fires - with zero errors on stdout/stderr.
📝 WalkthroughWalkthroughThe PR updates runtime world rendering, frame timing, environment grass variation, director exports, arena-indexed score commits, enemy scoring, HUD best-score lookup, and Perry world-loading documentation. Several files also receive comment and formatting corrections. ChangesRuntime arena updates
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
src/score.ts (1)
52-52: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse
KIND_POINTS.lengthinstead of a hardcoded magic number.This makes the bounds check robust against future additions to the
KIND_POINTSarray, preventing cases where point configurations are expanded but the length check remains stale.♻️ Proposed fix
- const base = kind >= 0 && kind < 7 ? KIND_POINTS[kind] : 10; + const base = kind >= 0 && kind < KIND_POINTS.length ? KIND_POINTS[kind] : 10;🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/score.ts` at line 52, Update the bounds check in the score calculation to compare kind against KIND_POINTS.length instead of the hardcoded value 7, while preserving the existing fallback behavior for out-of-range kinds.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/main.ts`:
- Around line 397-417: Update the mobile profile configuration in the
surrounding rendering setup to disable GTAO, alongside the existing mobile
exclusions for SSGI, SSR, and sun shafts. Preserve mobile shadows and bloom, and
leave GTAO enabled for non-MOBILE profiles.
---
Nitpick comments:
In `@src/score.ts`:
- Line 52: Update the bounds check in the score calculation to compare kind
against KIND_POINTS.length instead of the hardcoded value 7, while preserving
the existing fallback behavior for out-of-range kinds.
🪄 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: f0aa4042-2bb8-4891-9b3c-b926d6aa9974
📒 Files selected for processing (9)
docs/perry-quirks.mdsrc/combat.tssrc/director.tssrc/enemies.tssrc/environment.tssrc/hud.tssrc/main.tssrc/score.tssrc/weapons.ts
| // Lumen SW-GI is the big one — it re-bakes an SDF clipmap as the view moves, | ||
| // which is a GPU stall the phone has no headroom to absorb. SSR goes with it. | ||
| // | ||
| // GTAO stays ON. It was cut with the other two at first, but measured on an | ||
| // iPhone 16 Pro it doesn't cost a frame-rate tier: mid-wave the frame sits at | ||
| // 25.0 ms either way (the same ~40 fps), and the title screen holds 60. It buys | ||
| // back the contact shadows that seat the grass, trees and aliens on the ground | ||
| // instead of leaving them looking pasted over it — the cheapest of the three | ||
| // instead of leaving them looking pasted over it — the cheapest of the three | ||
| // screen-space passes by some margin, and the one with the best return. | ||
| // | ||
| // Read that "free" precisely, though: present mode is Fifo on a 120 Hz panel, | ||
| // so every frame snaps to a multiple of 8.33 ms and GTAO is being absorbed by | ||
| // slack inside a bucket rather than costing nothing. It eats margin. If a | ||
| // heavier wave starts tipping frames from the 25 ms bucket into the 33 ms one, | ||
| // this is the first thing to put back. (Per-pass GPU timings would settle it, | ||
| // but the profiler reports -1 on iOS — the Metal backend doesn't get | ||
| // TIMESTAMP_QUERY — so wall-clock at a fixed point in the wave is the honest | ||
| // but the profiler reports -1 on iOS — the Metal backend doesn't get | ||
| // TIMESTAMP_QUERY — so wall-clock at a fixed point in the wave is the honest | ||
| // instrument here.) | ||
| // | ||
| // Render scale stays at 0.5 (TSR reconstructs to native in the TAA pass), which | ||
| // on a 2622x1206 iPhone means a ~1311x603 internal buffer — the same | ||
| // on a 2622x1206 iPhone means a ~1311x603 internal buffer — the same |
There was a problem hiding this comment.
🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win
Disable GTAO on the MOBILE profile.
The comment indicates a deliberate choice to keep GTAO enabled. As per coding guidelines, the MOBILE profile must disable SSGI, SSR, GTAO, and sun shafts while retaining shadows and bloom. Please ensure GTAO is disabled on mobile platforms to comply with the project's performance budgets.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/main.ts` around lines 397 - 417, Update the mobile profile configuration
in the surrounding rendering setup to disable GTAO, alongside the existing
mobile exclusions for SSGI, SSR, and sun shafts. Preserve mobile shadows and
bloom, and leave GTAO enabled for non-MOBILE profiles.
Source: Coding guidelines
Two things the textually-clean auto-merge would have shipped broken: - #28 removed KIND_COUNT/ALIEN_GLB from main.ts imports; this branch's stageModels batch uses both. Re-added - a green Perry compile does not catch absent cross-module imports (ReferenceError at boot). - This branch (and #30) accidentally committed dxcompiler.dll/dxil.dll: both were branched before #29's .gitignore landed, so 'git add -A' swept the untracked DLLs in, and #30's merge carried them onto main. Untracked again here (files stay on disk - the game needs them beside the exe; see EN-058). Verified on the merged tree: compile green, 14 s batch run boots to the menu with [music] menu=1 calm=2 combat=3 and [anim] clipset=2 slot0=9 slot1=10 both live.
First slice of the 2026-07-16 full audit (game + engine). All quality-neutral by design — no visual or gameplay behavior changes.
Correctness
countAlive/spawnEnemy/despawnAllEnemies/damageEnemyin director.ts) — hud/combat/main call them every frame; only Perry's indifference to export semantics made it work. Exported the three that cross modules;spawnEnemystays internal.dtclamped to 100 ms — a stalled frame (SDF clipmap re-bake does 20–60 ms CPU; worse spikes exist) stepped the Euler integrators far enough to carry a charging dragoon over a wall's repulsion ring.perfTBwas never assigned — phase B reported negative, phase C absorbed it. Now bracketed atbeginMode3D.commitRun(0)/bestScore(0)hardcoded arena slot 0 at 5 sites — per-arena bests would merge silently the day a second arena ships. NowARENA_INDEX.const moving(one block, two meanings — tsc would reject it) and the never-implementedDIR.pending*fields.Perf hygiene (perf-audit finding 11 — all quality-neutral)
playerPosition()FFI read per frame (the enemy-projectile loop alone did up to 24/frame).Source repair
â€"→—etc.). Repair inverts the exact corruption (cp1252 byte-image must decode as valid UTF-8), so clean text is untouchable by construction. Verified comment-only — no string literal was affected.Verification
perry compilegreen (necessary, not sufficient here).ray_query=trueon DX12 — the DXC DLLs in the repo root are live (separate ticket coming for deploying them properly).Summary by CodeRabbit
New Features
Bug Fixes
Performance