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.

179 lines
4.6 KiB

-- ___ _ _ _
-- / __| |_ __ _| |_ ___ _ _(_)___
-- \__ \ _/ _` | _/ _ \ '_| / _ \
-- |___/\__\__,_|\__\___/_| |_\___/
--
-- Fetch metrics via attributes and
-- functions from the game's API.
local this = {}
this.overlays = {}
this.overlays.get_entity_count = function(parent, entity)
res, data = pcall(function()
return parent.get_entity_count(entity)
end)
if res then return data end
return nil
end
this.overlays.get_flow_count = function(parent, prototypeName, isInput, precisionIndex)
if type(isInput) ~= "boolean" then return nil end
if defines.flow_precision_index[precisionIndex] == nil then return nil end
res, data = pcall(function()
return parent.get_flow_count({
name=prototypeName,
input=isInput,
precision_index=defines.flow_precision_index[precisionIndex],
count=true
})
end)
return data
end
this.callOverlayFunction = function(functionName, callback, name, parent, args)
-- entity counts
if functionName == "get_entity_count" then
--data = parent.get_entity_count(args[1])
data = this.overlays.get_entity_count(parent, args[1])
if data == nil then
return false
else
this.doCallback(callback, name, data)
return true
end
-- flow statistics
-- worst hack to validate that parent inherits from FlowStatistics
elseif functionName == "get_flow_count" and string.sub(parent.object_name, -14) == "FlowStatistics" then
if args[2] == 'input' then isInput = true else isInput = false end
if args[1] == "*" then
if (isInput) then definedRef = parent.input_counts else definedRef = parent.output_counts end
for index, val in pairs(definedRef) do
data = this.overlays.get_flow_count(parent, index, isInput, args[3])
-- replace the wildcard in the name for subsequent metrics
iterationName = string.gsub(name, '%*', index, 1)
this.doCallback(callback, iterationName, data)
end
return true
else
data = this.overlays.get_flow_count(parent, args[1], isInput, args[3])
this.doCallback(callback, name, data)
return true
end
elseif functionName == "get_flow_count" then
game.players[1].print("Cannot get_flow_count for unexpected instance of:")
game.players[1].print(parent.object_name)
end
end
-- call the user-supplied function after a metric has been fetched
this.doCallback = function(callback, name, val)
pcall(callback, name, val)
end
-- recursive function to walk the game API for the requested name
-- (given in dot notation)
-- if successful calls callback(name, value) and returns true
-- otherwise returns false
-- returns true if wildcard with no matches
this.traverseApiForValues = function(name, callback, remainingPath, parent)
-- name is a required parameter
if not name then return false end
-- optional parameter used in recursion
remainingPath = remainingPath or name
-- pop next key from start of dot notation string
key = string.match(remainingPath, "[^.]+")
if not key then return false end
-- remove key from remaining path
remainingPath = string.sub(remainingPath, string.len(key)+2)
-- typecast numeric keys for table index references
if isNumeric(key) then key = tonumber(key) end
-- first function iteration uses game object
if not parent then
return this.traverseApiForValues(name, callback, remainingPath, game)
end
-- ignore requests for things that don't exist
if key ~= "*" and not checkTableKeyExists(parent, key) then
return false
end
-- callable functions
if type(parent[key]) == "function" then
-- parse the remaining path for function args
args = split(remainingPath, ".")
-- call the function
data = this.callOverlayFunction(key, callback, name, parent, args)
if data then return true end
return false
end
-- wildcards
if key == "*" and type(parent) == "table" then
wildcardRemainingPath = remainingPath
if not table_size(parent) then
return false
end
for idx, item in pairs(parent) do
-- choose a key for this wildcard match
iterationKey = idx
if checkTableKeyExists(item, name) then iterationKey = item.name end
-- replace the wildcard in the name for subsequent metrics
iterationName = string.gsub(name, '%*', iterationKey, 1)
this.traverseApiForValues(iterationName, callback, wildcardRemainingPath, item)
end
return true
end
-- return if nothing else to traverse
if string.len(remainingPath)<1 then
this.doCallback(callback, name, parent[key])
return true
end
return this.traverseApiForValues(name, callback, remainingPath, parent[key])
end
return this