Attempt to index nil value

Place to get help with not working mods / modding interface.
Post Reply
Kill3rCat
Burner Inserter
Burner Inserter
Posts: 17
Joined: Wed Aug 10, 2016 12:28 am
Contact:

Attempt to index nil value

Post by Kill3rCat »

I'm rather new to Lua, though I have dabbled in Java and C#. I've been encountering the following problem, and have had no luck in fixing it:

Error while running event on_player_ammo_inventory_changed (ID 36)
__Placeholdername__/control.lua:29: attempt to index field '?' (a nil value)

Code in question:

Code: Select all

script.on_event(defines.events.on_player_ammo_inventory_changed, function(event) onChange(event) end)
global.ammorack = {}
function check ()
if unexpected_condition then error() end
if player.opened.name=="nil" then end
end
function onChange(event)
local player = game.players[event.player_index]
if pcall(check) then player.print ("no error") else
				local oldrifleammo = 0
				local oldriflehcammo = 0
				local oldpistolammo = 0
				local rifleammo = 0
				local riflehcammo = 0
				local pistolammo = 0
				player.print ("script running")
				if global.ammorack[1] and global.ammorack[1][1] then oldrifleammo = global.ammorack[1][1] end
				if global.ammorack[1] and global.ammorack[1][2] then oldriflehcammo = global.ammorack[1][2] end
				if global.ammorack[1] and global.ammorack[1][3] then oldpistolammo = global.ammorack[1][3] end
				if player.get_item_count("charged-rifle-energy-cell") == "nil" then rifleammo = 0 else rifleammo = rifleammo + player.get_item_count("charged-rifle-energy-cell") end
				if player.get_item_count("supercharged-rifle-energy-cell") == "nil" then rifleammo = rifleammo + 0 else rifleammo = rifleammo + player.get_item_count("supercharged-rifle-energy-cell") end
				if player.get_item_count("charged-rifle-high-capacity-cell") == "nil" then riflehcammo = 0 else riflehcammo = riflehcammo + player.get_item_count("charged-rifle-high-capacity-cell") end
				if player.get_item_count("supercharged-rifle-high-capacity-cell") == "nil" then riflehcammo = riflehcammo + 0 else riflehcammo = riflehcammo + player.get_item_count("supercharged-rifle-high-capacity-cell") end
				if player.get_item_count("charged-pistol-energy-cell") == "nil" then pistolammo = 0 else pistolammo = pistolammo + player.get_item_count("charged-pistol-energy-cell") end
				if player.get_item_count("supercharged-pistol-energy-cell") == "nil" then pistolammo = pistolammo + 0 else pistolammo = pistolammo + player.get_item_count("supercharged-pistol-energy-cell") end
				if rifleammo < oldrifleammo then player.insert{name="rifle-energy-cell", count=((oldrifleammo)-(rifleammo))} end
				if riflehcammo < oldriflehcammo then player.insert{name="pistol-energy-cell", count=((oldriflehcammo)-(riflehcammo))} end
				if pistolammo < oldpistolammo then player.insert{name="pistol-energy-cell", count=((oldpistolammo)-(pistolammo))} end
				global.ammorack[player][1] = rifleammo
				global.ammorack[player][2] = riflehcammo
				global.ammorack[player][3] = pistolammo
end
end
The purpose of the code: To make a magazine type (charged energy cells) create items (energy cells) in the player's inventory when depleted.

As you can see, the script is run when the player's ammo inventory is modified. I am trying to use a multidimensional array to store the magazine count at the end of the script so it can be compared to the new contents of the players' inventory when the script is next run. The comparison checks for a difference in magazine quantity, and makes up the difference with depleted energy cells.

Nexela
Smart Inserter
Smart Inserter
Posts: 1828
Joined: Wed May 25, 2016 11:09 am
Contact:

Re: Attempt to index nil value

Post by Nexela »

global.ammorack[player][1] = rifleammo


I might have missed it but I don't see anywhere in your code block where you created global.ammorrack[player]

global.ammorack.nil = {rifleammo}
won't work :)

Kill3rCat
Burner Inserter
Burner Inserter
Posts: 17
Joined: Wed Aug 10, 2016 12:28 am
Contact:

Re: Attempt to index nil value

Post by Kill3rCat »

Yes, I thought that might be the case. However, upon addding

global.ammorrack[player] = player

to the code (just above the threee lines entering into the array near the bottom), I get a different error;

on_player_ammo_inventory_changed (ID 36)
LuaPlayer doesn't contain key 1.
stack traceback:
__Placeholdername__/control.lua:30: in function 'onChange'
__Placeholdername__/control.lua:1: in function <__Placeholdername__/control.lua:1>

I have a feeling I might be doing this wrong... as I said in my OP, I am quite new to Lua.

Nexela
Smart Inserter
Smart Inserter
Posts: 1828
Joined: Wed May 25, 2016 11:09 am
Contact:

Re: Attempt to index nil value

Post by Nexela »

You are storing the "player" object which doesn't have ammo blah blah


You want to create a variable with the same index as player.index

if not global.players[event.player_index] then global.players[event.player_index] = {} end
global.players[event.player_index].ammocount = 0

then anytime you want your mods data for the player its called almost the same way

player = game.players[player_index] -- this is "the player"
playerdatafrommod = global.players[player_index].ammocount -- this is "the players data stored from my mod"
Last edited by Nexela on Tue Aug 16, 2016 1:46 pm, edited 1 time in total.

User avatar
Adil
Filter Inserter
Filter Inserter
Posts: 945
Joined: Fri Aug 15, 2014 8:36 pm
Contact:

Re: Attempt to index nil value

Post by Adil »

Firstly, you should realize, that error logs without context are not as usable. At least mark the line mentioned by message in the code snippet you bring on the forums.

Secondly,

Code: Select all

global.ammorrack[player] = player
does the following: it takes player, uses it as an index to a new field and puts it in the same field, basically:

Code: Select all

table[1]=1
And of course, player object doesn't have any [1] field in it, and being the handle to underlying C++ data, it doesn't support addition of the fields.
What you want is:

Code: Select all

global.ammorrack[player.index] = global.ammorrack[player.index] or  {}--ensure that there is a table, to which you'll be adding fields
global.ammorrack[player.index][1]=whatever_you_want
As it may be noted, index in global.ammorrack has been changed by as well. It is because `player` was a reference to object, normal lua wouldn't mind, but in factorio these references are not constant over save-loads and certain other happenings of the game, so you cannot use game object references as index and hope the code works.
I do mods. Modding wiki is friend, it teaches how to mod. Api docs is friend too...
I also update mods, some of them even work.
Recently I did a mod tutorial.

Kill3rCat
Burner Inserter
Burner Inserter
Posts: 17
Joined: Wed Aug 10, 2016 12:28 am
Contact:

Re: Attempt to index nil value

Post by Kill3rCat »

@Nexala:

"player = game.players[player_index] -- this is "the player"
playerdatafrommod = global.players[player_index] -- this is "the players data stored from my mod""

Yes, but the problem with that solution is that (I think) it could only store one piece of data per player index; I need it to store 3.

----------------------------------

@Adil: "Firstly, you should realize, that error logs without context are not as usable. At least mark the line mentioned by message in the code snippet you bring on the forums."

Yes, quite right. Although I think the error log was in context, but the line was not given, I apologise for my laziness. I am also aware of what

Code: Select all

global.ammorack[player] = player
would do. That was quite intentional. I presumed you'd need a piece of data to enter into the array cell in order to create it, and the player index seemed like a convenient thing to dump in there (though I could've just used 1). The first entry in the multidimensional array has to be the same as the player index, or else how am I to associate the various bits of data with the specific players to whom they are relevant?

The solution you gave however looks like it could work - I'll try that, thanks.

Kill3rCat
Burner Inserter
Burner Inserter
Posts: 17
Joined: Wed Aug 10, 2016 12:28 am
Contact:

Re: Attempt to index nil value

Post by Kill3rCat »

Well, that worked! Errors no more.

Thanks to you fine chaps who took the time to reply, you've probably saved me a fair few hours of feverish Googling and headscratching.

Kill3rCat
Burner Inserter
Burner Inserter
Posts: 17
Joined: Wed Aug 10, 2016 12:28 am
Contact:

Re: Attempt to index nil value

Post by Kill3rCat »

Hmmm... fresh problems. I hope it's fine to post in this thread, now that the problem in the OP is resolved? Try as I might, I cannot figure out the source of this issue.

For some reason, it would appear that the data isn't being saved. No errors this time, however.

Code: Select all

script.on_event(defines.events.on_player_ammo_inventory_changed, function(event) onChange(event) end)
function check ()
if unexpected_condition then error() end
if player.opened.name=="nil" then end
end
function onChange(event)
local player = game.players[event.player_index]
if pcall(check) then player.print ("no error") else
				if global.ammorack then else global.ammorack = {} end
				local oldrifleammo = 0
				local oldriflehcammo = 0
				local oldpistolammo = 0
				local rifleammo = 0
				local riflehcammo = 0
				local pistolammo = 0
				player.print ("script running")
				if global.ammorack[player] and global.ammorack[player][1] then oldrifleammo = global.ammorack[player][1] end
				if global.ammorack[player] and global.ammorack[player][2] then oldriflehcammo = global.ammorack[player][2] end
				if global.ammorack[player] and global.ammorack[player][3] then player.print (global.ammorack[player][3]) oldpistolammo = global.ammorack[player][3] end
				print("oldpistolammo")
				print(oldpistolammo)
				if player.get_item_count("charged-rifle-energy-cell") == "nil" then rifleammo = 0 else rifleammo = rifleammo + player.get_item_count("charged-rifle-energy-cell") end
				if player.get_item_count("supercharged-rifle-energy-cell") == "nil" then rifleammo = rifleammo + 0 else rifleammo = rifleammo + player.get_item_count("supercharged-rifle-energy-cell") end
				if player.get_item_count("charged-rifle-high-capacity-cell") == "nil" then riflehcammo = 0 else riflehcammo = riflehcammo + player.get_item_count("charged-rifle-high-capacity-cell") end
				if player.get_item_count("supercharged-rifle-high-capacity-cell") == "nil" then riflehcammo = riflehcammo + 0 else riflehcammo = riflehcammo + player.get_item_count("supercharged-rifle-high-capacity-cell") end
				if player.get_item_count("charged-pistol-energy-cell") == "nil" then pistolammo = 0 else pistolammo = pistolammo + player.get_item_count("charged-pistol-energy-cell") end
				if player.get_item_count("supercharged-pistol-energy-cell") == "nil" then pistolammo = pistolammo + 0 else pistolammo = pistolammo + player.get_item_count("supercharged-pistol-energy-cell") end
				player.print("rifleammo")
				player.print(rifleammo)
				player.print("oldrifleammo")
				player.print(oldrifleammo)
				player.print("riflehcammo")
				player.print(riflehcammo) 
				player.print("oldriflehcammo")
				player.print(oldriflehcammo)
				player.print("pistolammo")
				player.print(pistolammo)
				player.print("oldpistolammo")
				player.print(oldpistolammo)
				if rifleammo < oldrifleammo then player.insert{name="rifle-energy-cell", count=((oldrifleammo)-(rifleammo))} end
				if riflehcammo < oldriflehcammo then player.insert{name="pistol-energy-cell", count=((oldriflehcammo)-(riflehcammo))} end
				if pistolammo < oldpistolammo then player.insert{name="pistol-energy-cell", count=((oldpistolammo)-(pistolammo))} end
				global.ammorack[player] = global.ammorack[player] or  {}
				global.ammorack[player][1] = rifleammo
				global.ammorack[player][2] = riflehcammo
				global.ammorack[player][3] = pistolammo
				player.print("newpistolammo")
				player.print(pistolammo)
				player.print(global.ammorack[player][3])
end
end
The output in the chat from the prints looks like

Image

What should be seen under 'oldpistolammo' is '0', '3', '2' to match pistolammo's '3', '2', '1'. Instead, nothing is seen for 'oldpistolammo'. I've gone over it several times now, and I cannot see why that might happen. Looking at the code, anybody have any idea why? (The 0's you see relate to the variable 'rifleammo'. For some reason, no other values for magazine types show up - may be related).

PS: Sorry for the huge image... I tried to put it in a spoiler, but it looks like this forum doesn't do collapsible spoilers.

User avatar
Adil
Filter Inserter
Filter Inserter
Posts: 945
Joined: Fri Aug 15, 2014 8:36 pm
Contact:

Re: Attempt to index nil value

Post by Adil »

The forum does collapsible spoilers.
Then, unrelated to problem in question is

Code: Select all

if player.opened.name=="nil" then end
this error. player.opened is nil when nothing is opened, you cannot ask the nil about its fields, and nil is a special name in lua, not a quoted string.

Secondly, you are mixing `print` and `player.print` methods, the former outputs text in the terminal if it exists and into log file, the latter is horrible debug logger due to 'optimizations' built in it, which cause it to munch some data silently.

Thirdly, player.print outputs errors if incorrect data is fed to it. You have a pcall there, which discards errors quietly. I'd bet it's either that one or one of the player.print quirks that causes the output provided.

That's however not to say that the code you've produced is correct :P
  • Firstly, I'd say you should get it all working with a single type of your ammo at first not all of them together.
    Secondly, you ask us to perform a debug of what is likely to be erroneous data flow in your code, but only provide a tiny snippet of code. It would be more productive to upload the whole mod and maybe a save with bug.
    Thirdly, people usually write `if`s in multiple lines, that makes them easier to read, unless they're indeed short. The easiness of reading is something you should care about if you expect someone else to analyze your code.
I'll also reiterate this passage from my previous post as it seem to have missed the spot:
Adil wrote:As it may be noted, index in global.ammorrack has been changed by as well. It is because `player` was a reference to object, normal lua wouldn't mind, but in factorio these references are not constant over save-loads and certain other happenings of the game, so you cannot use game object references as index and hope the code works.
If anything I'd bet on this being another reason of happening of what is happening.

edit: I've also believe this reference deserves mention in here.
I do mods. Modding wiki is friend, it teaches how to mod. Api docs is friend too...
I also update mods, some of them even work.
Recently I did a mod tutorial.

Nexela
Smart Inserter
Smart Inserter
Posts: 1828
Joined: Wed May 25, 2016 11:09 am
Contact:

Re: Attempt to index nil value

Post by Nexela »

2 problems 1, You are still trying to store everything into "the player" 2, the reason is is not erroring is because global.ammorack[player] is nil causing the IF check to fail (at least from your code sample)
if global.ammorack[player] and global.ammorack[player][1] then oldrifleammo = global.ammorack[player][1] end
change to
if global.ammorack[player.index] and global.ammorack[player.index][1] then oldrifleammo = global.ammorack[player.index][1] end




Also while this is valid it just looks horrible
if global.ammorack then else global.ammorack = {} end

either do
if not global.ammorack and global.ammorack[player.index] then global.ammorack[player.index] = {} end
or
global.ammorrack[player.index] = global.ammorrack[player.index] or {}

Nexela
Smart Inserter
Smart Inserter
Posts: 1828
Joined: Wed May 25, 2016 11:09 am
Contact:

Re: Attempt to index nil value

Post by Nexela »

Also if you don't have a specific reason for it your gonna beat your head off a table trying to remember which table is which.

global.ammorack[player.index][1] = a specific type of ammo so why not name the table
global.ammorack[player.index].rifleammo = rifleamo

Kill3rCat
Burner Inserter
Burner Inserter
Posts: 17
Joined: Wed Aug 10, 2016 12:28 am
Contact:

Re: Attempt to index nil value

Post by Kill3rCat »

@Nexela:
Player is the index, in my code. I think so, anyway

Code: Select all

local player = game.players[event.player_index]
Therefore,

Code: Select all

player.index
would be the same as

Code: Select all

event.player_index.index
would it not?

Also, I didn't know arrays could have fields, I thought they could only have cell entries.

@Adil:
I see, thank you.
"As it may be noted, index in global.ammorrack has been changed by as well. It is because `player` was a reference to object, normal lua wouldn't mind, but in factorio these references are not constant over save-loads and certain other happenings of the game, so you cannot use game object references as index and hope the code works."

What would you propose I use as an alternative, then? Continuity between save loads is important, but even more so is keeping individual players' ammocounts separate.

Oh, and by the way 'that snippet of code' is the entire control.lua file. Everything else is just prototypes and the occasional .png.

Thanks for the tip about player.opened, but it doesn't really matter since I am currently relying on it throwing an error. It's bad practice, I know, but once the code is actually functional, I'll go back and beautify.

Nexela
Smart Inserter
Smart Inserter
Posts: 1828
Joined: Wed May 25, 2016 11:09 am
Contact:

Re: Attempt to index nil value

Post by Nexela »

Kill3rCat wrote:@Nexela:
Player is the index, in my code. I think so, anyway

Code: Select all

local player = game.players[event.player_index]
Yes, This is the player "object"
Therefore,

Code: Select all

player.index
would be the same as

Code: Select all

event.player_index.index
would it not?
No. player.index IS the same as event.player_index (if you already have the player object)
My example was just an example using your player since you already had it

as has been explained earlier

what you are still doing is

Code: Select all

global.ammorrack[player] -- This stores your PLAYER inside the global
global.ammorack[player][1] -- Will not do what you want, I don't even know what it is but it is not what you think it is
global.ammorack[player].rifleammo -- this will error because the PLAYER object doesn't contain a rifleammo key.
what you need to do

Code: Select all

global.ammorrack[player.index] ={} --This stores an empty table with the KEY of [player.index] a NUMBER
global.ammorrack[player.index].player = player  -- This stores your player "object" in your global but we really don't need to do this so don't do this.
Whenever we need your mods global information we retrieve it almost the same way we retrieve the player from "game"

Code: Select all

player = game.players[event.player_index] -- retrieve the VALUE of the KEY (event.player_index) in game.players, this value is the PLAYER
playersammorack = global.ammorack[player.index] -- retrieves the VALUE of the index KEY(player.index which IS the same as event.player_index) in global.ammorack, this value is your saved global data
Now lets assume we have ammorack all fleshed out and we want to do something with EVERY player from ammorack

Code: Select all

for myplayersindex, data in pairs(global.ammorack)  do -- this is a rough example only
   game.players[myplayersindex].print("Hello!")
end
so in the same vein since we access them both almost the same we if we want to loop through ALL the players and update their ammo counts we do something like this

Code: Select all

for Index, player in pairs(game.players) do
  player.print(global.ammorack[index].rifleamo)
end

Nexela
Smart Inserter
Smart Inserter
Posts: 1828
Joined: Wed May 25, 2016 11:09 am
Contact:

Re: Attempt to index nil value

Post by Nexela »

This might work for what you want. no promises, untested

Code: Select all

local function onChange(event) -- Ammo has changed in some way, lets see if we need to update it
    local player = game.players[event.player_index]
    if player.opened or player.opened_self then return end -- Player has an inventory open so lets leave the function since they are probably moving ammo by hand

    if not global.ammorack then global.ammorack = {} end -- Create ammorack if it doesn't exist
    if not global.ammorack[player.index] then global.ammorack[player.index] = {} end -- create a player index for ammorack if it doesn't exist

    local ar = global.ammorack[player.index] -- lets shorten this up so we are working directly with the players global ammorack (ar)
    
    local inventory = player.get_inventory(defines.inventory.player_ammo)  -- limit our check to the ammo inventory
    
    ar.oldrifleammo = ar.oldrifleammo or 0 -- if old count is nil then initialize it with 0
       
    player.print("oldrifleammo =" .. tostring(ar.oldrifleammo))  -- convert oldrifle ammo to a string so we can concat it into our print message
    

    --This could probably be handled better by restructuring for a for -- pairs loop
    
        ar.rifleammo = (ar.rifleammo or 0) + (inventory.get_item_count("charged-rifle-energy-cell") or 0) -- use some magic math to store the new number


    if ar.rifleammo == (ar.oldrifleammo - 1) then --if the inventory changed by -1 then chances are we are shooting and not taking out stacks of bullets to get free depleted shells
        if player.cursor_stack and player.cursor_stack.valid_for_read and player.cursor_stack.name ~= "charged-rifle-energy-cell" then --check cursor for bullets!
            player.print("depleted magazine")
            player.insert{name="rifle-energy-cell", count=1} -- insert the depleted cell.
        end
    end
    ar.oldrifleammo = ar.rifleammo -- update the old rifle ammo count
end
script.on_event(defines.events.on_player_ammo_inventory_changed, function(event) onChange(event) end)

Kill3rCat
Burner Inserter
Burner Inserter
Posts: 17
Joined: Wed Aug 10, 2016 12:28 am
Contact:

Re: Attempt to index nil value

Post by Kill3rCat »

@Nexela:
Most enlightening, thank you. I don't like to copy other people's code, on a matter of pride (whatever I have left, after embarassing myself totally in this thread :lol: ).

I will however look at what you've written, and incorporate parts of it into my existing code. 'Inspiration', shall we say? Again, thanks for your help - and your patience.

Post Reply

Return to “Modding help”