diff --git a/flecs.c b/flecs.c index 77e919e15d..709a97f0eb 100644 --- a/flecs.c +++ b/flecs.c @@ -23594,6 +23594,9 @@ ECS_MOVE(EcsAlert, dst, src, { static ECS_CTOR(EcsAlertsActive, ptr, { ecs_map_init(&ptr->alerts, NULL); + ptr->info_count = 0; + ptr->warning_count = 0; + ptr->error_count = 0; }) static @@ -23605,6 +23608,9 @@ static ECS_MOVE(EcsAlertsActive, dst, src, { ecs_map_fini(&dst->alerts); dst->alerts = src->alerts; + dst->info_count = src->info_count; + dst->warning_count = src->warning_count; + dst->error_count = src->error_count; src->alerts = (ecs_map_t){0}; }) @@ -23637,6 +23643,15 @@ void flecs_alerts_add_alert_to_src( world, source, EcsAlertsActive); ecs_assert(active != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_entity_t severity = ecs_get_target(world, alert, ecs_id(EcsAlert), 0); + if (severity == EcsAlertInfo) { + active->info_count ++; + } else if (severity == EcsAlertWarning) { + active->warning_count ++; + } else if (severity == EcsAlertError) { + active->error_count ++; + } + ecs_entity_t *ptr = ecs_map_ensure(&active->alerts, alert); ecs_assert(ptr != NULL, ECS_INTERNAL_ERROR, NULL); ptr[0] = alert_instance; @@ -23654,6 +23669,15 @@ void flecs_alerts_remove_alert_from_src( ecs_assert(active != NULL, ECS_INTERNAL_ERROR, NULL); ecs_map_remove(&active->alerts, alert); + ecs_entity_t severity = ecs_get_target(world, alert, ecs_id(EcsAlert), 0); + if (severity == EcsAlertInfo) { + active->info_count --; + } else if (severity == EcsAlertWarning) { + active->warning_count --; + } else if (severity == EcsAlertError) { + active->error_count --; + } + if (!ecs_map_count(&active->alerts)) { ecs_remove(world, source, EcsAlertsActive); } else { @@ -24208,10 +24232,10 @@ void FlecsAlertsImport(ecs_world_t *world) { ecs_set_name_prefix(world, "Ecs"); ECS_COMPONENT_DEFINE(world, EcsAlert); ecs_remove_pair(world, ecs_id(EcsAlert), ecs_id(EcsIdentifier), EcsSymbol); + ECS_COMPONENT_DEFINE(world, EcsAlertsActive); ecs_set_name_prefix(world, "EcsAlert"); ECS_COMPONENT_DEFINE(world, EcsAlertInstance); - ECS_COMPONENT_DEFINE(world, EcsAlertsActive); ECS_COMPONENT_DEFINE(world, EcsAlertTimeout); ECS_TAG_DEFINE(world, EcsAlertInfo); @@ -24249,6 +24273,15 @@ void FlecsAlertsImport(ecs_world_t *world) { .copy = ecs_copy(EcsAlertInstance) }); + ecs_struct(world, { + .entity = ecs_id(EcsAlertsActive), + .members = { + { .name = "info_count", .type = ecs_id(ecs_i32_t) }, + { .name = "warning_count", .type = ecs_id(ecs_i32_t) }, + { .name = "error_count", .type = ecs_id(ecs_i32_t) } + } + }); + ECS_SYSTEM(world, MonitorAlerts, EcsPreStore, Alert, (Poly, Query)); ECS_SYSTEM(world, MonitorAlertInstances, EcsOnStore, Instance, flecs.metrics.Source, flecs.metrics.Value, ?EcsAlertTimeout, ?Disabled); diff --git a/flecs.h b/flecs.h index d194ab3c73..7f7b635738 100644 --- a/flecs.h +++ b/flecs.h @@ -7886,7 +7886,7 @@ ecs_entity_t ecs_field_src( int32_t index); /** Return field type size. - * Return type size of the field field. Returns 0 if the field has no data. + * Return type size of the field. Returns 0 if the field has no data. * * @param it The iterator. * @param index The index of the field in the iterator. @@ -12019,6 +12019,9 @@ typedef struct EcsAlertInstance { /** Map with active alerts for entity. */ typedef struct EcsAlertsActive { + int32_t info_count; + int32_t warning_count; + int32_t error_count; ecs_map_t alerts; } EcsAlertsActive; @@ -27541,7 +27544,7 @@ struct filter : filter_base, iterable { public: using filter_base::filter_base; - filter() : filter_base() { } // necessary not not confuse msvc + filter() : filter_base() { } // necessary not to confuse msvc filter(const filter& obj) : filter_base(obj) { } diff --git a/include/flecs/addons/alerts.h b/include/flecs/addons/alerts.h index 9e8547fa10..d6f060e72b 100644 --- a/include/flecs/addons/alerts.h +++ b/include/flecs/addons/alerts.h @@ -58,6 +58,9 @@ typedef struct EcsAlertInstance { /** Map with active alerts for entity. */ typedef struct EcsAlertsActive { + int32_t info_count; + int32_t warning_count; + int32_t error_count; ecs_map_t alerts; } EcsAlertsActive; diff --git a/include/flecs/addons/cpp/mixins/filter/impl.hpp b/include/flecs/addons/cpp/mixins/filter/impl.hpp index 1b768710c0..cf30c9a202 100644 --- a/include/flecs/addons/cpp/mixins/filter/impl.hpp +++ b/include/flecs/addons/cpp/mixins/filter/impl.hpp @@ -138,7 +138,7 @@ struct filter : filter_base, iterable { public: using filter_base::filter_base; - filter() : filter_base() { } // necessary not not confuse msvc + filter() : filter_base() { } // necessary not to confuse msvc filter(const filter& obj) : filter_base(obj) { } diff --git a/src/addons/alerts.c b/src/addons/alerts.c index fc32b4e8ee..de17a658d1 100644 --- a/src/addons/alerts.c +++ b/src/addons/alerts.c @@ -73,6 +73,9 @@ ECS_MOVE(EcsAlert, dst, src, { static ECS_CTOR(EcsAlertsActive, ptr, { ecs_map_init(&ptr->alerts, NULL); + ptr->info_count = 0; + ptr->warning_count = 0; + ptr->error_count = 0; }) static @@ -84,6 +87,9 @@ static ECS_MOVE(EcsAlertsActive, dst, src, { ecs_map_fini(&dst->alerts); dst->alerts = src->alerts; + dst->info_count = src->info_count; + dst->warning_count = src->warning_count; + dst->error_count = src->error_count; src->alerts = (ecs_map_t){0}; }) @@ -116,6 +122,15 @@ void flecs_alerts_add_alert_to_src( world, source, EcsAlertsActive); ecs_assert(active != NULL, ECS_INTERNAL_ERROR, NULL); + ecs_entity_t severity = ecs_get_target(world, alert, ecs_id(EcsAlert), 0); + if (severity == EcsAlertInfo) { + active->info_count ++; + } else if (severity == EcsAlertWarning) { + active->warning_count ++; + } else if (severity == EcsAlertError) { + active->error_count ++; + } + ecs_entity_t *ptr = ecs_map_ensure(&active->alerts, alert); ecs_assert(ptr != NULL, ECS_INTERNAL_ERROR, NULL); ptr[0] = alert_instance; @@ -133,6 +148,15 @@ void flecs_alerts_remove_alert_from_src( ecs_assert(active != NULL, ECS_INTERNAL_ERROR, NULL); ecs_map_remove(&active->alerts, alert); + ecs_entity_t severity = ecs_get_target(world, alert, ecs_id(EcsAlert), 0); + if (severity == EcsAlertInfo) { + active->info_count --; + } else if (severity == EcsAlertWarning) { + active->warning_count --; + } else if (severity == EcsAlertError) { + active->error_count --; + } + if (!ecs_map_count(&active->alerts)) { ecs_remove(world, source, EcsAlertsActive); } else { @@ -687,10 +711,10 @@ void FlecsAlertsImport(ecs_world_t *world) { ecs_set_name_prefix(world, "Ecs"); ECS_COMPONENT_DEFINE(world, EcsAlert); ecs_remove_pair(world, ecs_id(EcsAlert), ecs_id(EcsIdentifier), EcsSymbol); + ECS_COMPONENT_DEFINE(world, EcsAlertsActive); ecs_set_name_prefix(world, "EcsAlert"); ECS_COMPONENT_DEFINE(world, EcsAlertInstance); - ECS_COMPONENT_DEFINE(world, EcsAlertsActive); ECS_COMPONENT_DEFINE(world, EcsAlertTimeout); ECS_TAG_DEFINE(world, EcsAlertInfo); @@ -728,6 +752,15 @@ void FlecsAlertsImport(ecs_world_t *world) { .copy = ecs_copy(EcsAlertInstance) }); + ecs_struct(world, { + .entity = ecs_id(EcsAlertsActive), + .members = { + { .name = "info_count", .type = ecs_id(ecs_i32_t) }, + { .name = "warning_count", .type = ecs_id(ecs_i32_t) }, + { .name = "error_count", .type = ecs_id(ecs_i32_t) } + } + }); + ECS_SYSTEM(world, MonitorAlerts, EcsPreStore, Alert, (Poly, Query)); ECS_SYSTEM(world, MonitorAlertInstances, EcsOnStore, Instance, flecs.metrics.Source, flecs.metrics.Value, ?EcsAlertTimeout, ?Disabled); diff --git a/test/addons/project.json b/test/addons/project.json index 1484f63dc8..a72dfba6b4 100644 --- a/test/addons/project.json +++ b/test/addons/project.json @@ -1605,7 +1605,8 @@ "member_range_alert_two_instances", "member_range_from_var", "member_range_from_var_after_remove", - "retained_alert_w_dead_source" + "retained_alert_w_dead_source", + "alert_counts" ] }] } diff --git a/test/addons/src/Alerts.c b/test/addons/src/Alerts.c index f9e9d5f68e..74359213f0 100644 --- a/test/addons/src/Alerts.c +++ b/test/addons/src/Alerts.c @@ -2519,3 +2519,69 @@ void Alerts_retained_alert_w_dead_source(void) { ecs_fini(world); } + +void Alerts_alert_counts(void) { + ecs_world_t *world = ecs_init(); + + ECS_IMPORT(world, FlecsAlerts); + + ECS_COMPONENT(world, Position); + ECS_COMPONENT(world, Velocity); + ECS_COMPONENT(world, Mass); + + ecs_entity_t e1 = ecs_new_entity(world, "e1"); + ecs_add(world, e1, Position); + + ecs_entity_t alert_1 = ecs_alert(world, { + .entity = ecs_new_entity(world, "position_without_velocity"), + .filter.expr = "Position, !Velocity" + }); + test_assert(alert_1 != 0); + + ecs_entity_t alert_2 = ecs_alert(world, { + .entity = ecs_new_entity(world, "position_without_mass"), + .filter.expr = "Position, !Mass", + .severity = EcsAlertWarning + }); + test_assert(alert_2 != 0); + + ecs_progress(world, 1.0); + + test_assert(ecs_has(world, e1, EcsAlertsActive)); + test_int(ecs_count(world, EcsAlertInstance), 2); + { + test_assert(ecs_get_alert_count(world, e1, alert_1) == 1); + test_assert(ecs_get_alert_count(world, e1, alert_2) == 1); + + const EcsAlertsActive *active = ecs_get(world, e1, EcsAlertsActive); + test_assert(active != NULL); + test_int(active->error_count, 1); + test_int(active->warning_count, 1); + } + + ecs_add(world, e1, Velocity); + + ecs_progress(world, 1.0); + + test_assert(ecs_has(world, e1, EcsAlertsActive)); + test_int(ecs_count(world, EcsAlertInstance), 1); + { + test_assert(ecs_get_alert_count(world, e1, alert_1) == 0); + test_assert(ecs_get_alert_count(world, e1, alert_2) == 1); + + const EcsAlertsActive *active = ecs_get(world, e1, EcsAlertsActive); + test_assert(active != NULL); + test_int(active->error_count, 0); + test_int(active->warning_count, 1); + } + + ecs_add(world, e1, Mass); + + ecs_progress(world, 1.0); + + /* Verify that alerts have cleared */ + test_assert(!ecs_has(world, e1, EcsAlertsActive)); + test_int(ecs_count(world, EcsAlertInstance), 0); + + ecs_fini(world); +} diff --git a/test/addons/src/main.c b/test/addons/src/main.c index efc9a97cfc..426340dc0a 100644 --- a/test/addons/src/main.c +++ b/test/addons/src/main.c @@ -1533,6 +1533,7 @@ void Alerts_member_range_alert_two_instances(void); void Alerts_member_range_from_var(void); void Alerts_member_range_from_var_after_remove(void); void Alerts_retained_alert_w_dead_source(void); +void Alerts_alert_counts(void); bake_test_case Parser_testcases[] = { { @@ -7428,6 +7429,10 @@ bake_test_case Alerts_testcases[] = { { "retained_alert_w_dead_source", Alerts_retained_alert_w_dead_source + }, + { + "alert_counts", + Alerts_alert_counts } }; @@ -7674,7 +7679,7 @@ static bake_test_suite suites[] = { "Alerts", NULL, NULL, - 35, + 36, Alerts_testcases } }; diff --git a/test/meta/src/SerializeEntityToJson.c b/test/meta/src/SerializeEntityToJson.c index ae45476d07..c940859679 100644 --- a/test/meta/src/SerializeEntityToJson.c +++ b/test/meta/src/SerializeEntityToJson.c @@ -1104,13 +1104,13 @@ void SerializeEntityToJson_serialize_w_2_alerts(void) { "\"path\":\"e1\", " "\"ids\":[[\"Position\"]], " "\"alerts\":[{" - "\"alert\":\"position_without_velocity.e1_alert_1\", " - "\"message\":\"e1 has Position but not Velocity\", " - "\"severity\":\"Error\"" - "}, {" "\"alert\":\"position_without_mass.e1_alert_2\", " "\"message\":\"e1 has Position but not Mass\", " "\"severity\":\"Error\"" + "}, {" + "\"alert\":\"position_without_velocity.e1_alert_1\", " + "\"message\":\"e1 has Position but not Velocity\", " + "\"severity\":\"Error\"" "}]" "}"); ecs_os_free(json);