BeanCheeseBurrito / Flecs.NET

A C# wrapper for flecs
MIT License
145 stars 18 forks source link

How to register a system that doesn't query for any components? #6

Closed miguno closed 1 year ago

miguno commented 1 year ago

Let's say I want to create a system that prints delta_time on every tick. How can I do this with Flecs.NET?

Here's how to do it in C:

void S_PrintDeltaTime(ecs_iter_t *it) {
    printf("delta_time %f\n", (double)it->delta_time);
}

// This is the relevant line for this GH question.
ECS_SYSTEM(world, PrintDeltaTime, EcsOnUpdate, 0);

The system above doesn't query for any components which means it won't match any entities, but will still be run once for each call to ecs_progress().

The following attempts didn't work with Flecs.NET:

// Attempt 1: fails at runtime with "fatal: flecs.c: 44682: assert: table != NULL INTERNAL_ERROR"
_world.Routine(
    filter: _world.FilterBuilder(),
    /* ... */);

// Attempt 2: routine is never called
_world.Routine(
    filter: _world.FilterBuilder().With(0),
    /* ... */);

// Attempt 3: routine is never called
_world.Routine(
    filter: _world.FilterBuilder().Term(0),
    /* ... */);
BeanCheeseBurrito commented 1 year ago

That's strange, this code is working for me without crashing. What Nuget version are you using?

using System;
using Flecs.NET.Core;

using World world = World.Create();

world.Routine(
    filter: world.FilterBuilder(), // You can omit this
    callback: (Iter it) =>
    {
        Console.WriteLine(it.DeltaTime());
    }
);

world.App().Run();
miguno commented 1 year ago

I am using 3.2.7-build.12.

<PackageReference Include="Flecs.NET.Debug" Version="3.2.7-build.12" />
<PackageReference Include="Flecs.NET.Release" Version="3.2.7-build.12" />

Regarding your code snippet: When I remove the filter: ... line, then the app crashes with:

fatal: flecs.c: 44682: assert: table != NULL INTERNAL_ERROR
1   libflecs.dylib                      0x000000010e8c86cc flecs_log_msg + 3696
2   libflecs.dylib                      0x000000010e934590 ecs_printv_ + 328
3   libflecs.dylib                      0x000000010e833990 ecs_log_ + 156
4   libflecs.dylib                      0x000000010e8173c8 ecs_assert_ + 388
5   libflecs.dylib                      0x000000010e84f8c4 ecs_table_count + 92
6   ???                                 0x000000028173b5a4 0x0 + 10761778596
7   ???                                 0x000000028173b4e0 0x0 + 10761778400
8   ???                                 0x000000028173b3fc 0x0 + 10761778172
9   ???                                 0x000000028173f3b8 0x0 + 10761794488
10  ???                                 0x0000000281739c34 0x0 + 10761772084
11  ???                                 0x0000000281739714 0x0 + 10761770772
12  libflecs.dylib                      0x000000010ea02b18 ecs_run_intern + 3420
13  libflecs.dylib                      0x000000010ea01c24 flecs_run_pipeline_ops + 2472
14  libflecs.dylib                      0x000000010ea03b14 flecs_run_pipeline + 3580
15  libflecs.dylib                      0x000000010ea00778 flecs_workers_progress + 320
16  libflecs.dylib                      0x000000010ea04d84 ecs_progress + 808
17  ???                                 0x00000002817390ec 0x0 + 10761769196
18  ???                                 0x0000000281739014 0x0 + 10761768980
19  ???                                 0x0000000281737330 0x0 + 10761761584
20  ???                                 0x00000002817368d4 0x0 + 10761758932
21  ???                                 0x0000000281158040 0x0 + 10755604544
22  ???                                 0x00000002810e1830 0x0 + 10755119152
23  libcoreclr.dylib                    0x00000001056f2a48 CallDescrWorkerInternal + 132
24  libcoreclr.dylib                    0x00000001055bd24c _Z26CallDescrWorkerWithHandlerP13CallDescrDatai + 108
25  libcoreclr.dylib                    0x00000001055bd704 _ZN18MethodDescCallSite16CallTargetWorkerEPKmPmi + 640
26  libcoreclr.dylib                    0x00000001054e8890 _Z7RunMainP10MethodDescsPiPP8PtrArray + 548
27  libcoreclr.dylib                    0x00000001054e8b04 _ZN8Assembly17ExecuteMainMethodEPP8PtrArrayi + 268
28  libcoreclr.dylib                    0x000000010550f854 _ZN8CorHost215ExecuteAssemblyEjPKDsiPS1_Pj + 476
29  libcoreclr.dylib                    0x00000001054d71bc coreclr_execute_assembly + 224
30  libhostpolicy.dylib                 0x0000000104b38520 _Z19run_app_for_contextRK20hostpolicy_context_tiPPKc + 904
31  libhostpolicy.dylib                 0x0000000104b387a4 _Z7run_appiPPKc + 56
32  libhostpolicy.dylib                 0x0000000104b39028 corehost_main + 160
33  libhostfxr.dylib                    0x0000000104acb040 _ZN10fx_muxer_t24handle_exec_host_commandERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEERK19host_startup_info_tS8_RKNS0_13unordered_mapI13known_optionsNS0_6vectorIS6_NS4_IS6_EEEE18known_options_hashNS0_8equal_toISD_EENS4_INS0_4pairIKSD_SG_EEEEEEiPPKci11host_mode_tbPciPi + 1092
34  libhostfxr.dylib                    0x0000000104aca4c0 _ZN10fx_muxer_t7executeENSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEEiPPKcRK19host_startup_info_tPciPi + 852
35  libhostfxr.dylib                    0x0000000104ac77c0 hostfxr_main_startupinfo + 152
36  monotone                            0x0000000104a3d4d0 _Z9exe_startiPPKc + 1436
37  monotone                            0x0000000104a3d6c4 main + 160
38  dyld                                0x0000000188f31058 start + 2224
   at System.Environment.get_StackTrace()
   at Flecs.NET.Core.BindingContext.OsApiAbort()
   at Flecs.NET.Bindings.Native.ecs_table_count(ecs_table_t* table)
   at Flecs.NET.Bindings.Native.ecs_table_count(ecs_table_t* table)
   at Flecs.NET.Core.Table..ctor(ecs_world_t* world, ecs_table_t* table)
   at Flecs.NET.Core.Iter.Table()
   at monotone.Game1.<CreateSystems>b__11_2(Iter it) in Game1.cs:line 183
   at Flecs.NET.Core.Invoker.Iter(ecs_iter_t* iter, IterCallback callback)
   at Flecs.NET.Core.BindingContext.RoutineIter(ecs_iter_t* iter)
   at Flecs.NET.Bindings.Native.ecs_progress(ecs_world_t* world, Single delta_time)
   at Flecs.NET.Bindings.Native.ecs_progress(ecs_world_t* world, Single delta_time)
   at Flecs.NET.Core.World.Progress(Single deltaTime)
   at monotone.Game1.Update(GameTime gameTime) in Game1.cs:line 236
   at Microsoft.Xna.Framework.Game.DoUpdate(GameTime gameTime)
   at Microsoft.Xna.Framework.Game.Run(GameRunBehavior runBehavior)
   at Program.<Main>$(String[] args) in Program.cs:line 3
miguno commented 1 year ago

What I am currently doing is using a sentinel tag that the system queries, which is a workaround so that I can add a working query to the system and still know that the query is reasonably cheap (as, in my setup, there's only a single entity in the system with that sentinel tag).

miguno commented 1 year ago

I tested the code snippet you provided in a new project—it works. So the error lies elsewhere, will take a look.

BeanCheeseBurrito commented 1 year ago

Are you by any chance calling Iter.Table() anywhere in your callback function? When running a system that uses 0 terms, it looks like the table pointer is always null. In the C# API, I make a call to ecs_table_count in the table constructors which is what I think is causing the crash. I am working on updating to flecs version 3.2.8 right now and will add a null check before calling ecs_table_count but you should avoid using the table struct inside of a system if you can. This C++ sample produces the same assertion.

int main(void) {
  flecs::world ecs;

  ecs.system().iter([](flecs::iter it) {
    it.table().count();
  });

  ecs.app().run();
}
miguno commented 1 year ago

Yes, I do call Iter.Table() in the callback. Nice find!

That call is not necessary for the callback's functionality, so I can simply remove that call for the time being. FWIW, I used Iter.Table() to inspect what kind of entities the callback was iterating on, copying from the Hello World example from flecs.

you should avoid using the table struct inside of a system if you can.

Interesting, I was not aware of this being an anti-pattern (again, because it was shown in literally flecs' own first code example). Thanks for letting me know!

BeanCheeseBurrito commented 1 year ago

you should avoid using the table struct inside of a system if you can.

Sorry that was a typo, I mean't you shouldn't use it in an empty table system. It's fine to use tables otherwise. Closing this issue now!