159 lines
5.4 KiB
Lua

--[[
The Ore Tracker -- a cache for resource entities that are being tracked by YARM.
Provides two major helpers:
- add_entity(), which will store an entity's particulars and provide a
cache key to allow retrieving its data quickly, and
- get_entity_cache(), which will retrieve a table indexed by the
previously-mentioned cache key, containing data about the stored entity
(position, resource_amount, and a reference to the entity itself).
Internally, the ore tracker also continually iterates its entities and
updates the cache with their resource_amount, to allow callers to avoid
having to query the entity directly (thus, not crossing the Lua/C++
boundary unnecessarily).
Requires an `on_load` and `on_tick`, and relies on the setting
'YARM-entities-per-tick' to control the rate of updates.
]]
--
ore_tracker = {
-- Used for the entity updates spread over multiple ticks
iterator_state = nil,
--iterator_key = nil, -- maintained in `global.ore_tracker`!
iterator_func = nil,
-- Used to quickly check if an entity is already present.
-- Built in `on_load` and maintained by `add_entity`, based on
-- `global.ore_tracker` data.
position_cache = {},
}
local function position_to_string(position)
-- scale it up so (hopefully) any floating point component disappears,
-- then force it to be an integer with %d. not using util.positiontostr
-- as it uses %g and keeps the floating point component.
return string.format("%d,%d", position.x * 100, position.y * 100)
end
function ore_tracker.has_entity(entity)
if not entity or not entity.valid or entity.type ~= "resource" then return false end
local position_key = position_to_string(entity.position)
if ore_tracker.position_cache[position_key] then
return true
end
return false
end
--*f Add an entity to the ore tracker
--*r Returns the entity's tracker index;
-- Note: if the tracker already had the entity, it will simply return the
-- existing tracker index rather than create a new one.
function ore_tracker.add_entity(entity)
if not entity or not entity.valid or entity.type ~= "resource" then return nil end
if not global.ore_tracker or not global.ore_tracker.entities then
global.ore_tracker = {
entities = {},
}
end
local position_key = position_to_string(entity.position)
if ore_tracker.has_entity(entity) then
local its_index = ore_tracker.position_cache[position_key]
-- We're accessing the entity.position anyway, let's also use this
-- opportunity to update the tracker values (and be 1000% certain
-- that it's tracking the right entity).
local tracking_data = global.ore_tracker.entities[its_index]
tracking_data.entity = entity
tracking_data.valid = entity.valid
tracking_data.position = entity.position
tracking_data.resource_amount = entity.amount
return its_index
end
-- Otherwise, create the tracking data and store it, including position_cache
local entities = global.ore_tracker.entities
local next_index = #entities + 1
entities[next_index] = {
entity = entity,
valid = entity.valid,
position = entity.position,
resource_amount = entity.amount
}
ore_tracker.position_cache[position_key] = next_index
return next_index
end
function ore_tracker.get_entity_cache()
if not global.ore_tracker then return nil end
return global.ore_tracker.entities
end
function ore_tracker.on_load()
if not global.ore_tracker or not global.ore_tracker.entities then return end
for tracker_index, tracking_data in pairs(global.ore_tracker.entities) do
local key = position_to_string(tracking_data.position)
ore_tracker.position_cache[key] = tracker_index
end
end
local function update_entities_this_tick()
if not global.ore_tracker or not global.ore_tracker.entities then return end
local entities_per_tick = settings.global['YARM-entities-per-tick'].value
if not ore_tracker.iterator_func then
local possible_key = nil
ore_tracker.iterator_func, ore_tracker.iterator_state, possible_key =
pairs(global.ore_tracker.entities)
-- NB: A client joining the server will find iterator_key already set,
-- and we super-want it to resume from that synchronized key.
if not global.ore_tracker.iterator_key then
global.ore_tracker.iterator_key = possible_key
end
end
local key = global.ore_tracker.iterator_key
local state = ore_tracker.iterator_state
local iterator = ore_tracker.iterator_func
local tracking_data = nil
for i = 1, entities_per_tick do
key, tracking_data = iterator(state, key)
if key == nil then
global.ore_tracker.iterator_key = nil
ore_tracker.iterator_state = nil
ore_tracker.iterator_func = nil
return
end
if not tracking_data.entity or not tracking_data.entity.valid then
tracking_data.resource_amount = 0
tracking_data.entity = nil
tracking_data.valid = false
else
tracking_data.resource_amount = tracking_data.entity.amount
end
end
global.ore_tracker.iterator_key = key
ore_tracker.iterator_state = state
ore_tracker.iterator_func = iterator
end
function ore_tracker.on_tick(event)
update_entities_this_tick()
end