diff --git a/Makefile b/Makefile index 98dde40..b9ae452 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ BIN=open_card_table DEBUG=-g CFLAGS=`pkg-config --cflags lua5.3` -D_XOPEN_SOURCE=700 -D_DEFAULT_SOURCE -open_card_table: main.o oct_networking.o oct_termbox_sprite.o oct_log.o +open_card_table: main.o oct_networking.o oct_termbox_sprite.o oct_timer.o oct_log.o $(CC) $(DEBUG) $(CFLAGS) $(INC) $(CLIB) -o $(BIN) $^ %.o: %.c diff --git a/main.c b/main.c index 94f67e3..0b206f1 100644 --- a/main.c +++ b/main.c @@ -5,6 +5,7 @@ #include "termbox_render.h" #include "oct_termbox_sprite.h" #include "oct_networking.h" +#include "oct_timer.h" #include "oct_log.h" #define TB_IMPL @@ -53,6 +54,7 @@ int main(int argc, char* argv[]) { sigaction(SIGINT, &sa, NULL); struct tb_event ev; + // Main loop if we are using termbox if (config.needs_termbox) { while (!finish) { tb_clear(); @@ -70,9 +72,11 @@ int main(int argc, char* argv[]) { } tb_present(); oct_network_send_msgs(); + oct_timer_tick(L); } } else { + // Main loop if we are not using termbox while (!finish) { oct_network_recv_msgs(); lua_getglobal(L, "oct_loop"); @@ -83,6 +87,7 @@ int main(int argc, char* argv[]) { finish = 1; } oct_network_send_msgs(); + oct_timer_tick(L); } } deinitialize_everything(); @@ -211,6 +216,11 @@ int initialize_everything(char* lua_file) { deinitialize_everything(); return 0; } + + // Initialize timers + oct_timer_list_initialize(); + oct_timer_initialize_lua(L); + OCT_LOG_INFO("Begin running oct_init()"); lua_getglobal(L, "oct_init"); lua_pushstring(L, lua_args); @@ -254,6 +264,7 @@ int deinitialize_everything() { if (L) lua_close(L); oct_tb_sprite_list_deinitialize(); oct_network_ab_deinit(); + oct_timer_list_deinitialize(); //oct_network_node_deinit(); oct_log_deinit(); return 1; diff --git a/oct_timer.c b/oct_timer.c new file mode 100644 index 0000000..bb69403 --- /dev/null +++ b/oct_timer.c @@ -0,0 +1,179 @@ +#include +#include +#include +#include +#include + +#include "oct_timer.h" +#include "oct_log.h" + +struct oct_timer_list_ oct_timer_list; +int64_t prev_millis; + +int oct_timer_list_initialize() { + OCT_LOG_INFO("Initializing timer list") + oct_timer_list.size = 0; + oct_timer_list.first = NULL; + oct_timer_list.last = NULL; + + struct timespec now; + timespec_get(&now, TIME_UTC); + prev_millis = ((int64_t) now.tv_sec) * 1000 + ((int64_t) now.tv_nsec) / 1000000; + + + return 1; +} + +int oct_timer_list_deinitialize() { + struct oct_timer_elem* crawler = oct_timer_list.first; + while (crawler != NULL) { + struct oct_timer_elem* temp = crawler->next; + free(crawler); + crawler = temp; + } + oct_timer_list.first = NULL; + oct_timer_list.last = NULL; + oct_timer_list.size = 0; + + return 1; +} + +int oct_timer_register(lua_State *L) { + // Gather the arguments + char id[OCT_TIMER_MAXIMUM_ID_LENGTH]; + int64_t millis; + char function[OCT_TIMER_MAXIMUM_ID_LENGTH]; + char argument[OCT_TIMER_MAXIMUM_ID_LENGTH]; + strncpy(id, luaL_checkstring(L, -4), OCT_TIMER_MAXIMUM_ID_LENGTH); + millis = luaL_checkinteger(L, -3); + strncpy(function, luaL_checkstring(L, -2), OCT_TIMER_MAXIMUM_FUNCTION_NAME_LENGTH); + strncpy(argument, luaL_checkstring(L, -1), OCT_TIMER_MAXIMUM_ARGUMENT_LENGTH); + + // Check for any duplicate ids in the list + if (oct_timer_find(id)) { + OCT_LOG_ERROR("Tried to register a timer with a duplicate ID"); + lua_pushnil(L); + return 1; + } + + // We're good to go, insert at the end + struct oct_timer_elem* e = (struct oct_timer_elem*)malloc(sizeof(struct oct_timer_elem)); + strncpy(e->id, id, OCT_TIMER_MAXIMUM_ID_LENGTH); + e->millis = millis; + strncpy(e->lua_function, function, OCT_TIMER_MAXIMUM_FUNCTION_NAME_LENGTH); + strncpy(e->lua_argument, argument, OCT_TIMER_MAXIMUM_ARGUMENT_LENGTH); + if (!oct_timer_list.first) { + oct_timer_list.first = e; + } + e->next = NULL; + e->prev = oct_timer_list.last; + if (oct_timer_list.last) { + oct_timer_list.last->next = e; + } + oct_timer_list.last = e; + oct_timer_list.size++; + + // not sure if i have to return something + return 0; +} + +struct oct_timer_elem* oct_timer_find(char* id) { + struct oct_timer_elem* crawler = oct_timer_list.first; + while (crawler != NULL) { + if (strncmp(id, crawler->id, OCT_TIMER_MAXIMUM_ID_LENGTH) == 0) { + return crawler; + } + crawler = crawler->next; + } + return NULL; +} + +int oct_timer_unregister(char* id, int do_free) { + struct oct_timer_elem* e = oct_timer_find(id); + if (!e) { + OCT_LOG_WARNING("Tried to unregister a nonexistent timer"); + return 0; + } + + if (e == oct_timer_list.first) { + oct_timer_list.first = e->next; + if (e->next) { + e->next->prev = NULL; + } + } + else if (e == oct_timer_list.last) { + oct_timer_list.last = e->prev; + if (e->prev) { + e->prev->next = NULL; + } + } + else { + e->next->prev = e->prev; + e->prev->next = e->next; + } + + oct_timer_list.size--; + + if (do_free) { + free(e); + } + + // Not sure if I have to return something + return 0; +} + +int oct_timer_unregister_lua(lua_State *L) { + char id[OCT_TIMER_MAXIMUM_ID_LENGTH]; + strncpy(id, luaL_checkstring(L, -1), OCT_TIMER_MAXIMUM_ID_LENGTH); + + int res = oct_timer_unregister(id, 1); + if (!res) { + lua_pushnil(L); + return 1; + } + return 0; +} + +int oct_timer_initialize_lua(lua_State *L) { + OCT_LOG_INFO("Exporting timer lua functions"); + lua_pushcfunction(L, oct_timer_register); + lua_setglobal(L, "oct_timer_register"); + lua_pushcfunction(L, oct_timer_unregister_lua); + lua_setglobal(L, "oct_timer_unregister"); + // I don't think we need a metatable... + return 1; +} + +int oct_timer_tick(lua_State *L) { + struct timespec now; + timespec_get(&now, TIME_UTC); + int64_t millis = ((int64_t) now.tv_sec) * 1000 + ((int64_t) now.tv_nsec) / 1000000; + int64_t elapsed = millis - prev_millis; + + struct oct_timer_elem* crawler = oct_timer_list.first; + while (crawler != NULL) { + crawler->millis -= elapsed; + if (crawler->millis <= 0) { + struct oct_timer_elem* temp = crawler; + crawler = crawler->next; + // First, remove the timer + // lua can add a timer at the end of the function, don't want to complicate things by making the developer come up with a unique name + // So we unregister it, but don't free it yet + oct_timer_unregister(temp->id, 0); + + // call lua function with argument + lua_getglobal(L, temp->lua_function); + lua_pushstring(L, temp->lua_argument); + lua_call(L, 1, 0); + + // Now free the timer + free(temp); + } + else { + crawler = crawler->next; + } + } + + prev_millis = millis; + return 0; +} diff --git a/oct_timer.h b/oct_timer.h new file mode 100644 index 0000000..b8ea90f --- /dev/null +++ b/oct_timer.h @@ -0,0 +1,50 @@ +/** + Timer library for OCT + Lua functions will be able to register a function to be called after a certain amount of time. Timer values are updated on every call to the rendering loop, and when timer hits 0 the corresponding function is called + Optionally, we can also specify a string argument to be passed to the function when called + **/ +#ifndef OCT_TIMER_H +#define OCT_TIMER_H + +#include +#include +#include + +#define OCT_TIMER_MAXIMUM_FUNCTION_NAME_LENGTH 128 +#define OCT_TIMER_MAXIMUM_ARGUMENT_LENGTH 128 +#define OCT_TIMER_MAXIMUM_ID_LENGTH 128 +#define OCT_TIMER_INITIAL_NUM_TIMERS 10 +#define OCT_TIMER_LIST_REALLOC_LENGTH 10 + +struct oct_timer_elem { + char id[OCT_TIMER_MAXIMUM_ID_LENGTH]; + int64_t millis; + char lua_function[OCT_TIMER_MAXIMUM_FUNCTION_NAME_LENGTH]; + char lua_argument[OCT_TIMER_MAXIMUM_ARGUMENT_LENGTH]; + struct oct_timer_elem* next; + struct oct_timer_elem* prev; +}; + +// Timer list is a doubly-linked list with O(1) inserts +// I can't imagine we're cancelling timers that often so this is fine +// Instead I think we will be inserting most of the time +// I could optimize this by inserting new timers in order of time to execution, but I don't see a reason to optimize yet +// O(1) inserts, O(n) accesses +struct oct_timer_list_ { + uint32_t size; + struct oct_timer_elem* first; + struct oct_timer_elem* last; +}; + +extern struct oct_timer_list_ oct_timer_list; + +int oct_timer_list_initialize(); +int oct_timer_list_deinitialize(); +int oct_timer_initialize_lua(lua_State *L); +int oct_timer_register(lua_State *L); +int oct_timer_unregister_lua(lua_State *L); +int oct_timer_unregister(char* id, int do_free); // free = 0 => depend on user to free the unregistered elem, free = 1 => free the unregistered elem +struct oct_timer_elem* oct_timer_find(char* id); +int oct_timer_tick(lua_State *L); + +#endif diff --git a/test_timers.lua b/test_timers.lua new file mode 100644 index 0000000..ee39811 --- /dev/null +++ b/test_timers.lua @@ -0,0 +1,23 @@ +require("oct_utils") + +function emit_message(arg) + OCT_LOG_INFO("EMIT_MESSAGE_CALLED!") + oct_timer_register("emit_message_timer", 500, "emit_message", "") +end + +function emit_message2(arg) + OCT_LOG_INFO("EMIT_MESSAGE2 CALLED!") + oct_timer_register("emit_message_timer2", 100, "emit_message2", "") +end + + +function oct_init() + oct_timer_register("emit_message_timer", 500, "emit_message", "") + oct_timer_register("emit_message_timer2", 100, "emit_message2", "") + return OCT_NOT_NEEDS_NETWORKING, OCT_NOT_NEEDS_TERMBOX; +end + +function oct_loop(key) + +end +