Understanding Lua

Place to get help with not working mods / modding interface.
Aethyrium
Manual Inserter
Manual Inserter
Posts: 2
Joined: Mon Feb 09, 2026 8:55 pm
Contact:

Understanding Lua

Post by Aethyrium »

Hello friends!

-- Needless exposition
I am a long (long... long...) time Factorio player, but very new to modding. And by very new, I mean I like just started this week (and this is my first real foray into programming). And that journey started with learning how Lua works. So I ran through the Lua crash course on Codecademy, after which I felt like I had a fair rudimentary understanding of the language - enough that when I looked at other code, I felt confident I roughly (emphasis: "rough" - based on complexity of code) understood what was happening. So today I jumped right in to execute what I felt like a was a fairly basic idea for a mod (modifying a base recipe) just to put skills to the test. Many problems later, and a couple of hours later, my mod functions and does what I want it to do. Yay!

While I'd like to do some expanding to my mod, now that I have a portion of it working, I'd like to understand some things, and improve my coding here so I can establish good habits early. So...

-- My Mod
At the most basic, I wanted to add a teeny bit of complexity while preserving default ratio to the transport belt cost - also I wanted to increase general use of iron sticks. My answer was to add 2 sticks to the transport belt recipe, increase gears to 2, and produce 4 belts instead of two. This is where I landed, and it works - it's tested in game and functions (let's ignore that sticks require Electric Energy Distribution 1... I'll figure out how to fix that later / if I keep working on this particular project).

Code: Select all

data.raw.recipe["transport-belt"].ingredients =
	{
		{type = "item", name = "iron-plate", amount = 1},
		{type = "item", name = "iron-gear-wheel", amount = 2},
		{type = "item", name = "iron-stick", amount = 2},
	}
data.raw.recipe["transport-belt"].results = {{type="item", name="transport-belt", amount=4}}
-- The Questions
Of which, I have three... point five(ish) half a dozen. Specific questions are bolded.

First, is that while trying to figure out how to do this, and troubleshooting the various errors I had, I came across this thread/solution to the problem: viewtopic.php?t=40209

In this example mod, the code they use is very small:

Code: Select all

data.raw.recipe["transport-belt"].ingredients =
{
	{"copper-plate", 1},
	{"iron-gear-wheel", 2}
}
And boy did I try to make that work. I kept getting errors (something something dictionary ROOT.data.raw... I broke quality (actually recycling according to the error) at one point. It was wild). Now, once I found a mod on the portal that did something similar to mine (basic recipe tweaks... which I am thinking can be done multiple ways depending at what stage you do it at, but this seems beyond me at the moment), I saw that they were using the full breakout like I am using in my 'finalized' mod. Which is what I thought to do initially, and had I done, would have saved me like at least an hour - but as this solution said solved, I really tried to make it work for me.

Now my best guess here is that this is just old language that doesn't work anymore, which clicked once I got mine working. So my guess here is that the code from the linked thread doesn't work, because the way the code works in 2.0 is just not compatible with that formatting anymore. Can I get confirmation that this is the case, or otherwise, an explanation as to why that exact coding does not seem to work anymore (I couldn't make it work, even with direct copy)?

Question two is a little more in the weeds. In the code that functions there is this line:

Code: Select all

data.raw.recipe["transport-belt"].results = {{type="item", name="transport-belt", amount=4}}
If I were to break this down with my rudimentary understanding of Lua (please forgive any verbiage errors).
  • data.raw is calling the "master table" of information compiled by the game.
  • .recipe is designating a subsection of that.
  • ["transport-belt"] is selecting the object from that subsection that I want to mess with.
  • '.results =' is changing the variable that is the results of that object.
  • {{type="item", name="transport-belt", amount="4""}} are defining what you get.
Assuming my understanding is correct (and please correct me if I'm wrong!) my question (and bonus questions) here is: What is the purpose of the double {{}} around the last part, defining what you get? I (think I) understand why the first set of brackets is needed, we're creating a table to be referenced. But what is the second set of brackets actually doing, it isn't (as far as I can tell) attached to anything else?

Bonus questions about this here are that on the data.raw page of the wiki, it suggests that the '.recipe' potion of this code could be replaced with ["recipe"] in the same way we call the transport belt. So it'd look like this:

Code: Select all

data.raw.["recipe"]["transport-belt"].results = ...
Is this accurate? And if it is accurate, is there a preferred choice between which method is used, or if both, when is "generally considered" appropriate to use one over the other? Additionally, does that mean we couple replace '.results' with ["results"]? If not, can you explain why? And if that's the case, could you replace ["transport-belt"] with .transport-belt, and if not, why not?

Actually, on that note, why does '.results' work at all? Based on my understanding 'results =' is a variable we've defined <somewhere>. In this case in the recipe. I am guessing based on context that the 'thing.thing.thing.thing' format is a way for the code to read a table (which was present in my little Lua course, I have just since done some extra reading and am vaguely familiar) and that the way that data.raw works (basically) is that it compiles things into a table that can be read by the code in a file-like structure so it's basically the same thing as data/raw/recipes/transport-belt/results and the reason we're able to call it is because of tabling much earlier on... If this is way off base and too much to explain to a novice, I understand.

And the final question which isn't really about the mod or Lua specifically, is just about formatting conventions - which the internet seems to have mixed opinions on. How many spaces before a new line of code 2-3-4 (vs. tab in note++ which is what I have been using, and appears equal to 4). Nesting vs. not nesting? Are there objective formatting conventions I should be aware of out of the gate that aren't just "your" personal preference, or is this mostly a personal preference sort of thing?

-- Outro
That... Ended up being a lot. Sorry about that! Just a quick preemptive thank you to anyone who reads through this. And an extra thanks to anyone who can shed light on one or more of these questions for me, I really appreciate your time and assistance!
eugenekay
Filter Inserter
Filter Inserter
Posts: 995
Joined: Tue May 15, 2018 2:14 am
Contact:

Re: Understanding Lua

Post by eugenekay »

Aethyrium wrote: Mon Feb 09, 2026 10:19 pmCan I get confirmation that this is the case, or otherwise, an explanation as to why that exact coding does not seem to work anymore (I couldn't make it work, even with direct copy)?
Yes, version 2.0 changed how recipe ingredients are specified. There used to be a shorthand format for compatibility. The Quality mod chokes if a Recipe does not have a valid Ingredients because it auto-generates recipes for each quality level… it is generally easiest/faster to disable the DLC mods when experimenting with recipe changes unless they require a Space Age item - or use Logic to detect if they are enabled and conditionally add support.
Question two is a little more in the weeds. In the code that functions there is this line:

Code: Select all

data.raw.recipe["transport-belt"].results = {{type="item", name="transport-belt", amount=4}}
If I were to break this down with my rudimentary understanding of Lua (please forgive any verbiage errors).
  • data.raw is calling the "master table" of information compiled by the game.
  • .recipe is designating a subsection of that.
  • ["transport-belt"] is selecting the object from that subsection that I want to mess with.
  • '.results =' is changing the variable that is the results of that object.
  • {{type="item", name="transport-belt", amount="4""}} are defining what you get.
Assuming my understanding is correct (and please correct me if I'm wrong!) my question (and bonus questions) here is: What is the purpose of the double {{}} around the last part, defining what you get? I (think I) understand why the first set of brackets is needed, we're creating a table to be referenced. But what is the second set of brackets actually doing, it isn't (as far as I can tell) attached to anything else?
The outer brackets are defining a ProductPrototype; the inner brackets define a (or multiple) Item/FluidProductPrototype within that wrapper.
Bonus questions about this here are that on the data.raw page of the wiki, it suggests that the '.recipe' potion of this code could be replaced with ["recipe"] in the same way we call the transport belt. So it'd look like this:

Code: Select all

data.raw.["recipe"]["transport-belt"].results = ...
Is this accurate? And if it is accurate, is there a preferred choice between which method is used, or if both, when is "generally considered" appropriate to use one over the other? Additionally, does that mean we couple replace '.results' with ["results"]? If not, can you explain why? And if that's the case, could you replace ["transport-belt"] with .transport-belt, and if not, why not?

Actually, on that note, why does '.results' work at all? Based on my understanding 'results =' is a variable we've defined <somewhere>. In this case in the recipe. I am guessing based on context that the 'thing.thing.thing.thing' format is a way for the code to read a table (which was present in my little Lua course, I have just since done some extra reading and am vaguely familiar) and that the way that data.raw works (basically) is that it compiles things into a table that can be read by the code in a file-like structure so it's basically the same thing as data/raw/recipes/transport-belt/results and the reason we're able to call it is because of tabling much earlier on... If this is way off base and too much to explain to a novice, I understand.
Named properties (like “results”) can be accessed as either a .method; or as a a ["property"]. This is just Lua syntax being weird because it’s an interpreted language. I think Members of an array can only be accessed by name, eg ["transport-belt"]
And the final question which isn't really about the mod or Lua specifically, is just about formatting conventions - which the internet seems to have mixed opinions on. How many spaces before a new line of code 2-3-4 (vs. tab in note++ which is what I have been using, and appears equal to 4). Nesting vs. not nesting? Are there objective formatting conventions I should be aware of out of the gate that aren't just "your" personal preference, or is this mostly a personal preference sort of thing?
There is no single authority on spaces-per-indent-level in Lua. I like to use Tab characters, which are normally 8 characters wide.

Good Luck!
Aethyrium
Manual Inserter
Manual Inserter
Posts: 2
Joined: Mon Feb 09, 2026 8:55 pm
Contact:

Re: Understanding Lua

Post by Aethyrium »

Yes, version 2.0 changed how recipe ingredients are specified. There used to be a shorthand format for compatibility. The Quality mod chokes if a Recipe does not have a valid Ingredients because it auto-generates recipes for each quality level… it is generally easiest/faster to disable the DLC mods when experimenting with recipe changes unless they require a Space Age item - or use Logic to detect if they are enabled and conditionally add support.
Oh perfect, I'm glad I was right about this. Good to know disabling mods and what have you, I will keep that in mind. Thank you!
The outer brackets are defining a ProductPrototype; the inner brackets define a (or multiple) Item/FluidProductPrototype within that wrapper.
I think I'm following this. As I look at the code again, I'm recognizing that this (seems) to functionally be happening on the ingredient line as well.

Code: Select all

	{
		{type = "item", name = "iron-plate", amount = 1},
		{type = "item", name = "iron-gear-wheel", amount = 2}, -- input increased from 1 to 2
		{type = "item", name = "iron-stick", amount = 2}, -- input added
	}
Could also be written

Code: Select all

	{{type = "item", name = "iron-plate", amount = 1}, {type = "item", name = "iron-gear-wheel", amount = 2}, {type = "item", name = "iron-stick", amount = 2}}
...Of course this second writing also asks that you not have eyes, and I suspect is the sort of thing that makes anyone who programs regularly want to lynch you. But a little testing seems to suggest that it works just as well in practice. It could be written then also as something like:

Code: Select all

	{{type = "item", name = "iron-plate", amount = 1},
	{type = "item", name = "iron-gear-wheel", amount = 2},
	{type = "item", name = "iron-stick", amount = 2}}
As some kind of middle ground? My point being that these are all "stylistic" choices, rather than "functional" ones, right?

And as I was mucking around in the code, I was just wondering the same thing about some of the code written in the game:

Code: Select all

data.raw.recipe["transport-belt"].results = {{type="item", name="transport-belt", amount=2}}
Has no spaces between type = "item", etc, but it could be written that way to maintain the same formatting as the ingredients above - or vice versa, the ingredients could be written without the spaces. For example, the above can seamlessly be written as:

Code: Select all

data.raw.recipe["transport-belt"].results = {{type = "item", name = "transport-belt", amount = 2}}
Right?
Named properties (like “results”) can be accessed as either a .method; or as a a ["property"]. This is just Lua syntax being weird because it’s an interpreted language. I think Members of an array can only be accessed by name, eg ["transport-belt"]

There is no single authority on spaces-per-indent-level in Lua. I like to use Tab characters, which are normally 8 characters wide.
This is very informative, thank you!
eugenekay
Filter Inserter
Filter Inserter
Posts: 995
Joined: Tue May 15, 2018 2:14 am
Contact:

Re: Understanding Lua

Post by eugenekay »

Aethyrium wrote: Tue Feb 10, 2026 12:18 amAs some kind of middle ground? My point being that these are all "stylistic" choices, rather than "functional" ones, right?
Correct, whitespace is stripped out by the Lua interpreter at runtime outside of "quoted strings". The first format is probably the sanest for readability.
Post Reply

Return to “Modding help”