From 81951075729d117855a3a10719ac2dbbe8d1c3e5 Mon Sep 17 00:00:00 2001 From: oof2win2 Date: Sat, 14 May 2022 16:06:37 +0200 Subject: [PATCH] feat: add graftorio --- config/_file_loader.lua | 2 + config/expcore/roles.lua | 1 + config/graftorio.lua | 6 + modules/graftorio/forcestats.lua | 191 +++++++++++++++++++++++++++++++ modules/graftorio/general.lua | 65 +++++++++++ modules/graftorio/require.lua | 23 ++++ modules/graftorio/statics.lua | 35 ++++++ 7 files changed, 323 insertions(+) create mode 100644 config/graftorio.lua create mode 100644 modules/graftorio/forcestats.lua create mode 100644 modules/graftorio/general.lua create mode 100644 modules/graftorio/require.lua create mode 100644 modules/graftorio/statics.lua diff --git a/config/_file_loader.lua b/config/_file_loader.lua index 404261bf..f4915c94 100644 --- a/config/_file_loader.lua +++ b/config/_file_loader.lua @@ -71,6 +71,8 @@ return { 'modules.gui.server-ups', 'modules.commands.debug', + 'modules.graftorio.require', -- graftorio + --- Config Files 'config.expcore.command_auth_admin', -- commands tagged with admin_only are blocked for non admins 'config.expcore.command_auth_roles', -- commands must be allowed via the role config diff --git a/config/expcore/roles.lua b/config/expcore/roles.lua index 95b797bd..ef05d78d 100644 --- a/config/expcore/roles.lua +++ b/config/expcore/roles.lua @@ -52,6 +52,7 @@ Roles.new_role('Administrator','Admin') 'gui/warp-list/bypass-cooldown', 'gui/warp-list/bypass-proximity', 'command/connect-all', + 'command/collectdata' } Roles.new_role('Moderator','Mod') diff --git a/config/graftorio.lua b/config/graftorio.lua new file mode 100644 index 00000000..2f80e5fc --- /dev/null +++ b/config/graftorio.lua @@ -0,0 +1,6 @@ +return { + modules = { + ["forcestats"]=true, + ["general"]=true, + } +} \ No newline at end of file diff --git a/modules/graftorio/forcestats.lua b/modules/graftorio/forcestats.lua new file mode 100644 index 00000000..fa95dc69 --- /dev/null +++ b/modules/graftorio/forcestats.lua @@ -0,0 +1,191 @@ +local Event = require("utils.event") +local general = require("modules.graftorio.general") + +local lib = {} + +lib.collect_production = function() + for _, force in pairs(game.forces) do + ---@class ProductionStatistics + ---@field item_input table + ---@field item_output table + ---@field fluid_input table + ---@field fluid_output table + ---@field kill_input table + ---@field kill_output table + ---@field build_input table + ---@field build_output table + local stats = { + item_input = {}, + item_output = {}, + fluid_input = {}, + fluid_output = {}, + kill_input = {}, + kill_output = {}, + build_input = {}, + build_output = {}, + } + + for name, count in pairs(force.item_production_statistics.input_counts) do + local itemstats = stats.item_input[name] or {} + itemstats.count = count + stats.item_input[name] = itemstats + end + for name, count in pairs(force.item_production_statistics.output_counts) do + local itemstats = stats.item_output[name] or {} + itemstats.count = count + stats.item_output[name] = itemstats + end + + for name, count in pairs(force.fluid_production_statistics.input_counts) do + local fluidstats = stats.fluid_input[name] or {} + fluidstats.count = count + stats.fluid_input[name] = fluidstats + end + for name, count in pairs(force.fluid_production_statistics.output_counts) do + local fluidstats = stats.fluid_output[name] or {} + fluidstats.count = count + stats.fluid_output[name] = fluidstats + end + + for name, count in pairs(force.kill_count_statistics.input_counts) do + local killstats = stats.kill_input[name] or {} + killstats.count = count + stats.kill_input[name] = killstats + end + for name, count in pairs(force.kill_count_statistics.output_counts) do + local killstats = stats.kill_output[name] or {} + killstats.count = count + stats.kill_output[name] = killstats + end + + for name, count in pairs(force.entity_build_count_statistics.input_counts) do + local buildstats = stats.build_input[name] or {} + buildstats.count = count + stats.build_input[name] = buildstats + end + for name, count in pairs(force.entity_build_count_statistics.output_counts) do + local buildstats = stats.build_output[name] or {} + buildstats.count = count + stats.build_output[name] = buildstats + end + + general.data.output[force.name].production = stats + end +end + +lib.collect_loginet = function() + for _, force in pairs(game.forces) do + ---@class RobotStatistics + ---@field all_construction_robots uint + ---@field available_construction_robot uint + ---@field all_logistic_robots uint + ---@field available_logistic_robots uint + ---@field charging_robot_count uint + ---@field to_charge_robot_count uint + ---@field items table + ---@field pickups table + ---@field deliveries table + local stats = { + all_construction_robots = 0, + available_construction_robots = 0, + + all_logistic_robots = 0, + available_logistic_robots = 0, + + charging_robot_count = 0, + to_charge_robot_count = 0, + + items = {}, + pickups = {}, + deliveries = {}, + } + for _, networks in pairs(force.logistic_networks) do + for _, network in pairs(networks) do + stats.available_construction_robots = network.available_construction_robots + stats.all_construction_robots = network.all_construction_robots + + stats.available_logistic_robots = network.available_logistic_robots + stats.all_logistic_robots = network.all_logistic_robots + + stats.charging_robot_count = 0 + stats.to_charge_robot_count = 0 + for _, cell in pairs(network.cells) do + stats.charging_robot_count = (stats.charging_robot_count) + cell.charging_robot_count + stats.to_charge_robot_count = (stats.to_charge_robot_count) + cell.to_charge_robot_count + end + + if settings.general.data["graftorio-logistic-items"].value then + for name, v in pairs(network.get_contents()) do + stats.items[name] = (stats.items[name] or 0) + v + end + + -- pickups and deliveries of items + for _, point_list in pairs({ network.provider_points, network.requester_points, network.storage_points }) do + for _, point in pairs(point_list) do + for name, qty in pairs(point.targeted_items_pickup) do + stats.pickups[name] = (stats.pickups[name] or 0) + qty + end + for name, qty in pairs(point.targeted_items_deliver) do + stats.deliveries[name] = (stats.deliveries[name] or 0) + qty + end + end + end + end + end + end + general.data.output[force.name].robots = stats + end +end + + +---@class ResearchStatistics +---@field current Research +---@field queue Research[] + +---@class Research +---@field name string +---@field level uint +---@field progress double + + +Event.add(defines.events.on_research_finished, function(evt) + local research = evt.research + if not general.data.output[research.force.name] then general.data.output[research.force.name] = {} end + if not general.data.output[research.force.name].research then general.data.output[research.force.name].research = {} end + + local force_research = general.data.output[research.force.name].research or {} + table.remove(force_research, 1) + general.data.output[research.force.name].research = force_research +end) + +Event.add(defines.events.on_research_started, function(evt) + -- move queue up + local research = evt.research + if not general.data.output[research.force.name].research then general.data.output[research.force.name].research = {} end + + local force_research = general.data.output[research.force.name].research or {} + table.remove(force_research, 1) + general.data.output[research.force.name].research = force_research +end) + +Event.on_nth_tick(60, function() + for _, force in pairs(game.forces) do + if not general.data.output[force.name].research then general.data.output[force.name].research = {} end + + local force_research = general.data.output[force.name].research or {} + + force_research = {} + -- this works even if the queue is disabled, but it will always be just 1 long in that case + for _, research in pairs(force.research_queue) do + table.insert(force_research, { + name = research.name, + level = research.level, + progress = force.get_saved_technology_progress(research) or 0, + }) + end + + general.data.output[force.name].research = force_research + end +end) + +return lib diff --git a/modules/graftorio/general.lua b/modules/graftorio/general.lua new file mode 100644 index 00000000..29c9ca1f --- /dev/null +++ b/modules/graftorio/general.lua @@ -0,0 +1,65 @@ +local Event = require("utils.event") +local Global = require("utils.global") + +local lib = {} + +lib.data = { + output = {} +} + +Global.register(lib.data, function(tbl) + lib.data = tbl +end) + +---@class Statistics +---@field trains TrainStatistics +---@field power PowerStatistics +---@field production ProductionStatistics +---@field robots RobotStatistics +---@field other OtherStatistics + +Event.on_init(function() + ---@type table + lib.data.output = {} + for _, force in pairs(game.forces) do + lib.data.output[force.name] = {} + end +end) + +---@class OtherStatistics +---@field tick uint +---@field evolution EvolutionStatistics +---@field research ResearchStatistics + +---@class EvolutionStatistics +---@field evolution_factor double +---@field evolution_factor_by_pollution double +---@field evolution_factor_by_time double +---@field evolution_factor_by_killing_spawners double + +lib.collect_other = function() + for _, force in pairs(game.forces) do + ---@type OtherStatistics + local other = lib.data.output[force.name].other or {} + + other.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 + } + for k, v in pairs(other) do + lib.data.output[force.name].other[k] = v + end + end +end + +Event.add(defines.events.on_force_created, function(evt) + lib.data.output[evt.force.name] = {} +end) + +Event.add(defines.events.on_forces_merged, function(evt) + lib.data.output[evt.source_name] = nil +end) + +return lib diff --git a/modules/graftorio/require.lua b/modules/graftorio/require.lua new file mode 100644 index 00000000..c94d0608 --- /dev/null +++ b/modules/graftorio/require.lua @@ -0,0 +1,23 @@ +local Commands = require("expcore.commands") +local config = require("config.graftorio") + +local general = require("modules.graftorio.general") +local statics = require("modules.graftorio.statics") +local forcestats = {} +if config.modules.forcestats then + forcestats = require("modules.graftorio.forcestats") +end + +Commands.new_command("collectdata", "Collect data for RCON usage") + :add_param("location", true) + :register(function() + -- this must be first as it overwrites the stats + -- also makes the .other table for all forces + statics.collect_statics() + general.collect_other() + if config.modules.forcestats then + forcestats.collect_production() + end + rcon.print(game.table_to_json(general.data.output)) + return Commands.success() + end) diff --git a/modules/graftorio/statics.lua b/modules/graftorio/statics.lua new file mode 100644 index 00000000..3f065e47 --- /dev/null +++ b/modules/graftorio/statics.lua @@ -0,0 +1,35 @@ +local general = require("modules.graftorio.general") + +local lib = {} + +---@class StaticStatistics +---@field tick uint +---@field online_players string[] +---@field mods table +---@field seed table + +lib.collect_statics = function() + local stats = {} + stats.tick = game.tick + + stats.online_players = {} + for _, player in pairs(game.connected_players) do + table.insert(stats.online_players, player.name) + end + + stats.mods = {} + for name, version in pairs(game.active_mods) do + stats.mods[name] = version + end + + -- reason behind this is that the map gen settings can be changed during runtime so just get them fresh + stats.seed = {} + for _, surface in pairs(game.surfaces) do + stats.seed[surface.name] = surface.map_gen_settings.seed + end + for _, force in pairs(game.forces) do + general.data.output[force.name].other = stats + end +end + +return lib