Added entity queries by tag, tests for queries and tests for tagging.

This commit is contained in:
Eric-Paul Ickhorn 2024-02-01 01:55:13 +01:00
parent 53a63e61db
commit 4f549290ab
Signed by: epickh
GPG Key ID: F5EBBE013924D95F
12 changed files with 300 additions and 7 deletions

View File

@ -1,3 +1,5 @@
tests/internal/basic-tag-registry-usage
tests/internal/entity-lookup
tests/interface/entity-queries
tests/interface/entity-creation
tests/interface/entity-tagging-api

View File

@ -14,6 +14,7 @@ typedef void MtState;
typedef void (*MtEffectorFn) (MtEntity *entity, void *userdata, usz_t invocation_id);
typedef void (*MtTaskMainFn) (MtTask *task, void *userdata);
typedef bool_t (*MtQueryMatcherFn) (MtEntity *entity, void *userdata);
typedef struct
{
@ -48,6 +49,9 @@ void mt_remove_effector(MtState *state, const char *effector_name);
/// @param tag_names A nullpointer-terminated list of tag-ids that must be present in an entity for that entity to be effected.
void mt_add_tagged_tick_effector(MtState *state, char *effector_name, usz_t ticks_delta, MtEffectorFn function, char **tag_names);
usz_t mt_query(MtState *state, const char **tags, MtEntity **entities, usz_t count);
usz_t mt_match_query(MtState *state, const char **tags, MtEntity **entities, usz_t count, MtQueryMatcherFn matcher, void *userdata);
void mt_tag_i64(MtEntity *entity, char *name, i64_t integer);
void mt_tag_f64(MtEntity *entity, char *name, f64_t real);
void mt_tag_str(MtEntity *entity, char *name, char *string);

View File

@ -89,6 +89,7 @@ void mt_tag_vec2(MtEntity *entity, char *name, rr_vec2f_s vector);
void mt_tag_vec3(MtEntity *entity, char *name, rr_vec3f_s vector);
void mt_tag_vec4(MtEntity *entity, char *name, rr_vec4f_s vector);
bool_t mt_has_tag(MtEntity *entity, const char *name);
void mt_untag(MtEntity *entity, char *name);
i64_t mt_get_i64_tag(MtEntity *entity, char *name);

View File

@ -9,6 +9,7 @@
#include <librr/alloc/generic.h>
typedef struct MtState MtState;
typedef bool_t (*MtQueryMatcherFn) (MtEntity *entity, void *userdata);
struct MtState
{
@ -25,4 +26,7 @@ void mt_start(MtState *state);
MtEntity * mt_summon(MtState *state);
void mt_drop(MtEntity *entity);
usz_t mt_query(MtState *state, const char **tags, MtEntity **entities, usz_t count);
usz_t mt_match_query(MtState *state, const char **tags, MtEntity **entities, usz_t count, MtQueryMatcherFn matcher, void *userdata);
#endif // MT_STATE_H

View File

@ -30,6 +30,11 @@ struct MtTagEntry
{
u32_t identifier;
char *label;
usz_t entities_capacity;
usz_t num_entities;
/// @brief An array ofhe identifiers of all entities which have a tag of this type.
u32_t *entity_identifiers;
};
struct MtTagCollection
@ -46,5 +51,9 @@ void mt_delete_tag_registry(MtTagRegistry registry);
u32_t mt_get_tag_id(MtTagRegistry *registry, const char *tag_label);
const char * mt_get_tag_label(MtTagRegistry *registry, u32_t tag_identifier);
MtTagEntry * mt_get_tag_entry(MtTagRegistry *registry, const char *tag_label);
void mt_add_entity_to_tag_lookup_list(MtTagRegistry *registry, const char *tag_label, u32_t entity_identifier);
void mt_remove_entity_from_tag_lookup_list(MtTagRegistry *registry, const char *tag_label, u32_t entity_identifier);
#endif // MT_TAG_REGISTRY_H

View File

@ -89,6 +89,7 @@ void mt_delete_entity_data(MtEntity *entity)
{
if(entity->tags != NULL)
free(entity->tags);
entity->tags = NULL;
}

View File

@ -20,6 +20,8 @@ i16_t mt_get_entity_tag_slot_index(MtEntity *entity, const char *name)
MtTag * mt_get_entity_tag_slot(MtEntity *entity, char *name)
{
MtState *state = entity->parent_state;
i16_t tag_slot_index = mt_get_entity_tag_slot_index(entity, name);
if(tag_slot_index >= 0)
return &entity->tags[tag_slot_index];
@ -34,51 +36,87 @@ MtTag * mt_get_entity_tag_slot(MtEntity *entity, char *name)
entity->tags = realloc(entity->tags, sizeof(struct MtTag) * entity->tags_capacity);
}
entity->tags[entity->num_tags].identifier = mt_get_tag_id(&state->tag_registry, name);
return &entity->tags[entity->num_tags++];
}
void mt_tag_i64(MtEntity *entity, char *name, i64_t integer)
{
MtState *state = entity->parent_state;
MtTag *tag = mt_get_entity_tag_slot(entity, name);
mt_add_entity_to_tag_lookup_list(&state->tag_registry, name, entity->identifier);
tag->data.integer = integer;
}
void mt_tag_f64(MtEntity *entity, char *name, f64_t real)
{
MtState *state = entity->parent_state;
MtTag *tag = mt_get_entity_tag_slot(entity, name);
mt_add_entity_to_tag_lookup_list(&state->tag_registry, name, entity->identifier);
tag->data.real = real;
}
void mt_tag_str(MtEntity *entity, char *name, char *string)
{
MtState *state = entity->parent_state;
MtTag *tag = mt_get_entity_tag_slot(entity, name);
mt_add_entity_to_tag_lookup_list(&state->tag_registry, name, entity->identifier);
tag->data.string = string;
}
void mt_tag_ptr(MtEntity *entity, char *name, void *pointer)
{
MtState *state = entity->parent_state;
MtTag *tag = mt_get_entity_tag_slot(entity, name);
mt_add_entity_to_tag_lookup_list(&state->tag_registry, name, entity->identifier);
tag->data.pointer = pointer;
}
void mt_tag_vec2(MtEntity *entity, char *name, rr_vec2f_s vector)
{
MtState *state = entity->parent_state;
MtTag *tag = mt_get_entity_tag_slot(entity, name);
mt_add_entity_to_tag_lookup_list(&state->tag_registry, name, entity->identifier);
tag->data.vec2f = vector;
}
void mt_tag_vec3(MtEntity *entity, char *name, rr_vec3f_s vector)
{
MtState *state = entity->parent_state;
MtTag *tag = mt_get_entity_tag_slot(entity, name);
mt_add_entity_to_tag_lookup_list(&state->tag_registry, name, entity->identifier);
tag->data.vec3f = vector;
}
void mt_tag_vec4(MtEntity *entity, char *name, rr_vec4f_s vector)
{
MtState *state = entity->parent_state;
MtTag *tag = mt_get_entity_tag_slot(entity, name);
mt_add_entity_to_tag_lookup_list(&state->tag_registry, name, entity->identifier);
tag->data.vec4f = vector;
}
bool_t mt_has_tag(MtEntity *entity, const char *name)
{
MtState *state = entity->parent_state;
usz_t tag_id = mt_get_tag_id(&state->tag_registry, name);
usz_t tag_index = 0;
while(tag_index < entity->num_tags)
{
if(entity->tags[tag_index].identifier == tag_id)
return TRUE;
++tag_index;
}
return FALSE;
}
void mt_untag(MtEntity *entity, char *name)
{

107
core/src-c/queries.c Normal file
View File

@ -0,0 +1,107 @@
#include <state.h>
#include <entity.h>
usz_t mt_find_tag_with_least_entries(MtState *state, const char **tags)
{
u32_t least_entries_count = 0xffffffff;
usz_t least_entries_index = 0;
usz_t tag_index = 0;
while(tags[tag_index] != NULL)
{
MtTagEntry *tag_entry = mt_get_tag_entry(&state->tag_registry, tags[tag_index]);
if(tag_entry->num_entities > least_entries_count)
{
least_entries_count = tag_entry->num_entities;
least_entries_index = tag_index;
}
++tag_index;
}
return least_entries_index;
}
usz_t mt_count_entities_with_matching_tags(MtState *state, const char **tags)
{
usz_t num_matching_entities = 0;
const char *least_used_tag = tags[mt_find_tag_with_least_entries(state, tags)];
MtTagEntry *tag_entry = mt_get_tag_entry(&state->tag_registry, least_used_tag);
usz_t entity_index = 0;
while(entity_index < tag_entry->num_entities)
{
MtEntity *entity = mt_get_entity(state, tag_entry->entity_identifiers[entity_index]);
bool_t matching = TRUE;
usz_t wanted_tag_index = 0;
while(tags[wanted_tag_index] != NULL)
{
if(!mt_has_tag(entity, tags[wanted_tag_index]))
{
matching = FALSE;
break;
}
++wanted_tag_index;
}
if(matching)
++num_matching_entities;
++entity_index;
}
return num_matching_entities;
}
usz_t mt_query(MtState *state, const char **tags, MtEntity **entities, usz_t count)
{
if(count == 0)
return mt_count_entities_with_matching_tags(state, tags);;
usz_t num_written_entities = 0;
const char *least_used_tag = tags[mt_find_tag_with_least_entries(state, tags)];
MtTagEntry *tag_entry = mt_get_tag_entry(&state->tag_registry, least_used_tag);
usz_t entity_index = 0;
while((entity_index < tag_entry->num_entities) && (num_written_entities < count))
{
MtEntity *entity = mt_get_entity(state, tag_entry->entity_identifiers[entity_index]);
bool_t matching = TRUE;
usz_t wanted_tag_index = 0;
while(tags[wanted_tag_index] != NULL)
{
if(!mt_has_tag(entity, tags[wanted_tag_index]))
{
matching = FALSE;
break;
}
++wanted_tag_index;
}
if(matching)
{
entities[num_written_entities] = entity;
++num_written_entities;
}
++entity_index;
}
return num_written_entities;
}
usz_t mt_match_query(MtState *state, const char **tags, MtEntity **entities, usz_t count, MtQueryMatcherFn matcher, void *userdata)
{
usz_t num_matching_entities = mt_query(state, tags, entities, 0);
if(count == 0)
return num_matching_entities;
// TODO: If 'num_matching_entities" is too big, this must be allocated on the heap!
MtEntity *generally_matching_entities[num_matching_entities];
mt_query(state, tags, &generally_matching_entities[0], num_matching_entities);
usz_t written_entities = 0;
usz_t entity_index = 0;
while((entity_index < num_matching_entities) && (written_entities < count))
{
if(matcher(generally_matching_entities[entity_index], userdata))
{
entities[written_entities] = generally_matching_entities[entity_index];
++written_entities;
}
++entity_index;
}
return written_entities;
}

View File

@ -3,6 +3,8 @@
#include <librr/memory.h>
#include <stdlib.h>
#define MT_INITIAL_ENTITIES_CAPACITY_PER_TAG 32
MtTagRegistry mt_create_tag_registry(void *parent_state)
{
MtTagRegistry registry;
@ -93,7 +95,9 @@ MtTagEntry * mt_create_tag_hash_entry(MtTagRegistry *registry, const char *tag_l
// TODO: There MUST be an arena allocator for this!
entry->label = malloc(len_tag_label + 1);
rr_memcopy(entry->label, tag_label, len_tag_label+1);
entry->entities_capacity = MT_INITIAL_ENTITIES_CAPACITY_PER_TAG;
entry->entity_identifiers = calloc(sizeof(u32_t), entry->entities_capacity);
entry->num_entities = 0;
entry->identifier = registry->next_identifier;
++registry->next_identifier;
@ -120,7 +124,7 @@ MtTagEntry * mt_create_tag_hash_entry(MtTagRegistry *registry, const char *tag_l
return entry;
}
u32_t mt_get_tag_id(MtTagRegistry *registry, const char *tag_label)
MtTagEntry * mt_get_tag_entry(MtTagRegistry *registry, const char *tag_label)
{
u32_t collection_index = mt_tag_label_to_hash(tag_label, registry->num_collections);
MtTagCollection collection = registry->collections[collection_index];
@ -129,12 +133,16 @@ u32_t mt_get_tag_id(MtTagRegistry *registry, const char *tag_label)
{
MtTagEntry *entry = &registry->entries[collection.tag_indices[entry_index]];
if(rr_strings_equal(entry->label, tag_label))
return entry->identifier;
return entry;
++entry_index;
}
MtTagEntry *entry = mt_create_tag_hash_entry(registry, tag_label);
return entry->identifier;
return mt_create_tag_hash_entry(registry, tag_label);
}
u32_t mt_get_tag_id(MtTagRegistry *registry, const char *tag_label)
{
return mt_get_tag_entry(registry, tag_label)->identifier;
}
const char * mt_get_tag_label(MtTagRegistry *registry, u32_t tag_identifier)
@ -150,3 +158,47 @@ const char * mt_get_tag_label(MtTagRegistry *registry, u32_t tag_identifier)
}
return NULL;
}
u32_t mt_get_or_create_entity_id_slot_in_tag_entry(MtTagEntry *entry, u32_t entity_identifier)
{
// Option 1: Find the entity identifier (if it was already registered)
usz_t index = 0;
while(index < entry->num_entities)
{
if(entry->entity_identifiers[index] == entity_identifier)
return index;
++index;
}
// Option 2: Get a slot for the entity identifier and write the ID.
// Resize the entity_identifiers array if needed
if(entry->num_entities >= entry->entities_capacity)
{
entry->entities_capacity *= 2;
if(entry->entities_capacity == 0)
entry->entities_capacity = MT_INITIAL_ENTITIES_CAPACITY_PER_TAG;
entry->entity_identifiers = realloc(entry->entity_identifiers, sizeof(u32_t) * entry->entities_capacity);
}
usz_t entity_identifier_index = entry->num_entities;
entry->entity_identifiers[entity_identifier_index] = entity_identifier;
++entry->num_entities;
return entity_identifier_index;
}
void mt_add_entity_to_tag_lookup_list(MtTagRegistry *registry, const char *tag_label, u32_t entity_identifier)
{
MtTagEntry *entry = mt_get_tag_entry(registry, tag_label);
mt_get_or_create_entity_id_slot_in_tag_entry(entry, entity_identifier);
}
void mt_remove_entity_from_tag_lookup_list(MtTagRegistry *registry, const char *tag_label, u32_t entity_identifier)
{
MtTagEntry *entry = mt_get_tag_entry(registry, tag_label);
u32_t entity_index = mt_get_or_create_entity_id_slot_in_tag_entry(entry, entity_identifier);
entry->entity_identifiers[entity_index] = entry->entity_identifiers[entry->num_entities-1];
--entry->num_entities;
}

View File

@ -0,0 +1,49 @@
#include <state.h>
#include <entity.h>
#include <stdio.h>
int main()
{
MtState *state = mt_initialize("Interface Test");
MtEntity *entity_1 = mt_summon(state);
mt_tag_i64(entity_1, "some_tag", 3);
mt_tag_i64(entity_1, "another_tag", 42);
mt_tag_i64(entity_1, "listed_tag", 1337);
MtEntity *entity_2 = mt_summon(state);
mt_tag_i64(entity_2, "some_tag", 3);
mt_tag_i64(entity_2, "another_tag", 42);
MtEntity *entity_3 = mt_summon(state);
mt_tag_i64(entity_3, "some_tag", 3);
mt_tag_i64(entity_3, "another_tag", 42);
mt_tag_i64(entity_3, "listed_tag", 236512);
const char *tag_list[4] =
{
"some_tag",
"another_tag",
"listed_tag",
NULL
};
usz_t num_matching_entities = mt_query(state, tag_list, NULL, 0);
printf("Number of matching entities: %ld\n", num_matching_entities);
MtEntity *entities[8];
mt_query(state, tag_list, entities, 8);
printf("Entity 1: %p\nEntity 2: %p\nEntity 3: %p\n", (void *) entity_1, (void *) entity_2, (void *) entity_3);
usz_t entity_index = 0;
while(entity_index < num_matching_entities)
{
printf("Entity-Pointer: %p\n", (void *) entities[entity_index]);
++entity_index;
}
mt_drop(entity_1);
mt_drop(entity_2);
mt_drop(entity_3);
mt_cleanup(state);
return 0;
}

View File

@ -0,0 +1,26 @@
#include <maintree/maintree.h>
#include <stdio.h>
#define TEST_INTEGER 1337
int main()
{
MtState *state = mt_initialize("Interface Test");
MtEntity* entity = mt_summon(state);
mt_tag_i64(entity, "test-tag", TEST_INTEGER);
i64_t returned_integer = mt_get_i64_tag(entity, "test-tag");
if(returned_integer != TEST_INTEGER)
{
puts("The integer returned from the get_i64_tag-function doesn't match the tag which was put in!");
printf("Input: %d\nOutput: %ld\n", TEST_INTEGER, returned_integer);
mt_drop(entity);
mt_cleanup(state);
return -1;
}
mt_drop(entity);
mt_cleanup(state);
puts("The program reached the end; this test is considered as passed. SUCCESS!");
puts("Note: A memory leak check utility should be used to verify the success.");
return 0;
}

View File

@ -5,7 +5,7 @@
int main()
{
MtState *state = mt_initialize("Interface Test");;
MtState *state = mt_initialize("Internal Test");
MtEntity *entity = mt_summon(state);
MtEntity *looked_up_entity = mt_get_entity(state, entity->identifier);