diff --git a/CMakeLists.txt b/CMakeLists.txt index efe4448..0497996 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,7 @@ endif () add_library(${PROJECT_NAME} STATIC seika/seika.c + seika/event.c seika/file_system.c seika/logger.c seika/platform.c diff --git a/seika/ecs/ec_system.c b/seika/ecs/ec_system.c index c16508e..ab26847 100644 --- a/seika/ecs/ec_system.c +++ b/seika/ecs/ec_system.c @@ -209,7 +209,7 @@ void ska_ecs_system_event_entity_end(SkaEntity entity) { // NodeComponent* nodeComponent = (NodeComponent*)ska_ecs_component_manager_get_component_unchecked(entity, CreComponentDataIndex_NODE); // if (nodeComponent != NULL) { // // Note: Node events should not be created during this time -// ska_event_notify_observers(&nodeComponent->onSceneTreeExit, &(SESubjectNotifyPayload) { +// ska_event_notify_observers(&nodeComponent->onSceneTreeExit, &(SkaSubjectNotifyPayload) { // .data = &entity // }); // } @@ -219,7 +219,7 @@ void ska_ecs_system_event_entity_entered_scene(SkaEntity entity) { // Notify scene enter observers before calling it on systems // NodeComponent* nodeComponent = (NodeComponent*) ska_ecs_component_manager_get_component_unchecked(entity, CreComponentDataIndex_NODE); // if (nodeComponent != NULL) { -// ska_event_notify_observers(&nodeComponent->onSceneTreeEnter, &(SESubjectNotifyPayload) { +// ska_event_notify_observers(&nodeComponent->onSceneTreeEnter, &(SkaSubjectNotifyPayload) { // .data = &entity // }); // } diff --git a/seika/event.c b/seika/event.c new file mode 100644 index 0000000..fdd50cf --- /dev/null +++ b/seika/event.c @@ -0,0 +1,47 @@ +#include "event.h" + +#include "seika/memory.h" +#include "seika/assert.h" +#include "seika/data_structures/array_utils.h" + +//--- Observer ---// +SkaObserver* ska_observer_new(SkaObserverOnNotify onNotifyFunc) { + SKA_ASSERT(onNotifyFunc != NULL); + SkaObserver* observer = SKA_MEM_ALLOCATE(SkaObserver); + observer->on_notify = onNotifyFunc; + return observer; +} + +void ska_observer_delete(SkaObserver* observer) { + SKA_MEM_FREE(observer); +} + +//--- Event ---// +SkaEvent* ska_event_new() { + SkaEvent* event = SKA_MEM_ALLOCATE(SkaEvent); + event->observerCount = 0; + return event; +} + +void ska_event_delete(SkaEvent* event) { + SKA_MEM_FREE(event); +} + +bool ska_event_register_observer(SkaEvent* event, SkaObserver* observer) { + SKA_ASSERT(event != NULL); + SKA_ASSERT(observer != NULL); + SKA_ASSERT_FMT(event->observerCount + 1 < SKA_MAX_OBSERVERS, "Reached max observer count, consider increasing 'SKA_MAX_OBSERVERS'!"); + event->observers[event->observerCount++] = observer; + return true; +} + +bool ska_event_unregister_observer(SkaEvent* event, SkaObserver* observer) { + SKA_ARRAY_UTILS_REMOVE_ARRAY_ITEM(event->observers, event->observerCount, observer, NULL); + return true; +} + +void ska_event_notify_observers(SkaEvent* event, SkaSubjectNotifyPayload* payload) { + for (size_t i = 0; i < event->observerCount; i++) { + event->observers[i]->on_notify(payload); + } +} diff --git a/seika/event.h b/seika/event.h new file mode 100644 index 0000000..74b6401 --- /dev/null +++ b/seika/event.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include + +#define SKA_MAX_OBSERVERS 8 + +typedef struct SkaSubjectNotifyPayload { + void* data; // Primary data, be sure to cast properly +} SkaSubjectNotifyPayload; + +typedef void (*SkaObserverOnNotify)(SkaSubjectNotifyPayload*); + +// An observer that can subscribe to a subject +typedef struct SkaObserver { + SkaObserverOnNotify on_notify; +} SkaObserver; + +SkaObserver* ska_observer_new(SkaObserverOnNotify onNotifyFunc); +void ska_observer_delete(SkaObserver* observer); + +// A subscribable event +typedef struct SkaEvent { + size_t observerCount; + SkaObserver* observers[SKA_MAX_OBSERVERS]; +} SkaEvent; + +SkaEvent* ska_event_new(); +void ska_event_delete(SkaEvent* event); +bool ska_event_register_observer(SkaEvent* event, SkaObserver* observer); +bool ska_event_unregister_observer(SkaEvent* event, SkaObserver* observer); +void ska_event_notify_observers(SkaEvent* event, SkaSubjectNotifyPayload* payload); diff --git a/test/test.c b/test/test.c index 32c46ab..b0f4b67 100644 --- a/test/test.c +++ b/test/test.c @@ -1,6 +1,7 @@ #include #include "seika/memory.h" +#include "seika/event.h" #include "seika/input/input.h" #include "seika/data_structures/hash_map.h" #include "seika/data_structures/spatial_hash_map.h" @@ -38,7 +39,7 @@ int32 main(int32 argv, char** args) { RUN_TEST(seika_linked_list_test); RUN_TEST(seika_array2d_test); RUN_TEST(seika_asset_file_loader_test); -// RUN_TEST(seika_observer_test); + RUN_TEST(seika_observer_test); RUN_TEST(seika_curve_float_test); // RUN_TEST(seika_shader_instance_test); // RUN_TEST(seika_shader_file_parser_test); @@ -338,46 +339,46 @@ void seika_asset_file_loader_test(void) { ska_asset_file_loader_finalize(); } -//// Observer Test -//static bool hasObserved = false; -// -//void observer_func1(SESubjectNotifyPayload* payload) { -// hasObserved = true; -//} -// -//void observer_func2(SESubjectNotifyPayload* payload) { -// const int dataValue = *(int*) payload->data; -// if (dataValue == 3) { -// hasObserved = true; -// } -//} -// -//void seika_observer_test(void) { -// SEEvent* event = se_event_new(); -// // Test 1 - Simple test with passing a NULL payload -// SEObserver* observer = se_observer_new(observer_func1); -// se_event_register_observer(event, observer); -// TEST_ASSERT_EQUAL_INT(1, event->observerCount); -// se_event_notify_observers(event, NULL); -// TEST_ASSERT(hasObserved); -// se_event_unregister_observer(event, observer); -// TEST_ASSERT_EQUAL_INT(0, event->observerCount); -// hasObserved = false; -// -// // Test 2 - A slightly more complicated example filling out the payload -// se_observer_delete(observer); -// observer = se_observer_new(observer_func2); -// se_event_register_observer(event, observer); -// int dataValue = 3; -// se_event_notify_observers(event, &(SESubjectNotifyPayload) { -// .data = &dataValue -// }); -// TEST_ASSERT(hasObserved); -// -// // Clean up -// se_event_delete(event); -// se_observer_delete(observer); -//} +// Observer Test +static bool hasObserved = false; + +void observer_func1(SkaSubjectNotifyPayload* payload) { + hasObserved = true; +} + +void observer_func2(SkaSubjectNotifyPayload* payload) { + const int dataValue = *(int*) payload->data; + if (dataValue == 3) { + hasObserved = true; + } +} + +void seika_observer_test(void) { + SkaEvent* event = ska_event_new(); + // Test 1 - Simple test with passing a NULL payload + SkaObserver* observer = ska_observer_new(observer_func1); + ska_event_register_observer(event, observer); + TEST_ASSERT_EQUAL_INT(1, event->observerCount); + ska_event_notify_observers(event, NULL); + TEST_ASSERT(hasObserved); + ska_event_unregister_observer(event, observer); + TEST_ASSERT_EQUAL_INT(0, event->observerCount); + hasObserved = false; + + // Test 2 - A slightly more complicated example filling out the payload + ska_observer_delete(observer); + observer = ska_observer_new(observer_func2); + ska_event_register_observer(event, observer); + int dataValue = 3; + ska_event_notify_observers(event, &(SkaSubjectNotifyPayload) { + .data = &dataValue + }); + TEST_ASSERT(hasObserved); + + // Clean up + ska_event_delete(event); + ska_observer_delete(observer); +} void seika_curve_float_test(void) { SkaCurveFloat curve = { .controlPointCount = 0 };