# HG changeset patch # User Medo # Date 1339177944 -7200 # Node ID bf6cf4dd847a03cee507de4d3d803b4ea80ea40d # Parent 038e3415100a824624febeb87e97e3f62d1ab974 Implemented public API for letting the engine render maps diff -r 038e3415100a -r bf6cf4dd847a project_files/frontlib/demo.c --- a/project_files/frontlib/demo.c Thu Jun 07 02:45:18 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,67 +0,0 @@ -#include "demo.h" -#include "logging.h" - -#include -#include -#include - -static int demo_record(flib_vector demoBuffer, const void *data, size_t len) { - if(flib_vector_append(demoBuffer, data, len) < len) { - flib_log_e("Error recording demo: Out of memory."); - return -1; - } else { - return 0; - } -} - -int flib_demo_record_from_engine(flib_vector demoBuffer, const uint8_t *message, const char *playerName) { - if(!demoBuffer || !message || !playerName) { - flib_log_e("Call to flib_demo_record_from_engine with demoBuffer==null or message==null or playerName==null"); - return -1; - } - - if(strchr("?CEiQqHb", message[1])) { - return 0; // Those message types are not recorded in a demo. - } - - if(message[1] == 's') { - if(message[0] >= 3) { - // Chat messages are reformatted 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; - - // 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 "; - int size = snprintf(converted+1, 256, template, playerName, chatMsg); - converted[0] = size>255 ? 255 : size; - return demo_record(demoBuffer, converted, converted[0]+1); - } else { - return 0; // Malformed chat message is no reason to abort... - } - } else { - return demo_record(demoBuffer, message, message[0]+1); - } -} - -int flib_demo_record_to_engine(flib_vector demoBuffer, const uint8_t *message, size_t len) { - if(!demoBuffer || (len>0 && !message)) { - flib_log_e("Call to flib_demo_record_to_engine with demoBuffer==null or message==null"); - return -1; - } - return demo_record(demoBuffer, message, len); -} - -void flib_demo_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; - } -} diff -r 038e3415100a -r bf6cf4dd847a project_files/frontlib/demo.h --- a/project_files/frontlib/demo.h Thu Jun 07 02:45:18 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +0,0 @@ -/** - * Demo recording functions. Only used by the ipc game code. - */ - -#ifndef DEMO_H_ -#define DEMO_H_ - -#include "buffer.h" - -/** - * Record a message sent from the engine to the frontend. - * Returns 0 for OK, a negative value on error. - * Don't pass NULL. - */ -int flib_demo_record_from_engine(flib_vector demoBuffer, const uint8_t *message, const char *playerName); - -/** - * Record a message sent from the frontend to the engine. - * Returns 0 for OK, a negative value on error. - * Don't pass NULL. - */ -int flib_demo_record_to_engine(flib_vector demoBuffer, const uint8_t *message, size_t len); - -/** - * Replace game mode messages ("TL", "TD", "TS", "TN") in the recording to mirror - * the intended use. Pass 'S' for savegames, 'D' for demos. - */ -void flib_demo_replace_gamemode(flib_buffer buf, char gamemode); - -#endif /* DEMO_H_ */ diff -r 038e3415100a -r bf6cf4dd847a project_files/frontlib/frontlib.c --- a/project_files/frontlib/frontlib.c Thu Jun 07 02:45:18 2012 +0200 +++ b/project_files/frontlib/frontlib.c Fri Jun 08 19:52:24 2012 +0200 @@ -1,8 +1,8 @@ #include "frontlib.h" #include "logging.h" -#include "socket.h" +#include "model/map.h" +#include "ipc/mapconn.h" #include "ipc.h" -#include "model/cfg.h" #include #include @@ -46,7 +46,10 @@ 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"); + flib_ipc_send_messagestr(ipc, "e$mapgen 0"); + flib_ipc_send_messagestr(ipc, "e$template_filter 0"); + flib_ipc_send_messagestr(ipc, "etheme Jungle"); + flib_ipc_send_messagestr(ipc, "eaddteam 11111111111111111111111111111111 255 Medo42"); } static void onDisconnect(void *context) { @@ -59,7 +62,7 @@ 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); + flib_log_i("Writing demo (%u bytes)...", (unsigned)demobuf.size); FILE *file = fopen("testdemo.dem", "wb"); fwrite(demobuf.data, 1, demobuf.size, file); fclose(file); @@ -74,8 +77,36 @@ } } +static void handleMapSuccess(void *context, const uint8_t *bitmap, int numHedgehogs) { + printf("Drawing map for %i brave little hogs...", numHedgehogs); + int pixelnum = 0; + for(int y=0; y>3] & (1<<(7-(pixelnum&7)))) { + printf("#"); + } else { + printf(" "); + } + pixelnum++; + } + printf("\n"); + } + + flib_mapconn **connptr = context; + flib_mapconn_destroy(*connptr); + *connptr = NULL; +} + +static void handleMapFailure(void *context, const char *errormessage) { + flib_log_e("Map rendering failed: %s", errormessage); + + flib_mapconn **connptr = context; + flib_mapconn_destroy(*connptr); + *connptr = NULL; +} + int main(int argc, char *argv[]) { - flib_init(0); +/* flib_init(0); flib_cfg_meta *meta = flib_cfg_meta_from_ini("basicsettings.ini", "gamemods.ini"); flib_cfg *cfg = flib_cfg_create(meta, "DefaultScheme"); @@ -84,18 +115,25 @@ flib_cfg_meta_destroy(meta); flib_quit(); - return 0; -/* - 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); + return 0;*/ + + flib_init(0); + flib_map *mapconf = flib_map_create_regular("Jungle", TEMPLATEFILTER_CAVERN); + assert(mapconf); + + flib_mapconn *mapconn = flib_mapconn_create("foobart", mapconf); + assert(mapconn); - while(ipc) { - flib_ipc_tick(ipc); + flib_map_destroy(mapconf); + mapconf = NULL; + + flib_mapconn_onFailure(mapconn, &handleMapFailure, &mapconn); + flib_mapconn_onSuccess(mapconn, &handleMapSuccess, &mapconn); + + while(mapconn) { + flib_mapconn_tick(mapconn); } flib_log_i("Shutting down..."); flib_quit(); - return 0;*/ + return 0; } diff -r 038e3415100a -r bf6cf4dd847a project_files/frontlib/hwconsts.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/hwconsts.h Fri Jun 08 19:52:24 2012 +0200 @@ -0,0 +1,19 @@ +/** + * This file contains important constants which might need to be changed to adapt to + * changes in the engine or protocols. + */ + +#ifndef HWCONSTS_H_ +#define HWCONSTS_H_ + +#define HEDGEHOGS_PER_TEAM 8 +#define NETGAME_DEFAULT_PORT 46631 + + +#define WEAPONS_COUNT 55 +#define AMMOLINE_DEFAULT_QT "9391929422199121032235111001201000000211110101011111011" +#define AMMOLINE_DEFAULT_PROB "0405040541600655546554464776576666666155510101115411011" +#define AMMOLINE_DEFAULT_DELAY "0000000000000205500000040007004000000000220000000600000" +#define AMMOLINE_DEFAULT_CRATE "1311110312111111123114111111111111111211111101111111010" + +#endif diff -r 038e3415100a -r bf6cf4dd847a project_files/frontlib/ini/inihelper.c --- a/project_files/frontlib/ini/inihelper.c Thu Jun 07 02:45:18 2012 +0200 +++ b/project_files/frontlib/ini/inihelper.c Fri Jun 08 19:52:24 2012 +0200 @@ -1,5 +1,6 @@ #include "inihelper.h" #include "../logging.h" +#include "../util.h" #include #include @@ -8,25 +9,6 @@ #include #include -static char *flib_asprintf(const char *fmt, ...) { - va_list argp; - va_start(argp, fmt); - size_t requiredSize = vsnprintf(NULL, 0, fmt, argp)+1; - char *buf = malloc(requiredSize); - if(buf) { - vsnprintf(buf, requiredSize, fmt, argp); - } - va_end(argp); - return buf; -} - -char *inihelper_strdupnull(const char *str) { - if(!str) { - return NULL; - } - return flib_asprintf("%s", str); -} - static bool keychar_needs_urlencoding(char c) { return !isalnum(c); } @@ -50,7 +32,10 @@ if(!keychar_needs_urlencoding(inbuf[inpos])) { outbuf[outpos++] = inbuf[inpos++]; } else { - sprintf(outbuf+outpos, "%%%02X", inbuf[inpos]); + if(snprintf(outbuf+outpos, 4, "%%%02X", (unsigned)((uint8_t*)inbuf)[inpos])<0) { + free(outbuf); + return NULL; + } inpos++; outpos += 3; } @@ -106,7 +91,7 @@ } char *inihelper_getstringdup(dictionary *inifile, bool *error, const char *sectionName, const char *keyName) { - return inihelper_strdupnull(inihelper_getstring(inifile, error, sectionName, keyName)); + return flib_strdupnull(inihelper_getstring(inifile, error, sectionName, keyName)); } int inihelper_getint(dictionary *inifile, bool *error, const char *sectionName, const char *keyName) { diff -r 038e3415100a -r bf6cf4dd847a project_files/frontlib/ini/inihelper.h --- a/project_files/frontlib/ini/inihelper.h Thu Jun 07 02:45:18 2012 +0200 +++ b/project_files/frontlib/ini/inihelper.h Fri Jun 08 19:52:24 2012 +0200 @@ -12,11 +12,6 @@ /** * Returned buffer must be free()d */ -char *inihelper_strdupnull(const char *str); - -/** - * Returned buffer must be free()d - */ char *inihelper_urlencode(const char *inbuf); /** diff -r 038e3415100a -r bf6cf4dd847a project_files/frontlib/ipc.c --- a/project_files/frontlib/ipc.c Thu Jun 07 02:45:18 2012 +0200 +++ b/project_files/frontlib/ipc.c Fri Jun 08 19:52:24 2012 +0200 @@ -1,5 +1,5 @@ #include "ipc.h" -#include "ipcconn.h" +#include "ipc/ipcconn.h" #include "logging.h" #include diff -r 038e3415100a -r bf6cf4dd847a project_files/frontlib/ipc.h --- a/project_files/frontlib/ipc.h Thu Jun 07 02:45:18 2012 +0200 +++ b/project_files/frontlib/ipc.h Fri Jun 08 19:52:24 2012 +0200 @@ -2,6 +2,7 @@ #define IPC_H_ #include "buffer.h" +#include "model/weapon.h" #include #include @@ -40,10 +41,7 @@ int flib_ipc_send_map_named(flib_ipc ipc, const char *mappath); int flib_ipc_send_gamemods(flib_ipc ipc, uint32_t modflags); int flib_ipc_send_gamesetting(flib_ipc ipc, const char *settingname, int modflags); -int flib_ipc_send_weapon_loadout(flib_ipc ipc, const char *weapsettings); -int flib_ipc_send_weapon_delay(flib_ipc ipc, const char *weapsettings); -int flib_ipc_send_weapon_cratechance(flib_ipc ipc, const char *weapsettings); -int flib_ipc_send_weapon_crateammo(flib_ipc ipc, const char *weapsettings); +int flib_ipc_send_weaponset(flib_ipc ipc, flib_weaponset *set); int flib_ipc_send_conf_end(flib_ipc ipc); diff -r 038e3415100a -r bf6cf4dd847a project_files/frontlib/ipc/demo.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/ipc/demo.c Fri Jun 08 19:52:24 2012 +0200 @@ -0,0 +1,71 @@ +#include "demo.h" +#include "../logging.h" + +#include +#include +#include + +static int demo_record(flib_vector demoBuffer, const void *data, size_t len) { + if(flib_vector_append(demoBuffer, data, len) < len) { + flib_log_e("Error recording demo: Out of memory."); + return -1; + } else { + return 0; + } +} + +int flib_demo_record_from_engine(flib_vector demoBuffer, const uint8_t *message, const char *playerName) { + if(!demoBuffer || !message || !playerName) { + flib_log_e("Call to flib_demo_record_from_engine with demoBuffer==null or message==null or playerName==null"); + return -1; + } + + if(strchr("?CEiQqHb", message[1])) { + return 0; // Those message types are not recorded in a demo. + } + + if(message[1] == 's') { + if(message[0] >= 3) { + // Chat messages are reformatted 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; + + // 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 "; + int size = snprintf(converted+1, 256, template, playerName, chatMsg); + if(size>0) { + converted[0] = size>255 ? 255 : size; + return demo_record(demoBuffer, converted, converted[0]+1); + } else { + return 0; + } + } else { + return 0; // Malformed chat message is no reason to abort... + } + } else { + return demo_record(demoBuffer, message, message[0]+1); + } +} + +int flib_demo_record_to_engine(flib_vector demoBuffer, const uint8_t *message, size_t len) { + if(!demoBuffer || (len>0 && !message)) { + flib_log_e("Call to flib_demo_record_to_engine with demoBuffer==null or message==null"); + return -1; + } + return demo_record(demoBuffer, message, len); +} + +void flib_demo_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; + } +} diff -r 038e3415100a -r bf6cf4dd847a project_files/frontlib/ipc/demo.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/ipc/demo.h Fri Jun 08 19:52:24 2012 +0200 @@ -0,0 +1,30 @@ +/** + * Demo recording functions. Only used by the ipc game code. + */ + +#ifndef DEMO_H_ +#define DEMO_H_ + +#include "../buffer.h" + +/** + * Record a message sent from the engine to the frontend. + * Returns 0 for OK, a negative value on error. + * Don't pass NULL. + */ +int flib_demo_record_from_engine(flib_vector demoBuffer, const uint8_t *message, const char *playerName); + +/** + * Record a message sent from the frontend to the engine. + * Returns 0 for OK, a negative value on error. + * Don't pass NULL. + */ +int flib_demo_record_to_engine(flib_vector demoBuffer, const uint8_t *message, size_t len); + +/** + * Replace game mode messages ("TL", "TD", "TS", "TN") in the recording to mirror + * the intended use. Pass 'S' for savegames, 'D' for demos. + */ +void flib_demo_replace_gamemode(flib_buffer buf, char gamemode); + +#endif /* DEMO_H_ */ diff -r 038e3415100a -r bf6cf4dd847a project_files/frontlib/ipc/ipcconn.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/ipc/ipcconn.c Fri Jun 08 19:52:24 2012 +0200 @@ -0,0 +1,221 @@ +#include "ipcconn.h" +#include "../logging.h" +#include "../socket.h" +#include "demo.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... + */ +typedef struct _flib_ipcconn { + uint8_t readBuffer[8192]; + char playerName[256]; + + int readBufferSize; + + flib_acceptor acceptor; + uint16_t port; + + flib_tcpsocket sock; + flib_vector demoBuffer; +} _flib_ipcconn; + +flib_ipcconn flib_ipcconn_create(bool recordDemo, const char *localPlayerName) { + flib_ipcconn result = malloc(sizeof(_flib_ipcconn)); + flib_acceptor acceptor = flib_acceptor_create(0); + + if(!result || !acceptor) { + flib_log_e("Can't create ipcconn."); + free(result); + flib_acceptor_close(&acceptor); + return NULL; + } + + result->acceptor = acceptor; + result->sock = NULL; + result->readBufferSize = 0; + result->port = flib_acceptor_listenport(acceptor); + + if(localPlayerName) { + strncpy(result->playerName, localPlayerName, 255); + } else { + strncpy(result->playerName, "Player", 255); + } + + if(recordDemo) { + result->demoBuffer = flib_vector_create(); + } + + 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("Call to flib_ipcconn_port with ipc==null"); + return 0; + } + return ipc->port; +} + +void flib_ipcconn_destroy(flib_ipcconn *ipcptr) { + if(!ipcptr) { + flib_log_e("Call to flib_ipcconn_destroy with ipcptr==null"); + } else if(*ipcptr) { + flib_ipcconn ipc = *ipcptr; + flib_acceptor_close(&ipc->acceptor); + flib_socket_close(&ipc->sock); + flib_vector_destroy(&ipc->demoBuffer); + free(ipc); + *ipcptr = NULL; + } +} + +IpcConnState flib_ipcconn_state(flib_ipcconn ipc) { + if(!ipc) { + flib_log_e("Call to flib_ipcconn_state with ipc==null"); + 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); + } + } +} + +int flib_ipcconn_recv_message(flib_ipcconn ipc, void *data) { + if(!ipc || !data) { + flib_log_e("Call to flib_ipcconn_recv_message with ipc==null or data==null"); + return -1; + } + + if(!isMessageReady(ipc)) { + receiveToBuffer(ipc); + } + + if(isMessageReady(ipc)) { + if(ipc->demoBuffer) { + if(flib_demo_record_from_engine(ipc->demoBuffer, ipc->readBuffer, ipc->playerName) < 0) { + flib_log_w("Stopping demo recording due to an error."); + flib_vector_destroy(&ipc->demoBuffer); + } + } + 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("Call to flib_ipcconn_recv_map with ipc==null or data==null"); + 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; + } +} + +int flib_ipcconn_send_raw(flib_ipcconn ipc, const void *data, size_t len) { + if(!ipc || (!data && len>0)) { + flib_log_e("Call to flib_ipcconn_send_raw with ipc==null or data==null"); + 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) { + if(ipc->demoBuffer) { + if(flib_demo_record_to_engine(ipc->demoBuffer, data, len) < 0) { + flib_log_w("Stopping demo recording due to an error."); + flib_vector_destroy(&ipc->demoBuffer); + } + } + return 0; + } else { + flib_log_w("Failed or incomplete ICP write: engine connection lost."); + flib_socket_close(&ipc->sock); + return -1; + } +} + +int flib_ipcconn_send_message(flib_ipcconn ipc, void *data, size_t len) { + if(!ipc || (!data && len>0) || len>255) { + flib_log_e("Call to flib_ipcconn_send_message with ipc==null or data==null or len>255"); + 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("Call to flib_ipcconn_accept with ipc==null"); + } else if(!ipc->sock && ipc->acceptor) { + ipc->sock = flib_socket_accept(ipc->acceptor, true); + if(ipc->sock) { + flib_acceptor_close(&ipc->acceptor); + } + } +} + +flib_constbuffer flib_ipcconn_getrecord(flib_ipcconn ipc, bool save) { + if(!ipc) { + flib_log_e("Call to flib_ipcconn_getrecord with ipc==null"); + } + if(!ipc || !ipc->demoBuffer) { + flib_constbuffer result = {NULL, 0}; + return result; + } + flib_demo_replace_gamemode(flib_vector_as_buffer(ipc->demoBuffer), save ? 'S' : 'D'); + return flib_vector_as_constbuffer(ipc->demoBuffer); +} diff -r 038e3415100a -r bf6cf4dd847a project_files/frontlib/ipc/ipcconn.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/ipc/ipcconn.h Fri Jun 08 19:52:24 2012 +0200 @@ -0,0 +1,106 @@ +/* + * Low-level protocol support for the IPC connection to the engine. + */ + +#ifndef IPCCONN_H_ +#define IPCCONN_H_ + +#include "../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(bool recordDemo, const char *localPlayerName); + +uint16_t flib_ipcconn_port(flib_ipcconn ipc); + +/** + * Free resources, close sockets, and set the pointer to NULL. + */ +void flib_ipcconn_destroy(flib_ipcconn *ipcptr); + +/** + * 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); + +/** + * Get a record of the connection. This should be called after + * the connection is closed and all messages have been received. + * + * If demo recording was not enabled, or if the recording failed for some reason, + * the buffer will be empty. + * + * If save=true is passed, the result will be a savegame, otherwise it will be a + * demo. + * + * The buffer is only valid until flib_ipcconn_getsave is called again or the ipcconn + * is destroyed. + */ +flib_constbuffer flib_ipcconn_getrecord(flib_ipcconn ipc, bool save); + +#endif /* IPCCONN_H_ */ + diff -r 038e3415100a -r bf6cf4dd847a project_files/frontlib/ipc/ipcprotocol.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/ipc/ipcprotocol.c Fri Jun 08 19:52:24 2012 +0200 @@ -0,0 +1,96 @@ +#include "ipcprotocol.h" +#include "../util.h" +#include "../logging.h" + +#include +#include +#include + +int flib_ipc_append_message(flib_vector vec, const char *fmt, ...) { + int result = -1; + if(!vec || !fmt) { + flib_log_e("null parameter in flib_ipc_appendmessage"); + } else { + // 1 byte size prefix, 255 bytes max message length, 1 0-byte for vsnprintf + char msgbuffer[257]; + + // Format the message, leaving one byte at the start for the length + va_list argp; + va_start(argp, fmt); + int msgSize = vsnprintf(msgbuffer+1, 256, fmt, argp); + va_end(argp); + + if(msgSize > 255) { + flib_log_e("Message too long (%u bytes) in flib_ipc_appendmessage", (unsigned)msgSize); + } else if(msgSize<0) { + flib_log_e("printf error in flib_ipc_appendmessage"); + } else { + // Add the length prefix + ((uint8_t*)msgbuffer)[0] = msgSize; + + // Append it to the vector + if(flib_vector_append(vec, msgbuffer, msgSize+1) == msgSize+1) { + result = 0; + } + } + } + return result; +} + +int flib_ipc_append_mapconf(flib_vector vec, flib_map *map, bool mappreview) { + int result = -1; + flib_vector tempvector = flib_vector_create(); + if(!vec || !map) { + flib_log_e("null parameter in flib_ipc_append_mapconf"); + } else if(tempvector) { + bool error = false; + + if(map->mapgen == MAPGEN_NAMED) { + error |= flib_ipc_append_message(tempvector, "emap %s", map->name); + } + if(map->theme && !mappreview) { + error |= flib_ipc_append_message(tempvector, "etheme %s", map->theme); + } + error |= flib_ipc_append_message(tempvector, "e$template_filter %i", map->templateFilter); + error |= flib_ipc_append_message(tempvector, "e$mapgen %i", map->mapgen); + + if(map->mapgen == MAPGEN_MAZE) { + error |= flib_ipc_append_message(tempvector, "e$maze_size %i", map->mazeSize); + } + if(map->mapgen == MAPGEN_DRAWN) { + /* + * We have to split the drawn map data into several edraw messages here because + * it can be longer than the maximum message size. + */ + const char *edraw = "edraw "; + int edrawlen = strlen(edraw); + for(int offset=0; offsetdrawDataSize; offset+=200) { + int bytesRemaining = map->drawDataSize-offset; + int fragmentsize = bytesRemaining < 200 ? bytesRemaining : 200; + uint8_t messagesize = edrawlen + fragmentsize; + error |= (flib_vector_append(tempvector, &messagesize, 1) != 1); + error |= (flib_vector_append(tempvector, edraw, edrawlen) != edrawlen); + error |= (flib_vector_append(tempvector, map->drawData+offset, fragmentsize) != fragmentsize); + } + } + + if(!error) { + // Message created, now we can copy everything. + flib_constbuffer constbuf = flib_vector_as_constbuffer(tempvector); + if(flib_vector_append(vec, constbuf.data, constbuf.size) == constbuf.size) { + result = 0; + } + } + } + flib_vector_destroy(&tempvector); + return result; +} + +int flib_ipc_append_seed(flib_vector vec, const char *seed) { + if(!vec || !seed) { + flib_log_e("null parameter in flib_ipc_append_seed"); + return -1; + } else { + return flib_ipc_append_message(vec, "eseed %s", seed); + } +} diff -r 038e3415100a -r bf6cf4dd847a project_files/frontlib/ipc/ipcprotocol.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/ipc/ipcprotocol.h Fri Jun 08 19:52:24 2012 +0200 @@ -0,0 +1,38 @@ +#ifndef IPCPROTOCOL_H_ +#define IPCPROTOCOL_H_ + +#include "../buffer.h" +#include "../model/map.h" + +#include + +/** + * Create a message in the IPC protocol format and add it to + * the vector. Use a format string and extra parameters as with printf. + * + * Returns nonzero if something goes wrong. In that case the buffer + * contents are unaffected. + */ +int flib_ipc_append_message(flib_vector vec, const char *fmt, ...); + +/** + * Append IPC messages to the buffer that configure the engine for + * this map. + * + * Unfortunately the engine needs a slightly different configuration + * for generating a map preview. + * + * Returns nonzero if something goes wrong. In that case the buffer + * contents are unaffected. + */ +int flib_ipc_append_mapconf(flib_vector vec, flib_map *map, bool mappreview); + +/** + * Append a seed message to the buffer. + * + * Returns nonzero if something goes wrong. In that case the buffer + * contents are unaffected. + */ +int flib_ipc_append_seed(flib_vector vec, const char *seed); + +#endif /* IPCPROTOCOL_H_ */ diff -r 038e3415100a -r bf6cf4dd847a project_files/frontlib/ipc/mapconn.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/ipc/mapconn.c Fri Jun 08 19:52:24 2012 +0200 @@ -0,0 +1,174 @@ +#include "mapconn.h" +#include "ipcconn.h" +#include "ipcprotocol.h" + +#include "../logging.h" +#include "../buffer.h" + +#include + +typedef enum { + AWAIT_CONNECTION, + AWAIT_REPLY, + FINISHED +} mapconn_progress; + +struct _flib_mapconn { + uint8_t mapBuffer[IPCCONN_MAPMSG_BYTES]; + flib_ipcconn connection; + flib_vector configBuffer; + + mapconn_progress progress; + + void (*onSuccessCb)(void*, const uint8_t*, int); + void *onSuccessCtx; + + void (*onFailureCb)(void*, const char*); + void *onFailureCtx; + + bool running; + bool destroyRequested; +}; + +static void noop_handleSuccess(void *context, const uint8_t *bitmap, int numHedgehogs) {} +static void noop_handleFailure(void *context, const char *errormessage) {} + +static void clearCallbacks(flib_mapconn *conn) { + conn->onSuccessCb = &noop_handleSuccess; + conn->onFailureCb = &noop_handleFailure; +} + +static flib_vector createConfigBuffer(char *seed, flib_map *mapdesc) { + flib_vector result = NULL; + flib_vector tempbuffer = flib_vector_create(); + if(tempbuffer) { + bool error = false; + error |= flib_ipc_append_seed(tempbuffer, seed); + error |= flib_ipc_append_mapconf(tempbuffer, mapdesc, true); + error |= flib_ipc_append_message(tempbuffer, "!"); + if(!error) { + result = tempbuffer; + tempbuffer = NULL; + } + } + flib_vector_destroy(&tempbuffer); + return result; +} + +flib_mapconn *flib_mapconn_create(char *seed, flib_map *mapdesc) { + flib_mapconn *result = NULL; + flib_mapconn *tempConn = calloc(1, sizeof(flib_mapconn)); + if(tempConn) { + tempConn->connection = flib_ipcconn_create(false, "Player"); + tempConn->configBuffer = createConfigBuffer(seed, mapdesc); + if(tempConn->connection && tempConn->configBuffer) { + tempConn->progress = AWAIT_CONNECTION; + clearCallbacks(tempConn); + result = tempConn; + tempConn = NULL; + } + } + flib_mapconn_destroy(tempConn); + return result; +} + +void flib_mapconn_destroy(flib_mapconn *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_ipcconn_destroy(&conn->connection); + flib_vector_destroy(&conn->configBuffer); + free(conn); + } + } +} + +int flib_mapconn_getport(flib_mapconn *conn) { + if(!conn) { + flib_log_e("null parameter in flib_mapconn_getport"); + return 0; + } else { + return flib_ipcconn_port(conn->connection); + } +} + +void flib_mapconn_onSuccess(flib_mapconn *conn, void (*callback)(void* context, const uint8_t *bitmap, int numHedgehogs), void *context) { + if(!conn) { + flib_log_e("null parameter in flib_mapconn_onSuccess"); + } else { + conn->onSuccessCb = callback ? callback : &noop_handleSuccess; + conn->onSuccessCtx = context; + } +} + +void flib_mapconn_onFailure(flib_mapconn *conn, void (*callback)(void* context, const char *errormessage), void *context) { + if(!conn) { + flib_log_e("null parameter in flib_mapconn_onError"); + } else { + conn->onFailureCb = callback ? callback : &noop_handleFailure; + conn->onFailureCtx = context; + } +} + +static void flib_mapconn_wrappedtick(flib_mapconn *conn) { + if(conn->progress == AWAIT_CONNECTION) { + flib_ipcconn_accept(conn->connection); + switch(flib_ipcconn_state(conn->connection)) { + case IPC_CONNECTED: + { + flib_constbuffer configBuffer = flib_vector_as_constbuffer(conn->configBuffer); + if(flib_ipcconn_send_raw(conn->connection, configBuffer.data, configBuffer.size)) { + conn->progress = FINISHED; + conn->onFailureCb(conn->onFailureCtx, "Error sending map information to the engine."); + return; + } else { + conn->progress = AWAIT_REPLY; + } + } + break; + case IPC_NOT_CONNECTED: + conn->progress = FINISHED; + conn->onFailureCb(conn->onFailureCtx, "Engine connection closed unexpectedly."); + return; + default: + break; + } + } + + if(conn->progress == AWAIT_REPLY) { + if(flib_ipcconn_recv_map(conn->connection, conn->mapBuffer) >= 0) { + conn->progress = FINISHED; + conn->onSuccessCb(conn->onSuccessCtx, conn->mapBuffer, conn->mapBuffer[IPCCONN_MAPMSG_BYTES-1]); + return; + } else if(flib_ipcconn_state(conn->connection) != IPC_CONNECTED) { + conn->progress = FINISHED; + conn->onFailureCb(conn->onSuccessCtx, "Engine connection closed unexpectedly."); + return; + } + } +} + +void flib_mapconn_tick(flib_mapconn *conn) { + if(!conn) { + flib_log_e("null parameter in flib_mapconn_tick"); + } else if(conn->running) { + flib_log_w("Call to flib_mapconn_tick from a callback"); + } else if(conn->progress == FINISHED) { + flib_log_w("Call to flib_mapconn_tick, but we are already done. Best destroy your flib_mapconn object in the callbacks."); + } else { + conn->running = true; + flib_mapconn_wrappedtick(conn); + conn->running = false; + + if(conn->destroyRequested) { + flib_mapconn_destroy(conn); + } + } +} diff -r 038e3415100a -r bf6cf4dd847a project_files/frontlib/ipc/mapconn.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/ipc/mapconn.h Fri Jun 08 19:52:24 2012 +0200 @@ -0,0 +1,77 @@ +#ifndef IPC_MAPCONN_H_ +#define IPC_MAPCONN_H_ + +#include "../model/map.h" +#include + +#define MAPIMAGE_WIDTH 256 +#define MAPIMAGE_HEIGHT 128 +#define MAPIMAGE_BYTES (MAPIMAGE_WIDTH/8*MAPIMAGE_HEIGHT) + +struct _flib_mapconn; +typedef struct _flib_mapconn flib_mapconn; + +/** + * Start a new map rendering connection (mapconn). This means a listening socket + * will be started on a random unused port, waiting for a connection from the + * engine process. Once this connection is established, the required information + * will be sent to the engine, and the reply is read. + * + * No NULL parameters allowed, returns NULL on failure. + * Use flib_mapconn_destroy to free the returned object. + */ +flib_mapconn *flib_mapconn_create(char *seed, flib_map *mapdesc); + +/** + * Destroy the mapconn object. Passing NULL is allowed and does nothing. + * flib_mapconn_destroy may be called from inside a callback function. + */ +void flib_mapconn_destroy(flib_mapconn *conn); + +/** + * Returns the port on which the mapconn is listening. Only fails if you + * pass NULL (not allowed), in that case 0 is returned. + */ +int flib_mapconn_getport(flib_mapconn *conn); + +/** + * Set a callback which will receive the rendered map if the rendering succeeds. + * You can pass callback=NULL to unset a callback. + * + * Expected callback signature: + * void handleSuccess(void *context, const uint8_t *bitmap, int numHedgehogs) + * + * The context passed to the callback is the same pointer you provided when + * registering the callback. bitmap is a pointer to a buffer of size MAPIMAGE_BYTES + * containing a bit-packed image of size MAPIMAGE_WIDTH * MAPIMAGE_HEIGHT. + * numHedgehogs is the number of hogs that fit on this map. + * + * The bitmap pointer passed to the callback belongs to the caller, + * so it should not be stored elsewhere. Note that it remains valid + * inside the callback method even if flib_mapconn_destroy is called. + */ +void flib_mapconn_onSuccess(flib_mapconn *conn, void (*callback)(void* context, const uint8_t *bitmap, int numHedgehogs), void *context); + +/** + * Set a callback which will receive an error message if rendering fails. + * You can pass callback=NULL to unset a callback. + * + * Expected callback signature: + * void handleFailure(void *context, const char *errormessage) + * + * The context passed to the callback is the same pointer you provided when + * registering the callback. + * + * The error message passed to the callback belongs to the caller, + * so it should not be stored elsewhere. Note that it remains valid + * inside the callback method even if flib_mapconn_destroy is called. + */ +void flib_mapconn_onFailure(flib_mapconn *conn, void (*callback)(void* context, const char *errormessage), void *context); + +/** + * Perform I/O operations and call callbacks if something interesting happens. + * Should be called regularly. + */ +void flib_mapconn_tick(flib_mapconn *conn); + +#endif diff -r 038e3415100a -r bf6cf4dd847a project_files/frontlib/ipcconn.c --- a/project_files/frontlib/ipcconn.c Thu Jun 07 02:45:18 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,221 +0,0 @@ -#include "ipcconn.h" -#include "logging.h" -#include "socket.h" -#include "demo.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... - */ -typedef struct _flib_ipcconn { - uint8_t readBuffer[8192]; - char playerName[256]; - - int readBufferSize; - - flib_acceptor acceptor; - uint16_t port; - - flib_tcpsocket sock; - flib_vector demoBuffer; -} _flib_ipcconn; - -flib_ipcconn flib_ipcconn_create(bool recordDemo, const char *localPlayerName) { - flib_ipcconn result = malloc(sizeof(_flib_ipcconn)); - flib_acceptor acceptor = flib_acceptor_create(0); - - if(!result || !acceptor) { - flib_log_e("Can't create ipcconn."); - free(result); - flib_acceptor_close(&acceptor); - return NULL; - } - - result->acceptor = acceptor; - result->sock = NULL; - result->readBufferSize = 0; - result->port = flib_acceptor_listenport(acceptor); - - if(localPlayerName) { - strncpy(result->playerName, localPlayerName, 255); - } else { - strncpy(result->playerName, "Player", 255); - } - - if(recordDemo) { - result->demoBuffer = flib_vector_create(); - } - - flib_log_i("Started listening for IPC connections on port %u", result->port); - return result; -} - -uint16_t flib_ipcconn_port(flib_ipcconn ipc) { - if(!ipc) { - flib_log_e("Call to flib_ipcconn_port with ipc==null"); - return 0; - } - return ipc->port; -} - -void flib_ipcconn_destroy(flib_ipcconn *ipcptr) { - if(!ipcptr) { - flib_log_e("Call to flib_ipcconn_destroy with ipcptr==null"); - } else if(*ipcptr) { - flib_ipcconn ipc = *ipcptr; - flib_acceptor_close(&ipc->acceptor); - flib_socket_close(&ipc->sock); - flib_vector_destroy(&ipc->demoBuffer); - free(ipc); - *ipcptr = NULL; - } -} - -IpcConnState flib_ipcconn_state(flib_ipcconn ipc) { - if(!ipc) { - flib_log_e("Call to flib_ipcconn_state with ipc==null"); - 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); - } - } -} - -int flib_ipcconn_recv_message(flib_ipcconn ipc, void *data) { - if(!ipc || !data) { - flib_log_e("Call to flib_ipcconn_recv_message with ipc==null or data==null"); - return -1; - } - - if(!isMessageReady(ipc)) { - receiveToBuffer(ipc); - } - - if(isMessageReady(ipc)) { - if(ipc->demoBuffer) { - if(flib_demo_record_from_engine(ipc->demoBuffer, ipc->readBuffer, ipc->playerName) < 0) { - flib_log_w("Stopping demo recording due to an error."); - flib_vector_destroy(&ipc->demoBuffer); - } - } - 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)", ipc->readBufferSize, 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("Call to flib_ipcconn_recv_map with ipc==null or data==null"); - 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; - } -} - -int flib_ipcconn_send_raw(flib_ipcconn ipc, void *data, size_t len) { - if(!ipc || (!data && len>0)) { - flib_log_e("Call to flib_ipcconn_send_raw with ipc==null or data==null"); - 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) { - if(ipc->demoBuffer) { - if(flib_demo_record_to_engine(ipc->demoBuffer, data, len) < 0) { - flib_log_w("Stopping demo recording due to an error."); - flib_vector_destroy(&ipc->demoBuffer); - } - } - return 0; - } else { - flib_log_w("Failed or incomplete ICP write: engine connection lost."); - flib_socket_close(&ipc->sock); - return -1; - } -} - -int flib_ipcconn_send_message(flib_ipcconn ipc, void *data, size_t len) { - if(!ipc || (!data && len>0) || len>255) { - flib_log_e("Call to flib_ipcconn_send_message with ipc==null or data==null or len>255"); - 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("Call to flib_ipcconn_accept with ipc==null"); - } else if(!ipc->sock && ipc->acceptor) { - ipc->sock = flib_socket_accept(ipc->acceptor, true); - if(ipc->sock) { - flib_acceptor_close(&ipc->acceptor); - } - } -} - -flib_constbuffer flib_ipcconn_getrecord(flib_ipcconn ipc, bool save) { - if(!ipc) { - flib_log_e("Call to flib_ipcconn_getrecord with ipc==null"); - } - if(!ipc || !ipc->demoBuffer) { - flib_constbuffer result = {NULL, 0}; - return result; - } - flib_demo_replace_gamemode(flib_vector_as_buffer(ipc->demoBuffer), save ? 'S' : 'D'); - return flib_vector_as_constbuffer(ipc->demoBuffer); -} diff -r 038e3415100a -r bf6cf4dd847a project_files/frontlib/ipcconn.h --- a/project_files/frontlib/ipcconn.h Thu Jun 07 02:45:18 2012 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,106 +0,0 @@ -/* - * Low-level protocol support for the IPC connection to the engine. - */ - -#ifndef IPCCONN_H_ -#define IPCCONN_H_ - -#include "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(bool recordDemo, const char *localPlayerName); - -uint16_t flib_ipcconn_port(flib_ipcconn ipc); - -/** - * Free resources, close sockets, and set the pointer to NULL. - */ -void flib_ipcconn_destroy(flib_ipcconn *ipcptr); - -/** - * 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, 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); - -/** - * Get a record of the connection. This should be called after - * the connection is closed and all messages have been received. - * - * If demo recording was not enabled, or if the recording failed for some reason, - * the buffer will be empty. - * - * If save=true is passed, the result will be a savegame, otherwise it will be a - * demo. - * - * The buffer is only valid until flib_ipcconn_getsave is called again or the ipcconn - * is destroyed. - */ -flib_constbuffer flib_ipcconn_getrecord(flib_ipcconn ipc, bool save); - -#endif /* IPCCONN_H_ */ - diff -r 038e3415100a -r bf6cf4dd847a project_files/frontlib/logging.c --- a/project_files/frontlib/logging.c Thu Jun 07 02:45:18 2012 +0200 +++ b/project_files/frontlib/logging.c Fri Jun 08 19:52:24 2012 +0200 @@ -7,7 +7,7 @@ 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); + snprintf(ip, 16, "%u.%u.%u.%u", (unsigned)(numip>>24), (unsigned)((numip>>16)&0xff), (unsigned)((numip>>8)&0xff), (unsigned)(numip&0xff)); return ip; } diff -r 038e3415100a -r bf6cf4dd847a project_files/frontlib/model/cfg.c --- a/project_files/frontlib/model/cfg.c Thu Jun 07 02:45:18 2012 +0200 +++ b/project_files/frontlib/model/cfg.c Fri Jun 08 19:52:24 2012 +0200 @@ -4,6 +4,7 @@ #include "../iniparser/dictionary.h" #include "../ini/inihelper.h" #include "../logging.h" +#include "../util.h" #include @@ -56,7 +57,7 @@ } bool error = false; - result->settings[i].iniName = inihelper_strdupnull(sectionName); + result->settings[i].iniName = flib_strdupnull(sectionName); result->settings[i].title = inihelper_getstringdup(settingfile, &error, sectionName, "title"); result->settings[i].engineCommand = inihelper_getstringdup(settingfile, &error, sectionName, "command"); result->settings[i].image = inihelper_getstringdup(settingfile, &error, sectionName, "image"); @@ -78,7 +79,7 @@ } bool error = false; - result->mods[i].iniName = inihelper_strdupnull(sectionName); + result->mods[i].iniName = flib_strdupnull(sectionName); result->mods[i].bitmaskIndex = inihelper_getint(modfile, &error, sectionName, "bitmaskIndex"); if(error) { flib_log_e("Missing or malformed ini parameter in file %s, section %s", modpath, sectionName); @@ -109,7 +110,7 @@ result->modCount = meta->modCount; result->settingCount = meta->settingCount; - result->schemeName = inihelper_strdupnull(schemeName); + result->schemeName = flib_strdupnull(schemeName); result->mods = calloc(meta->modCount, sizeof(*result->mods)); result->settings = calloc(meta->settingCount, sizeof(*result->settings)); diff -r 038e3415100a -r bf6cf4dd847a project_files/frontlib/model/map.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/model/map.c Fri Jun 08 19:52:24 2012 +0200 @@ -0,0 +1,95 @@ +#include "map.h" + +#include "../ini/inihelper.h" +#include "../util.h" +#include "../logging.h" + +#include + +flib_map *flib_map_create_regular(const char *theme, int templateFilter) { + flib_map *result = NULL; + if(!theme) { + flib_log_e("null parameter in flib_map_create_regular"); + } else { + flib_map *newmap = calloc(1, sizeof(flib_map)); + if(newmap) { + newmap->mapgen = MAPGEN_REGULAR; + newmap->templateFilter = templateFilter; + newmap->theme = flib_strdupnull(theme); + if(newmap->theme) { + result = newmap; + newmap = NULL; + } + } + flib_map_destroy(newmap); + } + return result; +} + +flib_map *flib_map_create_maze(const char *theme, int mazeSize) { + flib_map *result = NULL; + if(!theme) { + flib_log_e("null parameter in flib_map_create_maze"); + } else { + flib_map *newmap = calloc(1, sizeof(flib_map)); + if(newmap) { + newmap->mapgen = MAPGEN_MAZE; + newmap->mazeSize = mazeSize; + newmap->theme = flib_strdupnull(theme); + if(newmap->theme) { + result = newmap; + newmap = NULL; + } + } + flib_map_destroy(newmap); + } + return result; +} + +flib_map *flib_map_create_named(const char *name) { + flib_map *result = NULL; + if(!name) { + flib_log_e("null parameter in flib_map_create_named"); + } else { + flib_map *newmap = calloc(1, sizeof(flib_map)); + if(newmap) { + newmap->mapgen = MAPGEN_NAMED; + newmap->name = flib_strdupnull(name); + if(newmap->name) { + result = newmap; + newmap = NULL; + } + } + flib_map_destroy(newmap); + } + return result; +} + +flib_map *flib_map_create_drawn(const char *theme, const uint8_t *drawData, int drawDataSize) { + flib_map *result = NULL; + if(!theme || !drawData) { + flib_log_e("null parameter in flib_map_create_named"); + } else { + flib_map *newmap = calloc(1, sizeof(flib_map)); + if(newmap) { + newmap->mapgen = MAPGEN_DRAWN; + newmap->drawData = flib_bufdupnull(drawData, drawDataSize); + newmap->drawDataSize = drawDataSize; + if(newmap->drawData) { + result = newmap; + newmap = NULL; + } + } + flib_map_destroy(newmap); + } + return result; +} + +void flib_map_destroy(flib_map *map) { + if(map) { + free(map->drawData); + free(map->name); + free(map->theme); + free(map); + } +} diff -r 038e3415100a -r bf6cf4dd847a project_files/frontlib/model/map.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/model/map.h Fri Jun 08 19:52:24 2012 +0200 @@ -0,0 +1,87 @@ +/** + * Data structure for defining a map. Note that most maps also depend on the + * random seed passed to the engine, if you store that in addition to the + * flib_map structure you have the whole recipe to exactly recreate a particular + * map. For named maps, you also need the corresponding files. + */ + +#ifndef MODEL_MAP_H_ +#define MODEL_MAP_H_ + +#include + +#define MAPGEN_REGULAR 0 +#define MAPGEN_MAZE 1 +#define MAPGEN_DRAWN 2 +#define MAPGEN_NAMED 3 + +#define TEMPLATEFILTER_ALL 0 +#define TEMPLATEFILTER_SMALL 1 +#define TEMPLATEFILTER_MEDIUM 2 +#define TEMPLATEFILTER_LARGE 3 +#define TEMPLATEFILTER_CAVERN 4 +#define TEMPLATEFILTER_WACKY 5 + +#define MAZE_SIZE_SMALL_TUNNELS 0 +#define MAZE_SIZE_MEDIUM_TUNNELS 1 +#define MAZE_SIZE_LARGE_TUNNELS 2 +#define MAZE_SIZE_SMALL_ISLANDS 3 +#define MAZE_SIZE_MEDIUM_ISLANDS 4 +#define MAZE_SIZE_LARGE_ISLANDS 5 + +typedef struct { + int mapgen; // Always one of the MAPGEN_ constants + char *theme; // Used for all except MAPGEN_NAMED + char *name; // Used for MAPGEN_NAMED + uint8_t *drawData; // Used for MAPGEN_DRAWN + int drawDataSize; // Used for MAPGEN_DRAWN + int templateFilter; // Used for MAPGEN_REGULAR + int mazeSize; // Used for MAPGEN_MAZE +} flib_map; + +/** + * Create a generated map. theme should be the name of a + * directory in "Themes" and templateFilter should be one of the + * TEMPLATEFILTER_* constants, but this is not checked before + * passing it to the engine. + * + * Use flib_map_destroy to free the returned object. + * No NULL parameters allowed, returns NULL on failure. + */ +flib_map *flib_map_create_regular(const char *theme, int templateFilter); + +/** + * Create a generated maze-type map. theme should be the name of a + * directory in "Themes" and mazeSize should be one of the + * MAZE_SIZE_* constants, but this is not checked before + * passing it to the engine. + * + * Use flib_map_destroy to free the returned object. + * No NULL parameters allowed, returns NULL on failure. + */ +flib_map *flib_map_create_maze(const char *theme, int mazeSize); + +/** + * Create a map from the Maps-Directory. name should be the name of a + * directory in "Maps", but this is not checked before + * passing it to the engine. If this is a mission, the corresponding + * script is used automatically. + * + * Use flib_map_destroy to free the returned object. + * No NULL parameters allowed, returns NULL on failure. + */ +flib_map *flib_map_create_named(const char *name); + +/** + * Create a hand-drawn map. Use flib_map_destroy to free the returned object. + * No NULL parameters allowed, returns NULL on failure. + */ +flib_map *flib_map_create_drawn(const char *theme, const uint8_t *drawData, int drawDataSize); + +/** + * Free the memory taken up by the map. Passing NULL is allowed and does nothing. + */ +void flib_map_destroy(flib_map *map); + + +#endif diff -r 038e3415100a -r bf6cf4dd847a project_files/frontlib/model/weapon.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/model/weapon.c Fri Jun 08 19:52:24 2012 +0200 @@ -0,0 +1,128 @@ +#include "weapon.h" + +#include "../ini/inihelper.h" +#include "../iniparser/iniparser.h" +#include "../logging.h" +#include "../util.h" + +#include +#include + +int set_field(char field[WEAPONS_COUNT+1], const char *line, bool no9) { + // Validate the new string + for(int i=0; i '9' || (no9 && line[i] == '9')) { + flib_log_e("Invalid character in weapon config string \"%.*s\", position %i", WEAPONS_COUNT, line, i); + return -1; + } + } + + bool lineEnded = false; + for(int i=0; iname = nameCopy; + nameCopy = NULL; + bool error = false; + error |= set_field(newSet->loadout, loadoutStr, false); + error |= set_field(newSet->crateprob, crateProbStr, false); + error |= set_field(newSet->crateammo, crateAmmoStr, false); + error |= set_field(newSet->delay, delayStr, false); + if(!error) { + result = newSet; + newSet = NULL; + } + } + free(nameCopy); + flib_weaponset_destroy(newSet); + } + return result; +} + +void flib_weaponset_destroy(flib_weaponset *cfg) { + if(cfg) { + free(cfg->name); + free(cfg); + } +} + +flib_weaponset *flib_weaponset_create(const char *name) { + return flib_weaponset_create_str(name, AMMOLINE_DEFAULT_QT, AMMOLINE_DEFAULT_PROB, AMMOLINE_DEFAULT_CRATE, AMMOLINE_DEFAULT_DELAY); +} + +flib_weaponset *flib_weaponset_from_ini(const char *filename) { + flib_weaponset *result = NULL; + if(!filename) { + flib_log_e("null parameter in flib_weaponset_from_ini"); + } else { + dictionary *settingfile = iniparser_load(filename); + if(!settingfile) { + flib_log_e("Error loading weapon scheme file %s", filename); + } else { + bool error = false; + char *name = inihelper_getstring(settingfile, &error, "weaponset", "name"); + char *loadout = inihelper_getstring(settingfile, &error, "weaponset", "loadout"); + char *crateprob = inihelper_getstring(settingfile, &error, "weaponset", "crateprob"); + char *crateammo = inihelper_getstring(settingfile, &error, "weaponset", "crateammo"); + char *delay = inihelper_getstring(settingfile, &error, "weaponset", "delay"); + if(error) { + flib_log_e("Missing key in weapon scheme file %s", filename); + } else { + result = flib_weaponset_create_str(name, loadout, crateprob, crateammo, delay); + } + } + iniparser_freedict(settingfile); + } + return result; +} + +int flib_weaponset_to_ini(const char *filename, const flib_weaponset *set) { + int result = -1; + if(!filename || !set) { + flib_log_e("null parameter in flib_weaponset_to_ini"); + } else { + dictionary *dict = dictionary_new(0); + if(dict) { + bool error = false; + // Add the sections + error |= iniparser_set(dict, "weaponset", NULL); + + // Add the values + error |= inihelper_setstr(dict, "weaponset", "name", set->name); + error |= inihelper_setstr(dict, "weaponset", "loadout", set->loadout); + error |= inihelper_setstr(dict, "weaponset", "crateprob", set->crateprob); + error |= inihelper_setstr(dict, "weaponset", "crateammo", set->crateammo); + error |= inihelper_setstr(dict, "weaponset", "delay", set->delay); + if(!error) { + FILE *inifile = fopen(filename, "wb"); + if(inifile) { + iniparser_dump_ini(dict, inifile); + fclose(inifile); + result = 0; + } + } + dictionary_del(dict); + } + } + return result; +} diff -r 038e3415100a -r bf6cf4dd847a project_files/frontlib/model/weapon.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/model/weapon.h Fri Jun 08 19:52:24 2012 +0200 @@ -0,0 +1,32 @@ +#ifndef MODEL_WEAPON_H_ +#define MODEL_WEAPON_H_ + +#include "../hwconsts.h" + +/** + * These values are all in the range 0..9 + * + * For loadout, 9 means inifinite ammo. + * For the other setting, 9 might actually be invalid, it's not possible to set more than 8 in the QtFrontend. (TODO) + */ +typedef struct { + char loadout[WEAPONS_COUNT+1]; + char crateprob[WEAPONS_COUNT+1]; + char crateammo[WEAPONS_COUNT+1]; + char delay[WEAPONS_COUNT+1]; + char *name; +} flib_weaponset; + +/** + * Returns a new weapon set, or NULL on error. + * name must not be NULL. + * + * The new weapon set is pre-filled with default + * settings (see hwconsts.h) + */ +flib_weaponset *flib_weaponset_create(const char *name); +flib_weaponset *flib_weaponset_from_ini(const char *filename); +int flib_weaponset_to_ini(const char *filename, const flib_weaponset *set); +void flib_weaponset_destroy(flib_weaponset *set); + +#endif diff -r 038e3415100a -r bf6cf4dd847a project_files/frontlib/socket.c --- a/project_files/frontlib/socket.c Thu Jun 07 02:45:18 2012 +0200 +++ b/project_files/frontlib/socket.c Fri Jun 08 19:52:24 2012 +0200 @@ -60,7 +60,7 @@ if(result->sock) { return result; } else { - flib_log_e("Unable to listen on port %u: %s", port, SDLNet_GetError()); + flib_log_e("Unable to listen on port %u: %s", (unsigned)port, SDLNet_GetError()); free(result); return NULL; } @@ -77,7 +77,7 @@ if(result->sock) { return result; } else { - flib_log_w("Unable to listen on port %u: %s", result->port, SDLNet_GetError()); + flib_log_w("Unable to listen on port %u: %s", (unsigned)result->port, SDLNet_GetError()); } } flib_log_e("Unable to listen on a random unused port."); @@ -155,7 +155,7 @@ } } -int flib_socket_send(flib_tcpsocket sock, void *data, int len) { +int flib_socket_send(flib_tcpsocket sock, const void *data, int len) { if(!sock || (len>0 && !data)) { flib_log_e("Call to flib_socket_send with sock==null or data==null"); return -1; diff -r 038e3415100a -r bf6cf4dd847a project_files/frontlib/socket.h --- a/project_files/frontlib/socket.h Thu Jun 07 02:45:18 2012 +0200 +++ b/project_files/frontlib/socket.h Fri Jun 08 19:52:24 2012 +0200 @@ -62,6 +62,6 @@ */ int flib_socket_nbrecv(flib_tcpsocket sock, void *data, int maxlen); -int flib_socket_send(flib_tcpsocket sock, void *data, int len); +int flib_socket_send(flib_tcpsocket sock, const void *data, int len); #endif /* SOCKET_H_ */ diff -r 038e3415100a -r bf6cf4dd847a project_files/frontlib/util.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/util.c Fri Jun 08 19:52:24 2012 +0200 @@ -0,0 +1,49 @@ +#include "util.h" + +#include +#include +#include +#include +#include + +char *flib_asprintf(const char *fmt, ...) { + va_list argp; + va_start(argp, fmt); + char *result = flib_vasprintf(fmt, argp); + va_end(argp); + return result; +} + +char *flib_vasprintf(const char *fmt, va_list args) { + char *result = NULL; + int requiredSize = vsnprintf(NULL, 0, fmt, args)+1; // Figure out how much memory we need, + if(requiredSize>=0) { + char *tmpbuf = malloc(requiredSize); // allocate it + if(tmpbuf) { + if(vsnprintf(tmpbuf, requiredSize, fmt, args)>=0) { // and then do the actual formatting. + result = tmpbuf; + tmpbuf = NULL; + } + } + free(tmpbuf); + } + return result; +} + +char *flib_strdupnull(const char *str) { + if(!str) { + return NULL; + } + return flib_asprintf("%s", str); +} + +void *flib_bufdupnull(const void *buf, size_t size) { + if(!buf || size==0) { + return NULL; + } + void *result = malloc(size); + if(result) { + memcpy(result, buf, size); + } + return result; +} diff -r 038e3415100a -r bf6cf4dd847a project_files/frontlib/util.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/util.h Fri Jun 08 19:52:24 2012 +0200 @@ -0,0 +1,36 @@ +#ifndef FLIB_UTIL_H_ +#define FLIB_UTIL_H_ + +#include +#include + +/** + * Prints a format string to a newly allocated buffer of the required size. + * Parameters are like those for printf. Returns NULL on error. + * + * Returned buffer must be free()d + */ +char *flib_asprintf(const char *fmt, ...); + +/** + * Exactly as flib_asprintf, but accepts va_args. + */ +char *flib_vasprintf(const char *fmt, va_list args); + +/** + * Return a duplicate of the provided string, or NULL if an error + * occurs or if str is already NULL. + * + * Returned buffer must be free()d + */ +char *flib_strdupnull(const char *str); + +/** + * Return a duplicate of the provided buffer, or NULL if an error + * occurs or if buf is already NULL or if size is 0. + * + * Returned buffer must be free()d + */ +void *flib_bufdupnull(const void *buf, size_t size); + +#endif