# HG changeset patch # User Medo # Date 1340133425 -7200 # Node ID 613998625a3c793e467ae8bd610fdae653786f46 # Parent 240620f46dd74dc05dac033b0a8e197e05a9aece frontlib: Started work on the server connection code diff -r 240620f46dd7 -r 613998625a3c project_files/frontlib/hwconsts.h --- a/project_files/frontlib/hwconsts.h Fri Jun 15 19:57:25 2012 +0200 +++ b/project_files/frontlib/hwconsts.h Tue Jun 19 21:17:05 2012 +0200 @@ -8,6 +8,8 @@ #define HEDGEHOGS_PER_TEAM 8 #define NETGAME_DEFAULT_PORT 46631 +#define PROTOCOL_VERSION 42 +#define MIN_SERVER_VERSION 1 #define GAMEMOD_PERHOGAMMO_MASKBIT 22 #define GAMEMOD_SHAREDAMMO_MASKBIT 16 diff -r 240620f46dd7 -r 613998625a3c project_files/frontlib/ipc/gameconn.c --- a/project_files/frontlib/ipc/gameconn.c Fri Jun 15 19:57:25 2012 +0200 +++ b/project_files/frontlib/ipc/gameconn.c Tue Jun 19 21:17:05 2012 +0200 @@ -1,5 +1,5 @@ #include "gameconn.h" -#include "ipcconn.h" +#include "ipcbase.h" #include "ipcprotocol.h" #include "../util/logging.h" #include "../util/util.h" @@ -15,7 +15,7 @@ } gameconn_state; struct _flib_gameconn { - flib_ipcconn *connection; + flib_ipcbase *ipcBase; flib_vector *configBuffer; flib_vector *demoBuffer; char *playerName; @@ -38,8 +38,8 @@ void (*onGameRecordedCb)(void *context, const uint8_t *record, int size, bool isSavegame); void *onGameRecordedCtx; - void (*onNetMessageCb)(void *context, const uint8_t *em, int size); - void *onNetMessageCtx; + void (*onEngineMessageCb)(void *context, const uint8_t *em, int size); + void *onEngineMessageCtx; bool running; bool destroyRequested; @@ -52,7 +52,7 @@ } static void defaultCallback_onChat(void* context, const char *msg, bool teamchat) {} static void defaultCallback_onGameRecorded(void *context, const uint8_t *record, int size, bool isSavegame) {} -static void defaultCallback_onNetMessage(void *context, const uint8_t *em, int size) {} +static void defaultCallback_onEngineMessage(void *context, const uint8_t *em, int size) {} static void clearCallbacks(flib_gameconn *conn) { conn->onConnectCb = &defaultCallback_onConnect; @@ -60,17 +60,17 @@ conn->onErrorMessageCb = &defaultCallback_onErrorMessage; conn->onChatCb = &defaultCallback_onChat; conn->onGameRecordedCb = &defaultCallback_onGameRecorded; - conn->onNetMessageCb = &defaultCallback_onNetMessage; + conn->onEngineMessageCb = &defaultCallback_onEngineMessage; } static flib_gameconn *flib_gameconn_create_partial(bool record, const char *playerName, bool netGame) { flib_gameconn *result = NULL; flib_gameconn *tempConn = flib_calloc(1, sizeof(flib_gameconn)); if(tempConn) { - tempConn->connection = flib_ipcconn_create(); + tempConn->ipcBase = flib_ipcbase_create(); tempConn->configBuffer = flib_vector_create(); tempConn->playerName = flib_strdupnull(playerName); - if(tempConn->connection && tempConn->configBuffer && tempConn->playerName) { + if(tempConn->ipcBase && tempConn->configBuffer && tempConn->playerName) { if(record) { tempConn->demoBuffer = flib_vector_create(); } @@ -135,7 +135,7 @@ clearCallbacks(conn); conn->destroyRequested = true; } else { - flib_ipcconn_destroy(conn->connection); + flib_ipcbase_destroy(conn->ipcBase); flib_vector_destroy(conn->configBuffer); flib_vector_destroy(conn->demoBuffer); free(conn->playerName); @@ -149,7 +149,7 @@ flib_log_e("null parameter in flib_gameconn_getport"); return 0; } else { - return flib_ipcconn_port(conn->connection); + return flib_ipcbase_port(conn->ipcBase); } } @@ -201,7 +201,7 @@ int result = -1; if(!conn || (!data && len>0)) { flib_log_e("null parameter in flib_gameconn_send_enginemsg"); - } else if(!flib_ipcconn_send_raw(conn->connection, data, len)) { + } else if(!flib_ipcbase_send_raw(conn->ipcBase, data, len)) { demo_append(conn, data, len); result = 0; } @@ -217,7 +217,7 @@ int size = snprintf((char*)converted+1, 256, "s%c%s", (char)msgtype, msg); if(size>0) { converted[0] = size>255 ? 255 : size; - if(!flib_ipcconn_send_raw(conn->connection, converted, converted[0]+1)) { + if(!flib_ipcbase_send_raw(conn->ipcBase, converted, converted[0]+1)) { demo_append(conn, converted, converted[0]+1); result = 0; } @@ -233,7 +233,7 @@ flib_log_e("null parameter in flib_gameconn_send_chatmsg"); } else if(format_chatmessage(converted, playername, msg)) { flib_log_e("Error formatting message in flib_gameconn_send_chatmsg"); - } else if(!flib_ipcconn_send_raw(conn->connection, converted, converted[0]+1)) { + } else if(!flib_ipcbase_send_raw(conn->ipcBase, converted, converted[0]+1)) { demo_append(conn, converted, converted[0]+1); result = 0; } @@ -285,23 +285,23 @@ } } -void flib_gameconn_onNetMessage(flib_gameconn *conn, void (*callback)(void *context, const uint8_t *em, int size), void* context) { +void flib_gameconn_onEngineMessage(flib_gameconn *conn, void (*callback)(void *context, const uint8_t *em, int size), void* context) { if(!conn) { - flib_log_e("null parameter in flib_gameconn_onNetMessage"); + flib_log_e("null parameter in flib_gameconn_onEngineMessage"); } else { - conn->onNetMessageCb = callback ? callback : &defaultCallback_onNetMessage; - conn->onNetMessageCtx = context; + conn->onEngineMessageCb = callback ? callback : &defaultCallback_onEngineMessage; + conn->onEngineMessageCtx = context; } } static void flib_gameconn_wrappedtick(flib_gameconn *conn) { if(conn->state == AWAIT_CONNECTION) { - flib_ipcconn_accept(conn->connection); - switch(flib_ipcconn_state(conn->connection)) { + flib_ipcbase_accept(conn->ipcBase); + switch(flib_ipcbase_state(conn->ipcBase)) { case IPC_CONNECTED: { flib_constbuffer configBuffer = flib_vector_as_constbuffer(conn->configBuffer); - if(flib_ipcconn_send_raw(conn->connection, configBuffer.data, configBuffer.size)) { + if(flib_ipcbase_send_raw(conn->ipcBase, configBuffer.data, configBuffer.size)) { conn->state = FINISHED; conn->onDisconnectCb(conn->onDisconnectCtx, GAME_END_ERROR); return; @@ -327,7 +327,7 @@ if(conn->state == CONNECTED) { uint8_t msgbuffer[257]; int len; - while(!conn->destroyRequested && (len = flib_ipcconn_recv_message(conn->connection, msgbuffer))>=0) { + while(!conn->destroyRequested && (len = flib_ipcbase_recv_message(conn->ipcBase, msgbuffer))>=0) { if(len<2) { flib_log_w("Received short message from IPC (<2 bytes)"); continue; @@ -383,13 +383,13 @@ default: // Engine message demo_append(conn, msgbuffer, len); - conn->onNetMessageCb(conn->onNetMessageCtx, msgbuffer, len); + conn->onEngineMessageCb(conn->onEngineMessageCtx, msgbuffer, len); break; } } } - if(flib_ipcconn_state(conn->connection) == IPC_NOT_CONNECTED) { + if(flib_ipcbase_state(conn->ipcBase) == IPC_NOT_CONNECTED) { conn->state = FINISHED; conn->onDisconnectCb(conn->onDisconnectCtx, GAME_END_ERROR); } diff -r 240620f46dd7 -r 613998625a3c project_files/frontlib/ipc/gameconn.h --- a/project_files/frontlib/ipc/gameconn.h Fri Jun 15 19:57:25 2012 +0200 +++ b/project_files/frontlib/ipc/gameconn.h Tue Jun 19 21:17:05 2012 +0200 @@ -1,7 +1,6 @@ #ifndef GAMECONN_H_ #define GAMECONN_H_ -#include "../util/buffer.h" #include "../model/gamesetup.h" #include @@ -73,7 +72,7 @@ * ...needs to be passed on to the server in a net game * handleEngineMessage(void *context, const uint8_t *em, int size) */ -void flib_gameconn_onNetMessage(flib_gameconn *conn, void (*callback)(void *context, const uint8_t *em, int size), void* context); +void flib_gameconn_onEngineMessage(flib_gameconn *conn, void (*callback)(void *context, const uint8_t *em, int size), void* context); // TODO efinish diff -r 240620f46dd7 -r 613998625a3c project_files/frontlib/ipc/ipcbase.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/ipc/ipcbase.c Tue Jun 19 21:17:05 2012 +0200 @@ -0,0 +1,200 @@ +#include "ipcbase.h" +#include "../util/logging.h" +#include "../util/util.h" +#include "../socket.h" + +#include +#include +#include +#include + +/* + * The receive buffer has to be able to hold any message that might be received. Normally + * the messages are at most 256 bytes, but the map preview contains 4097 bytes (4096 for a + * bitmap, 1 for the number of hogs which fit on the map). + * + * We don't need to worry about wasting a few kb though, and I like powers of two... + */ +struct _flib_ipcbase { + uint8_t readBuffer[8192]; + int readBufferSize; + + flib_acceptor *acceptor; + uint16_t port; + + flib_tcpsocket *sock; +}; + +flib_ipcbase *flib_ipcbase_create() { + flib_ipcbase *result = flib_calloc(1, sizeof(flib_ipcbase)); + flib_acceptor *acceptor = flib_acceptor_create(0); + + if(!result || !acceptor) { + free(result); + flib_acceptor_close(acceptor); + return NULL; + } + + result->acceptor = acceptor; + result->sock = NULL; + result->readBufferSize = 0; + result->port = flib_acceptor_listenport(acceptor); + + flib_log_i("Started listening for IPC connections on port %u", (unsigned)result->port); + return result; +} + +uint16_t flib_ipcbase_port(flib_ipcbase *ipc) { + if(!ipc) { + flib_log_e("null parameter in flib_ipcbase_port"); + return 0; + } + return ipc->port; +} + +void flib_ipcbase_destroy(flib_ipcbase *ipc) { + if(ipc) { + flib_acceptor_close(ipc->acceptor); + flib_socket_close(ipc->sock); + free(ipc); + } +} + +IpcState flib_ipcbase_state(flib_ipcbase *ipc) { + if(!ipc) { + flib_log_e("null parameter in flib_ipcbase_state"); + return IPC_NOT_CONNECTED; + } else if(ipc->sock) { + return IPC_CONNECTED; + } else if(ipc->acceptor) { + return IPC_LISTENING; + } else { + return IPC_NOT_CONNECTED; + } +} + +static bool isMessageReady(flib_ipcbase *ipc) { + return ipc->readBufferSize >= ipc->readBuffer[0]+1; +} + +static void receiveToBuffer(flib_ipcbase *ipc) { + if(ipc->sock) { + int size = flib_socket_nbrecv(ipc->sock, ipc->readBuffer+ipc->readBufferSize, sizeof(ipc->readBuffer)-ipc->readBufferSize); + if(size>=0) { + ipc->readBufferSize += size; + } else { + flib_socket_close(ipc->sock); + ipc->sock = NULL; + } + } +} + +int flib_ipcbase_recv_message(flib_ipcbase *ipc, void *data) { + if(!ipc || !data) { + flib_log_e("null parameter in flib_ipcbase_recv_message"); + return -1; + } + + if(!isMessageReady(ipc)) { + receiveToBuffer(ipc); + } + + if(isMessageReady(ipc)) { + int msgsize = ipc->readBuffer[0]+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)", (unsigned)ipc->readBufferSize, (unsigned)(ipc->readBuffer[0])+1); + ipc->readBufferSize = 0; + return -1; + } else { + return -1; + } +} + +int flib_ipcbase_recv_map(flib_ipcbase *ipc, void *data) { + if(!ipc || !data) { + flib_log_e("null parameter in flib_ipcbase_recv_map"); + return -1; + } + + receiveToBuffer(ipc); + + if(ipc->readBufferSize >= IPCBASE_MAPMSG_BYTES) { + memcpy(data, ipc->readBuffer, IPCBASE_MAPMSG_BYTES); + memmove(ipc->readBuffer, ipc->readBuffer+IPCBASE_MAPMSG_BYTES, ipc->readBufferSize-IPCBASE_MAPMSG_BYTES); + return IPCBASE_MAPMSG_BYTES; + } else { + return -1; + } +} + +static void logSentMsg(const uint8_t *data, size_t len) { + if(flib_log_isActive(FLIB_LOGLEVEL_DEBUG)) { + size_t msgStart = 0; + while(msgStart < len) { + uint8_t msglen = data[msgStart]; + if(msgStart+msglen < len) { + flib_log_d("[IPC OUT][%03u]%*.*s",(unsigned)msglen, (unsigned)msglen, (unsigned)msglen, data+msgStart+1); + } else { + uint8_t msglen2 = len-msgStart-1; + flib_log_d("[IPC OUT][%03u/%03u]%*.*s",(unsigned)msglen2, (unsigned)msglen, (unsigned)msglen2, (unsigned)msglen2, data+msgStart+1); + } + msgStart += (uint8_t)data[msgStart]+1; + } + } +} + +int flib_ipcbase_send_raw(flib_ipcbase *ipc, const void *data, size_t len) { + if(!ipc || (!data && len>0)) { + flib_log_e("null parameter in flib_ipcbase_send_raw"); + return -1; + } + if(!ipc->sock) { + flib_log_w("flib_ipcbase_send_raw: Not connected."); + return -1; + } + + if(flib_socket_send(ipc->sock, data, len) == len) { + logSentMsg(data, len); + return 0; + } else { + flib_log_w("Failed or incomplete ICP write: engine connection lost."); + flib_socket_close(ipc->sock); + ipc->sock = NULL; + return -1; + } +} + +int flib_ipcbase_send_message(flib_ipcbase *ipc, void *data, size_t len) { + if(!ipc || (!data && len>0)) { + flib_log_e("null parameter in flib_ipcbase_send_message"); + return -1; + } else if(len>255) { + flib_log_e("Overlong message (%zu bytes) in flib_ipcbase_send_message", len); + return -1; + } + + uint8_t sendbuf[256]; + sendbuf[0] = len; + memcpy(sendbuf+1, data, len); + return flib_ipcbase_send_raw(ipc, sendbuf, len+1); +} + +int flib_ipcbase_send_messagestr(flib_ipcbase *ipc, char *data) { + return flib_ipcbase_send_message(ipc, data, strlen(data)); +} + +void flib_ipcbase_accept(flib_ipcbase *ipc) { + if(!ipc) { + flib_log_e("null parameter in flib_ipcbase_accept"); + } else if(!ipc->sock && ipc->acceptor) { + ipc->sock = flib_socket_accept(ipc->acceptor, true); + if(ipc->sock) { + flib_acceptor_close(ipc->acceptor); + ipc->acceptor = NULL; + } + } +} diff -r 240620f46dd7 -r 613998625a3c project_files/frontlib/ipc/ipcbase.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/ipc/ipcbase.h Tue Jun 19 21:17:05 2012 +0200 @@ -0,0 +1,86 @@ +/* + * Low-level protocol support for the IPC connection to the engine. + */ + +#ifndef IPCBASE_H_ +#define IPCBASE_H_ + +#include +#include +#include + +#define IPCBASE_MAPMSG_BYTES 4097 + +typedef enum {IPC_NOT_CONNECTED, IPC_LISTENING, IPC_CONNECTED} IpcState; + +struct _flib_ipcbase; +typedef struct _flib_ipcbase flib_ipcbase; + +/** + * Start an engine connection by listening on a random port. The selected port can + * be queried with flib_ipcbase_port and has to be passed to the engine. + * + * Returns NULL on error. Destroy the created object with flib_ipcbase_destroy. + * + * We stop accepting new connections once a connection has been established, so you + * need to create a new ipcbase in order to start a new connection. + */ +flib_ipcbase *flib_ipcbase_create(); + +uint16_t flib_ipcbase_port(flib_ipcbase *ipc); + +/** + * Free resources and close sockets. + */ +void flib_ipcbase_destroy(flib_ipcbase *ipc); + +/** + * Determine the current connection state + */ +IpcState flib_ipcbase_state(flib_ipcbase *ipc); + +/** + * 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. + */ +int flib_ipcbase_recv_message(flib_ipcbase *ipc, void *data); + +/** + * Try to receive 4097 bytes. This is the size of the reply the engine sends + * when successfully queried for map data. The first 4096 bytes are a bit-packed + * twocolor image of the map (256x128), the last byte is the number of hogs that + * fit on the map. + */ +int flib_ipcbase_recv_map(flib_ipcbase *ipc, void *data); + +int flib_ipcbase_send_raw(flib_ipcbase *ipc, const void *data, size_t len); + +/** + * 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_ipcbase_send_message(flib_ipcbase *ipc, void *data, size_t len); + +/** + * Convenience function for sending a 0-delimited string. + */ +int flib_ipcbase_send_messagestr(flib_ipcbase *ipc, char *data); + +/** + * Try to accept a connection. Only has an effect in state IPC_LISTENING. + */ +void flib_ipcbase_accept(flib_ipcbase *ipc); + +#endif /* IPCBASE_H_ */ + diff -r 240620f46dd7 -r 613998625a3c project_files/frontlib/ipc/ipcconn.c --- a/project_files/frontlib/ipc/ipcconn.c Fri Jun 15 19:57:25 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,200 +0,0 @@ -#include "ipcconn.h" -#include "../util/logging.h" -#include "../util/util.h" -#include "../socket.h" - -#include -#include -#include -#include - -/* - * The receive buffer has to be able to hold any message that might be received. Normally - * the messages are at most 256 bytes, but the map preview contains 4097 bytes (4096 for a - * bitmap, 1 for the number of hogs which fit on the map). - * - * We don't need to worry about wasting a few kb though, and I like powers of two... - */ -struct _flib_ipcconn { - uint8_t readBuffer[8192]; - int readBufferSize; - - flib_acceptor *acceptor; - uint16_t port; - - flib_tcpsocket *sock; -}; - -flib_ipcconn *flib_ipcconn_create() { - flib_ipcconn *result = flib_malloc(sizeof(flib_ipcconn)); - flib_acceptor *acceptor = flib_acceptor_create(0); - - if(!result || !acceptor) { - free(result); - flib_acceptor_close(acceptor); - return NULL; - } - - result->acceptor = acceptor; - result->sock = NULL; - result->readBufferSize = 0; - result->port = flib_acceptor_listenport(acceptor); - - flib_log_i("Started listening for IPC connections on port %u", (unsigned)result->port); - return result; -} - -uint16_t flib_ipcconn_port(flib_ipcconn *ipc) { - if(!ipc) { - flib_log_e("null parameter in flib_ipcconn_port"); - return 0; - } - return ipc->port; -} - -void flib_ipcconn_destroy(flib_ipcconn *ipc) { - if(ipc) { - flib_acceptor_close(ipc->acceptor); - flib_socket_close(ipc->sock); - free(ipc); - } -} - -IpcConnState flib_ipcconn_state(flib_ipcconn *ipc) { - if(!ipc) { - flib_log_e("null parameter in flib_ipcconn_state"); - return IPC_NOT_CONNECTED; - } else if(ipc->sock) { - return IPC_CONNECTED; - } else if(ipc->acceptor) { - return IPC_LISTENING; - } else { - return IPC_NOT_CONNECTED; - } -} - -static bool isMessageReady(flib_ipcconn *ipc) { - return ipc->readBufferSize >= ipc->readBuffer[0]+1; -} - -static void receiveToBuffer(flib_ipcconn *ipc) { - if(ipc->sock) { - int size = flib_socket_nbrecv(ipc->sock, ipc->readBuffer+ipc->readBufferSize, sizeof(ipc->readBuffer)-ipc->readBufferSize); - if(size>=0) { - ipc->readBufferSize += size; - } else { - flib_socket_close(ipc->sock); - ipc->sock = NULL; - } - } -} - -int flib_ipcconn_recv_message(flib_ipcconn *ipc, void *data) { - if(!ipc || !data) { - flib_log_e("null parameter in flib_ipcconn_recv_message"); - return -1; - } - - if(!isMessageReady(ipc)) { - receiveToBuffer(ipc); - } - - if(isMessageReady(ipc)) { - int msgsize = ipc->readBuffer[0]+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)", (unsigned)ipc->readBufferSize, (unsigned)(ipc->readBuffer[0])+1); - ipc->readBufferSize = 0; - return -1; - } else { - return -1; - } -} - -int flib_ipcconn_recv_map(flib_ipcconn *ipc, void *data) { - if(!ipc || !data) { - flib_log_e("null parameter in flib_ipcconn_recv_map"); - return -1; - } - - receiveToBuffer(ipc); - - if(ipc->readBufferSize >= IPCCONN_MAPMSG_BYTES) { - memcpy(data, ipc->readBuffer, IPCCONN_MAPMSG_BYTES); - memmove(ipc->readBuffer, ipc->readBuffer+IPCCONN_MAPMSG_BYTES, ipc->readBufferSize-IPCCONN_MAPMSG_BYTES); - return IPCCONN_MAPMSG_BYTES; - } else { - return -1; - } -} - -static void logSentMsg(const uint8_t *data, size_t len) { - if(flib_log_isActive(FLIB_LOGLEVEL_DEBUG)) { - size_t msgStart = 0; - while(msgStart < len) { - uint8_t msglen = data[msgStart]; - if(msgStart+msglen < len) { - flib_log_d("[IPC OUT][%03u]%*.*s",(unsigned)msglen, (unsigned)msglen, (unsigned)msglen, data+msgStart+1); - } else { - uint8_t msglen2 = len-msgStart-1; - flib_log_d("[IPC OUT][%03u/%03u]%*.*s",(unsigned)msglen2, (unsigned)msglen, (unsigned)msglen2, (unsigned)msglen2, data+msgStart+1); - } - msgStart += (uint8_t)data[msgStart]+1; - } - } -} - -int flib_ipcconn_send_raw(flib_ipcconn *ipc, const void *data, size_t len) { - if(!ipc || (!data && len>0)) { - flib_log_e("null parameter in flib_ipcconn_send_raw"); - return -1; - } - if(!ipc->sock) { - flib_log_w("flib_ipcconn_send_raw: Not connected."); - return -1; - } - - if(flib_socket_send(ipc->sock, data, len) == len) { - logSentMsg(data, len); - return 0; - } else { - flib_log_w("Failed or incomplete ICP write: engine connection lost."); - flib_socket_close(ipc->sock); - ipc->sock = NULL; - return -1; - } -} - -int flib_ipcconn_send_message(flib_ipcconn *ipc, void *data, size_t len) { - if(!ipc || (!data && len>0)) { - flib_log_e("null parameter in flib_ipcconn_send_message"); - return -1; - } else if(len>255) { - flib_log_e("Overlong message (%zu bytes) in flib_ipcconn_send_message", len); - return -1; - } - - uint8_t sendbuf[256]; - sendbuf[0] = len; - memcpy(sendbuf+1, data, len); - return flib_ipcconn_send_raw(ipc, sendbuf, len+1); -} - -int flib_ipcconn_send_messagestr(flib_ipcconn *ipc, char *data) { - return flib_ipcconn_send_message(ipc, data, strlen(data)); -} - -void flib_ipcconn_accept(flib_ipcconn *ipc) { - if(!ipc) { - flib_log_e("null parameter in flib_ipcconn_accept"); - } else if(!ipc->sock && ipc->acceptor) { - ipc->sock = flib_socket_accept(ipc->acceptor, true); - if(ipc->sock) { - flib_acceptor_close(ipc->acceptor); - ipc->acceptor = NULL; - } - } -} diff -r 240620f46dd7 -r 613998625a3c project_files/frontlib/ipc/ipcconn.h --- a/project_files/frontlib/ipc/ipcconn.h Fri Jun 15 19:57:25 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,91 +0,0 @@ -/* - * Low-level protocol support for the IPC connection to the engine. - */ - -#ifndef IPCCONN_H_ -#define IPCCONN_H_ - -#include "../util/buffer.h" - -#include -#include - -#define IPCCONN_MAPMSG_BYTES 4097 - -typedef enum {IPC_NOT_CONNECTED, IPC_LISTENING, IPC_CONNECTED} IpcConnState; - -struct _flib_ipcconn; -typedef struct _flib_ipcconn flib_ipcconn; - -/** - * Start an engine connection by listening on a random port. The selected port can - * be queried with flib_ipcconn_port and has to be passed to the engine. - * - * The parameter "recordDemo" can be used to control whether demo recording should - * be enabled for this connection. The localPlayerName is needed for demo - * recording purposes. - * - * Returns NULL on error. Destroy the created object with flib_ipcconn_destroy. - * - * We stop accepting new connections once a connection has been established, so you - * need to create a new ipcconn in order to start a new connection. - */ -flib_ipcconn *flib_ipcconn_create(); - -uint16_t flib_ipcconn_port(flib_ipcconn *ipc); - -/** - * Free resources and close sockets. - */ -void flib_ipcconn_destroy(flib_ipcconn *ipc); - -/** - * Determine the current connection state - */ -IpcConnState flib_ipcconn_state(flib_ipcconn *ipc); - -/** - * 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. - */ -int flib_ipcconn_recv_message(flib_ipcconn *ipc, void *data); - -/** - * Try to receive 4097 bytes. This is the size of the reply the engine sends - * when successfully queried for map data. The first 4096 bytes are a bit-packed - * twocolor image of the map (256x128), the last byte is the number of hogs that - * fit on the map. - */ -int flib_ipcconn_recv_map(flib_ipcconn *ipc, void *data); - -int flib_ipcconn_send_raw(flib_ipcconn *ipc, const void *data, size_t len); - -/** - * 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(flib_ipcconn *ipc, void *data, size_t len); - -/** - * Convenience function for sending a 0-delimited string. - */ -int flib_ipcconn_send_messagestr(flib_ipcconn *ipc, char *data); - -/** - * Call regularly to allow background work to proceed - */ -void flib_ipcconn_accept(flib_ipcconn *ipc); - -#endif /* IPCCONN_H_ */ - diff -r 240620f46dd7 -r 613998625a3c project_files/frontlib/ipc/mapconn.c --- a/project_files/frontlib/ipc/mapconn.c Fri Jun 15 19:57:25 2012 +0200 +++ b/project_files/frontlib/ipc/mapconn.c Tue Jun 19 21:17:05 2012 +0200 @@ -1,5 +1,5 @@ #include "mapconn.h" -#include "ipcconn.h" +#include "ipcbase.h" #include "ipcprotocol.h" #include "../util/logging.h" @@ -15,8 +15,8 @@ } mapconn_state; struct _flib_mapconn { - uint8_t mapBuffer[IPCCONN_MAPMSG_BYTES]; - flib_ipcconn *connection; + uint8_t mapBuffer[IPCBASE_MAPMSG_BYTES]; + flib_ipcbase *ipcBase; flib_vector *configBuffer; mapconn_state progress; @@ -60,9 +60,9 @@ flib_mapconn *result = NULL; flib_mapconn *tempConn = flib_calloc(1, sizeof(flib_mapconn)); if(tempConn) { - tempConn->connection = flib_ipcconn_create(); + tempConn->ipcBase = flib_ipcbase_create(); tempConn->configBuffer = createConfigBuffer(seed, mapdesc); - if(tempConn->connection && tempConn->configBuffer) { + if(tempConn->ipcBase && tempConn->configBuffer) { tempConn->progress = AWAIT_CONNECTION; clearCallbacks(tempConn); result = tempConn; @@ -84,7 +84,7 @@ clearCallbacks(conn); conn->destroyRequested = true; } else { - flib_ipcconn_destroy(conn->connection); + flib_ipcbase_destroy(conn->ipcBase); flib_vector_destroy(conn->configBuffer); free(conn); } @@ -96,7 +96,7 @@ flib_log_e("null parameter in flib_mapconn_getport"); return 0; } else { - return flib_ipcconn_port(conn->connection); + return flib_ipcbase_port(conn->ipcBase); } } @@ -120,12 +120,12 @@ static void flib_mapconn_wrappedtick(flib_mapconn *conn) { if(conn->progress == AWAIT_CONNECTION) { - flib_ipcconn_accept(conn->connection); - switch(flib_ipcconn_state(conn->connection)) { + flib_ipcbase_accept(conn->ipcBase); + switch(flib_ipcbase_state(conn->ipcBase)) { case IPC_CONNECTED: { flib_constbuffer configBuffer = flib_vector_as_constbuffer(conn->configBuffer); - if(flib_ipcconn_send_raw(conn->connection, configBuffer.data, configBuffer.size)) { + if(flib_ipcbase_send_raw(conn->ipcBase, configBuffer.data, configBuffer.size)) { conn->progress = FINISHED; conn->onFailureCb(conn->onFailureCtx, "Error sending map information to the engine."); return; @@ -144,11 +144,11 @@ } if(conn->progress == AWAIT_REPLY) { - if(flib_ipcconn_recv_map(conn->connection, conn->mapBuffer) >= 0) { + if(flib_ipcbase_recv_map(conn->ipcBase, conn->mapBuffer) >= 0) { conn->progress = FINISHED; - conn->onSuccessCb(conn->onSuccessCtx, conn->mapBuffer, conn->mapBuffer[IPCCONN_MAPMSG_BYTES-1]); + conn->onSuccessCb(conn->onSuccessCtx, conn->mapBuffer, conn->mapBuffer[IPCBASE_MAPMSG_BYTES-1]); return; - } else if(flib_ipcconn_state(conn->connection) != IPC_CONNECTED) { + } else if(flib_ipcbase_state(conn->ipcBase) != IPC_CONNECTED) { conn->progress = FINISHED; conn->onFailureCb(conn->onSuccessCtx, "Engine connection closed unexpectedly."); return; diff -r 240620f46dd7 -r 613998625a3c project_files/frontlib/net/netbase.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/net/netbase.c Tue Jun 19 21:17:05 2012 +0200 @@ -0,0 +1,248 @@ +#include "netbase.h" +#include "../util/buffer.h" +#include "../util/logging.h" +#include "../util/util.h" +#include "../socket.h" + +#include +#include +#include +#include + +#define NET_READBUFFER_LIMIT (1024*1024) + +struct _flib_netbase { + flib_vector *readBuffer; + flib_tcpsocket *sock; +}; + +flib_netbase *flib_netbase_create(const char *server, uint16_t port) { + flib_netbase *result = NULL; + flib_netbase *newNet = flib_calloc(1, sizeof(flib_netbase)); + + if(newNet) { + newNet->readBuffer = flib_vector_create(); + newNet->sock = flib_socket_connect(server, port); + if(newNet->readBuffer && newNet->sock) { + flib_log_i("Connected to server %s:%u", server, (unsigned)port); + result = newNet; + newNet = NULL; + } + } + flib_netbase_destroy(newNet); + + return result; +} + +void flib_netbase_destroy(flib_netbase *net) { + if(net) { + flib_socket_close(net->sock); + flib_vector_destroy(net->readBuffer); + free(net); + } +} + +bool flib_netbase_connected(flib_netbase *net) { + if(!net) { + flib_log_e("null parameter in flib_netbase_connected"); + return false; + } else if(net->sock) { + return true; + } else { + return false; + } +} + +/** + * Parses and returns a message, and removes it from the vector. + */ +static flib_netmsg *parseMessage(flib_vector *vec) { + const uint8_t *partStart = flib_vector_data(vec); + const uint8_t *end = partStart+flib_vector_size(vec); + flib_netmsg *result = flib_netmsg_create(); + if(!result) { + return NULL; + } + + while(1) { + const uint8_t *partEnd = memchr(partStart, '\n', end-partStart); + if(!partEnd) { + // message incomplete + flib_netmsg_destroy(result); + return NULL; + } else if(partEnd-partStart == 0) { + // Zero-length part, message end marker. Remove the message from the vector. + uint8_t *vectorStart = flib_vector_data(vec); + size_t msgLen = partEnd+1-vectorStart; + memmove(vectorStart, partEnd+1, flib_vector_size(vec)-msgLen); + flib_vector_resize(vec, flib_vector_size(vec)-msgLen); + return result; + } else { + if(flib_netmsg_append_part(result, partStart, partEnd-partStart)) { + flib_netmsg_destroy(result); + return NULL; + } + partStart = partEnd+1; // Skip the '\n' + } + } + return NULL; // Never reached +} + +/** + * Receive some bytes and add them to the buffer. + * Returns the number of bytes received. + * Automatically closes the socket if an error occurs + * and sets sock=NULL. + */ +static int receiveToBuffer(flib_netbase *net) { + uint8_t buffer[256]; + if(!net->sock) { + return 0; + } else if(flib_vector_size(net->readBuffer) > NET_READBUFFER_LIMIT) { + flib_log_e("Net connection closed: Net message too big"); + flib_socket_close(net->sock); + net->sock = NULL; + return 0; + } else { + int size = flib_socket_nbrecv(net->sock, buffer, sizeof(buffer)); + if(size>=0) { + flib_vector_append(net->readBuffer, buffer, size); + return size; + } else { + flib_socket_close(net->sock); + net->sock = NULL; + return 0; + } + } +} + +flib_netmsg *flib_netbase_recv_message(flib_netbase *net) { + if(!net) { + flib_log_e("null parameter in flib_netbase_recv_message"); + return NULL; + } + + flib_netmsg *msg; + while(!(msg=parseMessage(net->readBuffer)) + && receiveToBuffer(net)) {} + + if(msg) { + return msg; + } else if(!net->sock && flib_vector_size(net->readBuffer)>0) { + // Connection is down and we didn't get a complete message, just flush the rest. + flib_vector_resize(net->readBuffer, 0); + } + return NULL; +} + +static void logSentMsg(const uint8_t *data, size_t len) { + if(flib_log_isActive(FLIB_LOGLEVEL_DEBUG)) { + flib_log_d("[NET OUT][%03u]%*.*s",(unsigned)len, (unsigned)len, (unsigned)len, data); + } +} + +int flib_netbase_send_raw(flib_netbase *net, const void *data, size_t len) { + if(!net || (!data && len>0)) { + flib_log_e("null parameter in flib_netbase_send_raw"); + return -1; + } + if(!net->sock) { + flib_log_w("flib_netbase_send_raw: Not connected."); + return -1; + } + + if(flib_socket_send(net->sock, data, len) == len) { + logSentMsg(data, len); + return 0; + } else { + flib_log_w("Failed or incomplete write: net connection lost."); + flib_socket_close(net->sock); + net->sock = NULL; + return -1; + } +} + +int flib_netbase_send_message(flib_netbase *net, flib_netmsg *msg) { + if(!net || !msg) { + flib_log_e("null parameter in flib_netbase_send_message"); + return -1; + } + + size_t totalSize = 0; + for(int i=0; ipartCount; i++) { + totalSize += strlen(msg->parts[i]) + 1; + } + totalSize++; // Last part ends in two '\n' instead of one + + uint8_t *buffer = flib_malloc(totalSize); + if(!buffer) { + return -1; + } + size_t pos = 0; + for(int i=0; ipartCount; i++) { + size_t partsize = strlen(msg->parts[i]); + memcpy(buffer+pos, msg->parts[i], partsize); + pos += partsize; + buffer[pos++] = '\n'; + } + buffer[pos++] = '\n'; + return flib_netbase_send_raw(net, buffer, pos); +} + +int flib_netbase_sendf(flib_netbase *net, const char *format, ...) { + int result = -1; + if(!net || !format) { + flib_log_e("null parameter in flib_netbase_sendf"); + } else { + va_list argp; + va_start(argp, format); + char *buffer = flib_vasprintf(format, argp); + if(buffer) { + result = flib_netbase_send_raw(net, buffer, strlen(buffer)); + } + free(buffer); + va_end(argp); + } + return result; +} + +flib_netmsg *flib_netmsg_create() { + flib_netmsg *result = flib_calloc(1, sizeof(flib_netmsg)); + if(result) { + result->partCount = 0; + result->parts = NULL; + return result; + } else { + return NULL; + } +} + +void flib_netmsg_destroy(flib_netmsg *msg) { + if(msg) { + for(int i=0; ipartCount; i++) { + free(msg->parts[i]); + } + free(msg->parts); + free(msg); + } +} + +int flib_netmsg_append_part(flib_netmsg *msg, const void *part, size_t partlen) { + int result = -1; + if(!msg) { + flib_log_e("null parameter in flib_netmsg_append_part"); + } else { + char **newParts = realloc(msg->parts, (msg->partCount+1)*sizeof(*msg->parts)); + if(newParts) { + msg->parts = newParts; + msg->parts[msg->partCount] = flib_malloc(partlen+1); + if(msg->parts[msg->partCount]) { + memcpy(msg->parts[msg->partCount], part, partlen); + msg->parts[msg->partCount][partlen] = 0; + msg->partCount++; + result = 0; + } + } + } + return result; +} diff -r 240620f46dd7 -r 613998625a3c project_files/frontlib/net/netbase.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/net/netbase.h Tue Jun 19 21:17:05 2012 +0200 @@ -0,0 +1,72 @@ +/* + * Low-level protocol support for the network connection + */ + +#ifndef NETBASE_H_ +#define NETBASE_H_ + +#include +#include +#include + +struct _flib_netbase; +typedef struct _flib_netbase flib_netbase; + +typedef struct { + int partCount; + char **parts; +} flib_netmsg; + +/** + * Start a connection to the specified Hedgewars server. + * + * Returns NULL on error. Destroy the created object with flib_netconn_destroy. + */ +flib_netbase *flib_netbase_create(const char *server, uint16_t port); + +/** + * Free resources and close sockets. + */ +void flib_netbase_destroy(flib_netbase *net); + +/** + * Determine the current connection state. Starts out true, and turns to + * false when we are disconnected from the server. + */ +bool flib_netbase_connected(flib_netbase *net); + +/** + * Receive a new message and return it as a flib_netmsg. The netmsg has to be + * destroyed with flib_netmsg_destroy after use. + * Returns NULL if no message is available. + * + * 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. + */ +flib_netmsg *flib_netbase_recv_message(flib_netbase *net); + +int flib_netbase_send_raw(flib_netbase *net, const void *data, size_t len); + +/** + * Write a single message to the server. This call blocks until the + * message is completely written or the connection is closed or an error occurs. + * + * Returns a negative value on failure. + */ +int flib_netbase_send_message(flib_netbase *net, flib_netmsg *msg); + +/** + * Send a message printf-style. + * + * flib_netbase_sendf(net, "%s\n\n", "TOGGLE_READY"); + * flib_netbase_sendf(net, "%s\n%s\n%i\n\n", "CFG", "MAPGEN", MAPGEN_MAZE); + */ +int flib_netbase_sendf(flib_netbase *net, const char *format, ...); + +flib_netmsg *flib_netmsg_create(); +void flib_netmsg_destroy(flib_netmsg *msg); +int flib_netmsg_append_part(flib_netmsg *msg, const void *param, size_t len); + +#endif /* NETBASE_H_ */ + diff -r 240620f46dd7 -r 613998625a3c project_files/frontlib/net/netconn.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/net/netconn.c Tue Jun 19 21:17:05 2012 +0200 @@ -0,0 +1,469 @@ +/* + * Hedgewars, a free turn based strategy game + * Copyright (c) 2006-2008 Igor Ulyanov + * Copyright (c) 2004-2012 Andrey Korotaev + * Copyright (c) 2012 Simeon Maxein + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include "netconn.h" +#include "netbase.h" +#include "netprotocol.h" +#include "../util/logging.h" +#include "../util/util.h" + +#include +#include +#include +#include + +struct _flib_netconn { + flib_netbase *netBase; + char *playerName; + + int netconnState; // One of the NETCONN_STATE constants + + void (*onErrorCb)(void* context, int errorCode, const char *errorMsg); + void *onErrorCtx; + + void (*onConnectedCb)(void *context, const char *serverMessage); + void *onConnectedCtx; + + bool running; + bool destroyRequested; +}; + +static void defaultCallback_onError(void* context, int errorCode, const char *errormsg) {} +static void defaultCallback_onConnected(void *context, const char *serverMessage) {} + +static void clearCallbacks(flib_netconn *conn) { + conn->onErrorCb = &defaultCallback_onError; + conn->onConnectedCb = &defaultCallback_onConnected; +} + + +flib_netconn *flib_netconn_create(const char *playerName, const char *host, uint16_t port) { + flib_netconn *result = NULL; + flib_netconn *newConn = flib_calloc(1, sizeof(flib_netconn)); + if(newConn) { + newConn->netconnState = NETCONN_STATE_AWAIT_CONNECTED; + newConn->running = false; + newConn->destroyRequested = false; + clearCallbacks(newConn); + newConn->netBase = flib_netbase_create(host, port); + newConn->playerName = flib_strdupnull(playerName); + if(newConn->netBase && newConn->playerName) { + result = newConn; + newConn = NULL; + } + } + flib_netconn_destroy(newConn); + return result; +} + +void flib_netconn_destroy(flib_netconn *conn) { + if(conn) { + if(conn->running) { + /* + * The function was called from a callback, 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(conn); + conn->destroyRequested = true; + } else { + flib_netbase_destroy(conn->netBase); + free(conn->playerName); + free(conn); + } + } +} + +void flib_netconn_onError(flib_netconn *conn, void (*callback)(void *context, int errorCode, const char *errorMsg), void* context) { + if(!conn) { + flib_log_e("null parameter in flib_netconn_onError"); + } else { + conn->onErrorCb = callback ? callback : &defaultCallback_onError; + conn->onErrorCtx = context; + } +} + +void flib_netconn_onConnected(flib_netconn *conn, void (*callback)(void *context, const char *serverMessage), void* context) { + if(!conn) { + flib_log_e("null parameter in flib_netconn_onConnected"); + } else { + conn->onConnectedCb = callback ? callback : &defaultCallback_onConnected; + conn->onConnectedCtx = context; + } +} + +static void flib_netconn_wrappedtick(flib_netconn *conn) { + flib_netmsg *netmsg; + flib_netbase *net = conn->netBase; + bool exit = false; + + while(!exit && !conn->destroyRequested && (netmsg=flib_netbase_recv_message(conn->netBase))) { + if(netmsg->partCount==0) { + flib_log_w("Empty server message"); + continue; + } + + const char *cmd = netmsg->parts[0]; + + if (!strcmp(cmd, "NICK") && netmsg->partCount>=2) { + free(conn->playerName); + conn->playerName = flib_strdupnull(netmsg->parts[1]); + if(!conn->playerName) { + // TODO handle error + } + // TODO callback? + } else if (!strcmp(cmd, "PROTO")) { + // The server just echoes this back apparently + } else if (!strcmp(cmd, "ERROR")) { + // TODO: onErrorMessage? + if (netmsg->partCount == 2) { + conn->onErrorCb(conn->onErrorCtx, NETCONN_ERROR_FROM_SERVER, netmsg->parts[1]); + } else { + conn->onErrorCb(conn->onErrorCtx, NETCONN_ERROR_FROM_SERVER, "Unknown Error"); + } + } else if(!strcmp(cmd, "WARNING")) { + // TODO: onWarnMessage? + if (netmsg->partCount == 2) { + conn->onErrorCb(conn->onErrorCtx, NETCONN_ERROR_FROM_SERVER, netmsg->parts[1]); + } else { + conn->onErrorCb(conn->onErrorCtx, NETCONN_ERROR_FROM_SERVER, "Unknown Warning"); + } + } else if(!strcmp(cmd, "CONNECTED")) { + if(netmsg->partCount<3 || atol(netmsg->parts[2])netconnState = NETCONN_STATE_DISCONNECTED; + conn->onErrorCb(conn->onErrorCtx, NETCONN_ERROR_SERVER_TOO_OLD, "Server too old"); + exit = true; + } else { + flib_netbase_sendf(net, "%s\n%s\n\n", "NICK", conn->playerName); + flib_netbase_sendf(net, "%s\n%i\n\n", "PROTO", (int)PROTOCOL_VERSION); + conn->netconnState = NETCONN_STATE_LOBBY; + } + } else if(!strcmp(cmd, "PING")) { + if (netmsg->partCount > 1) { + flib_netbase_sendf(net, "%s\n%s\n\n", "PONG", netmsg->parts[1]); + } else { + flib_netbase_sendf(net, "%s\n\n", "PONG"); + } + } else if(!strcmp(cmd, "ROOMS")) { + if(netmsg->partCount % 8 != 1) { + flib_log_w("Net: Malformed ROOMS message"); + } else { + // TODO + //QStringList tmp = lst; + //tmp.removeFirst(); + //m_roomsListModel->setRoomsList(tmp); + } + } else if (!strcmp(cmd, "SERVER_MESSAGE")) { + if(netmsg->partCount < 2) { + flib_log_w("Net: Empty SERVERMESSAGE message"); + } else { + // TODO + // emit serverMessage(lst[1]); + } + } else if (!strcmp(cmd, "CHAT")) { + if(netmsg->partCount < 3) { + flib_log_w("Net: Empty CHAT message"); + } else { + // TODO + // if (netClientState == InLobby) + // emit chatStringLobby(lst[1], HWProto::formatChatMsgForFrontend(lst[2])); + // else + // emit chatStringFromNet(HWProto::formatChatMsg(lst[1], lst[2])); + } + } else if (!strcmp(cmd, "INFO")) { + if(netmsg->partCount < 5) { + flib_log_w("Net: Malformed INFO message"); + } else { + // TODO +// QStringList tmp = lst; +// tmp.removeFirst(); +// if (netClientState == InLobby) +// emit chatStringLobby(tmp.join("\n").prepend('\x01')); +// else +// emit chatStringFromNet(tmp.join("\n").prepend('\x01')); + } + } else if(!strcmp(cmd, "SERVER_VARS")) { + // TODO +// QStringList tmp = lst; +// tmp.removeFirst(); +// while (tmp.size() >= 2) +// { +// if(tmp[0] == "MOTD_NEW") emit serverMessageNew(tmp[1]); +// else if(tmp[0] == "MOTD_OLD") emit serverMessageOld(tmp[1]); +// else if(tmp[0] == "LATEST_PROTO") emit latestProtocolVar(tmp[1].toInt()); +// +// tmp.removeFirst(); +// tmp.removeFirst(); +// } + } else if (!strcmp(cmd, "CLIENT_FLAGS")) { + if(netmsg->partCount < 3 || strlen(netmsg->parts[1]) < 2) { + flib_log_w("Net: Malformed CLIENT_FLAGS message"); + } else { + const char *flags = netmsg->parts[1]; + bool setFlag = flags[0] == '+'; + + for(int i=1; flags[i]; i++) { + switch(flags[i]) { + case 'r': + for(int j = 2; j < netmsg->partCount; ++j) { + if (!strcmp(conn->playerName, netmsg->parts[i])) { + // TODO + // if (isChief && !setFlag) ToggleReady(); + // else emit setMyReadyStatus(setFlag); + } + // TODO + // emit setReadyStatus(lst[i], setFlag); + } + break; + default: + flib_log_w("Net: Unknown flag %c in CLIENT_FLAGS message", flags[i]); + break; + } + } + } + } else if (!strcmp(cmd, "ADD_TEAM")) { + if(netmsg->partCount != 24) { + flib_log_w("Net: Bad ADD_TEAM message"); + } else { + // TODO +// QStringList tmp = lst; +// tmp.removeFirst(); +// emit AddNetTeam(tmp); + } + } else if (!strcmp(cmd, "REMOVE_TEAM")) { + if(netmsg->partCount != 2) { + flib_log_w("Net: Bad REMOVETEAM message"); + } else { + // TODO + // emit RemoveNetTeam(HWTeam(lst[1])); + } + } else if(!strcmp(cmd, "ROOMABANDONED")) { + conn->netconnState = NETCONN_STATE_LOBBY; +// TODO +// askRoomsList(); +// emit LeftRoom(tr("Room destroyed")); + } else if(!strcmp(cmd, "KICKED")) { + conn->netconnState = NETCONN_STATE_LOBBY; +// TODO +// askRoomsList(); +// emit LeftRoom(tr("You got kicked")); + } else if(!strcmp(cmd, "JOINED")) { + if(netmsg->partCount < 2) { + flib_log_w("Net: Bad JOINED message"); + } else { + for(int i = 1; i < netmsg->partCount; ++i) + { + bool isMe = !strcmp(conn->playerName, netmsg->parts[i]); + if (isMe) { + conn->netconnState = NETCONN_STATE_ROOM; +// TODO +// emit EnteredGame(); +// emit roomMaster(isChief); +// if (isChief) +// emit configAsked(); + } + +// TODO +// emit nickAdded(lst[i], isChief && !isMe)); +// emit chatStringFromNet(tr("%1 *** %2 has joined the room").arg('\x03').arg(lst[i])); + } + } + } else if(!strcmp(cmd, "LOBBY:JOINED")) { + if(netmsg->partCount < 2) { + flib_log_w("Net: Bad JOINED message"); + } else { + for(int i = 1; i < netmsg->partCount; ++i) + { + bool isMe = !strcmp(conn->playerName, netmsg->parts[i]); + if (isMe) { + conn->netconnState = NETCONN_STATE_LOBBY; + // TODO +// RawSendNet(QString("LIST")); +// emit connected(); + } + // TODO +// emit nickAddedLobby(lst[i], false); +// emit chatStringLobby(lst[i], tr("%1 *** %2 has joined").arg('\x03').arg("|nick|")); + } + } + } else if(!strcmp(cmd, "LEFT")) { + if(netmsg->partCount < 2) { + flib_log_w("Net: Bad LEFT message"); + } else { + // TODO +// emit nickRemoved(lst[1]); +// if (netmsg->partCount < 3) +// emit chatStringFromNet(tr("%1 *** %2 has left").arg('\x03').arg(lst[1])); +// else +// emit chatStringFromNet(tr("%1 *** %2 has left (%3)").arg('\x03').arg(lst[1], lst[2])); + } + } else if(!strcmp(cmd, "ROOM") && netmsg->partCount >= 2) { + const char *subcmd = netmsg->parts[1]; + if(!strcmp(subcmd, "ADD") && netmsg->partCount == 10) { + // TODO +// QStringList tmp = lst; +// tmp.removeFirst(); +// tmp.removeFirst(); +// +// m_roomsListModel->addRoom(tmp); + } else if(!strcmp(subcmd, "UPD") && netmsg->partCount == 11) { + // TODO +// QStringList tmp = lst; +// tmp.removeFirst(); +// tmp.removeFirst(); +// +// QString roomName = tmp.takeFirst(); +// m_roomsListModel->updateRoom(roomName, tmp); + } else if(!strcmp(subcmd, "DEL") && netmsg->partCount == 3) { + // TODO + // m_roomsListModel->removeRoom(lst[2]); + } else { + flib_log_w("Net: Unknown or malformed ROOM subcommand: %s", subcmd); + } + } else if(!strcmp(cmd, "LOBBY:LEFT")) { + if(netmsg->partCount < 2) { + flib_log_w("Net: Bad LOBBY:LEFT message"); + } else { + // TODO +// emit nickRemovedLobby(lst[1]); +// if (netmsg->partCount < 3) +// emit chatStringLobby(tr("%1 *** %2 has left").arg('\x03').arg(lst[1])); +// else +// emit chatStringLobby(lst[1], tr("%1 *** %2 has left (%3)").arg('\x03').arg("|nick|", lst[2])); + } + } else if (!strcmp(cmd, "RUN_GAME")) { + conn->netconnState = NETCONN_STATE_INGAME; + // TODO + // emit AskForRunGame(); + } else if (!strcmp(cmd, "ASKPASSWORD")) { + // TODO + // emit AskForPassword(mynick); + } else if (!strcmp(cmd, "NOTICE")) { + if(netmsg->partCount < 2) { + flib_log_w("Net: Bad NOTICE message"); + } else { + errno = 0; + long n = strtol(netmsg->parts[1], NULL, 10); + if(errno) { + flib_log_w("Net: Bad NOTICE message"); + } else { + // TODO + // handleNotice(n); + } + } + } else if (!strcmp(cmd, "TEAM_ACCEPTED")) { + if (netmsg->partCount != 2) { + flib_log_w("Net: Bad TEAM_ACCEPTED message"); + } else { + // TODO + // emit TeamAccepted(lst[1]); + } + } else if (!strcmp(cmd, "CFG")) { + if(netmsg->partCount < 3) { + flib_log_w("Net: Bad CFG message"); + } else { + // TODO +// QStringList tmp = lst; +// tmp.removeFirst(); +// tmp.removeFirst(); +// if (lst[1] == "SCHEME") +// emit netSchemeConfig(tmp); +// else +// emit paramChanged(lst[1], tmp); + } + } else if (!strcmp(cmd, "HH_NUM")) { + if (netmsg->partCount != 3) { + flib_log_w("Net: Bad TEAM_ACCEPTED message"); + } else { + // TODO +// HWTeam tmptm(lst[1]); +// tmptm.setNumHedgehogs(lst[2].toUInt()); +// emit hhnumChanged(tmptm); + } + } else if (!strcmp(cmd, "TEAM_COLOR")) { + if (netmsg->partCount != 3) { + flib_log_w("Net: Bad TEAM_COLOR message"); + } else { + // TODO +// HWTeam tmptm(lst[1]); +// tmptm.setColor(lst[2].toInt()); +// emit teamColorChanged(tmptm); + } + } else if (!strcmp(cmd, "EM")) { + if(netmsg->partCount < 2) { + flib_log_w("Net: Bad EM message"); + } else { + // TODO +// for(int i = 1; i < netmsg->partCount; ++i) { +// QByteArray em = QByteArray::fromBase64(lst[i].toAscii()); +// emit FromNet(em); +// } + } + } else if (!strcmp(cmd, "BYE")) { + if (netmsg->partCount < 2) { + flib_log_w("Net: Bad BYE message"); + } else { + if (!strcmp(netmsg->parts[1], "Authentication failed")) { + // TODO + //emit AuthFailed(); + } + // TODO +// m_game_connected = false; +// Disconnect(); +// emit disconnected(lst[1]); + } + } else if (!strcmp(cmd, "ADMIN_ACCESS")) { + // TODO + // emit adminAccess(true); + } else if (!strcmp(cmd, "ROOM_CONTROL_ACCESS")) { + if (netmsg->partCount < 2) { + flib_log_w("Net: Bad ROOM_CONTROL_ACCESS message"); + } else { + // TODO +// isChief = (lst[1] != "0"); +// emit roomMaster(isChief); + } + } else { + flib_log_w("Unknown server command: %s", cmd); + } + flib_netmsg_destroy(netmsg); + } +} + +void flib_netconn_tick(flib_netconn *conn) { + if(!conn) { + flib_log_e("null parameter in flib_netconn_tick"); + } else if(conn->running) { + flib_log_w("Call to flib_netconn_tick from a callback"); + } else if(conn->netconnState == NETCONN_STATE_DISCONNECTED) { + flib_log_w("Call to flib_netconn_tick, but we are already done."); + } else { + conn->running = true; + flib_netconn_wrappedtick(conn); + conn->running = false; + + if(conn->destroyRequested) { + flib_netconn_destroy(conn); + } + } +} diff -r 240620f46dd7 -r 613998625a3c project_files/frontlib/net/netconn.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/net/netconn.h Tue Jun 19 21:17:05 2012 +0200 @@ -0,0 +1,45 @@ +#ifndef NETCONN_H_ +#define NETCONN_H_ + +#include "../model/gamesetup.h" + +#include +#include +#include + +#define NETCONN_STATE_AWAIT_CONNECTED 0 +#define NETCONN_STATE_LOBBY 1 +#define NETCONN_STATE_ROOM 2 +#define NETCONN_STATE_INGAME 3 +#define NETCONN_STATE_DISCONNECTED 10 + +#define NETCONN_ERROR_SERVER_TOO_OLD 1 +#define NETCONN_ERROR_FROM_SERVER 2 + +struct _flib_netconn; +typedef struct _flib_netconn flib_netconn; + +flib_netconn *flib_netconn_create(const char *playerName, const char *host, uint16_t port); +void flib_netconn_destroy(flib_netconn *conn); + +/** + * This is called when we can't stay connected due to a problem, e.g. because the + * server version is too old, or we are unexpectedly disconnected. + * + * Once this callback has been called, you should destroy the flib_netconn. + */ +void flib_netconn_onError(flib_netconn *conn, void (*callback)(void *context, int errorCode, const char *errormsg), void* context); + +/** + * This is called when we receive a CONNECTED message from the server, which should be the first + * message arriving from the server. + */ +void flib_netconn_onConnected(flib_netconn *conn, void (*callback)(void *context, const char *serverMessage), void* context); + +/** + * Perform I/O operations and call callbacks if something interesting happens. + * Should be called regularly. + */ +void flib_netconn_tick(flib_netconn *conn); + +#endif diff -r 240620f46dd7 -r 613998625a3c project_files/frontlib/net/netprotocol.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/net/netprotocol.h Tue Jun 19 21:17:05 2012 +0200 @@ -0,0 +1,7 @@ +#ifndef NETPROTOCOL_H_ +#define NETPROTOCOL_H_ + + + + +#endif /* NETPROTOCOL_H_ */ diff -r 240620f46dd7 -r 613998625a3c project_files/frontlib/socket.c --- a/project_files/frontlib/socket.c Fri Jun 15 19:57:25 2012 +0200 +++ b/project_files/frontlib/socket.c Tue Jun 19 21:17:05 2012 +0200 @@ -121,6 +121,30 @@ return result; } +flib_tcpsocket *flib_socket_connect(const char *host, uint16_t port) { + flib_tcpsocket *result = NULL; + if(!host || port==0) { + flib_log_e("Invalid parameter in flib_socket_connect"); + } else { + IPaddress ip; + if(SDLNet_ResolveHost(&ip,host,port)==-1) { + flib_log_e("SDLNet_ResolveHost: %s\n", SDLNet_GetError()); + } else { + TCPsocket sock=SDLNet_TCP_Open(&ip); + if(!sock) { + flib_log_e("SDLNet_TCP_Open: %s\n", SDLNet_GetError()); + } else { + result = flib_socket_create(sock); + if(result) { + sock = NULL; + } + } + SDLNet_TCP_Close(sock); + } + } + return result; +} + void flib_socket_close(flib_tcpsocket *sock) { if(sock) { SDLNet_DelSocket(sock->sockset, (SDLNet_GenericSocket)sock->sock); diff -r 240620f46dd7 -r 613998625a3c project_files/frontlib/socket.h --- a/project_files/frontlib/socket.h Fri Jun 15 19:57:25 2012 +0200 +++ b/project_files/frontlib/socket.h Tue Jun 19 21:17:05 2012 +0200 @@ -49,6 +49,11 @@ flib_tcpsocket *flib_socket_accept(flib_acceptor *acceptor, bool localOnly); /** + * Try to connect to the server at the given address. + */ +flib_tcpsocket *flib_socket_connect(const char *host, uint16_t port); + +/** * Close the socket and free its memory. * If the socket is already NULL, nothing happens. */ diff -r 240620f46dd7 -r 613998625a3c project_files/frontlib/test.c --- a/project_files/frontlib/test.c Fri Jun 15 19:57:25 2012 +0200 +++ b/project_files/frontlib/test.c Tue Jun 19 21:17:05 2012 +0200 @@ -1,14 +1,17 @@ #include "frontlib.h" #include "util/logging.h" +#include "util/buffer.h" #include "model/map.h" #include "model/weapon.h" #include "model/schemelist.h" #include "ipc/mapconn.h" #include "ipc/gameconn.h" +#include "net/netbase.h" #include #include #include +#include // Callback function that will be called when the map is rendered static void handleMapSuccess(void *context, const uint8_t *bitmap, int numHedgehogs) { @@ -204,18 +207,37 @@ //testSave(); //testGame(); - flib_cfg_meta *meta = flib_cfg_meta_from_ini("metasettings.ini"); - assert(meta); - flib_schemelist *schemelist = flib_schemelist_from_ini(meta, "schemes.ini"); - assert(schemelist); + flib_netbase *conn = flib_netbase_create("140.247.62.101", 46631); - flib_schemelist_to_ini("Copy of Schemelist.ini", schemelist); - flib_schemelist_release(schemelist); - flib_cfg_meta_release(meta); - - flib_weaponsetlist *weaponsets = flib_weaponsetlist_from_ini("weapons.ini"); - assert(!flib_weaponsetlist_to_ini("copy of weapons.ini", weaponsets)); - flib_weaponsetlist_release(weaponsets); + while(flib_netbase_connected(conn)) { + flib_netmsg *msg = flib_netbase_recv_message(conn); + if(msg && msg->partCount>0) { + flib_log_i("[NET IN] %s", msg->parts[0]); + for(int i=1; ipartCount; i++) { + flib_log_i("[NET IN][-] %s", msg->parts[i]); + } + if(!strcmp(msg->parts[0], "CONNECTED")) { + flib_netmsg *nickmsg = flib_netmsg_create(); + flib_netmsg_append_part(nickmsg, "NICK", 4); + flib_netmsg_append_part(nickmsg, "Medo42_frontlib", 15); + flib_netmsg *protomsg = flib_netmsg_create(); + flib_netmsg_append_part(protomsg, "PROTO", 5); + flib_netmsg_append_part(protomsg, "42", 2); + flib_netbase_send_message(conn, nickmsg); + flib_netbase_send_message(conn, protomsg); + flib_netmsg_destroy(nickmsg); + flib_netmsg_destroy(protomsg); + } + if(!strcmp(msg->parts[0], "SERVER_MESSAGE")) { + flib_netmsg *quitmsg = flib_netmsg_create(); + flib_netmsg_append_part(quitmsg, "QUIT", 4); + flib_netmsg_append_part(quitmsg, "Just testing", 12); + flib_netbase_send_message(conn, quitmsg); + flib_netmsg_destroy(quitmsg); + } + } + flib_netmsg_destroy(msg); + } flib_quit(); return 0; diff -r 240620f46dd7 -r 613998625a3c project_files/frontlib/util/buffer.c --- a/project_files/frontlib/util/buffer.c Fri Jun 15 19:57:25 2012 +0200 +++ b/project_files/frontlib/util/buffer.c Tue Jun 19 21:17:05 2012 +0200 @@ -37,16 +37,61 @@ } } -static void try_realloc(flib_vector *vec, size_t newCapacity) { +static int setCapacity(flib_vector *vec, size_t newCapacity) { + if(newCapacity == vec->capacity) { + return 0; + } void *newData = realloc(vec->data, newCapacity); if(newData) { vec->data = newData; vec->capacity = newCapacity; + return 0; + } else { + return -1; + } +} + +static int allocateExtraCapacity(flib_vector *vec, size_t extraCapacity) { + if(extraCapacity <= SIZE_MAX - vec->capacity) { + return setCapacity(vec, vec->capacity + extraCapacity); + } else { + return -1; } } -static size_t getFreeCapacity(flib_vector *vec) { - return vec->capacity - vec->size; +int flib_vector_resize(flib_vector *vec, size_t newSize) { + if(!vec) { + flib_log_e("null parameter in flib_vector_resize"); + return -1; + } + + if(vec->capacity < newSize) { + // 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 = newSize - vec->capacity; + if(extraCapacity < minExtraCapacity) { + extraCapacity = minExtraCapacity; + } + + if(allocateExtraCapacity(vec, extraCapacity)) { + allocateExtraCapacity(vec, minExtraCapacity); + } + } else if(vec->capacity/2 > newSize) { + size_t newCapacity = newSize+newSize/4; + if(newCapacity < MIN_VECTOR_CAPACITY) { + newCapacity = MIN_VECTOR_CAPACITY; + } + setCapacity(vec, newCapacity); + } + + if(vec->capacity >= newSize) { + vec->size = newSize; + return 0; + } else { + return -1; + } } int flib_vector_append(flib_vector *vec, const void *data, size_t len) { @@ -55,35 +100,16 @@ return 0; } - 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; - } - } + if(len > SIZE_MAX-vec->size) { + return 0; } - memmove(((uint8_t*)vec->data) + vec->size, data, len); - vec->size += len; + size_t oldSize = vec->size; + if(flib_vector_resize(vec, vec->size+len)) { + return 0; + } + + memmove(((uint8_t*)vec->data) + oldSize, data, len); return len; } diff -r 240620f46dd7 -r 613998625a3c project_files/frontlib/util/buffer.h --- a/project_files/frontlib/util/buffer.h Fri Jun 15 19:57:25 2012 +0200 +++ b/project_files/frontlib/util/buffer.h Tue Jun 19 21:17:05 2012 +0200 @@ -41,6 +41,13 @@ void flib_vector_destroy(flib_vector *vec); /** + * Resize the vector. This changes the size, and ensures the capacity is large enough to + * for the new size. Can also free memory if the new size is smaller. There is no guarantee + * about the contents of extra memory. + */ +int flib_vector_resize(flib_vector *vec, size_t newSize); + +/** * 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 appending fails.