I'm getting a weird error with __newindex metamethod

So I created my metatable to test with:

local statKey = "Template"
local statValues = {Coins = 0, Kills = 0}

local metaStatTable = {
	__newindex = function(a, p)
		print(a, p)
		return p
	end,
}
local statTable = {}
setmetatable(statTable, metaStatTable)

And then I added a function to address the table, adding the stats to it:

local function customStats(player)
	if (statTable ~= nil) then
		local tableindexkey = statKey .. "_" .. player.UserId
		statTable[tableindexkey] = {}
		statTable[tableindexkey].data = statValues
		return tableindexkey
	end
end

And I keep getting this weird error on the line where I assign a data variable to my players stats: ServerScriptService.Sparkz.Main:100: attempt to index nil with ā€˜dataā€™

Not only that but the __newindex function runs as well and printsā€¦ So Iā€™m at a loss here I just donā€™t know what Iā€™m doing wrong at this point. Does anyone know what Iā€™m doing wrong or what Iā€™m missing?

You must declare that line like this: statTable[tableindexkey]["data"] = statValues

Youā€™re literally trying to index something that doesnā€™t exist. statTable[tableindexkey] is a table, and it doesnā€™t have any keys yet.

EDIT:

I may have misunderstood what youā€™re trying to do, and what I said doesnā€™t make any sense, ignore it. Instead of declaring it that way, why not use a table literal? Itā€™s faster, as it automatically allocates the memory for the table when itā€™s declared, and this time it may not error!

statTable[tableindexkey] = { ["data"] = statValues }

@2jammers Iā€™ll try that thanks for the help @Den_vers Idk what you mean I set the tableindexkey to a table. Then i assigned it another table as a variable named data.

This doesnā€™t do anything. Itā€™s literally just bogus, and it wonā€™t fix your problem.

1 Like

Thatā€™s how you insert keys into dictionaries? Thereā€™s no reason to be rude here.

Right here you override the behavior of the creation of new table keys. So when you try to add a key (in this case, tableindexkey) it does nothing.
(Also not sure what return p is for, __newindex doesnā€™t have anything special happen to return values AFAIK)

If you want it to also add the keys in, you need to add a rawset call.

__newindex = function(t, key, val)
	print(key, val)
	rawset(t, key, val)
end,
2 Likes

Iā€™m simply stating a fact, not being rude.

Also, itā€™s no different from saying a.b = c. Itā€™s inserting a key into a dictionary. Actually, preferably you would only use that method if youā€™re inserting anything into a table. a.b = c should be used for only strings.

Yeah @2jammers that didnā€™t fix my error still happened. I think maybe he might not understand that thereā€™s different ways to index stuff in a table.

@Den_vers This kind of solved my issue but another errors popping up now in an area where Iā€™m trying to add the coins to the stats.

Error: ServerScriptService.Sparkz.Main:147: ServerScriptService.Sparkz.Main:128: attempt to index nil with ā€˜dataā€™

Hereā€™s the part where I do that:

local indexkey = customStats(player)
	while true do
		task.wait(1)
		statTable[indexkey]["data"].Coins += 1
		print(statTable[indexkey]["data"].Coins)
	end

Question, if this is true, could you still use my table literal method to manually set the table, or no? Probably not, right?

I think @majdTRM is right. You should mark him as solution, my system is only working now because youā€™re not directly indexing the table, only assigning it a value, now. But when you try to index it, it errors because it doesnā€™t exist.

Doesnā€™t make much of a difference. Your method is probably faster, because it adds it in the table creation process, and it doesnā€™t need to look up the keys in the table.

The problem was that overriding __newindex made it so that adding keys did nothing.

1 Like

@majdTRM This helped I actually didnā€™t do any formatting wrong. Do you know why it only runs once though? Isnā€™t newIndex supposed to run every time something is indexed? Or am I supposed to use a different metamethod for that?

__newindex ā€œfiresā€ whenever a new index is inserted into a table. The only reason my system didnā€™t error is because I created the table and inserted a value into that same table at the same time. Therefore not needing to index nil.

1 Like

Well Iā€™m using __newindex because I tried __index but it doesnā€™t really fire when Iā€™m adding an index or just changing one. Iā€™m looking for a metamethod that will fire whenever I need to update a value in the table. The reason Iā€™m trying to find something like this is so I can update my leaderboard stats at ease for my framework Iā€™m working on.

__index only fires when you try to index something thatā€™s nil.

Ahh that is a little bit more complicated. See, __newindex only runs when creating new keys, not setting existing ones.

But what you can do is trick it by creating an intermidiary table. Instead of setting the values onto the same table, set it onto another one and add an __index method to return the values correctly.

Here is what I mean:

local statInternal = {}

local metaStatTable = {
	__newindex = function(t, k, v)
		print(k, v) -- Do other fancy stuff here
		statInternal[k] = v
	end,
	__index = function(t, k)
		return statInternal[k]
	end,
}
local statTable = {}
setmetatable(statTable, metaStatTable)

But Iā€™m not sure about setting values to nil (EDIT: Thankfully it does)

1 Like

Could you explain what youā€™re actually trying to do? What your main goal is?

Iā€™d give that a try but honestly I thought itā€™d be simpler than that. Iā€™m still a beginner at metatables so I think Iā€™ll just use the __call metamethod on the table which will fire refreshing the leaderboard whenever a values changed. And I can just check the name of the parameter in the __call metamethod to be able to determine if the stat is inside of the players leaderstats.

1 Like

Unfortunately for you, Luaā€™s metatables are very rudimentary. Which means that yes, the best solution to the problem has to get a little dirty and involved. Alas, there is no __indexset metamethod` (although, if you find it of value, you could try creating an RFC @ Roblox/Luau GitHub. But I donā€™t see a problem with the common solution to the problem. I donā€™t see it done any different way elsewhere.

Not sure how this will help but if you seem to know what you are doing, go ahead.