# HG changeset patch # User Medo # Date 1338837140 -7200 # Node ID 7c2eb284f9f1c1bae504b95250e1f66d53b7a298 # Parent 906e72caea7b27291c723471cf2afe38b7573975 Frontlib: Work on the callback mechanisms for IPC Work commit; It compiles and runs but will need a major overhaul tomorrow. diff -r 906e72caea7b -r 7c2eb284f9f1 project_files/frontlib/frontlib.c --- a/project_files/frontlib/frontlib.c Sun Jun 03 01:24:18 2012 +0200 +++ b/project_files/frontlib/frontlib.c Mon Jun 04 21:12:20 2012 +0200 @@ -1,13 +1,14 @@ #include "frontlib.h" #include "logging.h" #include "socket.h" -#include "ipcconn.h" +#include "ipc.h" #include #include #include #include #include +#include static int flib_initflags; @@ -39,47 +40,52 @@ } } +static void onConfigQuery(void *context) { + flib_log_i("Sending config..."); + flib_ipc ipc = (flib_ipc)context; + flib_ipc_send_messagestr(ipc, "TL"); + flib_ipc_send_messagestr(ipc, "eseed loremipsum"); + flib_ipc_send_messagestr(ipc, "escript Missions/Training/Basic_Training_-_Bazooka.lua"); +} + +static void onDisconnect(void *context) { + flib_log_i("Connection closed."); + flib_ipc_destroy((flib_ipc*)context); +} + +static void onGameEnd(void *context, int gameEndType) { + switch(gameEndType) { + case GAME_END_FINISHED: + flib_log_i("Game finished."); + flib_constbuffer demobuf = flib_ipc_getdemo(context); + flib_log_i("Writing demo (%u bytes)...", demobuf.size); + FILE *file = fopen("testdemo.dem", "wb"); + fwrite(demobuf.data, 1, demobuf.size, file); + fclose(file); + file = NULL; + break; + case GAME_END_HALTED: + flib_log_i("Game halted."); + break; + case GAME_END_INTERRUPTED: + flib_log_i("Game iterrupted."); + break; + } +} + int main(int argc, char *argv[]) { flib_init(0); - flib_ipcconn ipc = flib_ipcconn_create(true, "Medo42"); - char data[256]; - while(flib_ipcconn_state(ipc) != IPC_NOT_CONNECTED) { - flib_ipcconn_tick(ipc); - int size = flib_ipcconn_recv_message(ipc, data); - if(size>0) { - data[size]=0; - switch(data[0]) { - case 'C': - flib_log_i("Sending config..."); - flib_ipcconn_send_messagestr(ipc, "TL"); - flib_ipcconn_send_messagestr(ipc, "eseed loremipsum"); - flib_ipcconn_send_messagestr(ipc, "escript Missions/Training/Basic_Training_-_Bazooka.lua"); - break; - case '?': - flib_log_i("Sending pong..."); - flib_ipcconn_send_messagestr(ipc, "!"); - break; - case 'Q': - flib_log_i("Game interrupted."); - break; - case 'q': - flib_log_i("Game finished."); - flib_constbuffer demobuf = flib_ipcconn_getdemo(ipc); - flib_log_i("Writing demo (%u bytes)...", demobuf.size); - FILE *file = fopen("testdemo.dem", "wb"); - fwrite(demobuf.data, 1, demobuf.size, file); - fclose(file); - file = NULL; - break; - case 'H': - flib_log_i("Game halted."); - break; - } - } + flib_ipc ipc = flib_ipc_create(true, "Medo42"); + assert(ipc); + flib_ipc_onConfigQuery(ipc, &onConfigQuery, ipc); + flib_ipc_onDisconnect(ipc, &onDisconnect, &ipc); + flib_ipc_onGameEnd(ipc, &onGameEnd, ipc); + + while(ipc) { + flib_ipc_tick(ipc); } - flib_log_i("IPC connection lost."); - flib_ipcconn_destroy(&ipc); + flib_log_i("Shutting down..."); flib_quit(); return 0; } diff -r 906e72caea7b -r 7c2eb284f9f1 project_files/frontlib/ipc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/ipc.c Mon Jun 04 21:12:20 2012 +0200 @@ -0,0 +1,284 @@ +#include "ipc.h" +#include "ipcconn.h" +#include "logging.h" + +#include +#include + +typedef struct _flib_ipc { + flib_ipcconn connection; + IpcConnState oldConnState; + + void (*onConnectCb)(void*); + void *onConnectCtx; + + void (*onDisconnectCb)(void*); + void *onDisconnectCtx; + + void (*onConfigQueryCb)(void*); + void *onConfigQueryCtx; + + void (*onEngineErrorCb)(void*, const uint8_t*); + void *onEngineErrorCtx; + + void (*onGameEndCb)(void*, int); + void *onGameEndCtx; + + void (*onChatCb)(void*, const uint8_t*, int); + void *onChatCtx; + + void (*onEngineMessageCb)(void*, const uint8_t*, int); + void *onEngineMessageCtx; + + bool running; + bool destroyRequested; +} _flib_ipc; + +static void emptyCallback(void* ptr) {} +static void emptyCallback_int(void* ptr, int i) {} +static void emptyCallback_str(void* ptr, const uint8_t* str) {} +static void emptyCallback_str_int(void* ptr, const uint8_t* str, int i) {} + +static void clearCallbacks(flib_ipc ipc) { + ipc->onConnectCb = &emptyCallback; + ipc->onDisconnectCb = &emptyCallback; + ipc->onConfigQueryCb = &emptyCallback; + ipc->onEngineErrorCb = &emptyCallback_str; + ipc->onGameEndCb = &emptyCallback_int; + ipc->onChatCb = &emptyCallback_str_int; + ipc->onEngineMessageCb = &emptyCallback_str_int; +} + +flib_ipc flib_ipc_create(bool recordDemo, const char *localPlayerName) { + flib_ipc result = malloc(sizeof(_flib_ipc)); + flib_ipcconn connection = flib_ipcconn_create(recordDemo, localPlayerName); + + if(!result || !connection) { + free(result); + flib_ipcconn_destroy(&connection); + return NULL; + } + + result->connection = connection; + result->oldConnState = IPC_LISTENING; + result->running = false; + result->destroyRequested = false; + + clearCallbacks(result); + return result; +} + +void flib_ipc_destroy(flib_ipc *ipcptr) { + if(!ipcptr || !*ipcptr) { + return; + } + flib_ipc ipc = *ipcptr; + if(ipc->running) { + // The function was called from a callback of this ipc connection, + // so the tick function is still running and we delay the actual + // destruction. We ensure no further callbacks will be sent to prevent + // surprises. + clearCallbacks(ipc); + ipc->destroyRequested = true; + } else { + flib_ipcconn_destroy(&ipc->connection); + free(ipc); + } + *ipcptr = NULL; +} + +void flib_ipc_onConnect(flib_ipc ipc, void (*callback)(void* context), void* context) { + if(!ipc) { + flib_log_w("Call to flib_ipc_onConnect with ipc==null"); + return; + } + ipc->onConnectCb = callback ? callback : &emptyCallback; + ipc->onConnectCtx = context; +} + +void flib_ipc_onDisconnect(flib_ipc ipc, void (*callback)(void* context), void* context) { + if(!ipc) { + flib_log_w("Call to flib_ipc_onDisconnect with ipc==null"); + return; + } + ipc->onDisconnectCb = callback ? callback : &emptyCallback; + ipc->onDisconnectCtx = context; +} + +void flib_ipc_onConfigQuery(flib_ipc ipc, void (*callback)(void* context), void* context) { + if(!ipc) { + flib_log_w("Call to flib_ipc_onConfigQuery with ipc==null"); + return; + } + ipc->onConfigQueryCb = callback ? callback : &emptyCallback; + ipc->onConfigQueryCtx = context; +} + +void flib_ipc_onEngineError(flib_ipc ipc, void (*callback)(void* context, const uint8_t *error), void* context) { + if(!ipc) { + flib_log_w("Call to flib_ipc_onEngineError with ipc==null"); + return; + } + ipc->onEngineErrorCb = callback ? callback : &emptyCallback_str; + ipc->onEngineErrorCtx = context; +} + +void flib_ipc_onGameEnd(flib_ipc ipc, void (*callback)(void* context, int gameEndType), void* context) { + if(!ipc) { + flib_log_w("Call to flib_ipc_onGameEnd with ipc==null"); + return; + } + ipc->onGameEndCb = callback ? callback : &emptyCallback_int; + ipc->onGameEndCtx = context; +} + +void flib_ipc_onChat(flib_ipc ipc, void (*callback)(void* context, const uint8_t *messagestr, int teamchat), void* context) { + if(!ipc) { + flib_log_w("Call to flib_ipc_onChat with ipc==null"); + return; + } + ipc->onChatCb = callback ? callback : &emptyCallback_str_int; + ipc->onChatCtx = context; +} + +void flib_ipc_onEngineMessage(flib_ipc ipc, void (*callback)(void* context, const uint8_t *message, int len), void* context) { + if(!ipc) { + flib_log_w("Call to flib_ipc_onEngineMessage with ipc==null"); + return; + } + ipc->onEngineMessageCb = callback ? callback : &emptyCallback_str_int; + ipc->onEngineMessageCtx = context; +} + +static void flib_ipc_wrappedtick(flib_ipc ipc) { + if(ipc->oldConnState == IPC_NOT_CONNECTED) { + return; + } + + flib_ipcconn_tick(ipc->connection); + IpcConnState newstate = flib_ipcconn_state(ipc->connection); + if(ipc->oldConnState == IPC_LISTENING && newstate == IPC_CONNECTED) { + ipc->oldConnState = newstate; + ipc->onConnectCb(ipc->onConnectCtx); + } + + if(ipc->oldConnState == IPC_CONNECTED) { + uint8_t msgbuffer[257]; + int len; + while(!ipc->destroyRequested && (len = flib_ipcconn_recv_message(ipc->connection, msgbuffer))>=0) { + if(len<2) { + flib_log_w("Received short message from IPC (<2 bytes)"); + continue; + } + msgbuffer[len] = 0; + flib_log_i("[IPC in] %s", msgbuffer+1); + switch(msgbuffer[1]) { + case '?': + flib_ipcconn_send_messagestr(ipc->connection, "!"); + break; + case 'C': + ipc->onConfigQueryCb(ipc->onConfigQueryCtx); + break; + case 'E': + if(len>=3) { + msgbuffer[len-2] = 0; + ipc->onEngineErrorCb(ipc->onEngineErrorCtx, msgbuffer+2); + } + break; + case 'i': + // TODO + break; + case 'Q': + ipc->onGameEndCb(ipc->onGameEndCtx, GAME_END_INTERRUPTED); + break; + case 'q': + ipc->onGameEndCb(ipc->onGameEndCtx, GAME_END_FINISHED); + break; + case 'H': + ipc->onGameEndCb(ipc->onGameEndCtx, GAME_END_HALTED); + break; + case 's': + if(len>=3) { + msgbuffer[len-2] = 0; + ipc->onChatCb(ipc->onChatCtx, msgbuffer+2, 0); + } + break; + case 'b': + if(len>=3) { + msgbuffer[len-2] = 0; + ipc->onChatCb(ipc->onChatCtx, msgbuffer+2, 1); + } + break; + default: + ipc->onEngineMessageCb(ipc->onEngineMessageCtx, msgbuffer, len); + break; + } + } + } + + newstate = flib_ipcconn_state(ipc->connection); + if(newstate == IPC_NOT_CONNECTED) { + ipc->oldConnState = newstate; + ipc->onDisconnectCb(ipc->onDisconnectCtx); + } +} + +int flib_ipc_send_raw(flib_ipc ipc, void *data, size_t len) { + if(!ipc) { + flib_log_w("Call to flib_ipc_send_raw with ipc==null"); + return -1; + } + return flib_ipcconn_send_raw(ipc->connection, data, len); +} + +int flib_ipc_send_message(flib_ipc ipc, void *data, size_t len) { + if(!ipc) { + flib_log_w("Call to flib_ipc_send_message with ipc==null"); + return -1; + } + return flib_ipcconn_send_message(ipc->connection, data, len); +} + +int flib_ipc_send_messagestr(flib_ipc ipc, char *data) { + if(!ipc) { + flib_log_w("Call to flib_ipc_send_messagestr with ipc==null"); + return -1; + } + return flib_ipcconn_send_messagestr(ipc->connection, data); +} + +uint16_t flib_ipc_port(flib_ipc ipc) { + if(!ipc) { + flib_log_w("Call to flib_ipc_send_messagestr with ipc==null"); + return 0; + } + return flib_ipcconn_port(ipc->connection); +} + +flib_constbuffer flib_ipc_getdemo(flib_ipc ipc) { + if(!ipc) { + flib_log_w("Call to flib_ipc_send_messagestr with ipc==null"); + flib_constbuffer result = {NULL, 0}; + return result; + } + return flib_ipcconn_getdemo(ipc->connection); +} + +void flib_ipc_tick(flib_ipc ipc) { + if(!ipc) { + flib_log_w("Call to flib_ipc_tick with ipc==null"); + return; + } + if(ipc->running) { + flib_log_w("Call to flib_ipc_tick from a callback"); + return; + } + + ipc->running = true; + flib_ipc_wrappedtick(ipc); + ipc->running = false; + + if(ipc->destroyRequested) { + flib_ipc_destroy(&ipc); + } +} diff -r 906e72caea7b -r 7c2eb284f9f1 project_files/frontlib/ipc.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/ipc.h Mon Jun 04 21:12:20 2012 +0200 @@ -0,0 +1,39 @@ +#ifndef IPC_H_ +#define IPC_H_ + +#include "buffer.h" + +#include +#include +#include + +struct _flib_ipc; +typedef struct _flib_ipc *flib_ipc; + +typedef enum { + GAME_END_FINISHED, + GAME_END_INTERRUPTED, + GAME_END_HALTED +} flib_GameEndType; + +flib_ipc flib_ipc_create(bool recordDemo, const char *localPlayerName); +void flib_ipc_destroy(flib_ipc *ipcptr); + +void flib_ipc_onConnect(flib_ipc ipc, void (*callback)(void* context), void* context); +void flib_ipc_onDisconnect(flib_ipc ipc, void (*callback)(void* context), void* context); +void flib_ipc_onConfigQuery(flib_ipc ipc, void (*callback)(void* context), void* context); +void flib_ipc_onEngineError(flib_ipc ipc, void (*callback)(void* context, const uint8_t *error), void* context); +void flib_ipc_onGameEnd(flib_ipc ipc, void (*callback)(void* context, int gameEndType), void* context); +void flib_ipc_onChat(flib_ipc ipc, void (*callback)(void* context, const uint8_t *messagestr, int teamchat), void* context); +void flib_ipc_onEngineMessage(flib_ipc ipc, void (*callback)(void* context, const uint8_t *message, int len), void* context); + +int flib_ipc_send_raw(flib_ipc ipc, void *data, size_t len); +int flib_ipc_send_message(flib_ipc ipc, void *data, size_t len); +int flib_ipc_send_messagestr(flib_ipc ipc, char *data); + +uint16_t flib_ipc_port(flib_ipc ipc); +flib_constbuffer flib_ipc_getdemo(flib_ipc ipc); + +void flib_ipc_tick(flib_ipc ipc); + +#endif /* IPC_H_ */ diff -r 906e72caea7b -r 7c2eb284f9f1 project_files/frontlib/ipcconn.c --- a/project_files/frontlib/ipcconn.c Sun Jun 03 01:24:18 2012 +0200 +++ b/project_files/frontlib/ipcconn.c Mon Jun 04 21:12:20 2012 +0200 @@ -101,6 +101,7 @@ memcpy(chatMsg, message+2, message[0]-3); chatMsg[message[0]-3] = 0; + // If the message starts with /me, it will be displayed differently. char converted[257]; bool memessage = message[0] >= 7 && !memcmp(message+2, "/me ", 4); const char *template = memessage ? "s\x02* %s %s " : "s\x01%s: %s "; @@ -113,10 +114,6 @@ } } -/** - * Receive a single message and copy it into the data buffer. - * Returns the length of the received message, -1 when nothing is received. - */ int flib_ipcconn_recv_message(flib_ipcconn ipc, void *data) { flib_ipcconn_tick(ipc); @@ -129,15 +126,15 @@ } } - int msgsize = ipc->readBuffer[0]; - if(ipc->readBufferSize > msgsize) { + int msgsize = ipc->readBuffer[0]+1; + if(ipc->readBufferSize >= msgsize) { demo_record_from_engine(ipc, ipc->readBuffer); - memcpy(data, ipc->readBuffer+1, msgsize); - memmove(ipc->readBuffer, ipc->readBuffer+msgsize+1, ipc->readBufferSize-(msgsize+1)); - ipc->readBufferSize -= (msgsize+1); + memcpy(data, ipc->readBuffer, msgsize); + memmove(ipc->readBuffer, ipc->readBuffer+msgsize, ipc->readBufferSize-msgsize); + ipc->readBufferSize -= msgsize; return msgsize; } else if(!ipc->sock && ipc->readBufferSize>0) { - flib_log_w("Last message from engine data stream is incomplete (received %u of %u bytes)", ipc->readBufferSize-1, msgsize); + flib_log_w("Last message from engine data stream is incomplete (received %u of %u bytes)", ipc->readBufferSize, msgsize); ipc->readBufferSize = 0; return -1; } else { diff -r 906e72caea7b -r 7c2eb284f9f1 project_files/frontlib/ipcconn.h --- a/project_files/frontlib/ipcconn.h Sun Jun 03 01:24:18 2012 +0200 +++ b/project_files/frontlib/ipcconn.h Mon Jun 04 21:12:20 2012 +0200 @@ -43,10 +43,13 @@ IpcConnState flib_ipcconn_state(flib_ipcconn ipc); /** - * Receive a single message (up to 255 bytes) and copy it into the data buffer. + * Receive a single message (up to 256 bytes) and copy it into the data buffer. * Returns the length of the received message, a negative value if no message could * be read. * + * The first byte of a message is its content length, which is one less than the returned + * value. + * * Note: When a connection is closed, you probably want to call this function until * no further message is returned, to ensure you see all messages that were sent * before the connection closed.