Add timer infrastructure to engine, can now schedule lua func calls

This commit is contained in:
j4nk 2025-03-23 02:03:04 -04:00
parent 823f3b0e60
commit 0338155b3a
5 changed files with 264 additions and 1 deletions

View File

@ -5,7 +5,7 @@ BIN=open_card_table
DEBUG=-g DEBUG=-g
CFLAGS=`pkg-config --cflags lua5.3` -D_XOPEN_SOURCE=700 -D_DEFAULT_SOURCE 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) $^ $(CC) $(DEBUG) $(CFLAGS) $(INC) $(CLIB) -o $(BIN) $^
%.o: %.c %.o: %.c

11
main.c
View File

@ -5,6 +5,7 @@
#include "termbox_render.h" #include "termbox_render.h"
#include "oct_termbox_sprite.h" #include "oct_termbox_sprite.h"
#include "oct_networking.h" #include "oct_networking.h"
#include "oct_timer.h"
#include "oct_log.h" #include "oct_log.h"
#define TB_IMPL #define TB_IMPL
@ -53,6 +54,7 @@ int main(int argc, char* argv[]) {
sigaction(SIGINT, &sa, NULL); sigaction(SIGINT, &sa, NULL);
struct tb_event ev; struct tb_event ev;
// Main loop if we are using termbox
if (config.needs_termbox) { if (config.needs_termbox) {
while (!finish) { while (!finish) {
tb_clear(); tb_clear();
@ -70,9 +72,11 @@ int main(int argc, char* argv[]) {
} }
tb_present(); tb_present();
oct_network_send_msgs(); oct_network_send_msgs();
oct_timer_tick(L);
} }
} }
else { else {
// Main loop if we are not using termbox
while (!finish) { while (!finish) {
oct_network_recv_msgs(); oct_network_recv_msgs();
lua_getglobal(L, "oct_loop"); lua_getglobal(L, "oct_loop");
@ -83,6 +87,7 @@ int main(int argc, char* argv[]) {
finish = 1; finish = 1;
} }
oct_network_send_msgs(); oct_network_send_msgs();
oct_timer_tick(L);
} }
} }
deinitialize_everything(); deinitialize_everything();
@ -211,6 +216,11 @@ int initialize_everything(char* lua_file) {
deinitialize_everything(); deinitialize_everything();
return 0; return 0;
} }
// Initialize timers
oct_timer_list_initialize();
oct_timer_initialize_lua(L);
OCT_LOG_INFO("Begin running oct_init()"); OCT_LOG_INFO("Begin running oct_init()");
lua_getglobal(L, "oct_init"); lua_getglobal(L, "oct_init");
lua_pushstring(L, lua_args); lua_pushstring(L, lua_args);
@ -254,6 +264,7 @@ int deinitialize_everything() {
if (L) lua_close(L); if (L) lua_close(L);
oct_tb_sprite_list_deinitialize(); oct_tb_sprite_list_deinitialize();
oct_network_ab_deinit(); oct_network_ab_deinit();
oct_timer_list_deinitialize();
//oct_network_node_deinit(); //oct_network_node_deinit();
oct_log_deinit(); oct_log_deinit();
return 1; return 1;

179
oct_timer.c Normal file
View File

@ -0,0 +1,179 @@
#include <string.h>
#include <stdlib.h>
#include <lua5.3/lualib.h>
#include <lua5.3/lauxlib.h>
#include <time.h>
#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;
}

50
oct_timer.h Normal file
View File

@ -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 <inttypes.h>
#include <lua5.3/lualib.h>
#include <lua5.3/lauxlib.h>
#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

23
test_timers.lua Normal file
View File

@ -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