Advertisement
Guest User

Portable crafter for mc/oc (ver 0.9.3)

a guest
Jan 6th, 2016
368
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 17.54 KB | None | 0 0
  1. -- Robocraft version 0.9.3
  2. -- Public Domain
  3. local version = "0.9.3"
  4. local math = require("math")
  5. local shell = require("shell")
  6. local serialization = require("serialization")
  7. local fs = require("filesystem")
  8. local sides = require("sides")
  9. local robot = require("robot")
  10. local component= require("component")
  11. local inv = component.inventory_controller
  12. local crafting = component.crafting
  13.  
  14. if inv == nil then
  15.   print("Ошибка! Нет улучшения « Контроллер инвентаря».")
  16.   os.exit(1)
  17. end
  18.  
  19. if crafting == nil then
  20.   print("Ошибка! Нет улучшения «Верстак».")
  21.   os.exit(1)
  22. end
  23.  
  24. local args, options = shell.parse(...)
  25.  
  26. local BASE_DIR = shell.getWorkingDirectory()
  27.  
  28. local STORAGE_SIDE = sides.forward
  29. RESULT_SLOT = 4
  30.  
  31. function printf(s, ...)
  32.   return io.write(s:format(...))
  33. end
  34.  
  35. function sprintf(s, ...)
  36.   return s:format(...)
  37. end
  38.  
  39. function gridSlot(slot)
  40.   if slot <= 3 then
  41.     return slot
  42.   elseif slot <=6 then
  43.     return slot + 1
  44.   elseif slot <=9 then
  45.     return slot +2
  46.   end
  47. end
  48.  
  49. function loadFile(fn)
  50.     local _err = function(err)
  51.       return nil, sprintf("Не могу загрузить файл <%s> (%s)", fn, err)
  52.     end
  53.     local f, err = io.open(fn, "rb")
  54.     if err ~= nil then return _err(err) end
  55.     local content, err = f:read("*all")
  56.     if err ~= nil then return _err(err) end
  57.     local _, err = f:close()
  58.     if err ~= nil then return _err(err) end
  59.     return content
  60. end
  61.  
  62. function saveFile(fn, data)
  63.   local _err = function(err)
  64.       return nil, sprintf("Не могу сохранить файл <%s> (%s)", fn, err)
  65.     end
  66.   local f, err = io.open(fn, "wb")
  67.   if err ~= nil then return _err(err) end
  68.   local _, err = f:write(data)
  69.   if err ~= nil then return _err(err) end
  70.   local _, err = f:close()
  71.   if err ~= nil then return _err(err) end
  72. end
  73.  
  74. do
  75.  
  76.   local database = component.database
  77.   if database == nil then
  78.     print("Ошибка! Нет улучшения «База данных».")
  79.     os.exit(1)
  80.   end
  81.   local databaseSlot = 1
  82.   itemdb = { }
  83.   local items = { }
  84.   local itemsDir = fs.concat(BASE_DIR, "itemdb")
  85.  
  86.   local function init( ... )
  87.     if not fs.exists(itemsDir) then
  88.       local ok, err = fs.makeDirectory(itemsDir)
  89.       if err ~= nil then
  90.         printf("Ошибка! Не могу создать каталог для бд предметов (%s).", err)
  91.         os.exit(1)
  92.       end
  93.     end
  94.   end
  95.   init()
  96.  
  97.   function itemdb.flush()
  98.     for itemHash, item in pairs(items) do
  99.       if item.changed then
  100.         print("Обновлён:", item.label, item.hash)
  101.         if not itemdb.saveItem(item) then
  102.           return false
  103.         end
  104.         itemdb.makeLabel(item)
  105.       end
  106.     end
  107.     return true
  108.   end
  109.  
  110.   function itemdb.loadItem(itemHash)
  111.     local fn = fs.concat(itemsDir, itemHash)
  112.     if fs.exists(fn) then
  113.       local raw, err = loadFile(fn)
  114.       if err ~= nil then
  115.         printf("Ошибка! Не могу загрузить информацию о предмете <%s> (%s).\n",
  116.           itemHash, err)
  117.         os.exit(1)
  118.       end
  119.       return serialization.unserialize(raw)
  120.     end
  121.   end
  122.  
  123.   function itemdb.saveItem(item)
  124.     if item.changed then
  125.       local fn = fs.concat(itemsDir, item.hash)
  126.       item.changed = nil --TODO use xpcall
  127.       local err = saveFile(fn, serialization.serialize(item))
  128.       if err ~= nil then
  129.         item.changed = true
  130.         printf("Ошибка! Не могу сохранить информацию о предмете <%s> (%s).\n",
  131.             itemHash, err)
  132.         return false
  133.       end
  134.     end
  135.     return true
  136.   end
  137.  
  138.   local function computeHash()
  139.     return string.sub(database.computeHash(databaseSlot), 1, 6)
  140.   end
  141.  
  142.   local function get()
  143.     local itemHash = computeHash()
  144.     local item = items[itemHash]
  145.     if item == nil then
  146.       item = itemdb.loadItem(itemHash)
  147.       if item == nil then
  148.         item = database.get(databaseSlot)
  149.         item.tag = nil
  150.         item.hash = itemHash
  151.         item.changed = true
  152.       end
  153.       items[itemHash] = item
  154.     end
  155.     return item
  156.   end
  157.  
  158.   function itemdb.storeInternal(slot)
  159.     local r = inv.storeInternal(slot, database.address, databaseSlot)
  160.     if r == nil then return nil end
  161.     return get()
  162.   end
  163.  
  164.   function itemdb.store(slot)
  165.     local r = inv.store(STORAGE_SIDE, slot, database.address, databaseSlot)
  166.     if r == nil then return nil end
  167.     return get()
  168.   end
  169.  
  170.   function itemdb.compareInternal(slot, itemHash)
  171.     local r = inv.storeInternal(slot, database.address, databaseSlot)
  172.     if r == nil then return itemHash == nil end
  173.     local storedItemHash = computeHash()
  174.     return storedItemHash == itemHash
  175.   end
  176.  
  177.   function itemdb.get(itemHash)
  178.     local item = items[itemHash]
  179.     if item == nil then
  180.       item = itemdb.loadItem(itemHash)
  181.       if item ~= nil then
  182.         items[itemHash] = item
  183.       end
  184.     end
  185.     return item
  186.   end
  187.  
  188.   function itemdb.all()
  189.     return items
  190.   end
  191.  
  192.   local function makeLabelFileName(item)
  193.     local fn = item.label
  194.     fn = fn:gsub(" ", "_")
  195.     fn = fn:gsub("/", "_")
  196.     fn = fs.concat(BASE_DIR, fn)
  197.     return fn
  198.   end
  199.  
  200.   function itemdb.makeLabel(item)
  201.     local fn = makeLabelFileName(item)
  202.     local w = true
  203.     if fs.exists(fn) then
  204.       local itemHash, err = loadFile(fn)
  205.       if err ~= nil then
  206.         print(err)
  207.         os.exit(1)
  208.       end
  209.       local currentItem = itemdb.loadItem(itemHash)
  210.       w = currentItem.recipe == nil or item.recipe ~= nil
  211.     end
  212.     if w then
  213.       saveFile(fn, tostring(item.hash))
  214.     end
  215.   end
  216.  
  217.   function itemdb.makeAllLabels()
  218.     for name in fs.list(itemsDir) do
  219.       local item = itemdb.loadItem(name)  
  220.       itemdb.makeLabel(item)
  221.     end
  222.   end
  223.  
  224.   function itemdb.printReport()
  225.     for name in fs.list(itemsDir) do
  226.       local item = itemdb.loadItem(name)
  227.       local m = ""
  228.       if item.recipe ~= nil then
  229.         m = "!"
  230.       end
  231.       printf("%s%s/%s", m, item.hash, item.label)
  232.     end
  233.   end
  234.  
  235. end
  236.  
  237. do
  238.   storage = {}
  239.   local db
  240.  
  241.   local storageFn = fs.concat(BASE_DIR, "storage.db")
  242.  
  243.   function storage.load()
  244.     if fs.exists(storageFn) then
  245.       local raw, err = loadFile(storageFn)
  246.       db = serialization.unserialize(raw)
  247.       if err ~= nil then
  248.         printf("Ошибка! Не могу загрузить бд хранилища (%s).\n", err)
  249.         os.exit(1)
  250.       end
  251.     end
  252.     if db == nil then db = {freeSlots = {}} end
  253.   end
  254.  
  255.   function storage.save()
  256.     if not itemdb.flush() then
  257.       print("бд предметов не сохранена. Невозможно сохоанить бд хранилища")
  258.       return
  259.     end
  260.     local err = saveFile(storageFn, serialization.serialize(db))
  261.     if err ~= nil then
  262.       printf("Ошибка! Не могу сохранить бд хранилища (%s).\n", err)
  263.     end
  264.   end
  265.  
  266.   local function addStack(slot)
  267.     local item = itemdb.store(slot)
  268.     local stacks = db[item.hash]
  269.     if stacks == nil then
  270.       stacks = { slots = { }, size = 0 }
  271.       db[item.hash] = stacks
  272.     end
  273.     local stackSize = inv.getSlotStackSize(STORAGE_SIDE, slot)
  274.     stacks.size = stacks.size + stackSize
  275.     stacks.slots[slot] = { size = stackSize }
  276.     db.freeSlots[slot] = nil
  277.   end
  278.  
  279.   function storage.scanInventory()
  280.     db = {freeSlots = {}}
  281.     local size = inv.getInventorySize(STORAGE_SIDE)
  282.     for slot = 1, size, 1 do
  283.       if inv.getSlotStackSize(STORAGE_SIDE, slot) > 0 then
  284.         addStack(slot)
  285.       else
  286.         db.freeSlots[slot] = true
  287.       end
  288.     end
  289.   end
  290.  
  291.   function storage.count(itemHash)
  292.     assert(type(itemHash)=="string")
  293.     local stacks = db[itemHash]
  294.     if stacks == nil then return 0 end
  295.     return stacks.size
  296.   end
  297.  
  298.   local function checkSlot(slot, itemHash, stackSize)
  299.     if itemHash ~= nil then
  300.       local item = itemdb.store(slot)
  301.     end
  302.     if (item ~= nil and itemHash ~= item.hash)
  303.       or inv.getSlotStackSize(STORAGE_SIDE, slot) ~= stackSize then
  304.       print("Данные о хранилище устарели.")
  305.       print("Останов.")
  306.       os.exit(1)
  307.     end
  308.   end
  309.  
  310.   function storage.suckStack(itemHash, count)
  311.     if count == 0 then
  312.       return true
  313.     end
  314.     if robot.count() > 0 and not itemdb.compareInternal(robot.select(), itemHash) then
  315.       return false
  316.     end
  317.     local item = itemdb.get(itemHash)
  318.     if count > item.maxSize then
  319.       return false
  320.     end
  321.     local stacks = db[itemHash]
  322.     if stacks == nil then
  323.       return false
  324.     end
  325.     for slot, stack in pairs(stacks.slots) do
  326.       checkSlot(slot, itemHash, stack.size)
  327.       local take = count
  328.       if take > stack.size then
  329.         take = stack.size
  330.       end
  331.       inv.suckFromSlot(STORAGE_SIDE, slot, take)
  332.       stacks.size = stacks.size - take;
  333.       stack.size = stack.size - take;
  334.       if stack.size == 0 then
  335.         stacks.slots[slot] = nil
  336.         db.freeSlots[slot] = true
  337.       end
  338.       if stacks.size == 0 then
  339.         db[itemHash] = nil
  340.       end
  341.       count = count - take
  342.       if count == 0 then
  343.         break
  344.       end
  345.     end
  346.     return true
  347.   end
  348.  
  349.   function storage.dropStack(count)
  350.     local droppedStack = inv.getStackInInternalSlot(robot.select())
  351.     if droppedStack == nil then
  352.       return false
  353.     end
  354.     if count == nil then
  355.       count = droppedStack.size
  356.     end
  357.     if count > droppedStack.size then
  358.       return false
  359.     end
  360.     local item = itemdb.storeInternal(robot.select())
  361.     local stacks = db[item.hash]
  362.     if stacks ~= nil then
  363.       for slot, stack in pairs(stacks.slots) do
  364.         checkSlot(slot, item.hash, stack.size)
  365.         local free = item.maxSize - stack.size
  366.         local n = count
  367.         if n > free then
  368.           n = free
  369.         end
  370.         if n > 0 then
  371.           inv.dropIntoSlot(STORAGE_SIDE, slot, n)
  372.           stacks.size = stacks.size + n
  373.           stack.size = stack.size + n
  374.           count = count - n
  375.         end
  376.         if count == 0 then
  377.           return true
  378.         end
  379.       end
  380.     end
  381.    
  382.     for slot, _ in pairs(db.freeSlots) do
  383.       checkSlot(slot, nil, 0)
  384.       inv.dropIntoSlot(STORAGE_SIDE, slot)
  385.       addStack(slot)
  386.       return true
  387.     end
  388.  
  389.     print("Нет места в хранилище")
  390.     print("Останов.")
  391.     os.exit(1)
  392.   end
  393.  
  394.   function storage.printReport()
  395.     for id, st in pairs(db) do
  396.       local item = itemdb.get(id)
  397.       if item ~= nil then
  398.         print(item.label, st.size)
  399.       end
  400.     end
  401.   end
  402.  
  403. end
  404.  
  405. local deep = 0
  406. function recursiveCraft(requestedItem, requestedCount)
  407.   deep = deep + 1
  408.   printf("(%d) Крафт <%s * %d>:\n", deep, requestedItem.label, requestedCount)
  409.   local recipe = requestedItem.recipe
  410.   if recipe == nil then
  411.     printf("(%d) Невозможно выполнить крафт. Нет рецепта для <%s>\n",
  412.       deep, requestedItem.label)
  413.     return false
  414.   end
  415.   local items = countRecipeItems(recipe)
  416.   local n = math.ceil(requestedCount / recipe.n)
  417.   --подсчёт кол-ва необходимых ресурсов и крафт недостающих
  418.   ::recount::
  419.   local maxSize = math.min(n, requestedItem.maxSize, math.floor(64 / recipe.n))
  420.   local ok = true
  421.   printf("(%d) Подсчёт ресурсов.\n", deep)
  422.   for itemHash, nStacks in pairs(items) do
  423.     local item = itemdb.get(itemHash)
  424.     local nedded = nStacks * n
  425.     local itemCount = storage.count(itemHash)
  426.     if itemCount < nedded  then
  427.       printf("(%d) Нехватает <%s * %d>\n", deep,
  428.         item.label, nedded - itemCount)
  429.       if not recursiveCraft(item, nedded - itemCount) then
  430.           ok = false
  431.           break
  432.       end
  433.       goto recount
  434.     end
  435.     maxSize = math.min(item.maxSize, maxSize)
  436.   end
  437.   if ok then
  438.     printf("(%d) Выполняю крафт.\n", deep)
  439.     ok = craft(requestedItem, n, maxSize, recipe.grid)
  440.     if ok then
  441.       storage.dropStack()
  442.       printf("(%d) Крафт завершён.\n", deep)
  443.     else
  444.       printf("(%d) Ошибка крафта.\n", deep)
  445.     end
  446.   end
  447.   deep = deep - 1
  448.   return ok
  449. end
  450.  
  451. function craft(requestedItem, inCount, maxSize, grid)
  452.   local inStep = maxSize
  453.   while inCount > 0 do
  454.     local n = inStep
  455.     if inCount < n then
  456.       n = inCount
  457.     end
  458.     for i = 1, 9, 1 do
  459.       local itemHash = grid[i]
  460.       if itemHash ~= nil then
  461.         robot.select(gridSlot(i))
  462.         if not storage.suckStack(itemHash, n) then
  463.           print("Не могу положить предмет в сетку крафта.")
  464.           return false
  465.         end
  466.       end
  467.     end
  468.     robot.select(RESULT_SLOT)
  469.     if robot.count() > 0 then
  470.       storage.dropStack()
  471.     end
  472.     if not crafting.craft() then
  473.       return false
  474.     end
  475.     inCount = inCount - n
  476.   end
  477.   return true
  478. end
  479.  
  480. function countRecipeItems(recipe)
  481.   local counts = {}
  482.   for i = 1, 9, 1 do
  483.     local id = recipe.grid[i]
  484.     if id ~= nil then
  485.       local cnt = counts[id]
  486.       if cnt == nil then
  487.         cnt = 0
  488.       end
  489.       counts[id] = cnt + 1
  490.     end
  491.   end
  492.   return counts
  493. end
  494.  
  495. function writeRecipe()
  496.   print("Запись рецепта:")
  497.  
  498.   print("1. Анализ сетки крафта.")
  499.   local recipe = {
  500.    grid = { },
  501.    n = 1
  502.   }
  503.   for i = 1, 9, 1 do
  504.     local slot = 1
  505.     local item = itemdb.storeInternal(gridSlot(i))
  506.     if item ~= nil then
  507.       recipe.grid[i] = item.hash
  508.     end
  509.   end
  510.  
  511.   print("2. Пробный крафт.")
  512.   robot.select(RESULT_SLOT)
  513.   local ok = crafting.craft(1)
  514.   if not ok then
  515.     print("Неверный рецепт.")
  516.     return
  517.   end
  518.  
  519.   print("3. Сохраняю рецепт.")
  520.   local outItem = itemdb.storeInternal(4)
  521.   local stack = inv.getStackInInternalSlot(4)
  522.   recipe.n = stack.size
  523.   outItem.recipe = recipe
  524.   outItem.changed = true
  525.   print("Имя предмета:", outItem.label)
  526.   print("Хеш предмета:", outItem.hash)
  527.   print("Завершено успешно.")
  528. end
  529.  
  530. function clearWSlots()
  531.   for i = 1, 9, 1 do
  532.     robot.select(gridSlot(i))
  533.     storage.dropStack()
  534.   end
  535.   robot.select(RESULT_SLOT)
  536.   storage.dropStack()
  537. end
  538.  
  539. function usage()
  540.   print("Использование:")
  541.   print("craft -w", "Запись рецепта, выложенного в ле-вом верхнем углу инвентаря робота.")
  542.   print("craft <файл с хешем предмета> [<Кол-во>]",
  543.     "Выдаёт  предметы. Крафтит недостающие.")
  544.   print("Опции:")
  545.   print("-s", "Отправить результат крафта в хранилище.")
  546.   print("-o", "Не искать в хранилище. Только крафт.")
  547.   print("-u", "Просканировать хранилище.")
  548.   print("-c", "Очистить рабочие слоты робота.")
  549.   print("-r", "Вывести отчёт.")
  550.   print("-l", "Создать файлы с ид. предметов в текущем  каталоге.")
  551.   os.exit(0)
  552. end
  553.  
  554. function getParams(shift)
  555.   if shift == nil then shift = 0 end
  556.   local requestedCount = 1
  557.   local requestedItem
  558.   if args[1 + shift] ~= nil then
  559.     local fn = args[1 + shift]
  560.     local itemHash, err = loadFile(fn)
  561.     if err ~= nil then
  562.       print(err)
  563.       os.exit()
  564.     end
  565.     requestedItem = itemdb.get(itemHash)
  566.     if requestedItem == nil then
  567.       print("Нет информации в бд.")
  568.       os.exit()
  569.     end
  570.   end
  571.   if args[2 + shift] ~= nil then
  572.     requestedCount = tonumber(args[2 + shift])
  573.   end
  574.   return requestedItem, requestedCount
  575. end
  576.  
  577. function cmdGet()
  578.   local requestedItem, requestedCount = getParams()
  579.   local ok = true
  580.   local neddedCraft = requestedCount
  581.   if not options["o"] then
  582.     local itemCount = storage.count(requestedItem.hash)
  583.     neddedCraft = neddedCraft - itemCount
  584.     if itemCount < 0 then
  585.       neddedCraft = 0
  586.     end
  587.   end
  588.   if neddedCraft > 0 then
  589.     if not recursiveCraft(requestedItem, neddedCraft) then
  590.       return
  591.     end
  592.   end
  593.   if not options["s"] then
  594.     local count = requestedCount
  595.     for slot = 1, robot.inventorySize() do
  596.       robot.select(slot)
  597.       local n = count
  598.       if n > requestedItem.maxSize then
  599.         n = requestedItem.maxSize
  600.       end
  601.       if storage.suckStack(requestedItem.hash, n) then
  602.         count = count - n
  603.         if count == 0 then
  604.           break
  605.         end
  606.       else
  607.         print("Не могу выдать предмет.")
  608.         os.exit(1)
  609.       end
  610.     end
  611.     if count ~= 0 then
  612.       printf("Инвентарь полный. Выдано %d предметов.\n", requestedCount - count)
  613.     end
  614.   end
  615. end
  616.  
  617. function main( ... )
  618.   local cmd = false
  619.   if options["version"] then
  620.     print(version)
  621.     os.exit()
  622.   end
  623.  
  624.   if options["h"] then
  625.     usage()
  626.   end
  627.  
  628.   storage.load()
  629.  
  630.   if options["w"] then
  631.     cmd = true
  632.     writeRecipe()
  633.   end
  634.  
  635.   if options["u"] then
  636.     cmd = true
  637.     print("Обновление информации о доступных ресурсах")
  638.     storage.scanInventory()
  639.     print("Обновление завершено")
  640.   end
  641.  
  642.   if options["c"] then
  643.     cmd = true
  644.     clearWSlots()
  645.   end
  646.  
  647.   if args[1] ~= nil then
  648.     cmd = true
  649.     cmdGet()
  650.   end
  651.  
  652.   if options["r"] then
  653.     cmd = true
  654.     if options["d"] then
  655.       itemdb.printReport()
  656.     else
  657.       storage.printReport()
  658.     end
  659.   end
  660.  
  661.   if options["l"] then
  662.     cmd = true
  663.     itemdb.makeAllLabels()
  664.   end
  665.  
  666.   if not cmd then
  667.     usage()
  668.   end
  669.  
  670.   storage.save()
  671. end
  672.  
  673. main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement