diff --git a/plugins/statistics_exporter/instance.ts b/plugins/statistics_exporter/instance.ts index b824992d..91e68284 100644 --- a/plugins/statistics_exporter/instance.ts +++ b/plugins/statistics_exporter/instance.ts @@ -6,14 +6,26 @@ const { Gauge } = lib; type IpcStats = { game_tick: number, player_count: number, - game_flow_statistics: { - pollution_statistics: IpcFlowType, - }, - force_flow_statistics: { - [key:string]: { - [key:string]: IpcFlowType, + surface_statistics: { + [key: string]: { + game_flow_statistics: { + pollution_statistics: IpcFlowType, + }, + force_flow_statistics: { + [key:string]: { + [key:string]: IpcFlowType, + } + }, } }, + platforms: { + [key: string]: { + force: string, + surface: string, + speed: number, + weight: number, + } + } } type IpcFlowType = { @@ -34,29 +46,59 @@ const instanceGameTicksTotal = new Gauge( const instanceForceFlowStatistics = new Gauge( "clusterio_statistics_exporter_instance_force_flow_statistics", "Items/fluids/enemies/buildings produced/built/killed by a force", - { labels: ["instance_id", "force", "statistic", "direction", "name"] }, + { labels: ["instance_id", "surface", "force", "statistic", "direction", "name"] }, ); const instanceGameFlowStatistics = new Gauge( "clusterio_statistics_exporter_instance_game_flow_statistics", "Pollution produced/consumed in the game", - { labels: ["instance_id", "statistic", "direction", "name"] }, + { labels: ["instance_id", "surface", "statistic", "direction", "name"] }, +); +const instancePlatformMapping = new Gauge( + "clusterio_statistics_exporter_platform_mapping", + "Mapping of platform name to force, surface and instance", + { labels: ["instance_id", "platform_name", "force", "surface"] } +); +const instancePlatformSpeed = new Gauge( + "clusterio_statistics_exporter_platform_speed", + "Current speed of the platform", + { labels: ["instance_id", "force", "surface"] } +); +const instancePlatformWeight = new Gauge( + "clusterio_statistics_exporter_platform_weight", + "Current weight/mass of the platform", + { labels: ["instance_id", "force", "surface"] } ); function setForceFlowStatistic( instanceId: number, + surfaceName: string, forceName: string, statisticName: string, direction: string, item: string, value: number, ) { - instanceForceFlowStatistics.labels(String(instanceId), forceName, statisticName, direction, item).set(value); + instanceForceFlowStatistics.labels( + String(instanceId), + surfaceName, + forceName, + statisticName, + direction, + item + ).set(value); // For item and fluid statistics it's useful to compare the input flow with the // output flow, to simplify the comparison ensure both directions have a value. if (["item_production_statistics", "fluid_production_statistic"].includes(statisticName)) { let reverseDirection = direction === "input" ? "output" : "input"; - instanceForceFlowStatistics.labels(String(instanceId), forceName, statisticName, reverseDirection, item); + instanceForceFlowStatistics.labels( + String(instanceId), + surfaceName, + forceName, + statisticName, + reverseDirection, + item + ).set(value); } } @@ -84,21 +126,59 @@ export class InstancePlugin extends BaseInstancePlugin { instanceGameTicksTotal.labels(String(instanceId)).set(stats.game_tick); instancePlayerCount.labels(String(instanceId)).set(stats.player_count); - for (let [forceName, flowStatistics] of Object.entries(stats.force_flow_statistics)) { - for (let [statisticName, statistic] of Object.entries(flowStatistics)) { - for (let [direction, counts] of Object.entries(statistic)) { - for (let [item, value] of Object.entries(counts)) { - setForceFlowStatistic(instanceId, forceName, statisticName, direction, item, value); + // Remove existing platform mappings and stats for this instance + instancePlatformMapping.removeAll({ instance_id: String(instanceId) }); + instancePlatformSpeed.removeAll({ instance_id: String(instanceId) }); + instancePlatformWeight.removeAll({ instance_id: String(instanceId) }); + + // Set new platform mappings and stats + for (let [platformName, platform] of Object.entries(stats.platforms)) { + instancePlatformMapping.labels( + String(instanceId), + platformName, + platform.force, + platform.surface + ).set(1); + + instancePlatformSpeed.labels( + String(instanceId), + platform.force, + platform.surface + ).set(platform.speed); + + instancePlatformWeight.labels( + String(instanceId), + platform.force, + platform.surface + ).set(platform.weight); + } + + for (let [surfaceName, surfaceStats] of Object.entries(stats.surface_statistics)) { + for (let [forceName, flowStatistics] of Object.entries(surfaceStats.force_flow_statistics)) { + for (let [statisticName, statistic] of Object.entries(flowStatistics)) { + for (let [direction, counts] of Object.entries(statistic)) { + // eslint-disable-next-line max-depth + for (let [item, value] of Object.entries(counts)) { + setForceFlowStatistic( + instanceId, + surfaceName, + forceName, + statisticName, + direction, + item, + value + ); + } } } } - } - for (let [direction, counts] of Object.entries(stats.game_flow_statistics.pollution_statistics)) { - for (let [item, value] of Object.entries(counts)) { - instanceGameFlowStatistics.labels( - String(instanceId), "pollution_statistics", direction, item - ).set(value); + for (let [direction, counts] of Object.entries(surfaceStats.game_flow_statistics.pollution_statistics)) { + for (let [item, value] of Object.entries(counts)) { + instanceGameFlowStatistics.labels( + String(instanceId), surfaceName, "pollution_statistics", direction, item + ).set(value); + } } } } @@ -121,3 +201,6 @@ export const _instancePlayerCount = instancePlayerCount; export const _instanceGameTicksTotal = instanceGameTicksTotal; export const _instanceForceFlowStatistics = instanceForceFlowStatistics; export const _instanceGameFlowStatistics = instanceGameFlowStatistics; +export const _instancePlatformMapping = instancePlatformMapping; +export const _instancePlatformSpeed = instancePlatformSpeed; +export const _instancePlatformWeight = instancePlatformWeight; diff --git a/plugins/statistics_exporter/module/export.lua b/plugins/statistics_exporter/module/export.lua index 8537d0f8..33626000 100644 --- a/plugins/statistics_exporter/module/export.lua +++ b/plugins/statistics_exporter/module/export.lua @@ -11,32 +11,51 @@ local statistics = { statistics_exporter = {} function statistics_exporter.export() - --- @diagnostic disable-next-line undefined-field - local pollution = v2_stats and game.get_pollution_statistics(1) or game.pollution_statistics local stats = { game_tick = game.tick, player_count = #game.connected_players, - game_flow_statistics = { - pollution_statistics = { - --- TODO support multi surface pollution statistics - input = pollution.input_counts, - output = pollution.output_counts, - }, - }, - force_flow_statistics = {} + surface_statistics = {}, + platforms = {} } - for _, force in pairs(game.forces) do - local flow_statistics = {} - for _, statName in pairs(statistics) do - local stat = v2_stats and force["get_" .. statName](1) or force[statName] - flow_statistics[statName] = { - input = stat.input_counts, - output = stat.output_counts, - } + + for _, surface in pairs(game.surfaces) do + local surface_stats = { + game_flow_statistics = { + pollution_statistics = { + input = (v2_stats and game.get_pollution_statistics(surface.index) or surface.pollution_statistics).input_counts, + output = (v2_stats and game.get_pollution_statistics(surface.index) or surface.pollution_statistics).output_counts, + }, + }, + force_flow_statistics = {} + } + + for _, force in pairs(game.forces) do + local flow_statistics = {} + for _, statName in pairs(statistics) do + local stat = v2_stats and force["get_" .. statName](surface.index) or force[statName] + flow_statistics[statName] = { + input = stat.input_counts, + output = stat.output_counts, + } + end + surface_stats.force_flow_statistics[force.name] = flow_statistics end - stats.force_flow_statistics[force.name] = flow_statistics + + stats.surface_statistics[surface.name] = surface_stats end + if v2_stats then + for _, force in pairs(game.forces) do + for _, platform in pairs(force.platforms) do + stats.platforms[platform.name] = { + force = force.name, + surface = platform.surface.name, + speed = platform.speed, + weight = platform.weight, + } + end + end + end rcon.print(compat.table_to_json(stats)) end diff --git a/plugins/statistics_exporter/test/plugin.js b/plugins/statistics_exporter/test/plugin.js index 18251e27..4d315fbc 100644 --- a/plugins/statistics_exporter/test/plugin.js +++ b/plugins/statistics_exporter/test/plugin.js @@ -30,18 +30,30 @@ describe("statistics_exporter plugin", function() { JSON.stringify({ game_tick: 100, player_count: 3, - force_flow_statistics: { - player: { - item_production_statistics: { - input: { "inserter": 10 }, - output: { "iron-plate": 24 }, + surface_statistics: { + "nauvis": { + game_flow_statistics: { + pollution_statistics: { + input: { "boiler": 2000 }, + output: { "tree-proxy": 560 }, + }, + }, + force_flow_statistics: { + player: { + item_production_statistics: { + input: { "inserter": 10 }, + output: { "iron-plate": 24 }, + }, + }, }, }, }, - game_flow_statistics: { - pollution_statistics: { - input: { "boiler": 2000 }, - output: { "tree-proxy": 560 }, + platforms: { + "platform-1": { + force: "player", + surface: "nauvis", + speed: 1, + weight: 2, }, }, }) @@ -52,11 +64,20 @@ describe("statistics_exporter plugin", function() { assert.equal(instance._instancePlayerCount.labels("7357").get(), 3); assert.equal(instance._instanceGameTicksTotal.labels("7357").get(), 100); assert.equal(instance._instanceForceFlowStatistics.labels( - "7357", "player", "item_production_statistics", "input", "inserter").get(), + "7357", "nauvis", "player", "item_production_statistics", "input", "inserter").get(), 10); assert.equal(instance._instanceGameFlowStatistics.labels( - "7357", "pollution_statistics", "input", "boiler").get(), + "7357", "nauvis", "pollution_statistics", "input", "boiler").get(), 2000); + assert.equal(instance._instancePlatformMapping.labels( + "7357", "platform-1", "player", "nauvis").get(), + 1); + assert.equal(instance._instancePlatformSpeed.labels( + "7357", "player", "nauvis").get(), + 1); + assert.equal(instance._instancePlatformWeight.labels( + "7357", "player", "nauvis").get(), + 2); }); it("should pass on JSON parse errors", async function() { let string = "An error occured\n";