Problems with LuaControl.cancel_crafting()

Place to get help with not working mods / modding interface.
Pi-C
Smart Inserter
Smart Inserter
Posts: 1724
Joined: Sun Oct 14, 2018 8:13 am
Contact:

Problems with LuaControl.cancel_crafting()

Post by Pi-C »

I encountered some weird behaviour when I tried to copy the crafting queue from one character to another in miniMAXIme. LuaControl.crafting_queue is a read-only value, so I can only use that to read the recipes a player is crafting, but I can't write back the queue directly after switching characters, so some trickery is required.

My first attempt failed:

Code: Select all

 local queue = {}
 for i, item in pairs(player.crafting_queue or {}) do
  if not item.prerequisite then
    table.insert(queue, item)
  end
  player.cancel_crafting({index = i, count = item.count})
end
This doesn't work because player.crafting_queue is changed by player.cancel_crafting, and the index of each subsequent item will be shifted down. So I tried this:

Code: Select all

local queue = {}
while player.crafting_queue do
  i, item = next(p.crafting_queue)
  if not item.prerequisite then
    table.insert(queue, item)
  end
  player.cancel_crafting({index = 1, count = item.count})
end
This will work (as in "it won't crash"), but it isn't reliable because cancelling some (not all!) items at the start of queue may empty the queue completely, so I won't be able to store all the queue items I need.

To see what happens, start a sandbox game with all techs enabled + cheat mod disabled and run this code from the command line:

Code: Select all

/c
p = game.player; i = p.get_inventory(defines.inventory.god_main); i.clear()
p.insert({name = "iron-plate", count = 200})
p.insert({name = "copper-plate", count = 200})
p.begin_crafting({recipe = "programmable-speaker", count = 22})

game.print("Original crafting queue:")
for i, item in pairs(p.crafting_queue or {}) do
  game.print(i..": "..serpent.line(item))
end
You can see that there are 5 items in the queue when all intermediate products have to be crafted from iron- and copper-plates. Now run this:

Code: Select all

/c while p.crafting_queue do
  i, item = next(p.crafting_queue)
  game.print(i..": "..serpent.line(item))
  if not item.prerequisite then
    game.print("Found final product!")
  end
  p.cancel_crafting({index = 1, count = item.count})
end
As you can see, the loop is finished after just 2 iterations:
crafting_queue.png
crafting_queue.png (1.7 MiB) Viewed 785 times
I've finally settled on using one loop for storing the queue items where item.prerequisite == false and a second loop for cancelling the queue items:

Code: Select all

  if player.crafting_queue then
    player_data.crafting_queue = {}

    -- Store all crafting queue items that are final products 
    local add_this
    for i, item in pairs(player.crafting_queue) do
      if not item.prerequisite then
        add_this = { count = item.count, recipe = item.recipe }
        table.insert(player_data.crafting_queue, add_this)
        minime.writeDebug("Added item to crafting queue: %s", {add_this})
      end
    end

    -- Clear crafting queue!
    local i, item
    while player.crafting_queue do
      i, item = next(player.crafting_queue)
      minime.writeDebug("Cancelling crafting of queue item %s: %s", {i, item}, "line")
      player.cancel_crafting({index = 1, count = item.count})
    end
  end
  
  …
  
  
  -- Try to resume crafting after switching characters
  local crafting
  for i, item in pairs(player_data.crafting_queue or {}) do
    minime.writeDebug("New crafting queue item %s: %s", {i, item}, "line")
    player.begin_crafting(item)
  end
  player_data.crafting_queue = nil
This does work (as in "it won't crash" and "I can store all items I need and still cancel crafting"), but it isn't very intuitive. Is this really the way cancelling crafting queues is supposed to work?
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!

Theis
Inserter
Inserter
Posts: 29
Joined: Tue Sep 13, 2022 9:57 am
Contact:

Re: Problems with LuaControl.cancel_crafting()

Post by Theis »

If the problem is that you are changing the crafting queue order, then you can try to iterate backwards.

Pi-C
Smart Inserter
Smart Inserter
Posts: 1724
Joined: Sun Oct 14, 2018 8:13 am
Contact:

Re: Problems with LuaControl.cancel_crafting()

Post by Pi-C »

Run the command that crafts the programmable speakers. The crafting queue will contain 5 recipes, and the last one is for the actual programmable speakers. If you then run the command that cancels the crafting, the complete queue will have been removed after the second recipe (iron sticks) has been cancelled.

Now try running the first command again and use SHIFT+Left click on items 1 and 2 (copper cable 1 and iron sticks) to cancel these recipes. Crafting will continue. I don't understand why scripted and manual cancelling would lead to different results, that's why my question.
Theis wrote:
Sat Feb 18, 2023 6:01 am
If the problem is that you are changing the crafting queue order, then you can try to iterate backwards.
It's not the queue order that is changed, but items disappear. However, I do think it makes sense to iterate backwards: The last recipe is always that of a main product, not a prerequisite, so I don't have to check whether item.prerequisite is false. Cancelling the main product will also cancel its prerequisites, so after cancelling the last recipe, the queue will be either empty, or the last recipe on the next iteration will also be a main product. Of course, my stored queue will be reverted, so I'll also have to go backwards when I start crafting again:

Code: Select all

  if player.crafting_queue then
    player_data.reverse_crafting_queue = {}

    local i, item, add_this
    while player.crafting_queue do
      i = player.crafting_queue_size

      -- Store last item
      item = player.crafting_queue[i]
      add_this = { count = item.count, recipe = item.recipe }
      table.insert(player_data.reverse_crafting_queue, add_this)

      -- Remove recipe from player's crafting queue!
      player.cancel_crafting({index = i, count = item.count})
    end
  end

  …
  
      local items = player_data.reverse_crafting_queue
      if items then
        for i = #items, 1, -1 do
          player.begin_crafting(items[i])
        end
        player_data.reverse_crafting_queue = nil
      end
A good mod deserves a good changelog. Here's a tutorial (WIP) about Factorio's way too strict changelog syntax!

Post Reply

Return to “Modding help”