Hi community,
After 14min of intensive tests. Doesn't seem to be triggered by a specific event...I’m facing this error :
invalid key to ‘next’
stack tracebkack:
The obvious answer would be "you are iterating through a table and modifying it at the same time" but please read more.
I think I have a good understanding of what this means and how to avoid it usually.
However this time I can’t solve it and it happens very randomly ( = I couldn’t reproduce it).
If my code was wrong, wouldn't my app crash right from the start? (If things were that easy...).
I have spent two days making web searches before asking here.
I nearly spent all night on this.
What I have tried
Based on
the Lua Manual and
the following resource, this typically happens when :
The behavior of next is undefined if, during the traversal, you assign any value to a non-existent field in the table. You may however modify existing fields. In particular, you may clear existing fields.
I have tried to avoid those situations and the app
seems to run fine for an undetermined time, under normal conditions (= reasonable amount of objects on screen, simple collisions that happen in a linear way).
Sum-up
Let's say I’m creating a new instance of a “Bug” class every second.
“myBug[1]” is an instance of the Bug class that contains a sprite (myBug[1].sprite) and a box2d body (myBug[1].body).
OnCollision with myPlayer, bugs are destroyed.
The issue
The issue is very hard to reproduce, but “sometimes” an error is raised :
invalid key to ‘next’
stack tracebkack:
Note
This seem to happen when I’m doing stress tests (in that case spawn 3 objects every 400ms), when a lot of collisions are happening at the same time.
I couldn’t reproduce the issue when only a few bugs are on stage.
* Note : Images from the PlanetCute game prototyping assets pack at LostGarden.comIf somebody wants to help me, I believe it’s better if I give as many details as possible
More details
Creating new Bug instances
Every xxx ms, a new Bug instance is created.
I keep a list of all bodies (BODIES_LIST) in order to update their position each frame later.
Bug.lua-- ---------- Create a new Bug instance
--
function Bug:init()
(...)
-- Add to the list of bodies to update each frame
BODIES_LIST[self.body] = self --### Modifying BODIES_LIST
end |
MyScene:onEnterFrame()
-> Update bodies’ position
MyScene.lua-- ---------- Update bodies' position
--
function MyScene:onEnterFrame()
world:step(1/60, 8, 3)
for body, bug in pairs(BODIES_LIST) do --### Iterating through BODIES_LIST
bug:setPosition(body:getPosition())
end
end |
onCollision()
MyScene.lualocal function onBeginContact(event)
-- Test collision
-- If true, get myBug's value and dispatchEvent
myBug:dispatchEvent("collision")
end
-- MyScene is listening to collisions
world:addEventListener(Event.BEGIN_CONTACT, onBeginContact) |
Bug.lua-- Listen to "collision" and "destroy" events
function Bug:init()
self:addEventListener("collision", self.onCollision, self)
self:addEventListener("destroy", self.onDestroy, self)
end
function Bug:onCollision()
-- if collides with background, do nothing
(...)
-- if collides with player, dispatch "destroy" Event
self:dispatchEvent("destroy")
end |
Destroy and free()
Bug.luafunction Bug:onDestroy()
-- Stop
self.body:setLinearVelocity(0, 0)
-- Free
BODIES_LIST[self.body] = nil --### Modifying BODIES_LIST
-- Clean body
world:destroyBody(self.body)
self.body = nil
-- Clean sprite
self.sprite:removeFromParent()
self.sprite = nil
-- Clean self
self:removeFromParent()
self = nil
end |
Issue
MyScene.luaEven when I’m printing a lot of things in the console, I can’t seem to find where the issue is coming from.
As far as I can see, the only place where I’m iterating through my table is in MyScene:onEnterFrame() :
-- ---------- Update bodies' position
--
function MyScene:onEnterFrame()
world:step(1/60, 8, 3)
for body, bug in pairs(BODIES_LIST) do
bug(body:getPosition())
end
end |
But what I don’t understand is why would the app run for several minutes without raising an error and then suddenly it happens (even without user input).
If my code was totally wrong, an error would be raised right from when the app is launched, am I wrong?
Can you help?
I have been stuck with this issue for 2 days and I can’t stand it anymore.
- Would you have any suggestion so that I could make some progress?
- Would you make some progress anyway and release your game, knowing that this “should” not happen with a lot fewer objects on screen, and update later when you find out where this issue is coming from?
Thanks to anyone that can contribute.
I’m ready to send a bunch of
virtual cookies to thank you!
@ar2rsawseen interested?
Mells
Comments
So...bugs have bodies as well as other attributes, and you have a table of bugs indexed by bodies. You iterate over this and call a function on the bugs. Does this function destroy or modify the bodies in some cases; ie change the table index in the course of the loop...?
I'm wondering if you could use a table of bugs with a simple numerical index instead. Bugs and bodies are already associated, so you don't need a table that associates them again.
Likes: Mells
Likes: Mells
@BJG, @ar2rsawseen Sorry I made a mistake while I was trying to simplify my code.
The position change will make some bodies to collide, an event is dispatched to the parent object (instance of Bug). Depending on the type of collision, the Bug instance dispatches a detroy event.
When destroyed, the BODIES_LIST list is modified. I see, that's the first time I use box2D, so I looked at the example that is shipped with Gideros (Collision detection) and thought it was the best way to do it (see below).
A good presentation makes it easier.
I will try iterating through an indexed array.
The problem is that sometimes I have to play for more than 20 minutes to trigger the error, which is very frustrating (it's always triggered the moment that I think my problem is solved).
thank you again I will post an update later.
This night will be long, again.
thank you for your help, and if anybody comes up with a suggestion I can not tell how much I'm interested to hear about it.
Likes: gorkem, Mells
Likes: Mells
If you've now changed the code to avoid "pairs", maybe you could post up the new version...
Likes: Mells
@phongtt thank you for suggesting something
In that case I think the error would be raised right when the first instances are created. But I am not sure.
@BJG
Those are simplified versions of my current files.
I have added the fact that I am destroying objects after the end of a tween animation, I didn't think it was relevant but it seems that I was wrong.
Bug.lua
onEnterFrame()
MyScene.luaonCollision()
Destroy & free()
Bug.luaI really appreciate the help, thank you all.
https://play.google.com/store/apps/developer?id=My+name+is+Originality
@fxone )
Likes: fxone
@atilim
Ah? Do you mean that there is a (small) possibility that the error is not coming from my own code?
Likes: fxone
Likes: Mells
If that's the case, it will *really* be a weight lifted off my shoulder.
I am awaiting for the fix Does it mean you have an idea how to fix it now?
thank you and I'm sorry about wasting your time for all night :-\"
Looking forward to a fix.
best regards
It always happens on scene changes (although scene manager doesn't use tweens). However, that is the time I force a GC. I wonder if that is something to do with it.
Any news about the fix?
best regards
I have been waiting patiently for a fix but yes, it has been a showstopper for me since I reported the issue.
Still wondering why games that have been released didn't experience the issue, as my scene is pretty simple and this is happening very often when doing stress tests.
best regards
I will try as soon as I get the chance (currently away from home for a few days so can not guarantee it happens soon). Thank you for the alternative.
Hopefully @atilim will have come up with a magic trick before I get the chance to test your code.
@bowerandy, @Mells One question: Are you calling GTween.stopAll() function anywhere? If so, that may be the problem and fix is easy.
best regards
@bowerandy I'll use your way to fix it but it's possible to make without creating a new table in each frame.
First create a local table for that purpose (You can put it at gtween.lua:66):
And change the loop at gtween.lua:80 with:
Let me organize the code and push it to github.
Also you both describe @bowerandy as a temporary solution, what are its limitations?
@atilim : I don't get the technical, so my question is : is it safe to use it in production and does it solve the issue in all cases?
I haven't noticed any slow down with that fix (the table.copy version).
best regards
Sorry for late reply. I've pushed the fix on github: https://github.com/gideros/GTween
The only unimportant downside of @bowerandy's approach is creating a table with each frame. I've just improved his solution a little bit.
best
best regards