-
Notifications
You must be signed in to change notification settings - Fork 68
Description
Description
The upgradeController intent handler has a validation bug caused by JavaScript type coercion. When a creep has an empty store object {} (no energy key), the validation check object.store.energy <= 0 evaluates to false instead of true, allowing the upgrade to proceed with undefined energy values and creating database patches with NaN progress.
Steps to Reproduce
- Create a creep with an empty store:
{store: {}}(no energy key) - Submit an
upgradeControllerintent for that creep - Process the intent
Expected Behavior:
- Validation check
object.store.energy <= 0should return early (no patches) - Creep should have ActionLog patch only (validation failure)
Actual Behavior:
- Validation check passes (because
undefined <= 0isfalse) - Code continues processing with
object.store.energy = undefined - Creates patches with
NaNvalues:- Creep store:
{energy: NaN} - Controller progress:
NaN - Stats:
energyControlincremented byNaN
- Creep store:
Root Cause
File: src/processor/intents/creeps/upgradeController.js
Line: 9
if(object.type != 'creep' || object.spawning || !object.store || object.store.energy <= 0) {
return;
}Issue: JavaScript type coercion causes undefined <= 0 to evaluate to false:
{energy: 0}.energy→0→0 <= 0istrue✅ (validation works){}.energy→undefined→undefined <= 0isfalse❌ (validation bypassed)
This allows the code to proceed with:
var buildEffect = Math.min(buildPower, object.store.energy); // Math.min(5, undefined) → NaN
var boostedEffect = Math.floor(buildEffect + _.sum(boostedParts)); // Math.floor(NaN) → NaN
object.store.energy -= buildEffect; // undefined - NaN → NaNImpact
- Severity: Low (edge case unlikely in normal gameplay)
- Scope: Only affects creeps with empty store objects (no energy key)
- Data Corruption: Creates invalid database state with
NaNvalues - Gameplay Impact: Minimal (most creeps have explicit
{energy: 0}or{energy: n})
Suggested Fix
Option 1: Explicit undefined check (safest)
if(object.type != 'creep' || object.spawning || !object.store ||
object.store.energy === undefined || object.store.energy <= 0) {
return;
}Option 2: Nullish coalescing (more idiomatic)
if(object.type != 'creep' || object.spawning || !object.store ||
(object.store.energy ?? 0) <= 0) {
return;
}Option 3: Default value (defensive)
var energyAvailable = object.store.energy || 0;
if(object.type != 'creep' || object.spawning || !object.store || energyAvailable <= 0) {
return;
}Additional Context
This edge case occurs when a creep's store object exists but doesn't contain an energy key. While most game mechanics ensure stores have explicit resource values (e.g., {energy: 0}), certain edge cases or manual database manipulation could result in empty store objects {}.
Test Case: A creep with empty store {store: {}} and upgradeController intent produces unexpected results:
- Creates patches on both creep (with
NaNenergy) and controller (withNaNprogress) - Expected: Should return early with only ActionLog patch on creep (validation failure)
Related Files
Other intent handlers may have similar issues with undefined store values:
src/processor/intents/creeps/build.jssrc/processor/intents/creeps/repair.js- Any handler checking
object.store[resourceType] <= 0
Environment
- Engine: screeps (official engine)
- Reproducibility: 100% with empty store object