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

Crash in world update after restoring snapshot. #620

Closed
sixprime opened this issue Jan 24, 2022 · 3 comments
Closed

Crash in world update after restoring snapshot. #620

sixprime opened this issue Jan 24, 2022 · 3 comments
Labels
bug Something isn't working

Comments

@sixprime
Copy link

Describe the bug
Internal Flecs crash on invalid pointer after trying to update the world following a snapshot restore.

To Reproduce
Cf minimal repro source code below :

#include "flecs.h"

struct Player
{
    int id;
};

struct Input
{
    float x;
    float y;
    bool fire;
};

struct Position
{
    float x;
    float y;
};

struct Velocity
{
    float x;
    float y;
};

struct Bullet
{
    int instigator;
};

struct Lifetime
{
    int counter;
};

void ApplyPlayerMovementInput(flecs::iter& it, Velocity* velocity)
{
    flecs::column<const Input> input = it.term<const Input>(2);

    for (auto i : it)
    {
        velocity[i].x = input->x;
        velocity[i].y = input->y;
    }
}

void MovePlayer(flecs::iter& it, const Velocity* velocity, Position* position)
{
    for (auto i : it)
    {
        position[i].x += velocity[i].x;
        position[i].y += velocity[i].y;
    }
}

void ApplyPlayerFireInput(flecs::iter& it, const Player* player, const Velocity* velocity, const Position* position)
{
    flecs::column<const Input> input = it.term<const Input>(4);

    for (auto i : it)
    {
        if (input->fire)
        {
            it.world().entity()
                //.set_name("bullet")
                .set<Bullet>({ player[i].id })
                .set<Position>(position[i])
                .set<Velocity>({ velocity[i].x, velocity[i].y })
                .set<Lifetime>({ 2 });
        }
    }
}

void DestroyBullets(flecs::iter& it, Lifetime* lifetime)
{
    for (auto i : it)
    {
        lifetime[i].counter -= 1;

        if (lifetime[i].counter <= 0)
        {
            it.entity(i).destruct();
        }
    }
}

void MoveBullets(flecs::iter& it, const Velocity* velocity, Position* position)
{
    for (auto i : it)
    {
        position[i].x += velocity[i].x * 6.0f;
        position[i].y += velocity[i].y * 6.0f;
    }
}

int main()
{
    flecs::world world;

    // Initialize world.
    world.set<Input>({ 0.0f, 0.0f, false });

    auto player1 = world.entity("player1")
        .set<Player>({ 1 })
        .set<Position>({ 0.0f, 0.0f })
        .set<Velocity>({ 0.0f, 0.0f });

    world.system<Velocity>("ApplyPlayerMovementInput")
        .term<Input>().singleton()
        .term<Player>()
        .iter(ApplyPlayerMovementInput);

    world.system<const Velocity, Position>("MovePlayer")
        .term<Player>()
        .iter(MovePlayer);

    world.system<const Player, const Velocity, const Position>("ApplyPlayerFireInput")
        .term<Input>().singleton()
        .iter(ApplyPlayerFireInput);

    world.system<Lifetime>("DestroyBullets")
        .iter(DestroyBullets);

    world.system<const Velocity, Position>("MoveBullets")
        .term<Bullet>()
        .iter(MoveBullets);

    // Take initial world state snapshot.
    auto snapshot1 = flecs::snapshot(world);
    snapshot1.take();

    // Update world with input.
    world.set<Input>({ 1.0f, 0.0f, false });
    world.progress();

    world.set<Input>({ 1.0f, 0.0f, true });
    world.progress();

    // Restore initial world state.
    snapshot1.restore();

    // Update world with different input.
    world.set<Input>({ 1.0f, 0.0f, true });
    world.progress(); // <- crash
    // "Exception thrown: read access violation. **column** was 0x30."
    // Crash in flecs.c line 4728 : `int32_t size = column->size;`
    /* Callstack
        >	Game.exe!get_component_w_index(ecs_table_t * table, int column_index, int row) Line 4728	C
        Game.exe!ecs_get_id(const ecs_world_t * world, unsigned __int64 entity, unsigned __int64 id) Line 7634	C
        Game.exe!flecs_defer_set(ecs_world_t * world, ecs_stage_t * stage, ecs_op_kind_t op_kind, unsigned __int64 entity, unsigned __int64 component, int size, const void * value, void * * value_out, bool * is_added) Line 9301	C
        Game.exe!ecs_get_mut_id(ecs_world_t * world, unsigned __int64 entity, unsigned __int64 id, bool * is_added) Line 7705	C
        Game.exe!flecs::set<Bullet,0>(ecs_world_t * world, unsigned __int64 entity, Bullet && value, unsigned __int64 id) Line 11149	C++
        Game.exe!flecs::set<Bullet,Bullet>(ecs_world_t * world, unsigned __int64 entity, Bullet && value) Line 11222	C++
        Game.exe!flecs::entity_builder<flecs::entity>::set<Bullet,0>(Bullet && value) Line 13429	C++
        Game.exe!ApplyPlayerFireInput(flecs::iter & it, const Player * player, const Velocity * velocity, const Position * position) Line 67	C++
        Game.exe!flecs::_::iter_invoker<void (__cdecl*)(flecs::iter &,Player const *,Velocity const *,Position const *),Player const ,Velocity const ,Position const>::invoke_callback<flecs::_::term_ptr,flecs::_::term_ptr,flecs::_::term_ptr,0>(ecs_iter_t * iter, void(*)(flecs::iter &, const Player *, const Velocity *, const Position *) & func, unsigned __int64 __formal, flecs::array<flecs::_::term_ptr,3,void> & __formal, flecs::_::term_ptr <comps_0>, flecs::_::term_ptr <comps_1>, flecs::_::term_ptr <comps_2>) Line 14910	C++
        Game.exe!flecs::_::iter_invoker<void (__cdecl*)(flecs::iter &,Player const *,Velocity const *,Position const *),Player const ,Velocity const ,Position const>::invoke_callback<flecs::_::term_ptr,flecs::_::term_ptr,0>(ecs_iter_t * iter, void(*)(flecs::iter &, const Player *, const Velocity *, const Position *) & func, unsigned __int64 index, flecs::array<flecs::_::term_ptr,3,void> & columns, flecs::_::term_ptr <comps_0>, flecs::_::term_ptr <comps_1>) Line 14923	C++
        Game.exe!flecs::_::iter_invoker<void (__cdecl*)(flecs::iter &,Player const *,Velocity const *,Position const *),Player const ,Velocity const ,Position const>::invoke_callback<flecs::_::term_ptr,0>(ecs_iter_t * iter, void(*)(flecs::iter &, const Player *, const Velocity *, const Position *) & func, unsigned __int64 index, flecs::array<flecs::_::term_ptr,3,void> & columns, flecs::_::term_ptr <comps_0>) Line 14923	C++
        Game.exe!flecs::_::iter_invoker<void (__cdecl*)(flecs::iter &,Player const *,Velocity const *,Position const *),Player const ,Velocity const ,Position const>::invoke_callback<,0>(ecs_iter_t * iter, void(*)(flecs::iter &, const Player *, const Velocity *, const Position *) & func, unsigned __int64 index, flecs::array<flecs::_::term_ptr,3,void> & columns) Line 14923	C++
        Game.exe!flecs::_::iter_invoker<void (__cdecl*)(flecs::iter &,Player const *,Velocity const *,Position const *),Player const ,Velocity const ,Position const>::invoke(ecs_iter_t * iter) Line 14871	C++
        Game.exe!flecs::_::iter_invoker<void (__cdecl*)(flecs::iter &,Player const *,Velocity const *,Position const *),Player const ,Velocity const ,Position const>::run(ecs_iter_t * iter) Line 14878	C++
        Game.exe!ecs_run_intern(ecs_world_t * world, ecs_stage_t * stage, unsigned __int64 system, EcsSystem * system_data, int stage_current, int stage_count, float delta_time, int offset, int limit, const ecs_filter_t * filter, void * param) Line 26400	C
        Game.exe!ecs_pipeline_run(ecs_world_t * world, unsigned __int64 pipeline, float delta_time) Line 25476	C
        Game.exe!ecs_workers_progress(ecs_world_t * world, unsigned __int64 pipeline, float delta_time) Line 24979	C
        Game.exe!ecs_pipeline_run(ecs_world_t * world, unsigned __int64 pipeline, float delta_time) Line 25440	C
        Game.exe!ecs_progress(ecs_world_t * world, float user_delta_time) Line 25665	C
        Game.exe!flecs::world::progress(float delta_time) Line 11325	C++
        Game.exe!main() Line 150	C++
    */

    return 0;
}

Expected behavior
I'm expecting the world state to be properly restored and in a state where I can keep using it.

Additional context
Flecs version used : 2.6.4. But also crashes with a different callstack on Flecs 3.0.1.

@sixprime sixprime added the bug Something isn't working label Jan 24, 2022
@SanderMertens
Copy link
Owner

SanderMertens commented Jun 27, 2022

I was able to reduce the scenario to:

struct Foo
{
    int id;
};

int main()
{
    flecs::world world;

    auto snapshot1 = flecs::snapshot(world);
    
    snapshot1.take();
    
    world.component<Tag>();
    
    snapshot1.restore();

    return 0;

    // crashes in v3 on world destruct
}

@SanderMertens
Copy link
Owner

Another repro:

    ecs_world_t* world = ecs_mini();
    
    ECS_COMPONENT(world, foobar);

    ecs_entity_t e = ecs_new_id(world);
    ecs_add(world, e, foobar);
    ecs_set(world, e, foobar, { 10 });

    ecs_snapshot_t* snap = ecs_snapshot_take(world);
    ecs_entity_t f = ecs_new_id(world);
    ecs_add(world, f, foobar);
    ecs_set(world, f, foobar, { 11 });
    ecs_snapshot_restore(world, snap);

    ecs_entity_t c = ecs_new_id(world);
    ecs_add(world, c, foobar);
    ecs_set(world, c, foobar, { 11 });

    ecs_fini(world);

@SanderMertens
Copy link
Owner

Closing issue, as the snapshot feature has been removed from v4.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants