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
upgradeController intent for that creep
- Process the intent
Expected Behavior:
- Validation check
object.store.energy <= 0 should return early (no patches)
- Creep should have ActionLog patch only (validation failure)
Actual Behavior:
- Validation check passes (because
undefined <= 0 is false)
- Code continues processing with
object.store.energy = undefined
- Creates patches with
NaN values:
- Creep store:
{energy: NaN}
- Controller progress:
NaN
- Stats:
energyControl incremented by NaN
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 <= 0 is true ✅ (validation works)
{}.energy → undefined → undefined <= 0 is false ❌ (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 → NaN
Impact
- 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
NaN values
- 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
NaN energy) and controller (with NaN progress)
- 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.js
src/processor/intents/creeps/repair.js
- Any handler checking
object.store[resourceType] <= 0
Environment
- Engine: screeps (official engine)
- Reproducibility: 100% with empty store object
Description
The
upgradeControllerintent handler has a validation bug caused by JavaScript type coercion. When a creep has an empty store object{}(no energy key), the validation checkobject.store.energy <= 0evaluates tofalseinstead oftrue, allowing the upgrade to proceed withundefinedenergy values and creating database patches withNaNprogress.Steps to Reproduce
{store: {}}(no energy key)upgradeControllerintent for that creepExpected Behavior:
object.store.energy <= 0should return early (no patches)Actual Behavior:
undefined <= 0isfalse)object.store.energy = undefinedNaNvalues:{energy: NaN}NaNenergyControlincremented byNaNRoot Cause
File:
src/processor/intents/creeps/upgradeController.jsLine: 9
Issue: JavaScript type coercion causes
undefined <= 0to evaluate tofalse:{energy: 0}.energy→0→0 <= 0istrue✅ (validation works){}.energy→undefined→undefined <= 0isfalse❌ (validation bypassed)This allows the code to proceed with:
Impact
NaNvalues{energy: 0}or{energy: n})Suggested Fix
Option 1: Explicit undefined check (safest)
Option 2: Nullish coalescing (more idiomatic)
Option 3: Default value (defensive)
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: {}}andupgradeControllerintent produces unexpected results:NaNenergy) and controller (withNaNprogress)Related Files
Other intent handlers may have similar issues with
undefinedstore values:src/processor/intents/creeps/build.jssrc/processor/intents/creeps/repair.jsobject.store[resourceType] <= 0Environment