Table of Contents

Read production statistics

Problem. You want production rates over time — what the factory is delivering — not the instantaneous state of one machine.

Solution. The statistics trackers. The game already records delivery history; read it rather than sampling it yourself.

The tracker

namespace Game.Core.Statistics;

public interface IGameStatisticsTracker
{
    IReadOnlyList<IStatisticsTracker<UnifiedShapeId>> ShapeDeliveryTrackers { get; }
    IReadOnlyList<IStatisticsTracker<RocketGroupId>> RocketDeliveryTrackers { get; }
}

Two things are tracked: shape deliveries and rocket launches. That is the whole scope — this is not a general metrics system, and there is no per-building history here.

Both are lists of trackers, one per interval (the different time windows the statistics screen offers), so pick the tracker whose resolution matches your question.

Buckets

public interface IStatisticsTracker<TKey> : IStatisticsStream<TKey>
{
    IReadOnlyList<IStatisticsBucket<TKey>> Buckets { get; }
}

Everything is bucketed time series, keyed by what was delivered:

foreach (IStatisticsTracker<UnifiedShapeId> tracker in statistics.ShapeDeliveryTrackers)
{
    foreach (IStatisticsBucket<UnifiedShapeId> bucket in tracker.Buckets)
    {
        // per-interval totals per shape
    }
}

Note the key is UnifiedShapeId, not ShapeId — deliveries are aggregated across shape variants the research system considers equivalent (ResearchShapeUnifier). If you compare against a raw ShapeId from a belt you may not get a match.

The supporting types

Worth knowing they exist before you build anything time-series shaped yourself:

Type Role
GameStatisticsTracker the concrete tracker
IntervalBasedStatisticsTracker fixed-interval buckets
ISlidingWindowStatisticsTracker a moving window
SlidingWindowStatisticsStreamView a view over a window
AggregatedStatisticsTracker combines trackers
StatisticsBucket, StatisticsStream the storage primitives

If you need a rolling average of something the game does not track — machine efficiency, for instance — this is the vocabulary to imitate rather than reinvent: bucket by interval, keep a bounded number of buckets, aggregate on read.

Sending the player to the statistics screen

Cheaper than rendering your own charts:

Events?.ShowStatistics.Invoke();

See notifications and HUD screens. Pairing a notification with an action that opens statistics gives you a complete feature without any UI work.

The vanilla statistics UI

If you want to add to the existing screen rather than read from it, the tabs are HUDStatisticsTabDelivery, HUDStatisticsTabStructures, HUDStatisticsTabRocketLaunches, with HUDStatisticsChart doing the drawing. There is no interceptor for statistics tabs, so extending that screen means detouring (hooking) — which is a lot of work for something a notification and a side panel section can usually cover.

Gotchas

  • Delivery only. There is no built-in per-building production history. Any per-machine metric is yours to collect — see read machine state.
  • UnifiedShapeId ≠ ShapeId. Unify before comparing.
  • Buckets are bounded; old data rolls off. Do not expect whole-save history.
  • Reading every bucket of every tracker every frame is needless — statistics update on an interval, so read on the same cadence.
Note

IGameStatisticsTracker is verified, as is the bucket structure. How you obtain the instance was not verified — it is not on IGameSessionManagers. Expect to reach it via the session with the publicizer, or by hooking a type that receives it.