digtron/inventories.lua

168 lines
6.2 KiB
Lua

local mod_meta = digtron.mod_meta
-- indexed by digtron_id, set to true whenever the detached inventory's contents change
local dirty_inventories = {}
local detached_inventory_callbacks = {
-- Called when a player wants to move items inside the inventory.
-- Return value: number of items allowed to move.
allow_move = function(inv, from_list, from_index, to_list, to_index, count, player)
--allow anything in "main"
if to_list == "main" then
return count
end
--only allow fuel items in "fuel"
if to_list == "fuel" then
local stack = inv:get_stack(from_list, from_index)
if minetest.get_craft_result({method="fuel", width=1, items={stack}}).time ~= 0 then
return count
end
end
return 0
end,
-- Called when a player wants to put something into the inventory.
-- Return value: number of items allowed to put.
-- Return value -1: Allow and don't modify item count in inventory.
allow_put = function(inv, listname, index, stack, player)
-- Only allow fuel items to be placed in fuel
if listname == "fuel" then
if minetest.get_craft_result({method="fuel", width=1, items={stack}}).time ~= 0 then
return stack:get_count()
else
return 0
end
end
return stack:get_count() -- otherwise, allow all drops
end,
-- Called when a player wants to take something out of the inventory.
-- Return value: number of items allowed to take.
-- Return value -1: Allow and don't modify item count in inventory.
allow_take = function(inv, listname, index, stack, player)
return stack:get_count()
end,
-- Called after the actual action has happened, according to what was
-- allowed.
-- No return value.
on_move = function(inv, from_list, from_index, to_list, to_index, count, player)
dirty_inventories[inv:get_location().name] = true
end,
on_put = function(inv, listname, index, stack, player)
dirty_inventories[inv:get_location().name] = true
end,
on_take = function(inv, listname, index, stack, player)
dirty_inventories[inv:get_location().name] = true
end,
}
-- If the detached inventory doesn't exist, reads saved metadata version of the inventory and creates it
-- Doesn't do anything if the detached inventory already exists, the detached inventory is authoritative
local retrieve_inventory = function(digtron_id)
local inv = minetest.get_inventory({type="detached", name=digtron_id})
if inv == nil then
inv = minetest.create_detached_inventory(digtron_id, detached_inventory_callbacks)
local inv_string = mod_meta:get_string(digtron_id..":inv")
if inv_string ~= "" then
local inventory_table = minetest.deserialize(inv_string)
for listname, invlist in pairs(inventory_table) do
inv:set_size(listname, #invlist)
inv:set_list(listname, invlist)
end
end
end
return inv
end
-- Stores contents of detached inventory as a metadata string
local persist_inventory = function(digtron_id)
local inv = minetest.get_inventory({type="detached", name=digtron_id})
if inv == nil then
minetest.log("error", "[Digtron] persist_inventory attempted to record a nonexistent inventory "
.. digtron_id)
return
end
local lists = inv:get_lists()
local persist = {}
for listname, invlist in pairs(lists) do
local inventory = {}
for i, stack in ipairs(invlist) do
table.insert(inventory, stack:to_string()) -- convert into strings for serialization
end
persist[listname] = inventory
end
mod_meta:set_string(digtron_id..":inv", minetest.serialize(persist))
end
minetest.register_globalstep(function(dtime)
for digtron_id, _ in pairs(dirty_inventories) do
persist_inventory(digtron_id)
dirty_inventories[digtron_id] = nil
end
end)
-- There should only be one of these at a time, but it doesn't cost much to be safe.
local predictive_inventory = {}
-- Copies digtron's inventory into a temporary location so that a dig cycle can be run
-- using it without affecting the actual inventory until everything's been confirmed to work
local get_predictive_inventory = function(digtron_id)
local existing = predictive_inventory[digtron_id]
if existing then return existing end
-- Using digtron_id as the player_name parameter to prevent this from being sent on the network to anyone real.
local predictive_inv = minetest.create_detached_inventory("digtron_predictive_"..digtron_id, detached_inventory_callbacks, digtron_id)
predictive_inventory[digtron_id] = predictive_inv
local source_inv = retrieve_inventory(digtron_id)
-- Populate predictive inventory with the digtron's contents
for listname, invlist in pairs(source_inv:get_lists()) do
predictive_inv:set_size(listname, #invlist)
predictive_inv:set_list(listname, invlist)
end
return predictive_inv
end
-- Wipes predictive inventory without committing it (eg, on failure of predicted operation)
local clear_predictive_inventory = function(digtron_id)
local predictive_inv = predictive_inventory[digtron_id]
if not predictive_inv then
minetest.log("error", "[Digtron] clear_predictive_inventory called for " .. digtron_id
.. " but predictive inventory did not exist")
return
end
minetest.remove_detached_inventory("digtron_predictive_"..digtron_id)
predictive_inventory[digtron_id] = nil
if next(predictive_inventory) ~= nil then
minetest.log("warning", "[Digtron] multiple predictive inventories were in existence, this shouldn't be happening. File an issue with Digtron programmers.")
end
end
-- Copies the predictive inventory's contents into the actual digtron's inventory and wipes the predictive inventory
local commit_predictive_inventory = function(digtron_id)
local predictive_inv = predictive_inventory[digtron_id]
if not predictive_inv then
minetest.log("error", "[Digtron] commit_predictive_inventory called for " .. digtron_id
.. " but predictive inventory did not exist")
return
end
local source_inv = retrieve_inventory(digtron_id)
for listname, invlist in pairs(predictive_inv:get_lists()) do
source_inv:set_list(listname, invlist)
end
dirty_inventories[digtron_id] = true
clear_predictive_inventory(digtron_id)
end
return {
retrieve_inventory = retrieve_inventory,
persist_inventory = persist_inventory,
get_predictive_inventory = get_predictive_inventory,
commit_predictive_inventory = commit_predictive_inventory,
clear_predictive_inventory = clear_predictive_inventory,
}