Skip to content

upgradeController bypasses validation when creep store is empty object {} #151

@brunoamancio

Description

@brunoamancio

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

  1. Create a creep with an empty store: {store: {}} (no energy key)
  2. Submit an upgradeController intent for that creep
  3. 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}.energy00 <= 0 is true ✅ (validation works)
  • {}.energyundefinedundefined <= 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions