Crafting Combinator


Includes combinators that allow you to set or read the recipe of any crafting machine, get ingredients or products of a recipe and more!

Content
3 years ago
0.14 - 1.1
21.3K
Manufacturing

g How does signal-cache work?

I'm trying to write my own 2.0 version of the Recipe combinator. I'm looking at your code and i'm a bit confused.
I guess the signal-cache lamps are some sort of memory and comparison, but I have no idea how any of that metatable voodoo is supposed to work.
Could you please explain a bit on how the recipe combinator works?

2 months ago

The whole signals.lua file exists only because reading signals via the api is veeeery slow (or at least it was when i wrote that part). And there's quite a bit of reading happening all over the place. Iirc recipe combinator in particular only uses the get_highest function, which does what it says, but using the lamps as a cache - they have conditions such that unless the highest signal changes to something other than it was last time, it will do with a simple disabled check on the control behaviors of the lamps, instead of actually reading the signals. More or less, i don't quite remember the exact details.

The metatable is mostly just a shortcut - __index is called when a non-existent key is accessed on the table with this metatable and whatever it returns will be returned in place of the non-existent value. If you were to write the table access operator as a function it would look something like this:

function table_access_operator(table, key)
   local raw_value = rawget(table, key)
   if raw_value ~= nil then
      return raw_value
   end

   local metatable = getmetatable(table)
   local __index = metatable and metatable.__index
   if type(__index) == 'function' then
      return __index(table, key)
   end
   -- __index can also be a table, but that's not relevant here.

   return nil
end

So really, the only parts of the code that do "metatable voodoo" is when you read something from a table you got from global.signals.cache (basically the things returned from _M.cache.get), and even then, it only does it the first time, since the __index function assigns the result back to the table, meaning on following access of the same key it will actually be stored in the table itself and the metatable doesn't come into play at all.

To sum it up, the metatable is used for lazy initialization of the cache lamps. This can be done without the metatable, but you would have to manually check if the particular lamp exists (and create it if not) before using it, in a similar manner to what __index does now. The only other metatable-related thing is the usage of rawget, which is just like the table access operator, but it ignores metatables.

In the case of get_highest, there's three lamps in use as that was the minimum to make the cache work: highest, highest_present and highest_count. In the function you can see three parts: Everything above local highest = nil is the cache check, the local and the loop bellow is the computation in case of a miss and the rest is setting up the cache for the new value. The update_count parameter is just a small optimization for the cases where you don't care about the value of the signal, just that it is highest.

With all that said though, i have to wonder which part of this is worth porting to 2.0? I was of the impression that pretty much all of what this does is now possible in vanilla. And anything i thought at least a little useful would be a lot better done using those new features instead of the hacks in place currently, which i didn't feel was worth the effort. If you have a compelling argument for something that's not possible (or practical) in vanilla 2.0, i might be open to implementing something myself. The only remotely useful suggestion i got so far was to get the ingredients of a recipe more easily than using an actual assembler, but that didn't seem enough of a problem to me. Granted i haven't really had a need for anything too advanced in this regard in my 2.0 playthrough so far, so maybe im just missing something.

My auto-crafter from 1.1 used quite a few features that aren't included in 2.0.
For recipes that output more than one of the product, I had to use "find product" and "divide results by product/ingredient count" to get the number of crafting cycles, otherwise the machine would fill up with overproduced intermediate products. "find recipes" was important for products that could be made by more than one recipe. For example , Renai transportation has a recipe to make an open chest from an iron chest, and to make an iron chest from an open chest. Without being able to filter out recipes, the planner gets stuck in an infinite loop. I also used "find machines" to make sure that my system was only selecting recipes that could be made in certain machines.

a month ago

Hm so mostly its just about finding recipes with a certain product as well as the reverse, finding the products of a recipe. This seems still impossible in 2.0. The multiply/divide functions are more of an ease-of-use kind of thing - you could do that manually, it was just very ugly. But if i am gonna do any of this i don't see why they couldn't stay.

I suppose finding ingredients also still makes sense to have in a combinator, because eventhough it is technically possible with just a crafting machine, there's no easy way of doing it no matter what machine/planet/whatever the recipe requires. I'm not totally sure why you would need to work with recipes from other planets, but i can see why having all machines in one circuit would be useful.

Find uses also seems impossible still. I'm not sure how useful that one was to begin with, but it wouldn't hurt to include it as well. Same goes for find machines, though that one did have clear uses (also it could technically be done in 2.0, just in a very scuffed and unreliable way).

Also if the combinator is going to output recipes from other planets, there should also be some way to filter recipes by planet. And if that's going to be the case, built-in filtering by machine might also make sense.

Idk, thats just some thoughts. Basically crafting combinators are obsolete in 2.0, but recipe combinators are not. If i have time this weekend ill try making an mvp for one or two of these functions to see how viable it would be to implement them in some better way than what it is now. It is clear though that none of this will be backwards compatible in any way, but ig that's not really an issue.

I suppose "restrict to current planet" could be useful, but that wasn't really what I was thinking about.

Working with recipes wasn't for working with recipes from other planets. For example, AAI has recipes for green circuits from stone tablets, or green circuits from wood. If I just gave the assembler the "green circuit" signal, it might select the wood circuits even thoughthe system isn't being supplied with wood. Or it might get stuck thinking I can make an AI tank by adding circuits to a regular tank, which I make by removing AI from an AI tank, which I can make by adding circuits to a regular tank, and so on forever. So then I had to have logic in my planner that would filter out recipes that had unavailable ingredients and recipes that would cause an infinite loop.

a month ago

I know. Only the first paragraph of what i wrote was a direct response to what you described, the rest is just me trying to figure out what other functions are still useful. A public "note to self" of sorts.

Oh, ok.

https://github.com/ScruffyLookingVoltHerder/Recipe_combinator_2.0/blob/main/Recipe_Combinator_2.0/data-final-fixes.lua

An assembler cannot set its recipe off of a virtual signal, only an item signal or a recipe signal. There's a mod called "show all recipe signals" that simply unhides all of the recipe signals. I thought that was a bit cluttered, with the item signals and multiple recipe signals on the same tab, so I wrote some code to copy all of the recipes to their own tab. It doesn't have any organization or locale considerations yet, but it can set assembler recipes.

What would you like to do? one of us can run with it, or we could try and collaborate.

a month ago

I dont think collaborating on something like this would be particularly productive - i just havent had enough spare attention to dedicate to it. I did do some basic experiments but thats about it so far, but i do intend to work on it more properly when i can. No guarantees tho.

Of course feel free to do your own thing if you think im being too slow (:, the code is freely licensed for a reason.

I've gotten "find ingredients" and "find recipes" working, and i'm working on "find machines".

Your GUI code was very helpful, and saved me a whole bunch of time. Thanks for that. I think I'll be able to adapt your "find machines" code as well.

New response