Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

b2World::Step crashes in some cases after SetPosition and DestroyBody are called #79

Open
JakMobius opened this issue Jul 31, 2021 · 2 comments

Comments

@JakMobius
Copy link

The following code crashes at b2_dynamic_tree:47:

import {b2Body, b2BodyDef, b2BodyType} from "./box2d/dynamics/b2_body";
import {b2FixtureDef} from "./box2d/dynamics/b2_fixture";
import {b2PolygonShape} from "./box2d/collision/b2_polygon_shape";
import {b2Vec2} from "./box2d/common/b2_math";
import {b2World} from "./box2d/dynamics/b2_world";

const world = new b2World({x: 0, y: 0})

const bodies: b2Body[] = [];
{
    const bd: b2BodyDef = new b2BodyDef();
    bd.type = b2BodyType.b2_dynamicBody;

    bodies[0] = world.CreateBody(bd);

    const fd: b2FixtureDef = new b2FixtureDef();
    const shape: b2PolygonShape = new b2PolygonShape();
    const vs: b2Vec2[] = [];

    vs[0] = new b2Vec2(-0.33, -1);
    vs[1] = new b2Vec2(0.33, -1);
    vs[2] = new b2Vec2(0.33, 1);
    vs[3] = new b2Vec2(-0.33, 1);
    shape.Set(vs, 4);

    fd.shape = shape;

    bodies[0].CreateFixture(fd);
}
{
    const bd: b2BodyDef = new b2BodyDef();
    bd.type = b2BodyType.b2_staticBody;
    bd.position.Set(320, 640);

    bodies[1] = world.CreateBody(bd);

    const fd: b2FixtureDef = new b2FixtureDef();
    const shape: b2PolygonShape = new b2PolygonShape();
    const vs: b2Vec2[] = [];

    vs[0] = new b2Vec2(20, 20);
    vs[1] = new b2Vec2(20, 320);
    vs[2] = new b2Vec2(0, 320);
    vs[3] = new b2Vec2(0, 20);
    shape.Set(vs, 4);

    fd.shape = shape;

    bodies[1].CreateFixture(fd);
}

const body = bodies[0]
world.Step(0.002, 1, 1)

body.SetPositionXY(260.58118606916815, 935.4530055878474)
body.SetPositionXY(338.1343566894531, 932.1221297264099)

world.DestroyBody(body)
world.Step(0.002, 1, 1)

The stack trace:

    at b2TreeNode.get userData [as userData] (box2d/collision/b2_dynamic_tree.js:47:19)
    at b2BroadPhase.UpdatePairs (box2d/collision/b2_broad_phase.js:142:50)
    at b2ContactManager.FindNewContacts (box2d/dynamics/b2_contact_manager.js:115:27)
    at b2World.Step (box2d/dynamics/b2_world.js:373:35)

The issue seems to be in b2BroadPhase::BufferMove. When body get moved twice, its fixture proxies get duplicated in m_moveBuffer. When body is removed from the world, b2BroadPhase::UnBufferMove is called to remove those proxies from the m_moveBuffer. However, this method does not remove their duplicates. So, when UpdatePairs is called, they get processed as moved proxies even after their removal from the world, which causes null-check to fail.

This is not always the case. My game receives body move and destroy events from the server. It get crashed eventually, after several bullets were fired and removed from the world.

My suggested fix: Use ES6 Set for m_moveBuffer. Thus, no duplicates could be stored in this buffer.
As far as I understand, b2BroadPhase::MoveProxy method may be called a lot of times for single proxy (Its documentation says that it can be called as many times as needed). So this approach will likely improve overall performance, preventing same proxy pairs from being processed multiple times. (Also, as b2_broad_phase:121 line says, duplicate pairs are avoided, so this can lead to additional problems)

JakMobius added a commit to JakMobius/box2d.ts that referenced this issue Jul 31, 2021
@flyover
Copy link
Owner

flyover commented Jul 31, 2021

Thanks for reporting this; it should be fixed now. I didn't use the pull request because I've had performance issues iterating Map/Set/etc. The fix acts more like the upstream box2d code.

@JakMobius
Copy link
Author

So duplicate proxy pairs are OK? Isn't it a bottleneck? While debugging that, i've often seen that m_moveCount become very large sometimes, even on simple world setups.
I'm actually thinking about making some benchmarks with and w/o Set.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants