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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions