Hopefully posting another topic so soon isn't too annoying, as I have another question...
It seems that when a physics body is deleted, either when explicitly removing it with shape:destroy() and body:destroy() or when the garbage collector removes it, if that body is colliding with something it causes LOVE to freeze and crash. Does anyone have a method for removing a physics object/body that will not potentially cause this crash? There has to be some method I can call on a shape or body that will "prepare" it for deletion so it stops sending collision information or something...
Could this problem potentially be related to the collision callback functions, which I am using?
Edit: Well it turns out that the crash IS due to the collision callback functions, in case anyone was wondering. I think it's bad to mess with box2d within the collision callbacks. I need to find a way to get collision info but only deal with it later...
Crash on deleting physics bodies that are colliding?
Forum rules
Before you make a thread asking for help, read this.
Before you make a thread asking for help, read this.
Re: Crash on deleting physics bodies that are colliding?
Yes, the callbacks are not a safe place to change the physics world. The Box2D library assumes certain things about their behavior, and many of these things are spelled out in the Box2D manual proper. You can also read the LÖVE source for how it deals with contact objects internally, if you're familiar with C++.
Anyway, if you can't do something directly, the usual solution in programming is to defer it to when it is safe to do. Just promise to do it later and put a little note on the object so you'll know to do it.
That is to say, you should have some flags in your physics objects to mark actions you will do to them after the physics update is finished, but before the next one starts. Sometimes you also don't need to destroy your bodies, but can--for example--just let them fall off-screen (or move them yourself there!) and hit the edge of the world, where they will be frozen. This can only be used if you are not constantly creating new ones over a long period of time, though, because that would lead to memory filling up with unused stuff.
LATER ADDITION: Of course, the latter thing only makes sense if you stop drawing the removed object in subsequent frames and set it not to collide with anything in the world. This is kind of troublesome, so there is probably no use in trying it at all.
Anyway, if you can't do something directly, the usual solution in programming is to defer it to when it is safe to do. Just promise to do it later and put a little note on the object so you'll know to do it.
That is to say, you should have some flags in your physics objects to mark actions you will do to them after the physics update is finished, but before the next one starts. Sometimes you also don't need to destroy your bodies, but can--for example--just let them fall off-screen (or move them yourself there!) and hit the edge of the world, where they will be frozen. This can only be used if you are not constantly creating new ones over a long period of time, though, because that would lead to memory filling up with unused stuff.
LATER ADDITION: Of course, the latter thing only makes sense if you stop drawing the removed object in subsequent frames and set it not to collide with anything in the world. This is kind of troublesome, so there is probably no use in trying it at all.
Re: Crash on deleting physics bodies that are colliding?
Thank you very much for the help. Sadly though it looks like there may be a bug of some sort...
After doing a little more testing I was able to determine that calling the "world:setCallbacks()" method in love.load()--even with the callback functions set up properly and completely EMPTY--causes LOVE to lock up and crash when colliding objects are removed. Just as before, explicitly destroying them or letting the garbage collector handle them will both cause a crash if they are colliding with another body when they are removed. This ONLY happens if I have done the setCallbacks method, however. Simply commenting that line out makes the crashes go away.
I did look into just letting the bodies leave the world, but for my purposes it would be much better if I could directly create and destroy physics objects. As I mentioned above, the crash happens even when the callback functions are empty, which is why I'm considering this to be a bug in LOVE rather than an error that I may have made...
After doing a little more testing I was able to determine that calling the "world:setCallbacks()" method in love.load()--even with the callback functions set up properly and completely EMPTY--causes LOVE to lock up and crash when colliding objects are removed. Just as before, explicitly destroying them or letting the garbage collector handle them will both cause a crash if they are colliding with another body when they are removed. This ONLY happens if I have done the setCallbacks method, however. Simply commenting that line out makes the crashes go away.
I did look into just letting the bodies leave the world, but for my purposes it would be much better if I could directly create and destroy physics objects. As I mentioned above, the crash happens even when the callback functions are empty, which is why I'm considering this to be a bug in LOVE rather than an error that I may have made...
Re: Crash on deleting physics bodies that are colliding?
If you can post a minimal program (it's OK to remove the graphics for example) that crashes like your program, we can look at it and see if we can rewrite it safely, or there really is an issue worth putting up on the tracker. I still think the removals can be done safely in a variety of ways, but I cannot test and post any code of my own right now.
Funnily enough, most of the LÖVE programs I've written so far have used a constant amount of physics bodies in them. And not for any particular reason, but just because they work well that way
Funnily enough, most of the LÖVE programs I've written so far have used a constant amount of physics bodies in them. And not for any particular reason, but just because they work well that way
Re: Crash on deleting physics bodies that are colliding?
Alright, here's a nice complete LOVE main.lua program that I wrote to illustrate the problem I'm having. As you can see, the line in love.load() buffered by the comment saying "COMMENT THIS LINE OUT AND THE PROGRAM WILL NOT CRASH" is what is causing problems. I'd like to encourage everyone who sees this to run this code and see if they have the same problem.
To replicate the problem, simply left click to make a bunch of random rectangles. Once the rectangles are resting on each other, right click to remove the oldest rectangle. If the aforementioned line is not commented then LOVE will crash, even though the callback functions are completely empty. Comment the line out and everything will work, smooth as butter.
Thanks for all your help! Hopefully we can work this out and I can have some callback functions in my LOVE games.
To replicate the problem, simply left click to make a bunch of random rectangles. Once the rectangles are resting on each other, right click to remove the oldest rectangle. If the aforementioned line is not commented then LOVE will crash, even though the callback functions are completely empty. Comment the line out and everything will work, smooth as butter.
Code: Select all
function love.load()
math.randomseed(os.time())
math.random()
love.graphics.setBackgroundColor(50, 80, 120, 255)
local width = love.graphics.getWidth()
local height = love.graphics.getHeight()
--Create a world in Box2d:
TheWorld = love.physics.newWorld(10000, 10000)
TheWorld:setGravity(0, 700)
--COMMENT THIS LINE OUT AND THE PROGRAM WILL NOT CRASH!
TheWorld:setCallbacks(Coll_Add, Coll_Persist, Coll_Rem, Coll_Result)
-------------------------------------------------------
--Create a floor box for things to rest on:
TheWorldBody = love.physics.newBody(TheWorld, width/2, height/2, 0, 0)
TheWorldShape = love.physics.newRectangleShape(TheWorldBody, 0, width/3, width*2, 100, 0)
--Create the table that will hold the rects:
RectTable = {}
end
function love.update(dt)
TheWorld:update(dt)
end
function love.draw()
DrawRects()
DrawFloor()
DrawBodyCount(25, 25, 0)
end
-------------EMPTY CALLBACK FUNCTIONS--------
function Coll_Add(a, b, coll)
--Empty.
end
function Coll_Persist(a, b, coll)
--Empty.
end
function Coll_Rem(a, b, coll)
--Empty.
end
function Coll_Result(a, b, coll)
--Empty.
end
---------------------------------------------
function love.mousepressed(x, y, button)
if(button == "l") then
AddRect(x, y, math.random(5, 50), math.random(5, 50), math.random(0, 2*math.pi))
elseif(button == "r") then
KillRect(1) --Removes the oldest existing rectangle.
end
end
function AddRect(x, y, w, h, rot) --Adds a new rectangle to RectTable.
local addTable = {}
addTable.body = love.physics.newBody(TheWorld, x, y, 1, 1)
addTable.shape = love.physics.newRectangleShape(addTable.body, 0, 0, w, h, rot)
addTable.body:setMassFromShapes()
table.insert(RectTable, addTable)
end
function KillRect(index) --Removes the rectangle at position 'index' in RecTable.
local rect = RectTable[index]
if(rect) then
rect.shape:destroy()
rect.body:destroy()
rect.shape = nil
rect.body = nil
table.remove(RectTable, index)
end
end
function DrawRects() --This draws the rectangles in the table 'RectTable'
for i=#RectTable, 1, -1 do
love.graphics.setColor(255, 255, 255, 255)
love.graphics.polygon("fill", RectTable[i].shape:getPoints()) --white inside.
love.graphics.setColor(0, 0, 0, 255)
love.graphics.polygon("line", RectTable[i].shape:getPoints()) --black outline.
end
end
function DrawBodyCount(x, y, rot) --Prints the current number of bodies as seen by Box2d.
love.graphics.setColor(255, 255, 255, 255)
local num = (TheWorld:getBodyCount() - 1) --I'm subracting one from the body count because Box2d counts the world as a body.
local t = "Bodies: " .. tostring(num)
love.graphics.print(t, x, y, rot, 1, 1)
end
function DrawFloor()
love.graphics.setColor(255, 255, 255, 255)
love.graphics.polygon("fill", TheWorldShape:getPoints()) --white inside.
love.graphics.setColor(0, 0, 0, 255)
love.graphics.polygon("line", TheWorldShape:getPoints()) --black outline.
end
- Attachments
-
- main.lua
- (2.96 KiB) Downloaded 198 times
- kikito
- Inner party member
- Posts: 3153
- Joined: Sat Oct 03, 2009 5:22 pm
- Location: Madrid, Spain
- Contact:
Re: Crash on deleting physics bodies that are colliding?
I can't give this a completete test right now, but I think I see one problem.
On the function that creates the rectangles:
The shape doesn't have any data attached to it. Try adding a line that says
You can replace the "1" with whatever you want, as long as it is not nil.
Does the error still happen after this?
On the function that creates the rectangles:
Code: Select all
function AddRect(x, y, w, h, rot) --Adds a new rectangle to RectTable.
local addTable = {}
addTable.body = love.physics.newBody(TheWorld, x, y, 1, 1)
addTable.shape = love.physics.newRectangleShape(addTable.body, 0, 0, w, h, rot)
addTable.body:setMassFromShapes()
table.insert(RectTable, addTable)
end
Code: Select all
addTable.shape:setData(1)
Does the error still happen after this?
When I write def I mean function.
Re: Crash on deleting physics bodies that are colliding?
I can confirm that it does crash. On my computer it happens when a rect is killed when it is in contact with something.
What I did was change the right mouse button to kill the last rect in the RectTable by making the call KillRect(#RectTable) in love.mousepressed. I then ran the program several times and found that as long as I deleted teh last rect to be created while it was still falling, the program did not segfault. However, when it touched the ground or another rectangle that touched the ground, it crashed. The reason would be either any contact or indirect contact with the ground; and I can't say which one it is yet.
I'd say this happens consistently. When I have had more time to test this, I will post again.
I can't say anything about Kikito's view yet. I will look at it too.
What I did was change the right mouse button to kill the last rect in the RectTable by making the call KillRect(#RectTable) in love.mousepressed. I then ran the program several times and found that as long as I deleted teh last rect to be created while it was still falling, the program did not segfault. However, when it touched the ground or another rectangle that touched the ground, it crashed. The reason would be either any contact or indirect contact with the ground; and I can't say which one it is yet.
I'd say this happens consistently. When I have had more time to test this, I will post again.
I can't say anything about Kikito's view yet. I will look at it too.
Re: Crash on deleting physics bodies that are colliding?
Wow, it did not take long to localize the problem. On my end replacing the second to last callback with nil makes the crashes go away. Please try to confirm this. To be clear, the line that sets the callbacks should be:
With this code in place it does not segfault. How about you?
ADDED LATER: I think the problem is with the Remove callback causing a crash when a shape still in contact with something is destroyed. Here is a workaround that might work. Please report your results.
The way this works is that when a shape is to be destroyed, the code sets it to not collide with nothing at all. Then after the next physics update is run, this shape is no longer in contact with anything, and can then be destroyed without triggering a segfault. And by it working I am really just saying that it did not seem to crash for me.
This seems like an issue that should be on tracker. EDIT: And it si now, because I added it.
Code: Select all
TheWorld:setCallbacks(Coll_Add, Coll_Persist, nil, Coll_Result)
ADDED LATER: I think the problem is with the Remove callback causing a crash when a shape still in contact with something is destroyed. Here is a workaround that might work. Please report your results.
The way this works is that when a shape is to be destroyed, the code sets it to not collide with nothing at all. Then after the next physics update is run, this shape is no longer in contact with anything, and can then be destroyed without triggering a segfault. And by it working I am really just saying that it did not seem to crash for me.
This seems like an issue that should be on tracker. EDIT: And it si now, because I added it.
Code: Select all
function love.load()
math.randomseed(os.time())
math.random()
love.graphics.setBackgroundColor(50, 80, 120, 255)
local width = love.graphics.getWidth()
local height = love.graphics.getHeight()
--Create a world in Box2d:
TheWorld = love.physics.newWorld(10000, 10000)
TheWorld:setGravity(0, 700)
--COMMENT THIS LINE OUT AND THE PROGRAM WILL NOT CRASH!
TheWorld:setCallbacks(Coll_Add, Coll_Persist, Coll_Rem, Coll_Result)
-------------------------------------------------------
--Create a floor box for things to rest on:
TheWorldBody = love.physics.newBody(TheWorld, width/2, height/2, 0, 0)
TheWorldShape = love.physics.newRectangleShape(TheWorldBody, 0, width/3, width*2, 100, 0)
--Create the table that will hold the rects:
RectTable = {}
end
function love.update(dt)
TheWorld:update(dt)
if GetRid then
GetRid.shape:destroy()
GetRid.body:destroy()
GetRid = nil
end
end
function love.draw()
DrawRects()
DrawFloor()
DrawBodyCount(25, 25, 0)
end
-------------EMPTY CALLBACK FUNCTIONS--------
function Coll_Add(a, b, coll)
--Empty.
end
function Coll_Persist(a, b, coll)
--Empty.
end
function Coll_Rem(a, b, coll)
--Empty.
end
function Coll_Result(a, b, coll)
--Empty.
end
---------------------------------------------
function love.mousepressed(x, y, button)
if(button == "l") then
AddRect(x, y, math.random(5, 50), math.random(5, 50), math.random(0, 2*math.pi))
elseif(button == "r") then
KillRect(1) --Removes the oldest existing rectangle.
end
end
function AddRect(x, y, w, h, rot) --Adds a new rectangle to RectTable.
local addTable = {}
addTable.body = love.physics.newBody(TheWorld, x, y, 1, 1)
addTable.shape = love.physics.newRectangleShape(addTable.body, 0, 0, w, h, rot)
addTable.body:setMassFromShapes()
table.insert(RectTable, addTable)
end
function KillRect(index) --Removes the rectangle at position 'index' in RecTable.
local rect = RectTable[index]
if(rect) then
rect.shape:setMask(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16)
GetRid = rect
table.remove(RectTable, index)
end
end
function DrawRects() --This draws the rectangles in the table 'RectTable'
for i=#RectTable, 1, -1 do
love.graphics.setColor(255, 255, 255, 255)
love.graphics.polygon("fill", RectTable[i].shape:getPoints()) --white inside.
love.graphics.setColor(0, 0, 0, 255)
love.graphics.polygon("line", RectTable[i].shape:getPoints()) --black outline.
end
end
function DrawBodyCount(x, y, rot) --Prints the current number of bodies as seen by Box2d.
love.graphics.setColor(255, 255, 255, 255)
local num = (TheWorld:getBodyCount() - 1) --I'm subracting one from the body count because Box2d counts the world as a body.
local t = "Bodies: " .. tostring(num)
love.graphics.print(t, x, y, rot, 1, 1)
end
function DrawFloor()
love.graphics.setColor(255, 255, 255, 255)
love.graphics.polygon("fill", TheWorldShape:getPoints()) --white inside.
love.graphics.setColor(0, 0, 0, 255)
love.graphics.polygon("line", TheWorldShape:getPoints()) --black outline.
end
Last edited by pekka on Mon Mar 08, 2010 4:03 pm, edited 1 time in total.
Re: Crash on deleting physics bodies that are colliding?
Sorry for making three posts in a row. I should have thought this over before posting every little thing I discovered. This is now my final posting on the topic, I hope. I will answer questions, though, if something I wrote is totally unclear. It usually is
I have just added this in the issue tracker, because I consider it a bug that should be fixed. It is up to the devs to assign it a priority, though.
Note that my previous message contains a workaround. I will add this workaround into the Wiki at a suitable time, so people can find it easily. I'm afraid the time is not today. I am fairly confident the workaround is good enough, and being a Wiki, it will be improved if it is not.
I have just added this in the issue tracker, because I consider it a bug that should be fixed. It is up to the devs to assign it a priority, though.
Note that my previous message contains a workaround. I will add this workaround into the Wiki at a suitable time, so people can find it easily. I'm afraid the time is not today. I am fairly confident the workaround is good enough, and being a Wiki, it will be improved if it is not.
Re: Crash on deleting physics bodies that are colliding?
Thank you so much for your time pekka! I figured there would at least be some way to make shapes not collide with anything before getting rid of them. The "setMask(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16)" line will most likely suffice, at least until this is properly fixed (if it ever is). If worst comes to worst then I'll probably be able to find a way to get around needing the remove() callback altogether...
Thanks again!
UPDATE:
Just in case anyone was wondering, I made a very simple patch to my original code to keep it from crashing. All it took was a slight rewrite of the "add", "kill", and "draw" functions so that each body has a "dead" variable. Bodies are now properly destroyed in the draw loop (should be in the think loop, but this small program doesn't need to use that) rather than some other time. I'm pretty sure that precautions must still be taken when messing with the callback functions so that the physics world isn't changed (i.e. bodies aren't moved or deleted) within the callbacks.
Replace the three functions from the original code I pasted with these new ones and everything will be peachy.
Thanks again!
UPDATE:
Just in case anyone was wondering, I made a very simple patch to my original code to keep it from crashing. All it took was a slight rewrite of the "add", "kill", and "draw" functions so that each body has a "dead" variable. Bodies are now properly destroyed in the draw loop (should be in the think loop, but this small program doesn't need to use that) rather than some other time. I'm pretty sure that precautions must still be taken when messing with the callback functions so that the physics world isn't changed (i.e. bodies aren't moved or deleted) within the callbacks.
Replace the three functions from the original code I pasted with these new ones and everything will be peachy.
Code: Select all
function AddRect(x, y, w, h, rot) --Adds a new rectangle to RectTable.
local addTable = {}
addTable.dead = false
addTable.body = love.physics.newBody(TheWorld, x, y, 1, 1)
addTable.shape = love.physics.newRectangleShape(addTable.body, 0, 0, w, h, rot)
addTable.body:setMassFromShapes()
table.insert(RectTable, addTable)
end
function KillRect(index) --Removes the rectangle at position 'index' in RecTable.
local rect = RectTable[index]
if(rect) then
rect.shape:setMask(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16)
rect.dead = true
end
end
function DrawRects() --This draws the rectangles in the table 'RectTable'
for i=#RectTable, 1, -1 do
local rect = RectTable[i]
if(rect.dead) then --Properly kill the rect.
rect.shape:destroy()
rect.body:destroy()
rect.shape = nil
rect.body = nil
table.remove(RectTable, i)
else --Draw the rect normally.
love.graphics.setColor(255, 255, 255, 255)
love.graphics.polygon("fill", rect.shape:getPoints()) --white inside.
love.graphics.setColor(0, 0, 0, 255)
love.graphics.polygon("line", rect.shape:getPoints()) --black outline.
end
end
end
Who is online
Users browsing this forum: Ahrefs [Bot], Semrush [Bot] and 2 guests