Entt: Version 1.0.0 versus latest 2.0.0 and more stringent component requirements?

Created on 19 Oct 2017  路  12Comments  路  Source: skypjack/entt

I had a working entt based small project using entt version 1.0.0 (Jun 2017 approx.).

I upgraded to entt 2.0.0 and had to make minor changes to my code to get it to compile;
1) header location changed from

     #include <registry.hpp>
to
     #include <entt/entity/registry.hpp>

2) simplified from

using ECS = entt::DefaultRegistry<
 component::Body,
 component::Collideable,
 component::Particle,
 component::Renderable
>;

to

using ECS = entt::DefaultRegistry;

All compiled fine. Version 1.0.0 runs fine but version 2.0.0 fails. When version 2.0.0 ran I saw with gdb backtrace that an assert gives false and halts run in line 320 of registry.hpp.

Is the new version more stringent on the components? I have a suspect component defined as

#include <SFML/Graphics.hpp>

#include <iostream>

namespace component {

struct Body {
  Body(const sf::Vector2f &position, const sf::Vector2f &direction, float rotationd = 0.0)
    : position(position), direction(direction), rotationd(rotationd) {}

  sf::Vector2f position;
  sf::Vector2f direction;
  float rotation = 0.0, rotationd;
};

}; // namespace component

Is there something improper with this constructor as it relates to entt 2.0.0?

bug

Most helpful comment

Having signalling built into EnTT is a great addition. That's one of the news items that caught my eye right away made we want to fire up my old source code, compile with version 2.0.0 and then replace the signalling library with EnTT's.

All 12 comments

The problem isn't the component, it's the entity.
Line registry.hpp:320 contains an assert. It checks that an entity is still valid before to assign it a component.
You are trying to use an invalid entity with the registry, probably one you deleted before.

This kind of asserts are there to find exactly these types of errors in the client code.
Unfortunately I can't help you unless you show me your whole codebase, for the bug seems to be there.

If you can reproduce the error with a minimal working example, I'm at your disposal to help.

The only thing that comes to my mind is that EnTT v2 adds the version to the entity identifier and uses it to check for validity now.
If you inspect or modify the identifiers somehow or use auto generated entity identifiers, unlikely it works. The sole thing you can do with an identifier is storing it and reusing it as is later. If you store around identifiers and use to destroy entities, there are functions in the registry you can use to know if that identifier is still valid. You shouldn't reuse an old identifier that refers to a destroyed entity.

I kindly suggest you to check the README file. There is a section that explains exactly how the version works, how you can use it and what's a valid entity.
Again, the issue seems to be in the client code, I can't do that much without looking at it.

I'm closing this issue at the end of the day. Meanwhile feel free to ask whatever you want and let's see if we find what's the problem.

I read thru the README and thought I was all good with the code modifications I listed prior. My code style followed your benchmark.cpp code most similarly to lines 671+

    for (uint64_t i = 0; i < 150000L; i++) {
        auto entity = registry.create();
        entities.push_back(entity);
        registry.assign<Position>(entity, i, i);
        registry.assign<Velocity>(entity, i, i);
    }

Note above, I use the ecs label synonymous with your resgistry.

Yes, I will more carefully look at the README again. It's likely I missed something. I will work on trying to get a minimal code to replicate the problem.

In the meantime, the gdb shows the assertion failure in the following code inside emit_particles function just after the auto debris = ecs.create(); at the first assign ecs.assign<component::Body>(debris, ...)

namespace System {

struct ExplosionSystem {

  size_t idCollisionEvent; // handle to signal, used for disconnection of signal
  ExplosionSystem(ECS &ecs) : ecs(ecs) { configure(); }
  ~ExplosionSystem() { Events::CollisionEvent -= idCollisionEvent; } // unsubscribe

  void configure() {
    //events.subscribe<CollisionEvent>(*this);
    idCollisionEvent = Events::CollisionEvent += Simple::slot (this, &ExplosionSystem::receive); // subscribe
  }

  void update(float dt) {
    for (const auto &entity : collided) {
      emit_particles(entity);
      ecs.destroy(entity);
    }
    collided.clear();
  }

  void emit_particles(ECS::entity_type entity) {
    // required component(s)
    auto &body = ecs.get<component::Body>(entity);
    auto &renderable = ecs.get<component::Renderable>(entity);
    auto &collideable = ecs.get<component::Collideable>(entity);

    sf::Color colour = renderable.shape.getFillColor();
    colour.a = 200;

    float area = (M_PI * collideable.radius * collideable.radius) / 3.0;
    for (int i = 0; i < area; i++) {
      auto debris = ecs.create();

      float rotationd = r(720, 180);
      if (std::rand() % 2 == 0) rotationd = -rotationd;

      float offset = r(collideable.radius, 1);
      float angle = r(360) * M_PI / 180.0;
      ecs.assign<component::Body>(debris, 
        body.position + sf::Vector2f(offset * cos(angle), offset * sin(angle)),
        body.direction + sf::Vector2f(offset * 2 * cos(angle), offset * 2 * sin(angle)),
        rotationd);

      float radius = r(3, 1);
      ecs.assign<component::Particle>(debris, colour, radius, radius / 2);
    }
  }

  void receive(ECS::entity_type left, ECS::entity_type right) {
    // Events are immutable, so we can't destroy the entities here. We defer
    // the work until the update loop.
    collided.insert(left);
    collided.insert(right);
  }

private:
  ECS &ecs;
  std::unordered_set<ECS::entity_type> collided;
};

}; // namespace System

I am doing the assign shortly after the create entity call so no inspection or modification of entity identifiers. I do collect entities from another CollisionSystem as a collision event is triggered to fill up collided unordered_set here in ExplosionSystem then iterate thru those. Maybe that is out of the ordinary causing the problem?

Interesting. Let me review the code above. I'm at work at the moment, I'll do that in the evening.
I hope I'll back with an answer, a fix either for your code of for EnTT. Please, be patient.

@pocdn Just a question. Are you using the system in a multi threaded environment? I'm asking this for you modify the collided set within receive and it's undefined behavior if it happens while you iterate it in update.

Please feel no pressure from me. I am looking at this after work hours myself ... so definitely no rush or deadlines. Thank you for your help and your amazingly fast performance ecs library.

You're welcome. No pressure at all. I just like to help people in getting the best from EnTT. ;-)

I hope also you'll get rid soon of Simple and use signalling stuff provided with EnTT. I'm working on a bunch of classes to fill the gap and offer a full featured set of functionalities in that area.
Stay tuned.

Having signalling built into EnTT is a great addition. That's one of the news items that caught my eye right away made we want to fire up my old source code, compile with version 2.0.0 and then replace the signalling library with EnTT's.

Maybe I was too hasty to mark the issue as _invalid_. I managed to reproduce the problem. Not sure it's the same you are facing, but let me verify what's wrong.

You found the worst bug ever. Definitely my fault. Really thank you and shame on me!!
I'm pushing the fix and creating the version 2.0.1 - give me the time to add a test so that it won't happen anymore.

Please test version 2.0.1, the bug should be fixed. If it's so, feel free to close the issue.

To sum up: when an entity was destroyed I marked it as available but I stored the old identifier instead of the new one. The entity was recycled the next cycle but it wasn't valid anymore.

I'm sorry. I hope it works now.

Great, version 2.0.1 is working with my code now. All runs great.

p.s. I missed an earlier question that you had of me; I am running in a single thread, no multi-threading in my simple code test case.

Thank you very much.

You are welcome.
Please, consider to star the project once you've used it successfully. Your support helps spreading it and attiring more ideas.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

skypjack picture skypjack  路  4Comments

bilek993 picture bilek993  路  3Comments

Deins picture Deins  路  6Comments

blockspacer picture blockspacer  路  3Comments

jaspercb picture jaspercb  路  6Comments