Stats System Overview

July 8, 2026 · View on GitHub

Introduction

The Screeps bot features a unified statistics system (unifiedStats) that consolidates all performance tracking, metrics collection, and profiling into a single cohesive system. This replaced the previous dual-system approach (profiler + statsManager).

Architecture

┌─────────────────────────────────────────────────────────────┐
│                     Memory.stats Root                        │
│                  (Unified Stats Storage)                     │
│                    Nested Object Format                      │
└─────────────────────────────────────────────────────────────┘

                ┌─────────────┴─────────────┐
                │                           │
        ┌───────▼──────┐            ┌──────▼────────┐
        │ UnifiedStats │            │   Graphite    │
        │   Manager    │───────────▶│   Exporter    │
        └──────────────┘            └───────────────┘

    ┌───────────┼───────────┬──────────┬──────────┬──────────┐
    │           │           │          │          │          │
┌───▼───┐  ┌───▼────┐  ┌──▼───┐  ┌───▼───┐  ┌──▼────┐  ┌──▼────┐
│Empire │  │ Rooms  │  │Roles │  │Subsys │  │Native │  │Process│
│ Stats │  │ Stats  │  │Stats │  │ Stats │  │Calls  │  │ Stats │
└───────┘  └────────┘  └──────┘  └───────┘  └───────┘  └───────┘

        ┌───────┼────────┐
        │       │        │
    ┌───▼───┐┌─▼──┐┌────▼────┐
    │Metrics││CPU ││Pheromone│
    └───────┘└────┘└─────────┘

Components

1. UnifiedStats (core/unifiedStats.ts)

Single central manager for all statistics collection and publishing.

Responsibilities:

  • Collect stats from all subsystems
  • Aggregate and compute rolling averages
  • Publish nested object structure to Memory.stats
  • Track role execution via role: prefix
  • Collect pheromones from SwarmState
  • Track native API calls
  • Record kernel process stats

Key Methods:

  • measureSubsystem() - Measure CPU for any subsystem (including roles with role: prefix)
  • recordRoom() - Record room metrics (includes pheromones automatically)
  • recordNativeCall() - Track native API calls
  • collectProcessStats() - Collect kernel process stats
  • finalizeTick() - Publish all stats to Memory.stats at end of tick

2. Native Calls Tracker (core/nativeCallsTracker.ts)

Wraps Screeps API methods to track usage.

Tracked Methods:

  • PathFinder.search
  • Creep movement: moveTo, move
  • Creep actions: harvest, transfer, withdraw, build, repair, upgradeController
  • Creep combat: attack, rangedAttack, heal, dismantle
  • Creep utility: say

Features:

  • Minimal overhead (~0.01 CPU per call)
  • Can be enabled/disabled
  • Initialized once at bot startup

3. Room Integration (core/roomNode.ts and core/managers/)

Each room records its stats at end of tick through coordinated manager modules.

Architecture:

  • RoomNode - Orchestration layer for room processing
  • RoomDefenseManager - Threat assessment and tower control
  • RoomConstructionManager - Blueprint-based construction
  • RoomEconomyManager - Labs, factory, power spawn, links

Collected Metrics:

  • Controller level and progress
  • Energy availability and capacity
  • Storage energy
  • Creep and hostile counts
  • CPU usage (average and peak)
  • Pheromone levels
  • Danger level

4. Local Stats Consumers

Stats are published to Memory.stats and console output for local inspection and private-server validation. External Graphite/Loki exporter packages are no longer part of this repository.

Data Flow

Tick Start

    ├─> Room Processing
    │   ├─> Measure CPU (Profiler)
    │   ├─> Collect Metrics (Pheromone Manager)
    │   └─> Record Room Stats (StatsManager)

    ├─> Creep Execution
    │   ├─> Native Calls Tracking (if enabled)
    │   ├─> Measure CPU per Role (Profiler)
    │   └─> Record Role Stats (StatsManager)

    ├─> Subsystem Execution
    │   ├─> Measure CPU (Profiler)
    │   └─> Record Subsystem Stats (StatsManager)

    └─> Tick End
        ├─> Update Empire Stats
        ├─> Finalize Stats (publish to Memory)
        └─> Finalize Profiler

Tick Complete → Memory.stats populated → local inspection/private-server validation

Statistics Categories

Empire Statistics

Global metrics across all rooms:

  • Room count, creep count
  • Total storage energy
  • GCL/GPL progress
  • CPU usage and bucket
  • Heap memory usage
  • Market credits

Room Statistics

Per-room metrics:

  • RCL and controller progress
  • Energy levels and capacity
  • Creep and hostile counts
  • CPU usage
  • Energy harvested
  • Damage received
  • Danger level
  • Task-board backlog counts, reservations, and amount-aware delivery pressure

Subsystem Statistics

CPU tracking for major subsystems:

  • rooms (room processing)
  • kernel (process management)
  • spawns (spawn logic)
  • creeps (creep execution)
  • moveRequests (traffic management)
  • powerCreeps
  • visualizations
  • stats (self-monitoring)

Role Statistics

Per-role metrics:

  • Creep count
  • CPU usage (average and peak)
  • Number of creeps processed

Common roles: harvester, hauler, upgrader, builder, guard, scout, claimer, etc.

Pheromone Statistics

Per-room pheromone levels:

  • expand, harvest, build, upgrade
  • defense, war, siege, logistics
  • Dominant pheromone
  • Room intent/posture

Native Calls Statistics

API usage tracking:

  • PathFinder.search calls
  • Movement calls (moveTo, move)
  • Work actions (harvest, build, repair, upgrade)
  • Combat actions (attack, rangedAttack, heal)
  • Transfer actions (transfer, withdraw)
  • Other actions (dismantle, say)
  • Total calls per tick

Performance Characteristics

CPU Usage

  • StatsManager overhead: ~0.1-0.2 CPU per tick
  • Native calls tracking: ~0.01 CPU per tracked call (when enabled)
  • Profiler integration: No additional overhead (already existed)
  • Total impact: <1% of CPU budget in typical scenarios

Memory Usage

  • Structured stats: ~10-20 KB (in Memory.stats object)
  • Flattened stats: ~5-10 KB (as individual Memory keys)
  • Total: ~15-30 KB depending on empire size
  • Negligible impact on 2 MB Memory limit

Update Frequency

  • Empire stats: Every tick
  • Room stats: Every tick
  • Pheromone stats: Every 5 ticks (aligned with pheromone updates)
  • Role/Subsystem stats: Every tick
  • Native calls: Every tick (when enabled)

Configuration

Enable/Disable Stats

import { unifiedStats } from "./core/unifiedStats";

unifiedStats.setEnabled(false); // Disable
unifiedStats.setEnabled(true);  // Enable (default)

Configure via Constructor

// In unifiedStats.ts
new UnifiedStatsManager({
  enabled: true,
  smoothingFactor: 0.1,      // Higher = more weight on recent values
  trackNativeCalls: true,     // Track native API calls
  logInterval: 100,           // Log summary every N ticks (0 = never)
  segmentUpdateInterval: 10,  // Update memory segment every N ticks
  maxHistoryPoints: 1000      // Max historical data points in segment
});

Stats Access

Memory.stats Structure (Nested Objects)

// Access stats directly from Memory
Memory.stats = {
  tick: number,
  timestamp: number,
  cpu: { used, limit, bucket, percent, heap_mb },
  gcl: { level, progress, progress_total, progress_percent },
  gpl: { level },
  empire: { rooms, creeps, energy, credits },
  rooms: {
    [roomName]: {
      rcl, energy, controller, creeps, hostiles,
      brain: { danger, posture_code, colony_level_code },
      pheromones: { expand, harvest, build, upgrade, defense, war, siege, logistics, nukeTarget },
      metrics: { ... },
      taskBoard: {
        tasks, open_tasks, assigned_tasks,
        reservations, stale_reservations, blocked_reservations,
        amount, reserved_amount, remaining_amount,
        delivery_amount, delivery_reserved_amount, delivery_remaining_amount,
        critical_delivery_remaining_amount,
        by_type: {
          [taskType]: { tasks, open_tasks, assigned_tasks, reservations, amount, reserved_amount, remaining_amount }
        }
      },
      profiler: { avg_cpu, peak_cpu, samples }
    }
  },
  roles: {
    [roleName]: { count, avg_cpu, peak_cpu, calls, samples }
  },
  subsystems: {
    [subsystemName]: { avg_cpu, peak_cpu, calls, samples }
  },
  native: { pathfinder_search, move, harvest, ... },
  processes: {
    [processId]: { name, priority, state, cpu stats, ... }
  },
  creeps: {
    [creepName]: { role, home_room, current_room, cpu, action, ... }
  }
}

Room Task-Board Amount Telemetry

Memory.stats.rooms[roomName].taskBoard summarizes creep task-board pressure without polling raw Memory.creepTaskBoard:

  • tasks, open_tasks, assigned_tasks: task-count backlog. These are counts, not energy/work amounts.
  • reservations, stale_reservations, blocked_reservations: reservation health by count.
  • amount: total requested work/energy amount across all tasks.
  • reserved_amount: amount already reserved by creeps.
  • remaining_amount: unreserved amount, computed as max(0, amount - reserved_amount) per task.
  • delivery_amount, delivery_reserved_amount, delivery_remaining_amount: same totals restricted to delivery tasks (refillSpawn, refillExtension, refillTower, fillTerminalEnergy, storeEnergy).
  • critical_delivery_remaining_amount: unreserved amount for high-priority delivery tasks.
  • by_type: per-task-type counts and amount totals for isolating the source of backlog.

Interpretation: a room can have high remaining_amount because large storeEnergy tasks are waiting, while urgent spawn/extension refills dominate critical_delivery_remaining_amount. Compare remaining_amount, delivery_remaining_amount, critical_delivery_remaining_amount, and by_type before treating the room as generally starved.

Programmatic Access

import { unifiedStats } from "./core/unifiedStats";

// Get current snapshot
const snapshot = unifiedStats.getSnapshot();

// Access specific stats
const empireStats = snapshot.empire;
const roomStats = snapshot.rooms["W1N1"];
const roleStats = snapshot.roles["harvester"];
const subsystemStats = snapshot.subsystems["kernel"];
const nativeCallsStats = snapshot.native;

Integration with External Tools

Grafana Dashboards

  • Example Flux queries for all stat types
  • Recommended dashboard layouts
  • Alert configurations
  • Variable definitions

Graphite Exporter

The exporter automatically:

  • Scrapes nested object structure from Memory.stats
  • Flattens and tags stats with categories
  • Parses stat keys to extract metadata
  • Sends to Grafana Cloud Graphite endpoint

Custom Tools

Stats are published as nested objects for easy consumption:

// Access in console
Memory.stats.empire.rooms
Memory.stats.rooms.W1N1.rcl
Memory.stats.roles.harvester.count
Memory.stats.rooms.W1N1.pheromones.harvest
Memory.stats.rooms.W1N1.taskBoard.remaining_amount
Memory.stats.rooms.W1N1.taskBoard.critical_delivery_remaining_amount
Memory.stats.rooms.W1N1.taskBoard.by_type.refillSpawn.remaining_amount

Migration from Legacy Stats

See STATS_MIGRATION.md for:

  • Complete migration guide
  • Mapping from old to new stat keys
  • Backward compatibility notes
  • Transition checklist

Best Practices

1. Monitor Key Metrics

Focus on:

  • CPU usage (total and per subsystem/role)
  • CPU bucket level
  • Native calls (especially PathFinder)
  • Room danger levels
  • Energy balance

2. Use Stats for Optimization

  • Identify expensive subsystems/roles
  • Detect PathFinder overuse
  • Monitor room efficiency
  • Track pheromone balance

3. Set Up Alerts

Configure Grafana alerts for:

  • High CPU usage (>90% of limit)
  • Low bucket (<2000)
  • Rooms under attack (danger > 1)
  • Excessive PathFinder calls (>100/tick)

4. Regular Review

  • Check dashboard weekly
  • Review top CPU consumers
  • Analyze trends over time
  • Adjust bot parameters as needed

Future Enhancements

Potential improvements:

  • Memory segment persistence for historical stats
  • Stats export to external API
  • Custom stat categories via config
  • Real-time stats streaming
  • Advanced analytics and predictions
  • Automated performance optimization

Troubleshooting

Stats Not Appearing

  • Check statsManager.isEnabled() returns true
  • Verify bot is running (not in critical bucket mode)
  • Check Memory size hasn't exceeded limit

Native Calls Not Tracked

  • Verify native calls tracking is enabled
  • Check initialization in main loop
  • Ensure profiling config is enabled

Metrics Data Missing

  • Verify exporter is running
  • Check exporter logs for errors
  • Ensure Grafana Cloud is accessible
  • Verify API key and endpoint configuration

High Memory Usage

  • Disable native calls tracking if not needed
  • Reduce log interval
  • Consider memory segment storage

Conclusion

The unified stats system provides comprehensive visibility into bot performance and behavior. It enables data-driven optimization, early problem detection, and effective monitoring through Grafana dashboards. The system is designed to be efficient, extensible, and easy to use while maintaining backward compatibility with existing infrastructure.