Recipe keys or key or normal.key or normal.keys or ...

Coming soon!
Post Reply
User avatar
Yokmp
Inserter
Inserter
Posts: 26
Joined: Tue Aug 23, 2016 10:30 am
Contact:

Recipe keys or key or normal.key or normal.keys or ...

Post by Yokmp »

TL;DR
Please unify recipes to recipe.normal{ ingredients{}, results{} } and recipe.expensive{ ingredients{}, results{} }

What ?
Currently, if i want to get the results of a recipe i have to check for
recipe.result, recipe results or normal.result, normal.results or expensive.result, expensive.results
and if the table has keys or not. This is terrible.
It is just to complicated for such a simple task and opens itself up to bugs.

Why ?
Just to get you an idea what we poor modders have to do to get key-value tables out of a recipes results:

Code: Select all

---@param table table ``{"name", n?}``
---@return table ``{ name = "name", amount = n }``
local function add_pairs(table)
  if table.name then return table end
  local _t = table

  _t.name   = _t[1]      ;  _t[1] = nil
  _t.amount = _t[2] or 1 ;  _t[2] = nil

  return _t
end


---@param data_recipe table ``data.raw.recipe["name"]``
function recipe_get_keywords(data_recipe)
  local _return = {ingredients = nil, normal = nil, expensive = nil, result = nil, result_count = nil, results = nil}

    if data_recipe.ingredients then
      _return.ingredients = true
    end
    if data_recipe.normal then
      _return.normal = true
    end
    if data_recipe.expensive then
      _return.expensive = true
    end
    if data_recipe.result then
      _return.result = true
    end
    if data_recipe.result_count then
      _return.result_count = true
    end
    if data_recipe.results then
      _return.results = true
    end

  return _return
end


---@param data_recipe table ``data.raw.recipe["name"]``
---@param difficulty? table ``{result = boolean, results = boolean, normal = boolean, expensive = boolean}``
function recipe_get_results(data_recipe, difficulty)
  local _results = {results = {}, normal = {}, expensive = {}}
  local _difficulty = difficulty or recipe_get_keywords(data_recipe)

  if _difficulty.results then
    for i, value in ipairs(data_recipe.results) do
      _results.results[i] = add_pairs(value)
    end
  end
  if _difficulty.result then
    _results.results = add_pairs(data_recipe.result)
    if data_recipe.result_count then
      _results.results.amount = data_recipe.result_count
    end
  end
  if _difficulty.normal then
    if data_recipe.normal.result then
      _results.normal.name = data_recipe.normal.result
      _results.normal.amount = data_recipe.normal.result_amount or 1
    else
      for i, value in ipairs(data_recipe.normal.results) do
        _results.normal[i] = add_pairs(value)
      end
    end
  end
  if _difficulty.expensive then
    if data_recipe.expensive.result then
      _results.expensive.name = data_recipe.expensive.result
      _results.expensive.amount = data_recipe.result_count or 1
    else
      for i, value in ipairs(data_recipe.expensive.results) do
        _results.expensive[i] = add_pairs(value)
      end
    end
  end

  return _results
end
As mentioned in https://wiki.factorio.com/Prototype/Recipe#Recipe_data, if a difficulty is defined the non difficulty keys are ignored and if only one difficulty is defined then the one which is not defaults to the defined one. This means that if by standard all recipes which curently don't have a difficulty set could be set to "normal" and it would have no gameplay impact since the recipe would behave like it does now but modders only have to check if a normal or a expensive key exists. This would reduce the code by about 1/3. If we then also say that all ingredients and results need to have keys (which also means there would be no need for recipe.difficulty.result and result_amount) we will again get rid of a bunch of bloatcode. These simple changes would result in much smaller, cleaner code which is easier to maintain and debug like:

Code: Select all

---@param data_recipe table ``data.raw.recipe["name"]``
---@param difficulty? table ``{result = boolean, results = boolean, normal = boolean, expensive = boolean}``
function recipe_get_results(data_recipe)
  local _results = {normal = {}, expensive = {}}

  if data_recipe.normal then
    _results.normal = data_recipe.normal
  end
  if data_recipe.expensive then
    _results.expensive = data_recipe.expensive
  end

  return _results
end
By changing the current table design to a more unified one compatibility between certain mods will also benefit from it.
Shure, it will break some mods but then again, changing like 100 lines to a simple function (or even get rid of it) seems to be a good price to pay.
(This also applies to ingredients.)

Example recipe:

Code: Select all

  {
    type = "recipe",
    name = "kovarex-enrichment-process",
    energy_required = 60,
    category = "centrifuging",
    normal = {
      enabled = false,
      ingredients = {{ name = "uranium-235", amount = 40 }, { name = "uranium-238", amount = 5 }},
      main_product = "",
      results = {{ name = "uranium-235", amount = 41 }, { name = "uranium-238", amount = 2 }}
    },
    -- expensive would default to normal
    icon = "__base__/graphics/icons/kovarex-enrichment-process.png",
    icon_size = 64, icon_mipmaps = 4,
    subgroup = "intermediate-product",
    order = "r[uranium-processing]-c[kovarex-enrichment-process]",
    allow_decomposition = false
  }
EDIT:
This also applies (some way) to every other table which can have a difficulty setting like technology
Last edited by Yokmp on Mon Jun 21, 2021 1:56 pm, edited 1 time in total.

User avatar
ssilk
Global Moderator
Global Moderator
Posts: 12888
Joined: Tue Apr 16, 2013 10:35 pm
Contact:

Re: Recipe keys or key or normal.key or normal.keys or ...

Post by ssilk »

From own experience with Deadlocks stacking and fixing some bugs with it I can underline this.

But it’s in the wrong board. :) Moving from suggestions to modding interface requests.
Cool suggestion: Eatable MOUSE-pointers.
Have you used the Advanced Search today?
Need help, question? FAQ - Wiki - Forum help
I still like small signatures...

User avatar
eradicator
Smart Inserter
Smart Inserter
Posts: 5206
Joined: Tue Jul 12, 2016 9:03 am
Contact:

Re: Recipe keys or key or normal.key or normal.keys or ...

Post by eradicator »

+∞

Age old topic. I thought I even requested it once myself. I've definetly had several heated discussions about it. In some of which I said things I regret in retrospect (sorry for that).

Even if one mod author gets it all right on *reading* a recipe, they can still break another mods (broken) parsing if the format changes i.e. from implicit to explicit "normal". Seen globally it's a huge amount of wasted modder time because everyone has to implement their own parser.

What I assume is currently the stand of the dev team on this is:
  1. It's more important to have short vanilla recipe definitions than an easy to parse format.
  2. Changing it now would break too many mods.
In my humble opinion the whole "expensive" idea is a failed experiment. It is the kind of specific change that is far better handled by en-/disabling a mod. This could even be one (or several!) official mods that are built-in into vanilla. That approach would bring recipes back to a simple non-nested definition. The current "recipe data" also includes some weird things in difficulties that imho don't make any sense at all to be different for normal/expensive. For example allow_as_intermediate, always_show_made_in or hide_from_stats. These should be in the recipe table, not in each difficulty table.


I sure hope this is on the table for some cleanup for the addon, but I won't believe it until I see it...
Author of: Belt Planner, Hand Crank Generator, Screenshot Maker, /sudo and more.
Mod support languages: 日本語, Deutsch, English
My code in the post above is dedicated to the public domain under CC0.

User avatar
Yokmp
Inserter
Inserter
Posts: 26
Joined: Tue Aug 23, 2016 10:30 am
Contact:

Re: Recipe keys or key or normal.key or normal.keys or ...

Post by Yokmp »

What i would do, or Option A:
  • Implement the new standart and log a warning if a mod uses the old method
  • Could even state that it won't work from version 1.1x onward anymore
  • Then, if a mod still uses the "obsolete" method they get informed and will change their mod accordingly
  • If not, the Mod isn't maintained anymore; someone will fork it at some point
Option B:
  • Give us an "oficial parser" like util.get_gecipe_data(recipe_name) or something.
  • This isn't an elegant solution but not the worst one.
Option C:
  • Alter the tables when tey got read from disk before they're available to read by a mod/the game.
  • This is the worst solution i can think of atm, and will lead to some very confused modders wondering why their code didn't work and whatnot.
  • 'help' spams will be a usual business at this point ^^
I wish for A, could live with B and avoid the Forum for like ever with C

Kinda oftopic (but if you ever plan to radically change stuff pls take that into consideration):
also change the default value of keys like enabled to false because:
it would be better to parse them this way instead of checking

Code: Select all

 if key or key == nil then 
 -- do something 
 end
Since nil is falsy in LUA we get away with only checking if the key returns true(ish) or not.

I like the normal/expensive Idea but changing the concept of one thing means you'd have to do it for others to keep consintency.
Also naming it difficulty setting while the keys reflect normal/expensive game modes doesn't fit. Especially if you take in consideration that thechnologies can also have a difficulty setting. To be honest, ive never used that or see someone do that (so im not shure if the following would even work) but i imagine that one could do some fancy stuff like disabling the gun-turret in expensive and make it researchable or change the prerequisites for the flame-turret and uranium-processing so that the player gets them later in the game. This would make the game more difficult (if biters are there and not peaceful), and in a way satisfy the name difficulty setting.

curiosity
Filter Inserter
Filter Inserter
Posts: 325
Joined: Wed Sep 11, 2019 4:13 pm
Contact:

Re: Recipe keys or key or normal.key or normal.keys or ...

Post by curiosity »

Yokmp wrote:
Mon Jun 21, 2021 1:49 pm
Kinda oftopic (but if you ever plan to radically change stuff pls take that into consideration):
also change the default value of keys like enabled to false because:
it would be better to parse them this way instead of checking

Code: Select all

 if key or key == nil then 
 -- do something 
 end
Since nil is falsy in LUA we get away with only checking if the key returns true(ish) or not.

Code: Select all

if key ~= false then
Problem solved.

Bilka
Factorio Staff
Factorio Staff
Posts: 3132
Joined: Sat Aug 13, 2016 9:20 am
Contact:

Re: Recipe keys or key or normal.key or normal.keys or ...

Post by Bilka »

Please unify recipes to recipe.normal{ ingredients{}, results{} } and recipe.expensive{ ingredients{}, results{} }

Currently, if i want to get the results of a recipe i have to check for
recipe.result, recipe results or normal.result, normal.results or expensive.result, expensive.results and if the table has keys or not. This is terrible.
For 2.0, recipe prototypes are unified so that normal and expensive are no longer a thing (viewtopic.php?p=577191#p577191).
Furthermore, result and result_count have been removed so only results is allowed. Also, the short syntax ({"item-name", 39}) for ItemProductPrototype was removed.

Which makes this implemented, though not quite exactly as suggested.
I'm an admin over at https://wiki.factorio.com. Feel free to contact me if there's anything wrong (or right) with it.

Post Reply

Return to “Implemented for 2.0”