# HG changeset patch # User Medo # Date 1338640012 -7200 # Node ID fe76d24a25d7b7bf1ca8720151987d7bad0121cb # Parent c42949cfdd929595b8fd2d24a011f471ebf8dfdc Demo recording for the frontend library diff -r c42949cfdd92 -r fe76d24a25d7 frontlib/frontlib.c --- a/frontlib/frontlib.c Thu May 31 18:54:40 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,65 +0,0 @@ -#include "frontlib.h" -#include "logging.h" -#include "nonblocksockets.h" -#include "ipcconn.h" - -#include -#include -#include -#include -#include - -static int flib_initflags; - -int flib_init(int flags) { - flib_initflags = flags; - - if(!(flib_initflags | FRONTLIB_SDL_ALREADY_INITIALIZED)) { - if(SDL_Init(0)==-1) { - flib_log_e("Error in SDL_Init: %s", SDL_GetError()); - return -1; - } - } - - if(SDLNet_Init()==-1) { - flib_log_e("Error in SDLNet_Init: %s", SDLNet_GetError()); - if(!(flib_initflags | FRONTLIB_SDL_ALREADY_INITIALIZED)) { - SDL_Quit(); - } - return -1; - } - - flib_ipcconn_init(); - return 0; -} - -void flib_quit() { - flib_ipcconn_quit(); - - SDLNet_Quit(); - if(!(flib_initflags | FRONTLIB_SDL_ALREADY_INITIALIZED)) { - SDL_Quit(); - } -} - -int main(int argc, char *argv[]) { - flib_init(0); - int port = flib_ipcconn_listen(); - printf("%i\n", port); - fflush(stdout); - char data[256]; - while(flib_ipcconn_state() != IPC_NOT_CONNECTED) { - flib_ipcconn_tick(); - int size = flib_ipcconn_recv_message(data); - if(size>0) { - data[size]=0; - flib_log_i("IPC IN: %s", data); - if(data[0]=='?') { - flib_log_i("IPC OUT: !"); - flib_ipcconn_send_message("!", 1); - } - } - } - flib_log_i("IPC connection lost."); - return 0; -} diff -r c42949cfdd92 -r fe76d24a25d7 frontlib/frontlib.h --- a/frontlib/frontlib.h Thu May 31 18:54:40 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ -/* - * Public header file for the hedgewars frontent networking library. - * - * This is the only header you should need to include from frontend code. - */ - -#ifndef FRONTLIB_H_ -#define FRONTLIB_H_ - -#define FRONTLIB_SDL_ALREADY_INITIALIZED 1 - -/** - * Call this function before anything else in this library. - * - * If the calling program uses SDL, it needs to call SDL_Init before initializing - * this library and then pass FRONTLIB_SDL_ALREADY_INITIALIZED as flag to this function. - * - * Otherwise, pass 0 to let this library handle SDL_Init an SDL_Quit itself. - * - * Returns 0 on success, -1 on error. - */ -int flib_init(int flags); - -/** - * Free resources associated with the library. Call this function once - * the library is no longer needed. You can re-initialize the library by calling - * flib_init again. - */ -void flib_quit(); - -#endif /* FRONTLIB_H_ */ diff -r c42949cfdd92 -r fe76d24a25d7 frontlib/ipcconn.c --- a/frontlib/ipcconn.c Thu May 31 18:54:40 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,134 +0,0 @@ -#include "ipcconn.h" -#include "logging.h" -#include "nonblocksockets.h" - -#include -#include -static TCPsocket ipcListenSocket; -static NonBlockSocket ipcConnSocket; - -static uint8_t ipcReadBuffer[256]; -static int ipcReadBufferSize; - -void flib_ipcconn_init() { - ipcListenSocket = NULL; - ipcConnSocket = NULL; - ipcReadBufferSize = 0; -} - -void flib_ipcconn_quit() { - flib_ipcconn_close(); -} - -int flib_ipcconn_listen() { - if(ipcListenSocket || ipcConnSocket) { - flib_log_e("flib_ipcconn_listen: Already listening or connected."); - return -1; - } - IPaddress addr; - addr.host = INADDR_ANY; - - /* SDL_net does not seem to have a way to listen on a random unused port - and find out which port that is, so let's try to find one ourselves. */ - // TODO: Is socket binding fail-fast on all platforms? - srand(time(NULL)); - rand(); - for(int i=0; i<1000; i++) { - // IANA suggests using ports in the range 49152-65535 for things like this - int ipcPort = 49152+(rand()%(65535-49152)); - SDLNet_Write16(ipcPort, &addr.port); - ipcListenSocket = SDLNet_TCP_Open(&addr); - if(!ipcListenSocket) { - flib_log_w("Failed to start an IPC listening socket on port %i: %s", ipcPort, SDLNet_GetError()); - } else { - flib_log_i("Listening for IPC connections on port %i.", ipcPort); - return ipcPort; - } - } - flib_log_e("Unable to find a free port for IPC."); - return -1; -} - -void flib_ipcconn_close() { - if(ipcListenSocket) { - SDLNet_TCP_Close(ipcListenSocket); - ipcListenSocket = NULL; - } - flib_nbsocket_close(&ipcConnSocket); - ipcReadBufferSize = 0; -} - -IpcConnState flib_ipcconn_state() { - if(ipcConnSocket) { - return IPC_CONNECTED; - } else if(ipcListenSocket) { - return IPC_LISTENING; - } else { - return IPC_NOT_CONNECTED; - } -} - -/** - * 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(void *data) { - flib_ipcconn_tick(); - - if(ipcConnSocket) { - int size = flib_nbsocket_recv(ipcConnSocket, ipcReadBuffer+ipcReadBufferSize, sizeof(ipcReadBuffer)-ipcReadBufferSize); - if(size>=0) { - ipcReadBufferSize += size; - } else { - flib_nbsocket_close(&ipcConnSocket); - } - } - - int msgsize = ipcReadBuffer[0]; - if(ipcReadBufferSize > msgsize) { - memcpy(data, ipcReadBuffer+1, msgsize); - memmove(ipcReadBuffer, ipcReadBuffer+msgsize+1, ipcReadBufferSize-(msgsize+1)); - ipcReadBufferSize -= (msgsize+1); - return msgsize; - } else if(!ipcConnSocket && ipcReadBufferSize>0) { - flib_log_w("Last message from engine data stream is incomplete (received %u of %u bytes)", ipcReadBufferSize-1, msgsize); - ipcReadBufferSize = 0; - return -1; - } else { - return -1; - } -} - -int flib_ipcconn_send_message(void *data, size_t len) { - flib_ipcconn_tick(); - - if(!ipcConnSocket) { - flib_log_w("flib_ipcconn_send_message: Not connected."); - return -1; - } - if(len>255) { - flib_log_e("Attempt to send too much data to the engine in a single message."); - return -1; - } - - uint8_t sendbuf[256]; - sendbuf[0] = len; - memcpy(sendbuf+1, data, len); - if(flib_nbsocket_blocksend(ipcConnSocket, sendbuf, len+1) < len+1) { - flib_log_w("Failed or incomplete ICP write: engine connection lost."); - flib_nbsocket_close(&ipcConnSocket); - return -1; - } else { - return 0; - } -} - -void flib_ipcconn_tick() { - if(!ipcConnSocket && ipcListenSocket) { - ipcConnSocket = flib_nbsocket_accept(ipcListenSocket, true); - if(ipcConnSocket) { - SDLNet_TCP_Close(ipcListenSocket); - ipcListenSocket = NULL; - } - } -} diff -r c42949cfdd92 -r fe76d24a25d7 frontlib/ipcconn.h --- a/frontlib/ipcconn.h Thu May 31 18:54:40 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,67 +0,0 @@ -/* - * Low-level protocol support for the IPC connection to the engine. - */ - -#ifndef IPCCONN_H_ -#define IPCCONN_H_ - -#include - -typedef enum {IPC_NOT_CONNECTED, IPC_LISTENING, IPC_CONNECTED} IpcConnState; - -/** - * Called by flib_init(). Initialize everything related to ipc. - */ -void flib_ipcconn_init(); - -/** - * Called by flib_quit(). Free resources and shut down. - */ -void flib_ipcconn_quit(); - -/** - * Start listening for a connection from the engine. The system has to be in state - * IPC_NOT_CONNECTED when calling this function. - * - * Returns the port we started listening on, or a negative value if there is an error. - * - * We stop listening once a connection has been established, so if you want to start - * the engine again and talk to it you need to call this function again after the old - * connection is closed. - */ -int flib_ipcconn_listen(); - -/** - * Close the current IPC connection and/or stop listening for an incoming one. - * This also discards all unread messages. - */ -void flib_ipcconn_close(); - -/** - * Determine the current connection state - */ -IpcConnState flib_ipcconn_state(); - -/** - * Receive a single message (up to 255 bytes) and copy it into the data buffer. - * Returns the length of the received message, a negative value if no message could - * be read. - */ -int flib_ipcconn_recv_message(void *data); - -/** - * Write a single message (up to 255 bytes) to the engine. This call blocks until the - * message is completely written or the connection is closed or an error occurs. - * - * Calling this function in a state other than IPC_CONNECTED will fail immediately. - * Returns a negative value on failure. - */ -int flib_ipcconn_send_message(void *data, size_t len); - -/** - * Call regularly to allow background work to proceed - */ -void flib_ipcconn_tick(); - -#endif /* IPCCONN_H_ */ - diff -r c42949cfdd92 -r fe76d24a25d7 frontlib/logging.c --- a/frontlib/logging.c Thu May 31 18:54:40 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +0,0 @@ -#include "logging.h" - -#include -#include -#include - -char* flib_format_ip(uint32_t numip) { - static char ip[16]; - snprintf(ip, 16, "%u.%u.%u.%u", numip>>24, (numip>>16)&0xff, (numip>>8)&0xff, numip&0xff); - return ip; -} - -static void log_time(FILE *file) { - time_t timer; - char buffer[25]; - struct tm* tm_info; - - time(&timer); - tm_info = localtime(&timer); - - strftime(buffer, 25, "%Y-%m-%d %H:%M:%S", tm_info); - fprintf(file, "%s", buffer); -} - -static void flib_vflog(FILE *file, const char *prefix, const char *fmt, va_list args) { - log_time(file); - fprintf(file, " [%s]", prefix); - vfprintf(file, fmt, args); - fprintf(file, "\n"); -} - -void flib_log_e(const char *fmt, ...) { - va_list argp; - va_start(argp, fmt); - flib_vflog(stderr, "E", fmt, argp); - va_end(argp); -} - -void flib_log_w(const char *fmt, ...) { - va_list argp; - va_start(argp, fmt); - flib_vflog(stdout, "W", fmt, argp); - va_end(argp); -} - -void flib_log_i(const char *fmt, ...) { - va_list argp; - va_start(argp, fmt); - flib_vflog(stdout, "I", fmt, argp); - va_end(argp); -} diff -r c42949cfdd92 -r fe76d24a25d7 frontlib/logging.h --- a/frontlib/logging.h Thu May 31 18:54:40 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,16 +0,0 @@ -/* - * - */ - -#ifndef LOGGING_H_ -#define LOGGING_H_ - -#include - -char* flib_format_ip(uint32_t numip); - -void flib_log_e(const char *fmt, ...); -void flib_log_w(const char *fmt, ...); -void flib_log_i(const char *fmt, ...); - -#endif /* LOGGING_H_ */ diff -r c42949cfdd92 -r fe76d24a25d7 frontlib/nonblocksockets.c --- a/frontlib/nonblocksockets.c Thu May 31 18:54:40 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,76 +0,0 @@ -#include "nonblocksockets.h" -#include "logging.h" -#include - -static uint32_t get_peer_ip(TCPsocket sock) { - IPaddress *addr = SDLNet_TCP_GetPeerAddress(sock); - return SDLNet_Read32(&addr->host); -} - -static bool connection_is_local(TCPsocket sock) { - return get_peer_ip(sock) == (uint32_t)((127UL<<24)+1); // 127.0.0.1 -} - -void flib_nbsocket_close(NonBlockSocket *nbsockptr) { - NonBlockSocket nbsock = *nbsockptr; - if(nbsock!=NULL) { - SDLNet_DelSocket(nbsock->sockset, (SDLNet_GenericSocket)nbsock->sock); - SDLNet_TCP_Close(nbsock->sock); - SDLNet_FreeSocketSet(nbsock->sockset); - } - free(nbsock); - *nbsockptr = NULL; -} - -NonBlockSocket flib_nbsocket_accept(TCPsocket listensocket, bool localOnly) { - NonBlockSocket result = NULL; - if(!listensocket) { - flib_log_e("Attempt to accept a connection on a NULL socket."); - return NULL; - } - while(result==NULL) { - TCPsocket sock = SDLNet_TCP_Accept(listensocket); - if(!sock) { - // No incoming connections - return NULL; - } - if(localOnly && !connection_is_local(sock)) { - flib_log_i("Rejected nonlocal connection attempt from %s", flib_format_ip(get_peer_ip(sock))); - SDLNet_TCP_Close(sock); - } else { - result = malloc(sizeof(_NonBlockSocket)); - if(result==NULL) { - flib_log_e("Out of memory!"); - SDLNet_TCP_Close(sock); - return NULL; - } - result->sock = sock; - result->sockset = SDLNet_AllocSocketSet(1); - if(result->sockset==NULL) { - flib_log_e("Out of memory!"); - SDLNet_TCP_Close(sock); - free(result); - return NULL; - } - SDLNet_AddSocket(result->sockset, (SDLNet_GenericSocket)result->sock); - } - } - return result; -} - -int flib_nbsocket_recv(NonBlockSocket sock, void *data, int maxlen) { - if(!sock) { - flib_log_e("Attempt to receive on a NULL socket."); - return -1; - } - int readySockets = SDLNet_CheckSockets(sock->sockset, 0); - if(readySockets>0) { - int size = SDLNet_TCP_Recv(sock->sock, data, maxlen); - return size>0 ? size : -1; - } else if(readySockets==0) { - return 0; - } else { - flib_log_e("Error in select system call: %s", SDLNet_GetError()); - return -1; - } -} diff -r c42949cfdd92 -r fe76d24a25d7 frontlib/nonblocksockets.h --- a/frontlib/nonblocksockets.h Thu May 31 18:54:40 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +0,0 @@ -/* - * nonblocksockets.h - * - * Created on: 31.05.2012 - * Author: simmax - */ - -#ifndef NONBLOCKSOCKETS_H_ -#define NONBLOCKSOCKETS_H_ - -#include -#include - -typedef struct { - TCPsocket sock; - SDLNet_SocketSet sockset; -} _NonBlockSocket; - -typedef _NonBlockSocket *NonBlockSocket; - -/** - * Close the indicated socket, free its memory and set it to NULL. - * If the socket is already NULL, nothing happens. - */ -void flib_nbsocket_close(NonBlockSocket *socket); - -/** - * Try to accept a connection from a listening socket. - * if localOnly is true, this will only accept connections which came from 127.0.0.1 - * Returns NULL if nothing can be accepted. - */ -NonBlockSocket flib_nbsocket_accept(TCPsocket listensocket, bool localOnly); - -/** - * Attempt to receive up to maxlen bytes from the socket, but does not - * block if nothing is available. - * Returns the ammount of data received, 0 if there was nothing to receive, - * or a negative number if the connection was closed or an error occurred. - */ -int flib_nbsocket_recv(NonBlockSocket sock, void *data, int maxlen); - -/** - * We can't do a nonblocking send over SDL_net, so this function just forwards - * to SDLNet_TCP_Send for convenience, which blocks until all data is sent or an - * error occurs. The ammount of data actually sent is returned, negative value on error. - */ -static inline int flib_nbsocket_blocksend(NonBlockSocket sock, void *data, int len) { - return SDLNet_TCP_Send(sock->sock, data, len); -} - -#endif /* NONBLOCKSOCKETS_H_ */ diff -r c42949cfdd92 -r fe76d24a25d7 project_files/frontlib/buffer.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/buffer.c Sat Jun 02 14:26:52 2012 +0200 @@ -0,0 +1,90 @@ +#include "buffer.h" +#include "logging.h" + +#include +#include +#include + +typedef struct _flib_vector { + void *data; + size_t size; + size_t capacity; +} _flib_vector; + +flib_vector flib_vector_create() { + flib_vector result = malloc(sizeof(_flib_vector)); + if(result == NULL) { + return NULL; + } + result->data = malloc(16); + if(result->data == NULL) { + free(result); + return NULL; + } + result->size = 0; + result->capacity = 16; + return result; +} + +void flib_vector_destroy(flib_vector *vec) { + if(vec && *vec) { + free((*vec)->data); + free(*vec); + *vec = NULL; + } +} + +static void try_realloc(flib_vector vec, size_t newCapacity) { + void *newData = realloc(vec->data, newCapacity); + if(newData) { + vec->data = newData; + vec->capacity = newCapacity; + } +} + +static size_t getFreeCapacity(flib_vector vec) { + return vec->capacity - vec->size; +} + +int flib_vector_append(flib_vector vec, const void *data, size_t len) { + if(getFreeCapacity(vec) < len) { + // Resize exponentially for constant amortized time, + // But at least by as much as we need of course, + // and be extra careful with integer overflows... + size_t extraCapacity = (vec->capacity)/2; + + size_t minExtraCapacity = len - getFreeCapacity(vec); + if(extraCapacity < minExtraCapacity) { + extraCapacity = minExtraCapacity; + } + + if(extraCapacity <= SIZE_MAX - vec->capacity) { + try_realloc(vec, vec->capacity+extraCapacity); + } + + // Check if we were able to resize. + // If not, try to allocate at least what we need... + if(getFreeCapacity(vec) < len) { + try_realloc(vec, vec->capacity+minExtraCapacity); + + // Still not working? Then we fail. + if(getFreeCapacity(vec) < len) { + return 0; + } + } + } + + memmove(vec->data + vec->size, data, len); + vec->size += len; + return len; +} + +flib_buffer flib_vector_as_buffer(flib_vector vec) { + flib_buffer result = {vec->data, vec->size}; + return result; +} + +flib_constbuffer flib_vector_as_constbuffer(flib_vector vec) { + flib_constbuffer result = {vec->data, vec->size}; + return result; +} diff -r c42949cfdd92 -r fe76d24a25d7 project_files/frontlib/buffer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/buffer.h Sat Jun 02 14:26:52 2012 +0200 @@ -0,0 +1,58 @@ +#ifndef BUFFER_H_ +#define BUFFER_H_ + +#include +#include + +/** + * A simple struct to hold both the pointer to an array and its size, + * for e.g. conveniently returning it from a function. + * + * Convention: Size is zero iff data is a NULL pointer. + */ +typedef struct { + void *data; + size_t size; +} flib_buffer; + +/** + * Just like flib_buffer, but the contents are not supposed to be modified. + */ +typedef struct { + const void *data; + size_t size; +} flib_constbuffer; + +/** + * Simple variable-capacity data structure (opaque type). + */ +struct _flib_vector; +typedef struct _flib_vector *flib_vector; + +/** + * Create a new vector. Needs to be destroyed again later with flib_vector_destroy. + * May return NULL if memory runs out. + */ +flib_vector flib_vector_create(); + +/** + * Free the memory of this vector and set it to NULL. + */ +void flib_vector_destroy(flib_vector *vec); + +/** + * Append the provided data to the end of the vector, enlarging it as required. + * Returns the ammount of data appended, which is either len (success) or 0 (out of memory). + * The vector remains unchanged if an out of memory situation occurs. + */ +int flib_vector_append(flib_vector vec, const void *data, size_t len); + +/** + * Return a buffer or constbuffer pointing to the current contents of the vector. + * These will become invalid if the vector size or capacity is changed. + */ +flib_buffer flib_vector_as_buffer(flib_vector vec); +flib_constbuffer flib_vector_as_constbuffer(flib_vector vec); + + +#endif diff -r c42949cfdd92 -r fe76d24a25d7 project_files/frontlib/events.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/events.c Sat Jun 02 14:26:52 2012 +0200 @@ -0,0 +1,2 @@ +#include "events.h" + diff -r c42949cfdd92 -r fe76d24a25d7 project_files/frontlib/events.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/events.h Sat Jun 02 14:26:52 2012 +0200 @@ -0,0 +1,14 @@ +/* + * events.h + * + * Created on: 01.06.2012 + * Author: simmax + */ + +#ifndef EVENTS_H_ +#define EVENTS_H_ + + + + +#endif /* EVENTS_H_ */ diff -r c42949cfdd92 -r fe76d24a25d7 project_files/frontlib/frontlib.c --- a/project_files/frontlib/frontlib.c Thu May 31 18:54:40 2012 +0200 +++ b/project_files/frontlib/frontlib.c Sat Jun 02 14:26:52 2012 +0200 @@ -43,10 +43,8 @@ } int main(int argc, char *argv[]) { - flib_init(0); - int port = flib_ipcconn_listen(); - printf("%i\n", port); - fflush(stdout); + flib_init(FRONTLIB_SDL_ALREADY_INITIALIZED); + flib_ipcconn_start(true); char data[256]; while(flib_ipcconn_state() != IPC_NOT_CONNECTED) { flib_ipcconn_tick(); @@ -54,12 +52,36 @@ if(size>0) { data[size]=0; flib_log_i("IPC IN: %s", data); - if(data[0]=='?') { - flib_log_i("IPC OUT: !"); - flib_ipcconn_send_message("!", 1); + switch(data[0]) { + case 'C': + flib_log_i("Sending config..."); + flib_ipcconn_send_messagestr("TL"); + flib_ipcconn_send_messagestr("eseed loremipsum"); + flib_ipcconn_send_messagestr("escript Missions/Training/Basic_Training_-_Bazooka.lua"); + break; + case '?': + flib_log_i("Sending pong..."); + flib_ipcconn_send_messagestr("!"); + break; + case 'Q': + flib_log_i("Game interrupted."); + break; + case 'q': + flib_log_i("Game finished."); + flib_constbuffer demobuf = flib_ipcconn_getdemo(); + flib_log_i("Writing demo (%u bytes)...", demobuf.size); + FILE *file = fopen("testdemo.dem", "w"); + fwrite(demobuf.data, 1, demobuf.size, file); + fclose(file); + file = NULL; + break; + case 'H': + flib_log_i("Game halted."); + break; } } } flib_log_i("IPC connection lost."); + flib_quit(); return 0; } diff -r c42949cfdd92 -r fe76d24a25d7 project_files/frontlib/ipcconn.c --- a/project_files/frontlib/ipcconn.c Thu May 31 18:54:40 2012 +0200 +++ b/project_files/frontlib/ipcconn.c Sat Jun 02 14:26:52 2012 +0200 @@ -4,23 +4,32 @@ #include #include +#include +#include + static TCPsocket ipcListenSocket; static NonBlockSocket ipcConnSocket; static uint8_t ipcReadBuffer[256]; static int ipcReadBufferSize; +static flib_vector demoBuffer; +static char localPlayerName[255]; + void flib_ipcconn_init() { ipcListenSocket = NULL; ipcConnSocket = NULL; ipcReadBufferSize = 0; + demoBuffer=NULL; + strncpy(localPlayerName, "Local Player", 255); } void flib_ipcconn_quit() { + flib_vector_destroy(&demoBuffer); flib_ipcconn_close(); } -int flib_ipcconn_listen() { +int flib_ipcconn_start(bool recordDemo) { if(ipcListenSocket || ipcConnSocket) { flib_log_e("flib_ipcconn_listen: Already listening or connected."); return -1; @@ -42,6 +51,10 @@ flib_log_w("Failed to start an IPC listening socket on port %i: %s", ipcPort, SDLNet_GetError()); } else { flib_log_i("Listening for IPC connections on port %i.", ipcPort); + if(recordDemo) { + flib_vector_destroy(&demoBuffer); + demoBuffer = flib_vector_create(); + } return ipcPort; } } @@ -68,6 +81,44 @@ } } +static void demo_record(const void *data, size_t len) { + if(demoBuffer) { + if(flib_vector_append(demoBuffer, data, len) < len) { + // Out of memory, fail demo recording + flib_vector_destroy(&demoBuffer); + } + } +} + +static void demo_record_from_engine(const uint8_t *message) { + if(!demoBuffer || message[0]==0) { + return; + } + if(strchr("?CEiQqHb", message[1])) { + // Those message types are not recorded in a demo. + return; + } + + if(message[1] == 's') { + if(message[0] >= 3) { + // Chat messages get a special once-over to make them look as if they were received, not sent. + // Get the actual chat message as c string + char chatMsg[256]; + memcpy(chatMsg, message+2, message[0]-3); + chatMsg[message[0]-3] = 0; + + 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 "; + int size = snprintf(converted+1, 256, template, localPlayerName, chatMsg); + converted[0] = size>255 ? 255 : size; + demo_record(converted, converted[0]+1); + } + } else { + demo_record(message, message[0]+1); + } +} + /** * Receive a single message and copy it into the data buffer. * Returns the length of the received message, -1 when nothing is received. @@ -86,6 +137,7 @@ int msgsize = ipcReadBuffer[0]; if(ipcReadBufferSize > msgsize) { + demo_record_from_engine(ipcReadBuffer); memcpy(data, ipcReadBuffer+1, msgsize); memmove(ipcReadBuffer, ipcReadBuffer+msgsize+1, ipcReadBufferSize-(msgsize+1)); ipcReadBufferSize -= (msgsize+1); @@ -114,15 +166,20 @@ uint8_t sendbuf[256]; sendbuf[0] = len; memcpy(sendbuf+1, data, len); - if(flib_nbsocket_blocksend(ipcConnSocket, sendbuf, len+1) < len+1) { + if(flib_nbsocket_blocksend(ipcConnSocket, sendbuf, len+1) == len+1) { + demo_record(sendbuf, len+1); + return 0; + } else { flib_log_w("Failed or incomplete ICP write: engine connection lost."); flib_nbsocket_close(&ipcConnSocket); return -1; - } else { - return 0; } } +int flib_ipcconn_send_messagestr(char *data) { + return flib_ipcconn_send_message(data, strlen(data)); +} + void flib_ipcconn_tick() { if(!ipcConnSocket && ipcListenSocket) { ipcConnSocket = flib_nbsocket_accept(ipcListenSocket, true); @@ -132,3 +189,32 @@ } } } + +static void replace_gamemode(flib_buffer buf, char gamemode) { + size_t msgStart = 0; + char *data = (char*)buf.data; + while(msgStart+2 < buf.size) { + if(!memcmp(data+msgStart, "\x02T", 2)) { + data[msgStart+2] = gamemode; + } + msgStart += (uint8_t)data[msgStart]+1; + } +} + +flib_constbuffer flib_ipcconn_getdemo() { + if(!demoBuffer) { + flib_constbuffer result = {NULL, 0}; + return result; + } + replace_gamemode(flib_vector_as_buffer(demoBuffer), 'D'); + return flib_vector_as_constbuffer(demoBuffer); +} + +flib_constbuffer flib_ipcconn_getsave() { + if(!demoBuffer) { + flib_constbuffer result = {NULL, 0}; + return result; + } + replace_gamemode(flib_vector_as_buffer(demoBuffer), 'S'); + return flib_vector_as_constbuffer(demoBuffer); +} diff -r c42949cfdd92 -r fe76d24a25d7 project_files/frontlib/ipcconn.h --- a/project_files/frontlib/ipcconn.h Thu May 31 18:54:40 2012 +0200 +++ b/project_files/frontlib/ipcconn.h Sat Jun 02 14:26:52 2012 +0200 @@ -5,7 +5,10 @@ #ifndef IPCCONN_H_ #define IPCCONN_H_ +#include "buffer.h" + #include +#include typedef enum {IPC_NOT_CONNECTED, IPC_LISTENING, IPC_CONNECTED} IpcConnState; @@ -21,7 +24,11 @@ /** * Start listening for a connection from the engine. The system has to be in state - * IPC_NOT_CONNECTED when calling this function. + * IPC_NOT_CONNECTED when calling this function, and will be in state IPC_LISTENING + * if the function returns successfully. + * + * The parameter "recordDemo" can be used to control whether demo recording should + * be enabled for this connection. * * Returns the port we started listening on, or a negative value if there is an error. * @@ -29,7 +36,7 @@ * the engine again and talk to it you need to call this function again after the old * connection is closed. */ -int flib_ipcconn_listen(); +int flib_ipcconn_start(bool recordDemo); /** * Close the current IPC connection and/or stop listening for an incoming one. @@ -46,6 +53,10 @@ * Receive a single message (up to 255 bytes) and copy it into the data buffer. * Returns the length of the received message, a negative value if no message could * be read. + * + * 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. */ int flib_ipcconn_recv_message(void *data); @@ -59,9 +70,40 @@ int flib_ipcconn_send_message(void *data, size_t len); /** + * Convenience function for sending a 0-delimited string. + */ +int flib_ipcconn_send_messagestr(char *data); + +/** * Call regularly to allow background work to proceed */ void flib_ipcconn_tick(); +/** + * Get a demo record of the last connection. This should be called after + * the connection is closed and all messages have been received. + * + * If demo recording was not enabled in the last call to flib_ipcconn_start(), + * or if the recording failed for some reason, the buffer will be empty. + * + * The buffer is only valid until the next call to flib_ipcconn_start() or + * a call to flib_ipcconn_getsave() (save and demo records have some minor + * differences, and those are performed directly on the buffer before returning it). + */ +flib_constbuffer flib_ipcconn_getdemo(); + +/** + * Get a savegame record of the last connection. This should be called after + * the connection is closed and all messages have been received. + * + * If demo recording was not enabled in the last call to flib_ipcconn_start(), + * or if the recording failed for some reason, the buffer will be empty. + * + * The buffer is only valid until the next call to flib_ipcconn_start() or + * a call to flib_ipcconn_getdemo() (save and demo records have some minor + * differences, and those are performed directly on the buffer before returning it). + */ +flib_constbuffer flib_ipcconn_getsave(); + #endif /* IPCCONN_H_ */ diff -r c42949cfdd92 -r fe76d24a25d7 project_files/frontlib/logging.c --- a/project_files/frontlib/logging.c Thu May 31 18:54:40 2012 +0200 +++ b/project_files/frontlib/logging.c Sat Jun 02 14:26:52 2012 +0200 @@ -3,6 +3,7 @@ #include #include #include +#include char* flib_format_ip(uint32_t numip) { static char ip[16]; @@ -27,6 +28,7 @@ fprintf(file, " [%s]", prefix); vfprintf(file, fmt, args); fprintf(file, "\n"); + fflush(file); } void flib_log_e(const char *fmt, ...) {