BillBodkin

Storage

Oct 20th, 2021 (edited)
538
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 16.26 KB | None | 0 0
  1. -- pastebin run v9vZhxFR
  2.  
  3. local chests    = { peripheral.find("ironchest:obsidian_chest") }
  4. local monitor   =   peripheral.find("monitor")
  5. local modemName =   "bottom"
  6.  
  7. local inventory = {}
  8.  
  9. -- Util ---
  10.  
  11. function TableSize(tab)
  12.     if tab == nil then
  13.         return 0
  14.     end
  15.     local tabSize = 0
  16.     for aTab, bTab in pairs(tab) do
  17.         tabSize = tabSize + 1
  18.     end
  19.     return tabSize
  20. end
  21.  
  22. function Ternary(cond, T, F)
  23.     if cond then
  24.         return T
  25.     else
  26.         return F
  27.     end
  28. end
  29.  
  30. function Log(msg)
  31.     print(msg)
  32. end
  33.  
  34. function Err(e)
  35.     Log("ERROR!")
  36.     Log(textutils.serialize(e))
  37.     error(e)
  38. end
  39.  
  40. --- Inventory file ---
  41.  
  42. function Save()
  43.     local invFile = fs.open("inventory", "w")
  44.     invFile.write(textutils.serialise(inventory))
  45.     invFile.close()
  46. end
  47.  
  48. function Load()
  49.     local invFile = fs.open("inventory", "r")
  50.     if invFile == nil then
  51.         return
  52.     end
  53.     inventory = textutils.unserialise(invFile.readAll())
  54.     invFile.close()
  55. end
  56.  
  57. --- Inventory mapping ---
  58.  
  59. function MapInventory()
  60.     inventory["items"] = {}
  61.     inventory["itemLimits"] = {}--max stack size per item type, interact with this via GetItemLimit(chestName, slot, itemNbt)
  62.     inventory["partialStacks"] = {}--stacks that are not completly full or empty, should try to store or get from these first
  63.     inventory["itemCounts"] = {}
  64.     inventory["itemNames"] = {}
  65.    
  66.     for chestName, chest in pairs(chests) do
  67.         Log("Mapping inventory: " .. tostring(chestName) .. " / " .. tostring(table.getn(chests)))
  68.         local chestItems = chest.list()
  69.         for slot = 1, chest.size() do
  70.             --local item = chest.getItemDetail(slot)
  71.             local item = chestItems[slot]
  72.             if item == nil then
  73.                 SetSlot("", 0, chestName, slot)
  74.             else
  75.                 SetSlot(item.nbt, item.count, chestName, slot)
  76.                 AddItemCount(item.nbt, item.count)
  77.             end
  78.         end
  79.     end
  80.     Save()
  81.     Log("Inventory mapped")
  82. end
  83.  
  84. -- Get all locations of an item
  85. function GetItemInv(itemNbt)
  86.     if inventory["items"][itemNbt] == nil then
  87.         inventory["items"][itemNbt] = {}
  88.     end
  89.     if itemNbt ~= "" then
  90.         if inventory["partialStacks"][itemNbt] == nil then
  91.             inventory["partialStacks"][itemNbt] = {}
  92.         end
  93.         if inventory["itemCounts"][itemNbt] == nil then
  94.             inventory["itemCounts"][itemNbt] = 0
  95.         end
  96.     end
  97.     return inventory["items"][itemNbt]
  98. end
  99.  
  100. function AddItemCount(itemNbt, count)
  101.     GetItemInv(itemNbt)
  102.     inventory["itemCounts"][itemNbt] = inventory["itemCounts"][itemNbt] + count
  103. end
  104.  
  105. function GetItemLimit(chestName, slot, itemNbt)
  106.     if inventory["itemLimits"][itemNbt] == nil then
  107.         local itemDetail = nil
  108.         if chests[chestName] ~= nil then
  109.             itemDetail = chests[chestName].getItemDetail(slot)
  110.         else
  111.             itemDetail = peripheral.wrap(chestName).getItemDetail(slot)
  112.         end
  113.        
  114.         if itemDetail ~= nil then
  115.             inventory["itemLimits"][itemNbt] = itemDetail.maxCount
  116.         else
  117.             return 64
  118.         end
  119.     end
  120.     return inventory["itemLimits"][itemNbt]
  121. end
  122.  
  123. function GetItemName(chestName, slot, itemNbt)
  124.     if inventory["itemNames"][itemNbt] == nil then
  125.         if chestName == nil or slot == nil then
  126.             return nil
  127.         end
  128.         local itemDetail = nil
  129.         if chests[chestName] ~= nil then
  130.             itemDetail = chests[chestName].getItemDetail(slot)
  131.         else
  132.             itemDetail = peripheral.wrap(chestName).getItemDetail(slot)
  133.         end
  134.        
  135.         if itemDetail ~= nil then
  136.             inventory["itemNames"][itemNbt] = itemDetail.displayName
  137.         else
  138.             return nil
  139.         end
  140.     end
  141.     return inventory["itemNames"][itemNbt]
  142. end
  143.  
  144. -- Set a slot in the chests for where an item is
  145. function SetSlot(itemNbt, count, chestName, slot)
  146.     if count < 0 then
  147.         Err({
  148.             ["type"] = "Neg slot",
  149.             ["itemNbt"] = itemNbt,
  150.             ["itemCount"] = count,
  151.             ["chestName"] = chestName,
  152.             ["chestSlot"] = slot
  153.         })
  154.     end
  155.    
  156.     --set new value in items table
  157.     GetItemInv(itemNbt)
  158.     if inventory["items"][name][chestName] == nil then
  159.         inventory["items"][name][chestName] = {}
  160.     end
  161.     if itemNbt ~= "" then
  162.         if inventory["partialStacks"][itemNbt][chestName] == nil then
  163.             inventory["partialStacks"][itemNbt][chestName] = {}
  164.         end
  165.     end
  166.    
  167.     GetItemInv("")
  168.     if inventory["items"][""][chestName] == nil then
  169.         inventory["items"][""][chestName] = {}
  170.     end
  171.    
  172.     if count == 0 then
  173.         if itemNbt ~= "" then
  174.             inventory["items"][itemNbt][chestName][slot] = nil
  175.             inventory["partialStacks"][itemNbt][chestName][slot] = nil
  176.         end
  177.         inventory["items"][""][chestName][slot] = 0
  178.     else
  179.         inventory["items"][itemNbt][chestName][slot] = count
  180.         inventory["items"][""][chestName][slot] = nil
  181.        
  182.         if itemNbt ~= "" then
  183.             if count < GetItemLimit(chestName, slot, itemNbt) then
  184.                 inventory["partialStacks"][itemNbt][chestName][slot] = true
  185.             else
  186.                 inventory["partialStacks"][itemNbt][chestName][slot] = nil
  187.             end
  188.         end
  189.     end
  190.    
  191.     GetItemName(chestName, slot, itemNbt)--will set it if not on there
  192.  
  193.     --Save()
  194. end
  195.  
  196. --- Get free slots avaliable ---
  197. function CountEmptySlots()
  198.     if inventory.items[""] == nil then
  199.         return 0
  200.     end
  201.     local emptySlotCount = 0
  202.     for ecsn, ecs in pairs(inventory.items[""]) do
  203.         for esn, es in pairs(ecs) do
  204.             emptySlotCount = emptySlotCount + 1
  205.         end
  206.     end
  207.     return emptySlotCount
  208. end
  209.  
  210. --- IO ---
  211.  
  212. -- Take from outChest and store
  213. function Store(fromChest, fromSlot, toMove)
  214.     if CountEmptySlots() == 0 then
  215.         Log("Inventory full")
  216.         return 0
  217.     end
  218.     local item = peripheral.wrap(fromChest).getItemDetail(fromSlot)
  219.     if item ~= nil then
  220.         local totalMoved = 0
  221.         local maxStackSize = GetItemLimit(fromChest, fromSlot, item.nbt)--peripheral.wrap(fromChest).getItemLimit(fromSlot)
  222.         if toMove == nil then
  223.             toMove = maxStackSize
  224.         end
  225.         toMove = math.min(item.count, toMove)
  226.         Log("Storing " .. tostring(toMove) .. " " .. item.nbt)
  227.         function StoreToSlot(slotItemNbt)
  228.             local itemInv = GetItemInv(slotItemNbt)
  229.             for chestName, chestSlots in pairs(itemInv) do
  230.                 for slot, count in pairs(chestSlots) do
  231.                     if count < maxStackSize then
  232.                         local moved = chests[chestName].pullItems(fromChest, fromSlot, toMove, slot)
  233.                         toMove = toMove - moved
  234.                         totalMoved = totalMoved + moved
  235.                         if moved > 0 then
  236.                             SetSlot(item.nbt, count + moved, chestName, slot)
  237.                         end
  238.                         if toMove == 0 then
  239.                             return true
  240.                         end
  241.                         if toMove < 0 then
  242.                             Err({
  243.                                 ["type"] = "Over move",
  244.                                 ["chestName"] = chestName,
  245.                                 ["slotName"] = slotName,
  246.                                 ["itemNbt"] = item.nbt,
  247.                                 ["slotItemNbt"] = slotItemNbt,
  248.                                 ["totalToMove"] = toMove,
  249.                                 ["fromChest"] = fromChest,
  250.                                 ["fromSlot"] = fromSlot
  251.                             })
  252.                         end
  253.                     end
  254.                 end
  255.             end
  256.             return false
  257.         end
  258.         if maxStackSize ~= 1 and StoreToSlot(item.nbt) then
  259.             Log("Stored " .. tostring(totalMoved) .. " " .. item.nbt)
  260.             AddItemCount(item.nbt, totalMoved)
  261.             return totalMoved
  262.         elseif StoreToSlot("") then
  263.             Log("Stored " .. tostring(totalMoved) .. " " .. item.nbt)
  264.             AddItemCount(item.nbt, totalMoved)
  265.             return totalMoved
  266.         else
  267.             Err({
  268.                 ["type"] = "Desync"
  269.             })
  270.         end
  271.     end
  272.     Log("Was asked to store slot but is empty")
  273.     return 0
  274. end
  275.  
  276. -- Get from storage to outChest by name
  277. function Get(itemNbt, count, toChest, toSlot)
  278.     Log("Getting " .. tostring(count) .. " " .. itemNbt)
  279.     local totalMoved = 0
  280.     local itemInv = GetItemInv(itemNbt)--gets all slots where this item should be from file
  281.     for chestName, chestSlots in pairs(itemInv) do
  282.         for slotName, slotCount in pairs(chestSlots) do
  283.             local slotItemDetail = chests[chestName].getItemDetail(slotName)
  284.             if slotItemDetail == nil or slotItemDetail.nbt ~= itemNbt or slotItemDetail.count ~= slotCount then
  285.                 Err({
  286.                     ["type"] = "Slot desync",
  287.                     ["chestName"] = chestName,
  288.                     ["slotName"] = slotName,
  289.                     ["expectedItemNbt"] = itemNbt,
  290.                     ["expectedItemCount"] = slotCount,
  291.                     ["actualItemNbt"] = Ternary(slotItemDetail == nil, "Air", slotItemDetail.nbt),
  292.                     ["actualItemCount"] = Ternary(slotItemDetail == nil, 0, slotItemDetail.count),
  293.                 })
  294.             end
  295.             local moved = chests[chestName].pushItems(toChest, slotName, count - totalMoved, toSlot)
  296.             totalMoved = totalMoved + moved
  297.             --Log("Get item")
  298.             --Log(itemNbt)
  299.             if moved == 0 then
  300.                 --cant move any more as output slot full / diffrent item
  301.                 Log("Got " .. tostring(totalMoved) .. " " .. itemNbt)
  302.                 AddItemCount(itemNbt, -totalMoved)
  303.                 return totalMoved
  304.             end
  305.             SetSlot(itemNbt, slotCount - moved, chestName, slotName)
  306.             if count == totalMoved then
  307.                 Log("Got " .. tostring(totalMoved) .. " " .. itemNbt)
  308.                 AddItemCount(itemNbt, -totalMoved)
  309.                 return totalMoved
  310.             end
  311.             if totalMoved > count then
  312.                 Err({
  313.                     ["type"] = "Over move",
  314.                     ["chestName"] = chestName,
  315.                     ["slotName"] = slotName,
  316.                     ["itemNbt"] = itemNbt,
  317.                     ["totalToMove"] = count,
  318.                     ["toChest"] = toChest,
  319.                     ["toSlot"] = toSlot
  320.                 })
  321.             end
  322.         end
  323.     end
  324.     Log("Got " .. tostring(totalMoved) .. " " .. itemNbt)
  325.     AddItemCount(itemNbt, -totalMoved)
  326.     return totalMoved
  327. end
  328.  
  329. --- Queue ---
  330.  
  331. local Queue = {}
  332.  
  333. function Network()
  334.     while true do
  335.         local id, msg = rednet.receive("inv")
  336.         msg["fromID"] = id
  337.         table.insert(Queue, msg)
  338.         sleep(0)
  339.     end
  340. end
  341.  
  342. function ProcessQueue()
  343.     function ProcessItem(msg)
  344.         if msg["action"] == "store" then
  345.             if msg["chest"] == nil then
  346.                 rednet.send(msg["fromID"], {
  347.                     ["instructionRef"] = msg["instructionRef"],
  348.                     ["status"] = "fail",
  349.                     ["message"] = "Missing 'chest'"
  350.                 }, "invResp")
  351.                 return
  352.             end
  353.             if peripheral.wrap(msg["chest"]) == nil then
  354.                 rednet.send(msg["fromID"], {
  355.                     ["instructionRef"] = msg["instructionRef"],
  356.                     ["status"] = "fail",
  357.                     ["message"] = "Cant find 'chest'"
  358.                 }, "invResp")
  359.                 return
  360.             end
  361.             if msg["slot"] == nil then
  362.                 rednet.send(msg["fromID"], {
  363.                     ["instructionRef"] = msg["instructionRef"],
  364.                     ["status"] = "fail",
  365.                     ["message"] = "Missing 'slot'"
  366.                 }, "invResp")
  367.                 return
  368.             end
  369.             local moved = 0
  370.             if msg["count"] == nil then
  371.                 moved = Store(msg["chest"], msg["slot"])
  372.             else
  373.                 moved = Store(msg["chest"], msg["slot"], msg["count"])
  374.             end
  375.             rednet.send(msg["fromID"], {
  376.                 ["instructionRef"] = msg["instructionRef"],
  377.                 ["status"] = "success",
  378.                 ["moved"] = moved
  379.             }, "invResp")
  380.             return
  381.         end
  382.         if msg["action"] == "get" then
  383.             if msg["nbt"] == nil then
  384.                 rednet.send(msg["fromID"], {
  385.                     ["instructionRef"] = msg["instructionRef"],
  386.                     ["status"] = "fail",
  387.                     ["message"] = "Missing 'nbt'"
  388.                 }, "invResp")
  389.                 return
  390.             end
  391.             if msg["chest"] == nil then
  392.                 rednet.send(msg["fromID"], {
  393.                     ["instructionRef"] = msg["instructionRef"],
  394.                     ["status"] = "fail",
  395.                     ["message"] = "Missing 'chest'"
  396.                 }, "invResp")
  397.                 return
  398.             end
  399.             if peripheral.wrap(msg["chest"]) == nil then
  400.                 rednet.send(msg["fromID"], {
  401.                     ["instructionRef"] = msg["instructionRef"],
  402.                     ["status"] = "fail",
  403.                     ["message"] = "Cant find 'chest'"
  404.                 }, "invResp")
  405.                 return
  406.             end
  407.             if msg["count"] == nil then
  408.                 msg["count"] = 1
  409.             end
  410.             local moved = 0
  411.             if msg["slot"] == nil then
  412.                 moved = Get(msg["nbt"], msg["count"], msg["chest"])
  413.             else
  414.                 moved = Get(msg["nbt"], msg["count"], msg["chest"], msg["slot"])
  415.             end
  416.             rednet.send(msg["fromID"], {
  417.                 ["instructionRef"] = msg["instructionRef"],
  418.                 ["status"] = "success",
  419.                 ["moved"] = moved
  420.             }, "invResp")
  421.             return
  422.         end
  423.         if msg["action"] == "count" then
  424.             if msg["nbt"] == nil then
  425.                 rednet.send(msg["fromID"], {
  426.                     ["instructionRef"] = msg["instructionRef"],
  427.                     ["status"] = "fail",
  428.                     ["message"] = "Missing 'nbt'"
  429.                 }, "invResp")
  430.                 return
  431.             end
  432.             local count = inventory["itemCounts"][msg["nbt"]]
  433.             if count == nil then
  434.                 count = 0
  435.             end
  436.             rednet.send(msg["fromID"], {
  437.                 ["instructionRef"] = msg["instructionRef"],
  438.                 ["status"] = "success",
  439.                 ["count"] = count
  440.             }, "invResp")
  441.             return
  442.         end
  443.         if msg["action"] == "getCounts" then
  444.             rednet.send(msg["fromID"], {
  445.                 ["instructionRef"] = msg["instructionRef"],
  446.                 ["status"] = "success",
  447.                 ["name"] = inventory["itemCounts"]
  448.             }, "invResp")
  449.             return
  450.         end
  451.         if msg["action"] == "getName" then
  452.             if msg["nbt"] == nil then
  453.                 rednet.send(msg["fromID"], {
  454.                     ["instructionRef"] = msg["instructionRef"],
  455.                     ["status"] = "fail",
  456.                     ["message"] = "Missing 'nbt'"
  457.                 }, "invResp")
  458.                 return
  459.             end
  460.             rednet.send(msg["fromID"], {
  461.                 ["instructionRef"] = msg["instructionRef"],
  462.                 ["status"] = "success",
  463.                 ["name"] = GetItemName(nil, nil, msg["nbt"])
  464.             }, "invResp")
  465.             return
  466.         end
  467.         if msg["action"] == "getNames" then
  468.             rednet.send(msg["fromID"], {
  469.                 ["instructionRef"] = msg["instructionRef"],
  470.                 ["status"] = "success",
  471.                 ["name"] = inventory["itemNames"]
  472.             }, "invResp")
  473.             return
  474.         end
  475.     end
  476.     while true do
  477.         if TableSize(Queue) > 0 then
  478.             ProcessItem(table.remove(Queue))
  479.             sleep(0)
  480.         else
  481.             sleep(0.1)
  482.         end
  483.     end
  484. end
  485.  
  486. --- Run ---
  487.  
  488. MapInventory()
  489. --Load()
  490.  
  491. rednet.open(modemName)
  492. parallel.waitForAny(Network, ProcessQueue)
  493.  
  494. Save()
Add Comment
Please, Sign In to add comment