Export realtime game statistics to disk in JSON and other formats.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

328 lines
11 KiB

-- IDEAS: https://github.com/ncabatoff/promfacto
-- https://github.com/narc0tiq/YARM
-- get_flow_stats() uses these to generate stats
-- in theory, its better for external graphing to manage time
-- and simply send last 1 minute of data
local times = {
one_minute = defines.flow_precision_index.one_minute,
-- ten_minutes = defines.flow_precision_index.ten_minutes,
-- one_hour = defines.flow_precision_index.one_hour,
-- ten_hours = defines.flow_precision_index.ten_hours,
-- fifty_hours = defines.flow_precision_index.fifty_hours,
-- two_hundred_fifty_hours = defines.flow_precision_index.two_hundred_fifty_hours,
-- one_thousand_hours = defines.flow_precision_index.one_thousand_hours
}
local function get_technology_stats(research)
if research == nil then
return nil
end
return {
name = research.name,
localised_name = research.localised_name,
localised_description = research.localised_description,
enabled = research.enabled,
visible_when_disabled = research.visible_when_disabled,
upgrade = research.upgrade,
researched = research.researched,
research_unit_count = research.research_unit_count,
research_unit_energy = research.research_unit_energy,
level = research.level,
research_unit_count_formula = research.research_unit_count_formula
}
end
local function get_surface_stats(surface)
return {
-- chunks
-- trains
name = surface.name,
total_pollution = surface.get_total_pollution(),
pollution_statistics = {
input_counts = game.pollution_statistics.input_counts,
output_counts = game.pollution_statistics.output_counts
},
active_entities_count = game.get_active_entities_count(surface.index),
always_day = surface.always_day,
daytime = surface.daytime,
darkness = surface.darkness,
wind_speed = surface.wind_speed,
wind_orientation = surface.wind_orientation,
peaceful_mode = surface.peaceful_mode,
freeze_daytime = surface.freeze_daytime,
ticks_per_day = surface.ticks_per_day,
dusk = surface.dusk,
dawn = surface.dawn,
evening = surface.evening,
morning = surface.morning,
solar_power_multiplier = surface.solar_power_multiplier,
min_brightness = surface.min_brightness
}
end
local function get_flow_stats(flow, prototypes)
local stats = {
input_counts = flow.input_counts,
output_counts = flow.output_counts,
count = {},
per_time_frame = {}
}
for time_name, time_definition in pairs(times) do
stats.count[time_name] = {}
stats.per_time_frame[time_name] = {}
for index, val in pairs(stats.input_counts) do
stats.count[time_name][index] = flow.get_flow_count({
name = prototypes[index].name,
input=true,
precision_index=time_definition,
count=true
})
stats.per_time_frame[time_name][index] = flow.get_flow_count({
name = prototypes[index].name,
input=true,
precision_index=time_definition,
count=false
})
end
end
--input_counts_5m = flow.get_flow_count()
return stats
end
local function table_length(table)
if table == nil then return 0 end
local count = 0
for k,v in pairs(table) do count = count + 1 end
return count
end
local function get_force_surface_details(force, surface)
return {
spawn_position = force.get_spawn_position(surface),
logistic_networks_count = table_length(force.logistic_networks[surface])
}
end
local function get_force_stats(force)
local force_stats = {
index = force.index,
name = force.name,
-- CAPABILITIES
ai_controllable = force.ai_controllable,
ghost_time_to_live = force.ghost_time_to_live,
deconstruction_time_to_live = force.deconstruction_time_to_live,
max_successful_attempts_per_tick_per_construction_queue = force.max_successful_attempts_per_tick_per_construction_queue,
max_failed_attempts_per_tick_per_construction_queue = force.max_failed_attempts_per_tick_per_construction_queue,
auto_character_trash_slots = force.auto_character_trash_slots,
zoom_to_world_enabled = force.zoom_to_world_enabled,
zoom_to_world_ghost_building_enabled = force.zoom_to_world_ghost_building_enabled,
zoom_to_world_blueprint_enabled = force.zoom_to_world_blueprint_enabled,
zoom_to_world_deconstruction_planner_enabled = force.zoom_to_world_deconstruction_planner_enabled,
zoom_to_world_selection_tool_enabled = force.zoom_to_world_selection_tool_enabled,
character_logistic_requests = force.character_logistic_requests,
friendly_fire = force.friendly_fire,
share_chart = force.share_chart,
research_queue_enabled = force.research_queue_enabled,
research_enabled = force.research_enabled,
-- EVOLUTION
evolution_factor = force.evolution_factor,
evolution_factor_by_pollution = force.evolution_factor_by_pollution,
evolution_factor_by_time = force.evolution_factor_by_time,
evolution_factor_by_killing_spawners = force.evolution_factor_by_killing_spawners,
-- BONUSES
manual_mining_speed_modifier = force.manual_mining_speed_modifier,
laboratory_speed_modifier = force.laboratory_speed_modifier,
laboratory_productivity_bonus = force.laboratory_productivity_bonus,
worker_robots_speed_modifier = force.worker_robots_speed_modifier,
worker_robots_battery_modifier = force.worker_robots_battery_modifier,
worker_robots_storage_bonus = force.worker_robots_storage_bonus,
inserter_stack_size_bonus = force.inserter_stack_size_bonus,
stack_inserter_capacity_bonus = force.stack_inserter_capacity_bonus,
character_trash_slot_count = force.character_trash_slot_count,
maximum_following_robot_count = force.maximum_following_robot_count,
following_robots_lifetime_modifier = force.following_robots_lifetime_modifier,
character_running_speed_modifier = force.character_running_speed_modifier,
artillery_range_modifier = force.artillery_range_modifier,
character_build_distance_bonus = force.character_build_distance_bonus,
character_item_drop_distance_bonus = force.character_item_drop_distance_bonus,
character_reach_distance_bonus = force.character_reach_distance_bonus,
character_resource_reach_distance_bonus = force.character_resource_reach_distance_bonus,
character_item_pickup_distance_bonus = force.character_item_pickup_distance_bonus,
character_loot_pickup_distance_bonus = force.character_loot_pickup_distance_bonus,
character_inventory_slots_bonus = force.character_inventory_slots_bonus,
character_health_bonus = force.character_health_bonus,
mining_drill_productivity_bonus = force.mining_drill_productivity_bonus,
train_braking_force_bonus = force.train_braking_force_bonus,
-- technologies
-- recipes
-- SURFACES
surfaces = {},
-- ENTITIES
--get_entity_count(name) -- this has performance hit apparently
-- and ammo and turret prototypes for modifiers
-- loop through item entities:
-- get_item_launched()
-- loop through surfaces
-- get_trains()
-- players
-- logistic_networks
-- stats per opposing force?
entity_build_count_statistics = get_flow_stats(force.entity_build_count_statistics, game.entity_prototypes),
item_production_statistics = get_flow_stats(force.item_production_statistics, game.item_prototypes),
fluid_production_statistics = get_flow_stats(force.fluid_production_statistics, game.fluid_prototypes),
kill_count_statistics = get_flow_stats(force.kill_count_statistics, game.entity_prototypes),
rockets_launched = force.rockets_launched,
items_launched = force.items_launched,
--connected_players = force.connected_players,
previous_research = get_technology_stats(force.previous_research),
current_research = get_technology_stats(force.current_research),
--research_queue
}
for index, surface in pairs(game.surfaces) do
force_stats.surfaces[index] = get_force_surface_details(force, surface)
end
return force_stats
end
local function dump_stats()
for index, surface in pairs(game.surfaces) do
global.statorio.surfaces[index] = get_surface_stats(surface)
end
-- forces
for index, force in pairs(game.forces) do
global.statorio.forces[index] = get_force_stats(force)
end
game.print("Writing statistics to file "..game.tick..".json")
game.write_file(game.tick..".json", game.table_to_json(global.statorio))
end
local function set_next_update_tick()
global.statorio.next_update_tick = game.tick + 600
end
script.on_init(function()
-- when game starts, or mod added to existing save
-- use to initialize global variables
global.statorio = {}
global.statorio.server.game_modes.cheat_mode_enabled_ever = false
global.statorio.server.game_modes.cheat_mode_enabled = false
global.statorio.game_id = math.random()
global.statorio.forces = {}
global.statorio.surfaces = {}
global.statorio.players = {}
global.statorio.game = {}
global.statorio.game.is_demo = game.is_demo()
global.statorio.game.is_multiplayer = game.is_multiplayer()
-- too big to save each time
-- global.statorio.game.map_exchange_string = game.get_map_exchange_string()
-- map settings
-- difficulty settings
global.statorio.game.difficulty = game.difficulty
global.statorio.game.active_mods = game.active_mods
global.statorio.game.player_joins = 0
global.statorio.game.player_bans = 0
global.statorio.game.autosave_enabled = game.autosave_enabled
-- global.statorio.pollution_statistics = {}
set_next_update_tick()
end)
script.on_configuration_changed(function(data)
-- mod changes, deal with prototype or game setting changes
-- data contains changes
-- http://lua-api.factorio.com/latest/Concepts.html#ConfigurationChangedData
end)
script.on_event(defines.events.on_tick, function(e)
-- A tick occured before the mod initialized
if global.statorio == nil then return end
-- The mod is waiting
if game.tick < global.statorio.next_update_tick then return end
set_next_update_tick()
-- Generate stats
dump_stats()
end)
script.on_event(defines.events.on_player_cheat_mode_enabled, function(e)
global.statorio.server.game_modes.cheat_mode_enabled_ever = true
global.statorio.server.game_modes.cheat_mode_enabled = true
end)
script.on_event(defines.events.on_player_cheat_mode_disabled, function(e)
global.statorio.server.game_modes.cheat_mode_enabled = false
end)
script.on_event(defines.events.on_player_joined_game, function(e)
if global.statorio.players[e.player_index] == nil then
-- create blank player entry
global.statorio.players[e.player_index] = {
index = e.player_index,
name = game.players[e.player_index].name,
tag = game.players[e.player_index].tag,
color = game.players[e.player_index].color,
chat_color = game.players[e.player_index].chat_color,
joins = 0,
bans = 0,
connected = game.players[e.player_index].connected,
admin = game.players[e.player_index].admin,
afk_time = game.players[e.player_index].afk_time,
online_time = game.players[e.player_index].online_time,
last_online = game.players[e.player_index].last_online
}
end
global.statorio.players[e.player_index].joins = global.statorio.players[e.player_index].joins + 1
global.statorio.game.player_joins = global.statorio.game.player_joins + 1
end)
script.on_event(defines.events.on_player_banned, function(e)
global.statorio.players[e.player_index].bans = global.statorio.players[e.player_index].bans + 1
global.statorio.game.player_bans = global.statorio.game.player_bans + 1
end)