2018-07-26 09:48:51 +00:00
-- Chest backend
-- Currently just the one for Dragon. Will not actually work yet.
2018-07-26 14:56:42 +00:00
local w = require " lib "
local d = require " luadash "
2018-07-26 09:28:37 +00:00
2018-07-26 14:56:42 +00:00
local conf = w.load_config ( {
" buffer_internal " ,
" buffer_external "
} )
local BUFFER_OUT_SLOT = 1
local BUFFER_IN_SLOT = 2
2018-07-26 09:28:37 +00:00
-- Find all chests or shulker boxes
2018-07-26 21:08:27 +00:00
local inventories = d.map_with_key ( w.find_peripherals ( function ( type , name , wrapped )
2018-07-26 14:56:42 +00:00
return string.find ( name , " chest " ) or string.find ( name , " shulker " )
2018-07-26 21:08:27 +00:00
end ) , function ( _ , p ) return p.name , p.wrapped end )
2018-07-26 09:28:37 +00:00
2018-07-26 15:32:48 +00:00
local display_name_cache = { }
2018-07-26 14:56:42 +00:00
2018-07-26 09:28:37 +00:00
-- Gets the display name of the given item (in the given chest peripheral & slot)
-- If its name is not cached, cache it.
-- If it is, just return the cached name
2018-07-26 14:56:42 +00:00
local function cache ( item , chest , slot )
2018-07-26 15:32:48 +00:00
local idx = w.get_internal_identifier ( item )
2018-07-26 09:28:37 +00:00
2018-07-26 15:32:48 +00:00
if display_name_cache [ idx ] then
return display_name_cache [ idx ]
2018-07-26 09:28:37 +00:00
else
2018-07-27 08:52:59 +00:00
local n = chest.getItemMeta ( slot ) . displayName
2018-07-26 15:32:48 +00:00
display_name_cache [ idx ] = n
2018-07-26 09:28:37 +00:00
return n
end
end
local index = { }
2018-07-26 14:56:42 +00:00
-- Update the index for the given peripheral
local function update_index_for ( name )
2018-07-26 09:28:37 +00:00
local inv = inventories [ name ]
local data = inv.list ( )
for slot , item in pairs ( data ) do
2018-07-26 14:56:42 +00:00
data [ slot ] . display_name = cache ( item , inv , slot )
2018-07-26 09:28:37 +00:00
end
index [ name ] = data
2018-07-27 10:51:28 +00:00
2018-07-27 10:58:54 +00:00
print ( " Indexed " .. name .. " . " )
2018-07-26 09:28:37 +00:00
end
2018-07-26 14:56:42 +00:00
-- Reindex all connected inventories
local function update_index ( )
2018-07-27 10:51:28 +00:00
print " Full indexing started. "
2018-07-26 09:28:37 +00:00
for n in pairs ( inventories ) do
2018-07-27 09:42:43 +00:00
update_index_for ( n )
2018-07-26 09:28:37 +00:00
sleep ( )
end
2018-07-27 10:51:28 +00:00
print " Full indexing complete. "
2018-07-26 09:28:37 +00:00
end
2018-07-26 14:56:42 +00:00
-- Finds all items matching a certain predicate.
-- Returns a table of tables of { name, slot, item }
local function find ( predicate )
local ret = { }
for inventory , items in pairs ( index ) do
2018-07-26 09:28:37 +00:00
for slot , item in pairs ( items ) do
2018-07-26 14:56:42 +00:00
local ok , extra = predicate ( item ) -- allow predicates to return some extra data which will come out in resulting results
if ok then
table.insert ( ret , { location = { inventory = inventory , slot = slot } , item = item , extra = extra } )
2018-07-26 09:28:37 +00:00
end
end
end
2018-07-26 14:56:42 +00:00
return ret
2018-07-26 09:28:37 +00:00
end
2018-07-26 14:56:42 +00:00
-- Finds space in the chest system. Returns the name of an inventory which has space.
local function find_space ( )
2018-07-26 09:28:37 +00:00
for name , items in pairs ( index ) do
if # items < inventories [ name ] . size ( ) then
return name
end
end
end
2018-07-27 12:59:32 +00:00
local function find_by_ID_meta_NBT ( ID , meta , NBT )
2018-07-26 09:28:37 +00:00
return find ( function ( item )
return
2018-07-26 14:56:42 +00:00
( not meta or item.damage == meta ) and -- if metadata provided, ensure match
2018-07-27 13:05:19 +00:00
( not ID or item.name == ID ) and -- if internal name provided, ensure match
2018-07-27 12:59:32 +00:00
( not NBT or item.nbtHash == NBT ) -- if NBT hash provided, ensure match
2018-07-26 09:28:37 +00:00
end )
end
2018-07-26 14:56:42 +00:00
local function search ( query , threshold )
local threshold = threshold or 4
local results = find ( function ( item )
local distance = d.distance ( query , item.display_name )
if distance < threshold then
return true , distance
else return false end
end )
2018-07-27 08:57:14 +00:00
return d.sort_by ( results , function ( x ) return x.extra end ) -- sort returned results by closeness to query
2018-07-26 09:28:37 +00:00
end
2018-07-27 11:41:07 +00:00
-- Retrives items from a location in storage and puts them in the buffer
2018-07-26 15:15:48 +00:00
local function fetch_by_location ( loc , limit )
2018-07-26 14:56:42 +00:00
local peripheral_name , slot , limit = loc.inventory , loc.slot , limit or 64
return peripheral.call ( conf.buffer_internal , " pullItems " , peripheral_name , slot , limit , BUFFER_OUT_SLOT )
end
2018-07-26 09:28:37 +00:00
2018-07-27 11:41:07 +00:00
-- Clears out the buffer into storage.
2018-07-27 10:44:24 +00:00
local function clear_buffer ( )
for i = 1 , peripheral.call ( conf.buffer_internal , " size " ) do
local space_location = find_space ( )
if not space_location then error ( " Storage capacity reached. Please add more chests or shulker boxes. " ) end
2018-07-27 13:03:24 +00:00
peripheral.call ( conf.buffer_internal , " pushItems " , space_location , i )
2018-07-27 10:44:24 +00:00
os.queueEvent ( " reindex " , space_location )
sleep ( )
end
end
2018-07-26 14:56:42 +00:00
local function server ( command )
if command.type == " buffers " then -- Sends the external address of the buffer
return conf.buffer_external
elseif command.type == " reindex " then
2018-07-27 08:47:57 +00:00
os.queueEvent " reindex "
2018-07-26 14:56:42 +00:00
elseif command.type == " extract " then
2018-07-27 13:03:24 +00:00
local result = find_by_ID_meta_NBT ( command.ID , command.meta , command.NBT )
2018-07-27 09:39:32 +00:00
local first_available = result [ 1 ]
-- Check if we have an item, and its stack is big enough; otherwise, send back an error.
local quantity_missing = 0
2018-07-27 10:45:55 +00:00
if not first_available then quantity_missing = command.quantity or 1
2018-07-27 09:39:32 +00:00
elseif command.quantity and command.quantity > first_available.item . count then quantity_missing = command.quantity - first_available.item . count end
if quantity_missing > 0 then return w.errors . make ( w.errors . NOITEMS , { type = w.get_internal_identifier ( command ) , quantity = quantity_missing } ) end
2018-07-27 10:44:24 +00:00
local items_moved_from_storage = fetch_by_location ( first_available.location , command.quantity )
2018-07-27 09:48:32 +00:00
2018-07-27 10:44:24 +00:00
os.queueEvent ( " reindex " , first_available.location . inventory ) -- I'm too lazy to manually update the item properly, and indexing is fast enough, so just do this
2018-07-27 09:48:32 +00:00
2018-07-27 09:39:32 +00:00
if command.destination_inventory then
2018-07-27 10:44:24 +00:00
-- push items to destination
items_moved_to_destination = peripheral.call ( conf.buffer_external , " pushItems " , command.destination_inventory , BUFFER_OUT_SLOT , command.quantity , command.destination_slot )
-- If destination didn't accept all items, clear out the buffer.
if items_moved_to_destination < items_moved_from_storage then
clear_buffer ( )
end
end
return { moved = items_moved_to_destination or items_moved_from_storage , item = first_available.item }
elseif command.type == " insert " then
local inventory_with_space = find_space ( )
if not inventory_with_space then return w.errors . make ( w.errors . NOSPACE ) end -- if there's not space, say so in error
if command.from_inventory and command.from_slot then
2018-07-27 11:41:07 +00:00
peripheral.call ( conf.buffer_external , " pullItems " , command.from_inventory , command.from_slot , command.quantity , BUFFER_IN_SLOT ) -- pull from from_inventory to buffer
2018-07-27 09:39:32 +00:00
end
2018-07-27 11:41:07 +00:00
local moved = peripheral.call ( conf.buffer_internal , " pushItems " , inventory_with_space , BUFFER_IN_SLOT ) -- push from buffer to free space
if moved == 0 then clear_buffer ( ) end -- if for some reason it broke and there's no space, clear out buffer
os.queueEvent ( " reindex " , inventory_with_space )
2018-07-27 10:44:24 +00:00
return { moved = moved }
2018-07-27 11:50:45 +00:00
elseif command.type == " search " then
2018-07-27 12:59:32 +00:00
local matching_items = d.map ( search ( command.query , command.threshold ) , function ( x ) return x.item end )
local out = { }
for _ , stack in pairs ( matching_items ) do
local i = w.get_internal_identifier ( stack )
if out [ i ] then out [ i ] = out [ i ] + stack.count
else out [ i ] = stack.count end
end
2018-07-27 13:07:07 +00:00
return out
2018-07-26 09:28:37 +00:00
end
end
2018-07-27 08:47:57 +00:00
local function indexer_thread ( )
while true do
2018-07-27 10:44:24 +00:00
local _ , inventory = os.pullEvent " reindex "
if inventory then update_index_for ( inventory ) else update_index ( ) end
2018-07-27 08:47:57 +00:00
end
end
2018-07-26 20:59:42 +00:00
w.init ( )
2018-07-27 10:44:24 +00:00
parallel.waitForAll ( function ( ) os.queueEvent ( " reindex " ) w.serve ( server , " storage " ) end , indexer_thread )