using util.merge how does it works?

Place to get help with not working mods / modding interface.
User avatar
Palaber1984
Inserter
Inserter
Posts: 32
Joined: Sat Feb 16, 2019 10:09 am
Contact:

using util.merge how does it works?

Post by Palaber1984 »

Hello Friends.

i am developing my own Mod about Power Poles. Honestly it is even in the Mod Portal called "Advanced Power Poles" and it is working so far.

So my question turns around how to efficiently write a code with less words and stuff. I have an entity.lua, where just copied the entities from the Orginal Entity in Base folder, and this works so far! https://drive.google.com/open?id=14h3T1 ... m2wlGUzVQD

Then i found another mod where entities are put together in compact way, and more less words then the original . it uses a class called util.merge.
so build my entity.lua up using the the same code and just changed a few things
https://drive.google.com/open?id=1K1akZ ... MH1KHNTqrg

Here i show a few lines of that code, that is normally longer.

Code: Select all

data:extend({
---------------------------	
-- Small Electric Pole 1 --
---------------------------

util.merge{data.raw["electric-pole"]["small-electric-pole"],
  {
    name = "small-electric-pole",
    minable = {mining_time = 0.1, result = "small-electric-pole"},
    max_health = 50,
    maximum_wire_distance = 7.5,
    supply_area_distance = 2.5,
	result_count = 1,
  }
},

---------------------------	
-- Small Electric Pole 2 --
---------------------------

util.merge{data.raw["electric-pole"]["small-electric-pole-2"],
  {
    name = "small-electric-pole-2",
    minable = {mining_time = 0.1, result = "small-electric-pole-2"},
    max_health = 50,
    maximum_wire_distance = 16,
    supply_area_distance = 1.5,
  }
},

})

And when i try to load my mod with this compact version it shows me following error
https://drive.google.com/open?id=1yYT0C ... r62S0h7LYr

The only thing that has changed compared to the original code, where i get this, is that the other code starts with

Code: Select all

if settings.startup["ABC"].value == true then
And End with

Code: Select all

End
ABC is just a fiction name. And because i dont use any settings in my mod i deleted this part and make my mod like this
https://drive.google.com/open?id=1K1akZ ... MH1KHNTqrg


So, where is my problem loading the game and getting this error??? Please , i could make my mod using the long way, but i want to understand it and learn it the shortway
https://drive.google.com/open?id=1yYT0C ... r62S0h7LYr


Big THX

User avatar
darkfrei
Smart Inserter
Smart Inserter
Posts: 2904
Joined: Thu Nov 20, 2014 11:11 pm
Contact:

Re: using util.merge how does it works?

Post by darkfrei »

I saw this code, but never used it:
(pseudocode)

Code: Select all

local new_table = util.merge {original_table, correction_table}

Adamo
Filter Inserter
Filter Inserter
Posts: 479
Joined: Sat May 24, 2014 7:00 am
Contact:

Re: using util.merge how does it works?

Post by Adamo »

Palaber1984 wrote:
Sun Sep 15, 2019 2:43 pm

Code: Select all

data:extend({
---------------------------	
-- Small Electric Pole 1 --
---------------------------

util.merge{data.raw["electric-pole"]["small-electric-pole"],
  {
    name = "small-electric-pole",
    minable = {mining_time = 0.1, result = "small-electric-pole"},
    max_health = 50,
    maximum_wire_distance = 7.5,
    supply_area_distance = 2.5,
	result_count = 1,
  }
},

---------------------------	
-- Small Electric Pole 2 --
---------------------------

util.merge{data.raw["electric-pole"]["small-electric-pole-2"],
  {
    name = "small-electric-pole-2",
    minable = {mining_time = 0.1, result = "small-electric-pole-2"},
    max_health = 50,
    maximum_wire_distance = 16,
    supply_area_distance = 1.5,
  }
},

})
I think what util.merge does is take the first table, replace any entries in the first table with entries under the same name or index in the second table, then return a copy of the modified first table. In your examples, you appear to be trying to merge tables with themselves: "small-electric-pole" with "small-electric-pole" and "small-electric-pole-2" with "small-electric-pole-2". Since small-electric-pole-2 is presumably still nil in data.raw["electric-pole"], it simply returns a nil table or a copy of the second table in its completeness (not sure which of these choices this implementation makes), which in either case does not include a "type" value. If you want to use merge, what you want to do, there, I think, it put electric-pole instead of electric-pole-2 in the first argument to merge, then put the CHANGES you want to make to small-electric-pole so that it becomes small-electric-pole-2 in the table in the second argument. That should return a complete, new table, with the name and other values for "small-electric-pole-2", including being of type "electric-pole". But see DaveMcW's post below for what I think is a better approach using util.deepcopy().
Last edited by Adamo on Sun Sep 15, 2019 4:19 pm, edited 2 times in total.

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

Re: using util.merge how does it works?

Post by eradicator »

For references, this is the function you're using.

Code: Select all

-- Recursively merges and/or deep-copies tables.
-- Entries in later tables override entries in earlier ones, unless
-- both entries are themselves tables, in which case they are recursively merged.
-- Non-merged tables are deep-copied, so that the result is brand new.
function util.merge(tables)
  local ret = {}
  for i, tab in ipairs(tables) do
    for k, v in pairs(tab) do
      if (type(v) == "table") then
        if (type(ret[k] or false) == "table") then
          ret[k] = util.merge{ret[k], v}
        else
          ret[k] = table.deepcopy(v)
        end
      else
        ret[k] = v
      end
    end
  end
  return ret
end
I've always recommended against using util.merge() if you are unable to understand it's code yourself. It has too many weird edge cases that you need to understand, or you will run into bugs like this.

In this case: Because data.raw['electric-pole']['electric-pole-2'] doesn't exist, util.merge() returns an empty table. And that is exactly what your error message shows.
emptytable.png
emptytable.png (4.59 KiB) Viewed 3820 times
Oh, and btw the forum supports attaching screenshots to posts, which is much easier to read then jumping back and forth to an external site.
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
Palaber1984
Inserter
Inserter
Posts: 32
Joined: Sat Feb 16, 2019 10:09 am
Contact:

Re: using util.merge how does it works?

Post by Palaber1984 »

eradicator wrote:
Sun Sep 15, 2019 4:04 pm

Oh, and btw the forum supports attaching screenshots to posts, which is much easier to read then jumping back and forth to an external site.
My picture are stored in a puplic folder of my google drive. So I don’t know how to show them here. I am getting strange link.

User avatar
DaveMcW
Smart Inserter
Smart Inserter
Posts: 3700
Joined: Tue May 13, 2014 11:06 am
Contact:

Re: using util.merge how does it works?

Post by DaveMcW »

What you really want is table.deepcopy().

Code: Select all

local pole = table.deepcopy(data.raw["electric-pole"]["small-electric-pole"])
pole.name = "small-electric-pole-2"
pole.minable = {mining_time = 0.1, result = "small-electric-pole"}
pole.max_health = 50
pole.maximum_wire_distance = 7.5
pole.supply_area_distance = 2.5
data:extend{pole}

Adamo
Filter Inserter
Filter Inserter
Posts: 479
Joined: Sat May 24, 2014 7:00 am
Contact:

Re: using util.merge how does it works?

Post by Adamo »

DaveMcW wrote:
Sun Sep 15, 2019 4:12 pm
What you really want is table.deepcopy().

Code: Select all

local pole = table.deepcopy(data.raw["electric-pole"]["small-electric-pole"])
pole.name = "small-electric-pole-2"
pole.minable = {mining_time = 0.1, result = "small-electric-pole"}
pole.max_health = 50
pole.maximum_wire_distance = 7.5
pole.supply_area_distance = 2.5
data:extend{pole}
Plus one. I should have mentioned in my original response that this is how I do it.

User avatar
Palaber1984
Inserter
Inserter
Posts: 32
Joined: Sat Feb 16, 2019 10:09 am
Contact:

Re: using util.merge how does it works?

Post by Palaber1984 »

I used bobs power mod. There u can see this situation in the entity/pole.lua
https://mods.factorio.com/mod/bobpower

User avatar
Palaber1984
Inserter
Inserter
Posts: 32
Joined: Sat Feb 16, 2019 10:09 am
Contact:

Re: using util.merge how does it works?

Post by Palaber1984 »

Adamo wrote:
Sun Sep 15, 2019 4:15 pm
DaveMcW wrote:
Sun Sep 15, 2019 4:12 pm
What you really want is table.deepcopy().

Code: Select all

local pole = table.deepcopy(data.raw["electric-pole"]["small-electric-pole"])
pole.name = "small-electric-pole-2"
pole.minable = {mining_time = 0.1, result = "small-electric-pole"}
pole.max_health = 50
pole.maximum_wire_distance = 7.5
pole.supply_area_distance = 2.5
data:extend{pole}
Plus one. I should have mentioned in my original response that this is how I do it.
what makes me so confusing, is why you are calling it first pole and then you add another name small-electric-pole-2?

Code: Select all

local pole = table.deepcopy(data.raw["electric-pole"]["small-electric-pole"])
small-electric-pole-2.name = "small-electric-pole-2"
small-electric-pole-2.minable = {mining_time = 0.1, result = "small-electric-pole"}
small-electric-pole-2.max_health = 50
small-electric-pole-2.maximum_wire_distance = 7.5
small-electric-pole-2.supply_area_distance = 2.5
data:extend{small-electric-pole-2}
isn't it in my opinion to call the data:extend{small-electric-pole-2}??
Last edited by Palaber1984 on Sun Sep 15, 2019 4:28 pm, edited 1 time in total.

User avatar
DaveMcW
Smart Inserter
Smart Inserter
Posts: 3700
Joined: Tue May 13, 2014 11:06 am
Contact:

Re: using util.merge how does it works?

Post by DaveMcW »

Do you like this better?

Code: Select all

local pole2 = table.deepcopy(data.raw["electric-pole"]["small-electric-pole"])
pole2.name = "small-electric-pole-2"
pole2.minable = {mining_time = 0.1, result = "small-electric-pole-2"}
pole2.max_health = 50
pole2.maximum_wire_distance = 7.5
pole2.supply_area_distance = 2.5
data:extend{pole2}

User avatar
Palaber1984
Inserter
Inserter
Posts: 32
Joined: Sat Feb 16, 2019 10:09 am
Contact:

Re: using util.merge how does it works?

Post by Palaber1984 »

DaveMcW wrote:
Sun Sep 15, 2019 4:26 pm
Do you like this better?

Code: Select all

local pole2 = table.deepcopy(data.raw["electric-pole"]["small-electric-pole"])
pole2.name = "small-electric-pole-2"
pole2.minable = {mining_time = 0.1, result = "small-electric-pole-2"}
pole2.max_health = 50
pole2.maximum_wire_distance = 7.5
pole2.supply_area_distance = 2.5
data:extend{pole2}
sorry, you must now that i am a noob. and i need short explanation. why can't you directly call the local part

Code: Select all

local smart-electric-pole-2 = table.deepcopy(data.raw["electric-pole"]["small-electric-pole"])
are there problems using the same name?

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

Re: using util.merge how does it works?

Post by eradicator »

DaveMcW wrote:
Sun Sep 15, 2019 4:12 pm
What you really want is table.deepcopy().

Code: Select all

local pole = table.deepcopy(data.raw["electric-pole"]["small-electric-pole"])
pole.name = "small-electric-pole-2"
pole.minable = {mining_time = 0.1, result = "small-electric-pole"}
pole.max_health = 50
pole.maximum_wire_distance = 7.5
pole.supply_area_distance = 2.5
data:extend{pole}
I think what he wants is to get around the ugly verbosity of exactly that method. Some sort of simple function. Maybe like this?

Code: Select all

local function create_new_from_existing(parent_name,changes)
  local new = util.table.deepcopy(data.raw[child.type][parent_name])
  for k,v in pairs(changes) do new[k] = util.table.deepcopy(v) end
  data:extend{new}
  end
Which then allows him to easily create a copy of something existing with slightly different attributes.

Code: Select all

  
create_new_from_existing(
  'small-electric-pole'
  ,{
    name = "small-electric-pole-2",
    type = 'electric-pole',
    minable = {mining_time = 0.1, result = "small-electric-pole-2"},
    max_health = 50,
    maximum_wire_distance = 16,
    supply_area_distance = 1.5,
  })
Btw, i do recommend you write that function on your own for your own needs and don't blindly copy my untested example code ;).
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.

Adamo
Filter Inserter
Filter Inserter
Posts: 479
Joined: Sat May 24, 2014 7:00 am
Contact:

Re: using util.merge how does it works?

Post by Adamo »

Palaber1984 wrote:
Sun Sep 15, 2019 4:24 pm
Adamo wrote:
Sun Sep 15, 2019 4:15 pm
DaveMcW wrote:
Sun Sep 15, 2019 4:12 pm
What you really want is table.deepcopy().

Code: Select all

local pole = table.deepcopy(data.raw["electric-pole"]["small-electric-pole"])
pole.name = "small-electric-pole-2"
pole.minable = {mining_time = 0.1, result = "small-electric-pole"}
pole.max_health = 50
pole.maximum_wire_distance = 7.5
pole.supply_area_distance = 2.5
data:extend{pole}
Plus one. I should have mentioned in my original response that this is how I do it.
what makes me so confusing, is why you are calling it first pole and then you add another name small-electric-pole-2?

Code: Select all

local pole = table.deepcopy(data.raw["electric-pole"]["small-electric-pole"])
small-electric-pole-2.name = "small-electric-pole-2"
small-electric-pole-2.minable = {mining_time = 0.1, result = "small-electric-pole"}
small-electric-pole-2.max_health = 50
small-electric-pole-2.maximum_wire_distance = 7.5
small-electric-pole-2.supply_area_distance = 2.5
data:extend{small-electric-pole-2}
isn't it in my opinion to call the data:extend{small-electric-pole-2}??
You're not calling it first pole, exactly. What you're doing is COPYING the first pole, then you're tweaking the parameters you need to tweak to make it the new pole you want, including renaming it to "small-electric-pole-2", then you send that new table up to be added as well. So the point is you're just saving yourself work by using all the existing properties in "small-electric-pole", then only changing what you need, then adding the changed profile to the game as a separate object.

User avatar
DaveMcW
Smart Inserter
Smart Inserter
Posts: 3700
Joined: Tue May 13, 2014 11:06 am
Contact:

Re: using util.merge how does it works?

Post by DaveMcW »

small-electric-pole-2 is not a legal name in Lua. It thinks you are subtracting "electric", "pole", and 2 from "small".

User avatar
Palaber1984
Inserter
Inserter
Posts: 32
Joined: Sat Feb 16, 2019 10:09 am
Contact:

Re: using util.merge how does it works?

Post by Palaber1984 »

DaveMcW wrote:
Sun Sep 15, 2019 4:51 pm
small-electric-pole-2 is not a legal name in Lua. It thinks you are subtracting "electric" and "pole" from "small".
and when i use smallelectricpole?

Adamo
Filter Inserter
Filter Inserter
Posts: 479
Joined: Sat May 24, 2014 7:00 am
Contact:

Re: using util.merge how does it works?

Post by Adamo »

Palaber1984 wrote:
Sun Sep 15, 2019 4:53 pm
DaveMcW wrote:
Sun Sep 15, 2019 4:51 pm
small-electric-pole-2 is not a legal name in Lua. It thinks you are subtracting "electric" and "pole" from "small".
and when i use smallelectricpole?
You need to differentiate between the variable names you're using when you set something equal to something else and the string names used in the game data. You can't use "-" in variable names, but you can use it in strings like you put in the name value in an entity table. So "small-electric-pole-2" is a valid string to use as a name in the table, but small-electric-pole-2 is not a valid variable name you can store a table inside. These do not need to match in any way. The variable name you use when you do

local smallelectricpole = data.raw["electric-pole"]["small-electric-pole"]

is the smallelectricpole part, and it could be anything, doesn't matter. As long as it's always the same throughout your code. The value of "name" inside that table is what the game will use as your entity name, and is independent of the variable name you may or may not have used to store the table at any point.

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

Re: using util.merge how does it works?

Post by eradicator »

"Names (also called identifiers) in Lua can be any string of letters, digits, and underscores, not beginning with a digit and not being a reserved word. Identifiers are used to name variables, table fields, and labels. "

From the official manual: http://www.lua.org/manual/5.3/manual.html#3.1
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
Palaber1984
Inserter
Inserter
Posts: 32
Joined: Sat Feb 16, 2019 10:09 am
Contact:

Re: using util.merge how does it works?

Post by Palaber1984 »

Adamo wrote:
Sun Sep 15, 2019 5:00 pm
Palaber1984 wrote:
Sun Sep 15, 2019 4:53 pm
DaveMcW wrote:
Sun Sep 15, 2019 4:51 pm
small-electric-pole-2 is not a legal name in Lua. It thinks you are subtracting "electric" and "pole" from "small".
and when i use smallelectricpole?
You need to differentiate between the variable names you're using when you set something equal to something else and the string names used in the game data. You can't use "-" in variable names, but you can use it in strings like you put in the name value in an entity table. So "small-electric-pole-2" is a valid string to use as a name in the table, but small-electric-pole-2 is not a valid variable name you can store a table inside. These do not need to match in any way. The variable name you use when you do

local smallelectricpole = data.raw["electric-pole"]["small-electric-pole"]

is the smallelectricpole part, and it could be anything, doesn't matter. As long as it's always the same throughout your code. The value of "name" inside that table is what the game will use as your entity name, and is independent of the variable name you may or may not have used to store the table at any point.
Ah understand. And if I have multiple entities like small-electric-Pole-1, small-electric-Pole-2, small-electric-Pole-3,
Then have to use every time a new variable or, can I use the same for all?

Adamo
Filter Inserter
Filter Inserter
Posts: 479
Joined: Sat May 24, 2014 7:00 am
Contact:

Re: using util.merge how does it works?

Post by Adamo »

Palaber1984 wrote:
Sun Sep 15, 2019 5:18 pm
Ah understand. And if I have multiple entities like small-electric-Pole-1, small-electric-Pole-2, small-electric-Pole-3,
Then have to use every time a new variable or, can I use the same for all?
You only need the variable if you're passing the data around, essentially. THe game picks up the data when you take a table put it into the data:extend() function. So, you could use the SAME variable if you did something like:

Code: Select all

local entity = util.deepcopy(data.raw["electric-pole]["small-electric-pole"])
entity.name = "small-electric-pole-2
... all other changes ...

data:extend({entity})
-- Note at this point "small-electric-pole-2" is now in the game data. Now you could reuse the existing variable if you want to, but it doesn't matter.

entity = util.deepcopy(data.raw["electric-pole]["small-electric-pole"])
entity.name = "small-electric-pole-3"
... other changes ...
data:extend({entity})
But this is not necessarily a great method, because, for example, if you're not doing a deepcopy() after each data:extend, and depending on how data:extend() functions, you could end up changing the data you already put into the game using data:extend(). I explained this to illustrate how the variable name is independent of the game object name, but I want to be clear, you should probably avoid reusing variable names like this unless you know exactly what you're doing. Instead, just make a variable for each pole you want to make, then deepcopy() the existing pole into that variable, tweak the values, and run a data:extend() on the tweaked tables. Done.

User avatar
Palaber1984
Inserter
Inserter
Posts: 32
Joined: Sat Feb 16, 2019 10:09 am
Contact:

Re: using util.merge how does it works?

Post by Palaber1984 »

Adamo wrote:
Sun Sep 15, 2019 5:31 pm
Palaber1984 wrote:
Sun Sep 15, 2019 5:18 pm
Ah understand. And if I have multiple entities like small-electric-Pole-1, small-electric-Pole-2, small-electric-Pole-3,
Then have to use every time a new variable or, can I use the same for all?
You only need the variable if you're passing the data around, essentially. THe game picks up the data when you take a table put it into the data:extend() function. So, you could use the SAME variable if you did something like:

Code: Select all

local entity = util.deepcopy(data.raw["electric-pole]["small-electric-pole"])
entity.name = "small-electric-pole-2
... all other changes ...

data:extend({entity})
-- Note at this point "small-electric-pole-2" is now in the game data. Now you could reuse the existing variable if you want to, but it doesn't matter.

entity = util.deepcopy(data.raw["electric-pole]["small-electric-pole"])
entity.name = "small-electric-pole-3"
... other changes ...
data:extend({entity})
But this is not necessarily a great method, because, for example, if you're not doing a deepcopy() after each data:extend, and depending on how data:extend() functions, you could end up changing the data you already put into the game using data:extend(). I explained this to illustrate how the variable name is independent of the game object name, but I want to be clear, you should probably avoid reusing variable names like this unless you know exactly what you're doing. Instead, just make a variable for each pole you want to make, then deepcopy() the existing pole into that variable, tweak the values, and run a data:extend() on the tweaked tables. Done.
Ok thx. This helps me much. I will experiment a bit and will see which way is best for me.
I have another question

Post Reply

Return to “Modding help”