# HG changeset patch # User Medo # Date 1340577727 -7200 # Node ID 5608ac657362e9b5bec9577804b4680a0fd8af44 # Parent 5b0aeef8ba2ae67a1a7f96fd6b2044baa299636f frontlib: Intermittent commit. Things are still in flux but we're getting there :) diff -r 5b0aeef8ba2a -r 5608ac657362 project_files/frontlib/base64/base64.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/base64/base64.c Mon Jun 25 00:42:07 2012 +0200 @@ -0,0 +1,423 @@ +/* base64.c -- Encode binary data using printable characters. + Copyright (C) 1999, 2000, 2001, 2004, 2005, 2006 Free Software + Foundation, Inc. + + 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; either version 2, or (at your option) + any later version. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +/* Written by Simon Josefsson. Partially adapted from GNU MailUtils + * (mailbox/filter_trans.c, as of 2004-11-28). Improved by review + * from Paul Eggert, Bruno Haible, and Stepan Kasal. + * + * See also RFC 3548 . + * + * Be careful with error checking. Here is how you would typically + * use these functions: + * + * bool ok = base64_decode_alloc (in, inlen, &out, &outlen); + * if (!ok) + * FAIL: input was not valid base64 + * if (out == NULL) + * FAIL: memory allocation error + * OK: data in OUT/OUTLEN + * + * size_t outlen = base64_encode_alloc (in, inlen, &out); + * if (out == NULL && outlen == 0 && inlen != 0) + * FAIL: input too long + * if (out == NULL) + * FAIL: memory allocation error + * OK: data in OUT/OUTLEN. + * + */ + +/* Get prototype. */ +#include "base64.h" + +/* Get malloc. */ +#include + +/* Get UCHAR_MAX. */ +#include + +/* C89 compliant way to cast 'char' to 'unsigned char'. */ +static inline unsigned char +to_uchar (char ch) +{ + return ch; +} + +/* Base64 encode IN array of size INLEN into OUT array of size OUTLEN. + If OUTLEN is less than BASE64_LENGTH(INLEN), write as many bytes as + possible. If OUTLEN is larger than BASE64_LENGTH(INLEN), also zero + terminate the output buffer. */ +void +base64_encode (const char *restrict in, size_t inlen, + char *restrict out, size_t outlen) +{ + static const char b64str[64] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + while (inlen && outlen) + { + *out++ = b64str[(to_uchar (in[0]) >> 2) & 0x3f]; + if (!--outlen) + break; + *out++ = b64str[((to_uchar (in[0]) << 4) + + (--inlen ? to_uchar (in[1]) >> 4 : 0)) + & 0x3f]; + if (!--outlen) + break; + *out++ = + (inlen + ? b64str[((to_uchar (in[1]) << 2) + + (--inlen ? to_uchar (in[2]) >> 6 : 0)) + & 0x3f] + : '='); + if (!--outlen) + break; + *out++ = inlen ? b64str[to_uchar (in[2]) & 0x3f] : '='; + if (!--outlen) + break; + if (inlen) + inlen--; + if (inlen) + in += 3; + } + + if (outlen) + *out = '\0'; +} + +/* Allocate a buffer and store zero terminated base64 encoded data + from array IN of size INLEN, returning BASE64_LENGTH(INLEN), i.e., + the length of the encoded data, excluding the terminating zero. On + return, the OUT variable will hold a pointer to newly allocated + memory that must be deallocated by the caller. If output string + length would overflow, 0 is returned and OUT is set to NULL. If + memory allocation failed, OUT is set to NULL, and the return value + indicates length of the requested memory block, i.e., + BASE64_LENGTH(inlen) + 1. */ +size_t +base64_encode_alloc (const char *in, size_t inlen, char **out) +{ + size_t outlen = 1 + BASE64_LENGTH (inlen); + + /* Check for overflow in outlen computation. + * + * If there is no overflow, outlen >= inlen. + * + * If the operation (inlen + 2) overflows then it yields at most +1, so + * outlen is 0. + * + * If the multiplication overflows, we lose at least half of the + * correct value, so the result is < ((inlen + 2) / 3) * 2, which is + * less than (inlen + 2) * 0.66667, which is less than inlen as soon as + * (inlen > 4). + */ + if (inlen > outlen) + { + *out = NULL; + return 0; + } + + *out = malloc (outlen); + if (!*out) + return outlen; + + base64_encode (in, inlen, *out, outlen); + + return outlen - 1; +} + +/* With this approach this file works independent of the charset used + (think EBCDIC). However, it does assume that the characters in the + Base64 alphabet (A-Za-z0-9+/) are encoded in 0..255. POSIX + 1003.1-2001 require that char and unsigned char are 8-bit + quantities, though, taking care of that problem. But this may be a + potential problem on non-POSIX C99 platforms. + + IBM C V6 for AIX mishandles "#define B64(x) ...'x'...", so use "_" + as the formal parameter rather than "x". */ +#define B64(_) \ + ((_) == 'A' ? 0 \ + : (_) == 'B' ? 1 \ + : (_) == 'C' ? 2 \ + : (_) == 'D' ? 3 \ + : (_) == 'E' ? 4 \ + : (_) == 'F' ? 5 \ + : (_) == 'G' ? 6 \ + : (_) == 'H' ? 7 \ + : (_) == 'I' ? 8 \ + : (_) == 'J' ? 9 \ + : (_) == 'K' ? 10 \ + : (_) == 'L' ? 11 \ + : (_) == 'M' ? 12 \ + : (_) == 'N' ? 13 \ + : (_) == 'O' ? 14 \ + : (_) == 'P' ? 15 \ + : (_) == 'Q' ? 16 \ + : (_) == 'R' ? 17 \ + : (_) == 'S' ? 18 \ + : (_) == 'T' ? 19 \ + : (_) == 'U' ? 20 \ + : (_) == 'V' ? 21 \ + : (_) == 'W' ? 22 \ + : (_) == 'X' ? 23 \ + : (_) == 'Y' ? 24 \ + : (_) == 'Z' ? 25 \ + : (_) == 'a' ? 26 \ + : (_) == 'b' ? 27 \ + : (_) == 'c' ? 28 \ + : (_) == 'd' ? 29 \ + : (_) == 'e' ? 30 \ + : (_) == 'f' ? 31 \ + : (_) == 'g' ? 32 \ + : (_) == 'h' ? 33 \ + : (_) == 'i' ? 34 \ + : (_) == 'j' ? 35 \ + : (_) == 'k' ? 36 \ + : (_) == 'l' ? 37 \ + : (_) == 'm' ? 38 \ + : (_) == 'n' ? 39 \ + : (_) == 'o' ? 40 \ + : (_) == 'p' ? 41 \ + : (_) == 'q' ? 42 \ + : (_) == 'r' ? 43 \ + : (_) == 's' ? 44 \ + : (_) == 't' ? 45 \ + : (_) == 'u' ? 46 \ + : (_) == 'v' ? 47 \ + : (_) == 'w' ? 48 \ + : (_) == 'x' ? 49 \ + : (_) == 'y' ? 50 \ + : (_) == 'z' ? 51 \ + : (_) == '0' ? 52 \ + : (_) == '1' ? 53 \ + : (_) == '2' ? 54 \ + : (_) == '3' ? 55 \ + : (_) == '4' ? 56 \ + : (_) == '5' ? 57 \ + : (_) == '6' ? 58 \ + : (_) == '7' ? 59 \ + : (_) == '8' ? 60 \ + : (_) == '9' ? 61 \ + : (_) == '+' ? 62 \ + : (_) == '/' ? 63 \ + : -1) + +static const signed char b64[0x100] = { + B64 (0), B64 (1), B64 (2), B64 (3), + B64 (4), B64 (5), B64 (6), B64 (7), + B64 (8), B64 (9), B64 (10), B64 (11), + B64 (12), B64 (13), B64 (14), B64 (15), + B64 (16), B64 (17), B64 (18), B64 (19), + B64 (20), B64 (21), B64 (22), B64 (23), + B64 (24), B64 (25), B64 (26), B64 (27), + B64 (28), B64 (29), B64 (30), B64 (31), + B64 (32), B64 (33), B64 (34), B64 (35), + B64 (36), B64 (37), B64 (38), B64 (39), + B64 (40), B64 (41), B64 (42), B64 (43), + B64 (44), B64 (45), B64 (46), B64 (47), + B64 (48), B64 (49), B64 (50), B64 (51), + B64 (52), B64 (53), B64 (54), B64 (55), + B64 (56), B64 (57), B64 (58), B64 (59), + B64 (60), B64 (61), B64 (62), B64 (63), + B64 (64), B64 (65), B64 (66), B64 (67), + B64 (68), B64 (69), B64 (70), B64 (71), + B64 (72), B64 (73), B64 (74), B64 (75), + B64 (76), B64 (77), B64 (78), B64 (79), + B64 (80), B64 (81), B64 (82), B64 (83), + B64 (84), B64 (85), B64 (86), B64 (87), + B64 (88), B64 (89), B64 (90), B64 (91), + B64 (92), B64 (93), B64 (94), B64 (95), + B64 (96), B64 (97), B64 (98), B64 (99), + B64 (100), B64 (101), B64 (102), B64 (103), + B64 (104), B64 (105), B64 (106), B64 (107), + B64 (108), B64 (109), B64 (110), B64 (111), + B64 (112), B64 (113), B64 (114), B64 (115), + B64 (116), B64 (117), B64 (118), B64 (119), + B64 (120), B64 (121), B64 (122), B64 (123), + B64 (124), B64 (125), B64 (126), B64 (127), + B64 (128), B64 (129), B64 (130), B64 (131), + B64 (132), B64 (133), B64 (134), B64 (135), + B64 (136), B64 (137), B64 (138), B64 (139), + B64 (140), B64 (141), B64 (142), B64 (143), + B64 (144), B64 (145), B64 (146), B64 (147), + B64 (148), B64 (149), B64 (150), B64 (151), + B64 (152), B64 (153), B64 (154), B64 (155), + B64 (156), B64 (157), B64 (158), B64 (159), + B64 (160), B64 (161), B64 (162), B64 (163), + B64 (164), B64 (165), B64 (166), B64 (167), + B64 (168), B64 (169), B64 (170), B64 (171), + B64 (172), B64 (173), B64 (174), B64 (175), + B64 (176), B64 (177), B64 (178), B64 (179), + B64 (180), B64 (181), B64 (182), B64 (183), + B64 (184), B64 (185), B64 (186), B64 (187), + B64 (188), B64 (189), B64 (190), B64 (191), + B64 (192), B64 (193), B64 (194), B64 (195), + B64 (196), B64 (197), B64 (198), B64 (199), + B64 (200), B64 (201), B64 (202), B64 (203), + B64 (204), B64 (205), B64 (206), B64 (207), + B64 (208), B64 (209), B64 (210), B64 (211), + B64 (212), B64 (213), B64 (214), B64 (215), + B64 (216), B64 (217), B64 (218), B64 (219), + B64 (220), B64 (221), B64 (222), B64 (223), + B64 (224), B64 (225), B64 (226), B64 (227), + B64 (228), B64 (229), B64 (230), B64 (231), + B64 (232), B64 (233), B64 (234), B64 (235), + B64 (236), B64 (237), B64 (238), B64 (239), + B64 (240), B64 (241), B64 (242), B64 (243), + B64 (244), B64 (245), B64 (246), B64 (247), + B64 (248), B64 (249), B64 (250), B64 (251), + B64 (252), B64 (253), B64 (254), B64 (255) +}; + +#if UCHAR_MAX == 255 +# define uchar_in_range(c) true +#else +# define uchar_in_range(c) ((c) <= 255) +#endif + +/* Return true if CH is a character from the Base64 alphabet, and + false otherwise. Note that '=' is padding and not considered to be + part of the alphabet. */ +bool +isbase64 (char ch) +{ + return uchar_in_range (to_uchar (ch)) && 0 <= b64[to_uchar (ch)]; +} + +/* Decode base64 encoded input array IN of length INLEN to output + array OUT that can hold *OUTLEN bytes. Return true if decoding was + successful, i.e. if the input was valid base64 data, false + otherwise. If *OUTLEN is too small, as many bytes as possible will + be written to OUT. On return, *OUTLEN holds the length of decoded + bytes in OUT. Note that as soon as any non-alphabet characters are + encountered, decoding is stopped and false is returned. This means + that, when applicable, you must remove any line terminators that is + part of the data stream before calling this function. */ +bool +base64_decode (const char *restrict in, size_t inlen, + char *restrict out, size_t *outlen) +{ + size_t outleft = *outlen; + + while (inlen >= 2) + { + if (!isbase64 (in[0]) || !isbase64 (in[1])) + break; + + if (outleft) + { + *out++ = ((b64[to_uchar (in[0])] << 2) + | (b64[to_uchar (in[1])] >> 4)); + outleft--; + } + + if (inlen == 2) + break; + + if (in[2] == '=') + { + if (inlen != 4) + break; + + if (in[3] != '=') + break; + + } + else + { + if (!isbase64 (in[2])) + break; + + if (outleft) + { + *out++ = (((b64[to_uchar (in[1])] << 4) & 0xf0) + | (b64[to_uchar (in[2])] >> 2)); + outleft--; + } + + if (inlen == 3) + break; + + if (in[3] == '=') + { + if (inlen != 4) + break; + } + else + { + if (!isbase64 (in[3])) + break; + + if (outleft) + { + *out++ = (((b64[to_uchar (in[2])] << 6) & 0xc0) + | b64[to_uchar (in[3])]); + outleft--; + } + } + } + + in += 4; + inlen -= 4; + } + + *outlen -= outleft; + + if (inlen != 0) + return false; + + return true; +} + +/* Allocate an output buffer in *OUT, and decode the base64 encoded + data stored in IN of size INLEN to the *OUT buffer. On return, the + size of the decoded data is stored in *OUTLEN. OUTLEN may be NULL, + if the caller is not interested in the decoded length. *OUT may be + NULL to indicate an out of memory error, in which case *OUTLEN + contains the size of the memory block needed. The function returns + true on successful decoding and memory allocation errors. (Use the + *OUT and *OUTLEN parameters to differentiate between successful + decoding and memory error.) The function returns false if the + input was invalid, in which case *OUT is NULL and *OUTLEN is + undefined. */ +bool +base64_decode_alloc (const char *in, size_t inlen, char **out, + size_t *outlen) +{ + /* This may allocate a few bytes too much, depending on input, + but it's not worth the extra CPU time to compute the exact amount. + The exact amount is 3 * inlen / 4, minus 1 if the input ends + with "=" and minus another 1 if the input ends with "==". + Dividing before multiplying avoids the possibility of overflow. */ + size_t needlen = 3 * (inlen / 4) + 2; + + *out = malloc (needlen); + if (!*out) + return true; + + if (!base64_decode (in, inlen, *out, &needlen)) + { + free (*out); + *out = NULL; + return false; + } + + if (outlen) + *outlen = needlen; + + return true; +} diff -r 5b0aeef8ba2a -r 5608ac657362 project_files/frontlib/base64/base64.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/base64/base64.h Mon Jun 25 00:42:07 2012 +0200 @@ -0,0 +1,45 @@ +/* base64.h -- Encode binary data using printable characters. + Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc. + Written by Simon Josefsson. + + 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; either version 2, or (at your option) + any later version. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ + +#ifndef BASE64_H +# define BASE64_H + +/* Get size_t. */ +# include + +/* Get bool. */ +# include + +/* This uses that the expression (n+(k-1))/k means the smallest + integer >= n/k, i.e., the ceiling of n/k. */ +# define BASE64_LENGTH(inlen) ((((inlen) + 2) / 3) * 4) + +extern bool isbase64 (char ch); + +extern void base64_encode (const char *restrict in, size_t inlen, + char *restrict out, size_t outlen); + +extern size_t base64_encode_alloc (const char *in, size_t inlen, char **out); + +extern bool base64_decode (const char *restrict in, size_t inlen, + char *restrict out, size_t *outlen); + +extern bool base64_decode_alloc (const char *in, size_t inlen, + char **out, size_t *outlen); + +#endif /* BASE64_H */ diff -r 5b0aeef8ba2a -r 5608ac657362 project_files/frontlib/ipc/gameconn.c --- a/project_files/frontlib/ipc/gameconn.c Thu Jun 21 21:32:12 2012 +0200 +++ b/project_files/frontlib/ipc/gameconn.c Mon Jun 25 00:42:07 2012 +0200 @@ -85,7 +85,7 @@ return result; } -flib_gameconn *flib_gameconn_create(const char *playerName, flib_gamesetup *setup, bool netgame) { +flib_gameconn *flib_gameconn_create(const char *playerName, const flib_gamesetup *setup, bool netgame) { flib_gameconn *result = NULL; flib_gameconn *tempConn = flib_gameconn_create_partial(true, playerName, netgame); if(tempConn) { @@ -124,6 +124,22 @@ return result; } +flib_gameconn *flib_gameconn_create_campaign(const char *playerName, const char *seed, const char *script) { + flib_gameconn *result = NULL; + flib_gameconn *tempConn = flib_gameconn_create_partial(true, playerName, false); + if(tempConn) { + if(!flib_ipc_append_message(tempConn->configBuffer, "TL") + && !flib_ipc_append_seed(tempConn->configBuffer, seed) + && !flib_ipc_append_script(tempConn->configBuffer, script) + && !flib_ipc_append_message(tempConn->configBuffer, "!")) { + result = tempConn; + tempConn = NULL; + } + } + flib_gameconn_destroy(tempConn); + return result; +} + void flib_gameconn_destroy(flib_gameconn *conn) { if(conn) { if(conn->running) { diff -r 5b0aeef8ba2a -r 5608ac657362 project_files/frontlib/ipc/gameconn.h --- a/project_files/frontlib/ipc/gameconn.h Thu Jun 21 21:32:12 2012 +0200 +++ b/project_files/frontlib/ipc/gameconn.h Mon Jun 25 00:42:07 2012 +0200 @@ -15,9 +15,11 @@ struct _flib_gameconn; typedef struct _flib_gameconn flib_gameconn; -flib_gameconn *flib_gameconn_create(const char *playerName, flib_gamesetup *setup, bool netgame); +flib_gameconn *flib_gameconn_create(const char *playerName, const flib_gamesetup *setup, bool netgame); flib_gameconn *flib_gameconn_create_playdemo(const uint8_t *demo, int size); flib_gameconn *flib_gameconn_create_loadgame(const char *playerName, const uint8_t *save, int size); +flib_gameconn *flib_gameconn_create_campaign(const char *playerName, const char *seed, const char *script); + void flib_gameconn_destroy(flib_gameconn *conn); /** diff -r 5b0aeef8ba2a -r 5608ac657362 project_files/frontlib/ipc/ipcbase.c --- a/project_files/frontlib/ipc/ipcbase.c Thu Jun 21 21:32:12 2012 +0200 +++ b/project_files/frontlib/ipc/ipcbase.c Mon Jun 25 00:42:07 2012 +0200 @@ -73,10 +73,6 @@ } } -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); @@ -89,6 +85,10 @@ } } +static bool isMessageReady(flib_ipcbase *ipc) { + return ipc->readBufferSize >= ipc->readBuffer[0]+1; +} + int flib_ipcbase_recv_message(flib_ipcbase *ipc, void *data) { if(!ipc || !data) { flib_log_e("null parameter in flib_ipcbase_recv_message"); @@ -183,10 +183,6 @@ 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"); diff -r 5b0aeef8ba2a -r 5608ac657362 project_files/frontlib/ipc/ipcbase.h --- a/project_files/frontlib/ipc/ipcbase.h Thu Jun 21 21:32:12 2012 +0200 +++ b/project_files/frontlib/ipc/ipcbase.h Mon Jun 25 00:42:07 2012 +0200 @@ -13,7 +13,6 @@ typedef enum {IPC_NOT_CONNECTED, IPC_LISTENING, IPC_CONNECTED} IpcState; -struct _flib_ipcbase; typedef struct _flib_ipcbase flib_ipcbase; /** @@ -27,10 +26,13 @@ */ flib_ipcbase *flib_ipcbase_create(); +/** + * Return the listening port + */ uint16_t flib_ipcbase_port(flib_ipcbase *ipc); /** - * Free resources and close sockets. + * Free resources and close sockets. NULL safe. */ void flib_ipcbase_destroy(flib_ipcbase *ipc); @@ -61,6 +63,10 @@ */ int flib_ipcbase_recv_map(flib_ipcbase *ipc, void *data); +/** + * Blocking send bytes over the socket. No message framing will be added. + * Returns 0 on success. + */ int flib_ipcbase_send_raw(flib_ipcbase *ipc, const void *data, size_t len); /** @@ -68,16 +74,11 @@ * 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. + * Returns 0 on success. */ 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); diff -r 5b0aeef8ba2a -r 5608ac657362 project_files/frontlib/ipc/ipcprotocol.c --- a/project_files/frontlib/ipc/ipcprotocol.c Thu Jun 21 21:32:12 2012 +0200 +++ b/project_files/frontlib/ipc/ipcprotocol.c Mon Jun 25 00:42:07 2012 +0200 @@ -48,11 +48,22 @@ bool error = false; if(map->mapgen == MAPGEN_NAMED) { - error |= flib_ipc_append_message(tempvector, "emap %s", map->name); + if(map->name) { + error |= flib_ipc_append_message(tempvector, "emap %s", map->name); + } else { + flib_log_e("Missing map name"); + error = true; + } } if(map->theme && !mappreview) { - error |= flib_ipc_append_message(tempvector, "etheme %s", map->theme); + if(map->theme) { + error |= flib_ipc_append_message(tempvector, "etheme %s", map->theme); + } else { + flib_log_e("Missing map theme"); + error = true; + } } + error |= flib_ipc_append_seed(tempvector, map->seed); error |= flib_ipc_append_message(tempvector, "e$template_filter %i", map->templateFilter); error |= flib_ipc_append_message(tempvector, "e$mapgen %i", map->mapgen); @@ -97,6 +108,15 @@ } } +int flib_ipc_append_script(flib_vector *vec, const char *script) { + if(!vec || !script) { + flib_log_e("null parameter in flib_ipc_append_script"); + return -1; + } else { + return flib_ipc_append_message(vec, "escript %s", script); + } +} + int flib_ipc_append_gamescheme(flib_vector *vec, const flib_cfg *scheme) { int result = -1; flib_vector *tempvector = flib_vector_create(); @@ -155,7 +175,8 @@ error |= flib_ipc_append_message(tempvector, "eammstore"); } - char *hash = team->hash ? team->hash : "00000000000000000000000000000000"; + // TODO + char *hash = team->ownerName ? team->ownerName : "00000000000000000000000000000000"; error |= flib_ipc_append_message(tempvector, "eaddteam %s %"PRIu32" %s", hash, team->color, team->name); if(team->remoteDriven) { @@ -212,7 +233,6 @@ bool sharedAmmo = false; error |= flib_ipc_append_message(vec, netgame ? "TN" : "TL"); - error |= flib_ipc_append_seed(vec, setup->seed); if(setup->map) { error |= flib_ipc_append_mapconf(tempvector, setup->map, false); } @@ -225,13 +245,13 @@ // Shared ammo has priority over per-hog ammo perHogAmmo = !sharedAmmo && getGameMod(setup->gamescheme, GAMEMOD_PERHOGAMMO_MASKBIT); } - if(setup->teams && setup->teamcount>0) { - uint32_t *clanColors = flib_calloc(setup->teamcount, sizeof(uint32_t)); + if(setup->teams && setup->teamCount>0) { + uint32_t *clanColors = flib_calloc(setup->teamCount, sizeof(uint32_t)); if(!clanColors) { error = true; } else { int clanCount = 0; - for(int i=0; iteamcount; i++) { + for(int i=0; iteamCount; i++) { flib_team *team = setup->teams[i]; bool newClan = false; diff -r 5b0aeef8ba2a -r 5608ac657362 project_files/frontlib/ipc/ipcprotocol.h --- a/project_files/frontlib/ipc/ipcprotocol.h Thu Jun 21 21:32:12 2012 +0200 +++ b/project_files/frontlib/ipc/ipcprotocol.h Mon Jun 25 00:42:07 2012 +0200 @@ -39,6 +39,14 @@ int flib_ipc_append_seed(flib_vector *vec, const char *seed); /** + * Append a script message to the buffer. + * + * Returns nonzero if something goes wrong. In that case the buffer + * contents are unaffected. + */ +int flib_ipc_append_script(flib_vector *vec, const char *script); + +/** * Append the game scheme to the buffer. * * Returns nonzero if something goes wrong. In that case the buffer diff -r 5b0aeef8ba2a -r 5608ac657362 project_files/frontlib/ipc/mapconn.c --- a/project_files/frontlib/ipc/mapconn.c Thu Jun 21 21:32:12 2012 +0200 +++ b/project_files/frontlib/ipc/mapconn.c Mon Jun 25 00:42:07 2012 +0200 @@ -39,12 +39,11 @@ conn->onFailureCb = &noop_handleFailure; } -static flib_vector *createConfigBuffer(char *seed, flib_map *mapdesc) { +static flib_vector *createConfigBuffer(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) { @@ -56,12 +55,12 @@ return result; } -flib_mapconn *flib_mapconn_create(char *seed, flib_map *mapdesc) { +flib_mapconn *flib_mapconn_create(flib_map *mapdesc) { flib_mapconn *result = NULL; flib_mapconn *tempConn = flib_calloc(1, sizeof(flib_mapconn)); if(tempConn) { tempConn->ipcBase = flib_ipcbase_create(); - tempConn->configBuffer = createConfigBuffer(seed, mapdesc); + tempConn->configBuffer = createConfigBuffer(mapdesc); if(tempConn->ipcBase && tempConn->configBuffer) { tempConn->progress = AWAIT_CONNECTION; clearCallbacks(tempConn); diff -r 5b0aeef8ba2a -r 5608ac657362 project_files/frontlib/ipc/mapconn.h --- a/project_files/frontlib/ipc/mapconn.h Thu Jun 21 21:32:12 2012 +0200 +++ b/project_files/frontlib/ipc/mapconn.h Mon Jun 25 00:42:07 2012 +0200 @@ -9,7 +9,6 @@ #define MAPIMAGE_HEIGHT 128 #define MAPIMAGE_BYTES (MAPIMAGE_WIDTH/8*MAPIMAGE_HEIGHT) -struct _flib_mapconn; typedef struct _flib_mapconn flib_mapconn; /** @@ -21,7 +20,7 @@ * 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); +flib_mapconn *flib_mapconn_create(flib_map *mapdesc); /** * Destroy the mapconn object. Passing NULL is allowed and does nothing. diff -r 5b0aeef8ba2a -r 5608ac657362 project_files/frontlib/model/cfg.c --- a/project_files/frontlib/model/cfg.c Thu Jun 21 21:32:12 2012 +0200 +++ b/project_files/frontlib/model/cfg.c Mon Jun 25 00:42:07 2012 +0200 @@ -34,7 +34,7 @@ flib_cfg_meta_release(cfg->meta); free(cfg->mods); free(cfg->settings); - free(cfg->schemeName); + free(cfg->name); free(cfg); } } @@ -155,11 +155,11 @@ } result->meta = flib_cfg_meta_retain(meta); - result->schemeName = flib_strdupnull(schemeName); + result->name = flib_strdupnull(schemeName); result->mods = flib_calloc(meta->modCount, sizeof(*result->mods)); result->settings = flib_calloc(meta->settingCount, sizeof(*result->settings)); - if(!result->mods || !result->settings || !result->schemeName) { + if(!result->mods || !result->settings || !result->name) { flib_cfg_destroy(result); return NULL; } @@ -173,7 +173,7 @@ flib_cfg *flib_cfg_copy(flib_cfg *cfg) { flib_cfg *result = NULL; if(cfg) { - result = flib_cfg_create(cfg->meta, cfg->schemeName); + result = flib_cfg_create(cfg->meta, cfg->name); if(result) { memcpy(result->mods, cfg->mods, cfg->meta->modCount * sizeof(*cfg->mods)); memcpy(result->settings, cfg->settings, cfg->meta->settingCount * sizeof(*cfg->settings)); diff -r 5b0aeef8ba2a -r 5608ac657362 project_files/frontlib/model/cfg.h --- a/project_files/frontlib/model/cfg.h Thu Jun 21 21:32:12 2012 +0200 +++ b/project_files/frontlib/model/cfg.h Mon Jun 25 00:42:07 2012 +0200 @@ -24,6 +24,10 @@ int bitmaskIndex; } flib_cfg_mod_meta; +/** + * The order of the meta information in the arrays is the same as the order + * of the mod/setting information in the net protocol messages. + */ typedef struct { int _referenceCount; int settingCount; @@ -36,7 +40,7 @@ int _referenceCount; flib_cfg_meta *meta; - char *schemeName; + char *name; int *settings; bool *mods; } flib_cfg; diff -r 5b0aeef8ba2a -r 5608ac657362 project_files/frontlib/model/gamesetup.h --- a/project_files/frontlib/model/gamesetup.h Thu Jun 21 21:32:12 2012 +0200 +++ b/project_files/frontlib/model/gamesetup.h Mon Jun 25 00:42:07 2012 +0200 @@ -1,8 +1,6 @@ /** * A complete game configuration that contains all settings for a * local or networked game. - * - * It should be noted that the meta-configuration is not included. */ #ifndef MODEL_GAMESETUP_H_ @@ -14,12 +12,11 @@ #include "team.h" typedef struct { - char *seed; // required - char *script; // optional - flib_cfg *gamescheme; // optional - flib_map *map; // optional - flib_team **teams; // optional - int teamcount; + char *script; + flib_cfg *gamescheme; + flib_map *map; + int teamCount; + flib_team **teams; } flib_gamesetup; #endif diff -r 5b0aeef8ba2a -r 5608ac657362 project_files/frontlib/model/map.c --- a/project_files/frontlib/model/map.c Thu Jun 21 21:32:12 2012 +0200 +++ b/project_files/frontlib/model/map.c Mon Jun 25 00:42:07 2012 +0200 @@ -9,6 +9,7 @@ static void flib_map_destroy(flib_map *map) { if(map) { + free(map->seed); free(map->drawData); free(map->name); free(map->theme); @@ -16,85 +17,88 @@ } } -flib_map *flib_map_create_regular(const char *theme, int templateFilter) { +flib_map *flib_map_create_regular(const char *seed, const char *theme, int templateFilter) { flib_map *result = NULL; - if(!theme) { + if(!seed || !theme) { flib_log_e("null parameter in flib_map_create_regular"); } else { - flib_map *newmap = flib_calloc(1, sizeof(flib_map)); - if(newmap) { - newmap->_referenceCount = 1; - newmap->mapgen = MAPGEN_REGULAR; - newmap->templateFilter = templateFilter; - newmap->theme = flib_strdupnull(theme); - if(newmap->theme) { - result = newmap; - newmap = NULL; - } - } - flib_map_destroy(newmap); + flib_map newmap = {0}; + newmap.mapgen = MAPGEN_REGULAR; + newmap.name = "+rnd+"; + newmap.seed = (char*)seed; + newmap.theme = (char*)theme; + newmap.templateFilter = templateFilter; + result = flib_map_copy(&newmap); + } + return result; +} + +flib_map *flib_map_create_maze(const char *seed, const char *theme, int mazeSize) { + flib_map *result = NULL; + if(!seed || !theme) { + flib_log_e("null parameter in flib_map_create_maze"); + } else { + flib_map newmap = {0}; + newmap.mapgen = MAPGEN_MAZE; + newmap.name = "+maze+"; + newmap.seed = (char*)seed; + newmap.theme = (char*)theme; + newmap.mazeSize = mazeSize; + result = flib_map_copy(&newmap); } return result; } -flib_map *flib_map_create_maze(const char *theme, int mazeSize) { +flib_map *flib_map_create_named(const char *seed, const char *name) { flib_map *result = NULL; - if(!theme) { - flib_log_e("null parameter in flib_map_create_maze"); + if(!seed || !name) { + flib_log_e("null parameter in flib_map_create_named"); + } else { + flib_map newmap = {0}; + newmap.mapgen = MAPGEN_NAMED; + newmap.name = (char*)name; + newmap.seed = (char*)seed; + result = flib_map_copy(&newmap); + } + return result; +} + +flib_map *flib_map_create_drawn(const char *seed, const char *theme, const uint8_t *drawData, int drawDataSize) { + flib_map *result = NULL; + if(!seed || !theme || (!drawData && drawDataSize)) { + flib_log_e("null parameter in flib_map_create_drawn"); } else { - flib_map *newmap = flib_calloc(1, sizeof(flib_map)); + flib_map newmap = {0}; + newmap.mapgen = MAPGEN_DRAWN; + newmap.name = "+drawn+"; + newmap.seed = (char*)seed; + newmap.theme = (char*)theme; + newmap.drawData = (uint8_t*) drawData; + newmap.drawDataSize = drawDataSize; + result = flib_map_copy(&newmap); + } + return result; +} + +flib_map *flib_map_copy(const flib_map *map) { + flib_map *result = NULL; + if(map) { + flib_map *newmap = flib_map_retain(flib_calloc(1, sizeof(flib_map))); if(newmap) { - newmap->_referenceCount = 1; - newmap->mapgen = MAPGEN_MAZE; - newmap->mazeSize = mazeSize; - newmap->theme = flib_strdupnull(theme); - if(newmap->theme) { + newmap->mapgen = map->mapgen; + newmap->drawDataSize = map->drawDataSize; + newmap->drawData = flib_bufdupnull(map->drawData, map->drawDataSize); + newmap->mazeSize = map->mazeSize; + newmap->name = flib_strdupnull(map->name); + newmap->seed = flib_strdupnull(map->seed); + newmap->templateFilter = map->templateFilter; + newmap->theme = flib_strdupnull(map->theme); + if((newmap->drawData || !map->drawData) && (newmap->name || !map->name) && (newmap->seed || !map->seed) && (newmap->theme || !map->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 = flib_calloc(1, sizeof(flib_map)); - if(newmap) { - newmap->_referenceCount = 1; - 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 = flib_calloc(1, sizeof(flib_map)); - if(newmap) { - newmap->_referenceCount = 1; - newmap->mapgen = MAPGEN_DRAWN; - newmap->drawData = flib_bufdupnull(drawData, drawDataSize); - newmap->drawDataSize = drawDataSize; - if(newmap->drawData) { - result = newmap; - newmap = NULL; - } - } - flib_map_destroy(newmap); + flib_map_release(newmap); } return result; } diff -r 5b0aeef8ba2a -r 5608ac657362 project_files/frontlib/model/map.h --- a/project_files/frontlib/model/map.h Thu Jun 21 21:32:12 2012 +0200 +++ b/project_files/frontlib/model/map.h Mon Jun 25 00:42:07 2012 +0200 @@ -1,14 +1,17 @@ /** - * 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. + * Data structure for defining a map. This contains the whole recipe to + * exactly recreate a particular map. For named maps, you also need the + * corresponding files. + * + * The required fields depend on the map generator, see the comments + * at the struct for details. */ #ifndef MODEL_MAP_H_ #define MODEL_MAP_H_ #include +#include #define MAPGEN_REGULAR 0 #define MAPGEN_MAZE 1 @@ -32,8 +35,9 @@ typedef struct { int _referenceCount; int mapgen; // Always one of the MAPGEN_ constants + char *name; // The name of the map for MAPGEN_NAMED, otherwise one of "+rnd+", "+maze+" or "+drawn+". + char *seed; // Used for all maps 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 @@ -49,7 +53,7 @@ * 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); +flib_map *flib_map_create_regular(const char *seed, const char *theme, int templateFilter); /** * Create a generated maze-type map. theme should be the name of a @@ -60,7 +64,7 @@ * 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); +flib_map *flib_map_create_maze(const char *seed, const char *theme, int mazeSize); /** * Create a map from the Maps-Directory. name should be the name of a @@ -71,13 +75,18 @@ * 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); +flib_map *flib_map_create_named(const char *seed, 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); +flib_map *flib_map_create_drawn(const char *seed, const char *theme, const uint8_t *drawData, int drawDataSize); + +/** + * Create a deep copy of the map. Returns NULL on failure or if NULL was passed. + */ +flib_map *flib_map_copy(const flib_map *map); /** * Increase the reference count of the object. Call this if you store a pointer to it somewhere. diff -r 5b0aeef8ba2a -r 5608ac657362 project_files/frontlib/model/schemelist.c --- a/project_files/frontlib/model/schemelist.c Thu Jun 21 21:32:12 2012 +0200 +++ b/project_files/frontlib/model/schemelist.c Mon Jun 25 00:42:07 2012 +0200 @@ -125,7 +125,7 @@ bool error = false; char *key = makePrefixedName(index+1, "name"); - error |= !key || flib_ini_set_str(ini, key, scheme->schemeName); + error |= !key || flib_ini_set_str(ini, key, scheme->name); free(key); for(int i=0; imodCount && !error; i++) { @@ -184,7 +184,7 @@ flib_cfg *flib_schemelist_find(flib_schemelist *list, const char *name) { if(list && name) { for(int i=0; ischemeCount; i++) { - if(!strcmp(name, list->schemes[i]->schemeName)) { + if(!strcmp(name, list->schemes[i]->name)) { return list->schemes[i]; } } diff -r 5b0aeef8ba2a -r 5608ac657362 project_files/frontlib/model/team.c --- a/project_files/frontlib/model/team.c Thu Jun 21 21:32:12 2012 +0200 +++ b/project_files/frontlib/model/team.c Mon Jun 25 00:42:07 2012 +0200 @@ -3,17 +3,19 @@ #include "../util/inihelper.h" #include "../util/util.h" #include "../util/logging.h" +#include "../util/refcounter.h" + #include #include static flib_team *from_ini_handleError(flib_team *result, flib_ini *settingfile) { flib_ini_destroy(settingfile); - flib_team_destroy(result); + flib_team_release(result); return NULL; } flib_team *flib_team_from_ini(const char *filename) { - flib_team *result = flib_calloc(1, sizeof(flib_team)); + flib_team *result = flib_team_retain(flib_calloc(1, sizeof(flib_team))); flib_ini *ini = NULL; if(!filename) { @@ -189,8 +191,15 @@ return result; } -void flib_team_destroy(flib_team *team) { +flib_team *flib_team_retain(flib_team *team) { if(team) { + flib_retain(&team->_referenceCount, "flib_team"); + } + return team; +} + +void flib_team_release(flib_team *team) { + if(team && flib_release(&team->_referenceCount, "flib_team")) { for(int i=0; ihogs[i].name); free(team->hogs[i].hat); @@ -208,7 +217,7 @@ } } free(team->bindings); - free(team->hash); + free(team->ownerName); free(team); } } @@ -221,3 +230,68 @@ } } } + +char *strdupWithError(const char *in, bool *error) { + char *out = flib_strdupnull(in); + if(in && !out) { + *error = true; + } + return out; +} + +flib_team *flib_team_copy(flib_team *team) { + flib_team *result = NULL; + if(team) { + flib_team *tmpTeam = flib_team_retain(flib_calloc(1, sizeof(flib_team))); + if(tmpTeam) { + bool error = false; + + for(int i=0; ihogs[i].name = strdupWithError(team->hogs[i].name, &error); + tmpTeam->hogs[i].hat = strdupWithError(team->hogs[i].hat, &error); + tmpTeam->hogs[i].rounds = team->hogs[i].rounds; + tmpTeam->hogs[i].kills = team->hogs[i].kills; + tmpTeam->hogs[i].deaths = team->hogs[i].deaths; + tmpTeam->hogs[i].suicides = team->hogs[i].suicides; + tmpTeam->hogs[i].difficulty = team->hogs[i].difficulty; + tmpTeam->hogs[i].initialHealth = team->hogs[i].initialHealth; + tmpTeam->hogs[i].weaponset = flib_weaponset_retain(team->hogs[i].weaponset); + } + + tmpTeam->name = strdupWithError(team->name, &error); + tmpTeam->grave = strdupWithError(team->grave, &error); + tmpTeam->fort = strdupWithError(team->fort, &error); + tmpTeam->voicepack = strdupWithError(team->voicepack, &error); + tmpTeam->flag = strdupWithError(team->flag, &error); + tmpTeam->ownerName = strdupWithError(team->ownerName, &error); + + tmpTeam->bindingCount = team->bindingCount; + if(team->bindings) { + tmpTeam->bindings = flib_calloc(team->bindingCount, sizeof(flib_binding)); + if(tmpTeam->bindings) { + for(int i=0; ibindingCount; i++) { + tmpTeam->bindings[i].action = strdupWithError(team->bindings[i].action, &error); + tmpTeam->bindings[i].binding = strdupWithError(team->bindings[i].binding, &error); + } + } else { + error = true; + } + } + + tmpTeam->rounds = team->rounds; + tmpTeam->wins = team->wins; + tmpTeam->campaignProgress = team->campaignProgress; + + tmpTeam->color = team->color; + tmpTeam->hogsInGame = team->hogsInGame; + tmpTeam->remoteDriven = team->remoteDriven; + + if(!error) { + result = tmpTeam; + tmpTeam = 0; + } + } + flib_team_release(tmpTeam); + } + return result; +} diff -r 5b0aeef8ba2a -r 5608ac657362 project_files/frontlib/model/team.h --- a/project_files/frontlib/model/team.h Thu Jun 21 21:32:12 2012 +0200 +++ b/project_files/frontlib/model/team.h Mon Jun 25 00:42:07 2012 +0200 @@ -44,6 +44,7 @@ } flib_hog; typedef struct { + int _referenceCount; flib_hog hogs[HEDGEHOGS_PER_TEAM]; char *name; char *grave; @@ -64,7 +65,7 @@ uint32_t color; int hogsInGame; bool remoteDriven; - char *hash; // TODO calculate at the appropriate time... i.e. before trying to send the config to the engine + char *ownerName; } flib_team; /** @@ -96,6 +97,22 @@ */ void flib_team_set_weaponset(flib_team *team, flib_weaponset *set); -void flib_team_destroy(flib_team *team); +/** + * Increase the reference count of the object. Call this if you store a pointer to it somewhere. + * Returns the parameter. + */ +flib_team *flib_team_retain(flib_team *team); + +/** + * Decrease the reference count of the object and free it if this was the last reference. + */ +void flib_team_release(flib_team *team); + +/** + * Create a deep copy of a team. Returns NULL on failure. + * The referenced weaponsets are not copied, so the new + * team references the same weaponsets. + */ +flib_team *flib_team_copy(flib_team *team); #endif /* TEAM_H_ */ diff -r 5b0aeef8ba2a -r 5608ac657362 project_files/frontlib/model/weapon.c --- a/project_files/frontlib/model/weapon.c Thu Jun 21 21:32:12 2012 +0200 +++ b/project_files/frontlib/model/weapon.c Mon Jun 25 00:42:07 2012 +0200 @@ -81,30 +81,38 @@ } } +flib_weaponset *flib_weaponset_from_ammostring(const char *name, const char *ammostring) { + flib_weaponset *result = NULL; + if(!name || !ammostring) { + flib_log_e("null parameter in flib_weaponset_from_ammostring"); + } else { + result = flib_weaponset_create(name); + if(result) { + int fieldlen = strlen(ammostring)/4; + setField(result->loadout, ammostring, fieldlen, false); + setField(result->crateprob, ammostring + fieldlen, fieldlen, true); + setField(result->delay, ammostring + 2*fieldlen, fieldlen, true); + setField(result->crateammo, ammostring + 3*fieldlen, fieldlen, true); + } + } + return result; +} + static int fillWeaponsetFromIni(flib_weaponsetlist *list, flib_ini *ini, int index) { int result = -1; char *keyname = flib_ini_get_keyname(ini, index); char *decodedKeyname = flib_urldecode(keyname); - - if(decodedKeyname) { - flib_weaponset *set = flib_weaponset_create(decodedKeyname); + char *ammostring = NULL; + if(decodedKeyname && !flib_ini_get_str(ini, &ammostring, keyname)) { + flib_weaponset *set = flib_weaponset_from_ammostring(decodedKeyname, ammostring); if(set) { - char *value = NULL; - if(!flib_ini_get_str(ini, &value, keyname)) { - int fieldlen = strlen(value)/4; - setField(set->loadout, value, fieldlen, false); - setField(set->crateprob, value+1*fieldlen, fieldlen, true); - setField(set->delay, value+2*fieldlen, fieldlen, true); - setField(set->crateammo, value+3*fieldlen, fieldlen, true); - result = flib_weaponsetlist_insert(list, set, list->weaponsetCount); - } - free(value); + result = flib_weaponsetlist_insert(list, set, list->weaponsetCount); } flib_weaponset_release(set); } - + free(ammostring); + free(decodedKeyname); free(keyname); - free(decodedKeyname); return result; } diff -r 5b0aeef8ba2a -r 5608ac657362 project_files/frontlib/model/weapon.h --- a/project_files/frontlib/model/weapon.h Thu Jun 21 21:32:12 2012 +0200 +++ b/project_files/frontlib/model/weapon.h Mon Jun 25 00:42:07 2012 +0200 @@ -45,6 +45,12 @@ void flib_weaponset_release(flib_weaponset *weaponset); /** + * Create a weaponset from an ammostring. This format is used both in the ini files + * and in the net protocol. + */ +flib_weaponset *flib_weaponset_from_ammostring(const char *name, const char *ammostring); + +/** * Load a list of weaponsets from the ini file. * Returns NULL on error. */ diff -r 5b0aeef8ba2a -r 5608ac657362 project_files/frontlib/net/netconn.c --- a/project_files/frontlib/net/netconn.c Thu Jun 21 21:32:12 2012 +0200 +++ b/project_files/frontlib/net/netconn.c Mon Jun 25 00:42:07 2012 +0200 @@ -18,93 +18,21 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ -#include "netconn.h" -#include "netbase.h" +// TODO: Check the state transitions. Document with a diagram or something + +#include "netconn_internal.h" #include "netprotocol.h" #include "../util/logging.h" #include "../util/util.h" #include "../model/roomlist.h" #include "../md5/md5.h" +#include "../base64/base64.h" -#include #include #include #include #include -struct _flib_netconn { - flib_netbase *netBase; - char *playerName; - flib_cfg_meta *metaCfg; - flib_roomlist *roomList; - - int netconnState; // One of the NETCONN_STATE constants - - bool isAdmin; // Player is server administrator - bool isChief; // Player can modify the current room - - - void (*onMessageCb)(void *context, int msgtype, const char *msg); - void *onMessageCtx; - - void (*onConnectedCb)(void *context); - void *onConnectedCtx; - - void (*onDisconnectedCb)(void *context, int reason, const char *message); - void *onDisconnectedCtx; - - void (*onRoomAddCb)(void *context, const flib_roomlist_room *room); - void *onRoomAddCtx; - - void (*onRoomDeleteCb)(void *context, const char *name); - void *onRoomDeleteCtx; - - void (*onRoomUpdateCb)(void *context, const char *oldName, const flib_roomlist_room *room); - void *onRoomUpdateCtx; - - void (*onChatCb)(void *context, const char *nick, const char *msg); - void *onChatCtx; - - void (*onLobbyJoinCb)(void *context, const char *nick); - void *onLobbyJoinCtx; - - void (*onLobbyLeaveCb)(void *context, const char *nick, const char *partMessage); - void *onLobbyLeaveCtx; - - void (*onRoomJoinCb)(void *context, const char *nick); - void *onRoomJoinCtx; - - void (*onRoomLeaveCb)(void *context, const char *nick, const char *partMessage); - void *onRoomLeaveCtx; - - void (*onNickTakenCb)(void *context, const char *nick); - void *onNickTakenCtx; - - void (*onNickAcceptCb)(void *context, const char *nick); - void *onNickAcceptCtx; - - void (*onPasswordRequestCb)(void *context, const char *nick); - void *onPasswordRequestCtx; - - void (*onRoomChiefStatusCb)(void *context, bool isChief); - void *onRoomChiefStatusCtx; - - void (*onReadyStateCb)(void *context, const char *nick, bool ready); - void *onReadyStateCtx; - - void (*onEnterRoomCb)(void *context, bool chief); - void *onEnterRoomCtx; - - void (*onLeaveRoomCb)(void *context, int reason, const char *message); - void *onLeaveRoomCtx; - - void (*onTeamAddCb)(void *context, flib_team *team); - void *onTeamAddCtx; - - bool running; - bool destroyRequested; -}; - static void defaultCallback_onMessage(void *context, int msgtype, const char *msg) { flib_log_i("Net: [%i] %s", msgtype, msg); } @@ -115,6 +43,7 @@ static void defaultCallback_int_str(void *context, int i, const char *str) {} static void defaultCallback_str_str(void *context, const char *str1, const char *str2) {} static void defaultCallback_str_bool(void *context, const char *str, bool b) {} +static void defaultCallback_str_int(void *context, const char *str, int i) {} static void defaultCallback_onRoomAdd(void *context, const flib_roomlist_room *room) {} static void defaultCallback_onRoomUpdate(void *context, const char *oldName, const flib_roomlist_room *room) {} @@ -149,6 +78,10 @@ } static void defaultCallback_onTeamAdd(void *context, flib_team *team) {} +static void defaultCallback_onTeamColorChanged(void *context, const char *teamName, uint32_t color) {} +static void defaultCallback_onCfgScheme(void *context, flib_cfg *scheme) {} +static void defaultCallback_onMapChanged(void *context, const flib_map *map, int changetype) {} +static void defaultCallback_onWeaponsetChanged(void *context, flib_weaponset *weaponset) {} static void clearCallbacks(flib_netconn *conn) { flib_netconn_onMessage(conn, NULL, NULL); @@ -163,12 +96,24 @@ flib_netconn_onRoomJoin(conn, NULL, NULL); flib_netconn_onRoomLeave(conn, NULL, NULL); flib_netconn_onNickTaken(conn, NULL, NULL); - flib_netconn_onNickAccept(conn, NULL, NULL); flib_netconn_onPasswordRequest(conn, NULL, NULL); flib_netconn_onRoomChiefStatus(conn, NULL, NULL); - flib_netconn_onReadyStateCb(conn, NULL, NULL); - flib_netconn_onEnterRoomCb(conn, NULL, NULL); - flib_netconn_onTeamAddCb(conn, NULL, NULL); + flib_netconn_onReadyState(conn, NULL, NULL); + flib_netconn_onEnterRoom(conn, NULL, NULL); + flib_netconn_onLeaveRoom(conn, NULL, NULL); + flib_netconn_onTeamAdd(conn, NULL, NULL); + flib_netconn_onTeamDelete(conn, NULL, NULL); + flib_netconn_onRunGame(conn, NULL, NULL); + flib_netconn_onTeamAccepted(conn, NULL, NULL); + flib_netconn_onHogCountChanged(conn, NULL, NULL); + flib_netconn_onTeamColorChanged(conn, NULL, NULL); + flib_netconn_onEngineMessage(conn, NULL, NULL); + flib_netconn_onCfgScheme(conn, NULL, NULL); + flib_netconn_onMapChanged(conn, NULL, NULL); + flib_netconn_onScriptChanged(conn, NULL, NULL); + flib_netconn_onWeaponsetChanged(conn, NULL, NULL); + flib_netconn_onAdminAccess(conn, NULL, NULL); + flib_netconn_onServerVar(conn, NULL, NULL); } flib_netconn *flib_netconn_create(const char *playerName, flib_cfg_meta *metacfg, const char *host, uint16_t port) { @@ -183,6 +128,7 @@ newConn->isChief = false; newConn->metaCfg = flib_cfg_meta_retain(metacfg); newConn->roomList = flib_roomlist_create(); + newConn->map = flib_map_create_named("", "NoSuchMap"); newConn->running = false; newConn->destroyRequested = false; clearCallbacks(newConn); @@ -212,6 +158,7 @@ flib_netbase_destroy(conn->netBase); flib_cfg_meta_release(conn->metaCfg); flib_roomlist_destroy(conn->roomList); + flib_map_release(conn->map); free(conn->playerName); free(conn); } @@ -238,65 +185,6 @@ return result; } -int flib_netconn_send_quit(flib_netconn *conn, const char *quitmsg) { - int result = -1; - if(!conn) { - flib_log_e("null parameter in flib_netconn_send_quit"); - } else { - result = flib_netbase_sendf(conn->netBase, "%s\n%s\n\n", "QUIT", quitmsg ? quitmsg : "User quit"); - } - return result; -} - -int flib_netconn_send_chat(flib_netconn *conn, const char *chat) { - int result = -1; - if(!conn || !chat) { - flib_log_e("null parameter in flib_netconn_send_chat"); - } else { - result = flib_netbase_sendf(conn->netBase, "%s\n%s\n\n", "CHAT", chat); - } - return result; -} - -int flib_netconn_send_nick(flib_netconn *conn, const char *nick) { - int result = -1; - if(!conn || !nick) { - flib_log_e("null parameter in flib_netconn_send_nick"); - } else { - char *tmpName = flib_strdupnull(nick); - if(tmpName) { - if(!flib_netbase_sendf(conn->netBase, "%s\n%s\n\n", "NICK", nick)) { - free(conn->playerName); - conn->playerName = tmpName; - tmpName = NULL; - result = 0; - } - } - free(tmpName); - } - return result; -} - -int flib_netconn_send_password(flib_netconn *conn, const char *latin1Passwd) { - int result = -1; - if(!conn || !latin1Passwd) { - flib_log_e("null parameter in flib_netconn_send_password"); - } else { - md5_state_t md5state; - uint8_t md5bytes[16]; - char md5hex[33]; - md5_init(&md5state); - md5_append(&md5state, (unsigned char*)latin1Passwd, strlen(latin1Passwd)); - md5_finish(&md5state, md5bytes); - for(int i=0;inetBase, "%s\n%s\n\n", "PASSWORD", md5hex); - } - return result; -} - /* * Callback registration functions */ @@ -412,15 +300,6 @@ } } -void flib_netconn_onNickAccept(flib_netconn *conn, void (*callback)(void *context, const char *nick), void* context) { - if(!conn) { - flib_log_e("null parameter in flib_netconn_onNickAccept"); - } else { - conn->onNickAcceptCb = callback ? callback : &defaultCallback_str; - conn->onNickAcceptCtx = context; - } -} - void flib_netconn_onPasswordRequest(flib_netconn *conn, void (*callback)(void *context, const char *nick), void* context) { if(!conn) { flib_log_e("null parameter in flib_netconn_onPasswordRequest"); @@ -442,42 +321,162 @@ } } -void flib_netconn_onReadyStateCb(flib_netconn *conn, void (*callback)(void *context, const char *nick, bool ready), void* context) { +void flib_netconn_onReadyState(flib_netconn *conn, void (*callback)(void *context, const char *nick, bool ready), void* context) { if(!conn) { - flib_log_e("null parameter in flib_netconn_onReadyStateCb"); + flib_log_e("null parameter in flib_netconn_onReadyState"); } else { conn->onReadyStateCb = callback ? callback : &defaultCallback_str_bool; conn->onReadyStateCtx = context; } } -void flib_netconn_onEnterRoomCb(flib_netconn *conn, void (*callback)(void *context, bool chief), void *context) { +void flib_netconn_onEnterRoom(flib_netconn *conn, void (*callback)(void *context, bool chief), void *context) { if(!conn) { - flib_log_e("null parameter in flib_netconn_onEnterRoomCb"); + flib_log_e("null parameter in flib_netconn_onEnterRoom"); } else { conn->onEnterRoomCb = callback ? callback : &defaultCallback_bool; conn->onEnterRoomCtx = context; } } -void flib_netconn_onLeaveRoomCb(flib_netconn *conn, void (*callback)(void *context, int reason, const char *message), void *context) { +void flib_netconn_onLeaveRoom(flib_netconn *conn, void (*callback)(void *context, int reason, const char *message), void *context) { if(!conn) { - flib_log_e("null parameter in flib_netconn_onLeaveRoomCb"); + flib_log_e("null parameter in flib_netconn_onLeaveRoom"); } else { conn->onLeaveRoomCb = callback ? callback : &defaultCallback_int_str; conn->onLeaveRoomCtx = context; } } -void flib_netconn_onTeamAddCb(flib_netconn *conn, void (*callback)(void *context, flib_team *team), void *context) { +void flib_netconn_onTeamAdd(flib_netconn *conn, void (*callback)(void *context, flib_team *team), void *context) { if(!conn) { - flib_log_e("null parameter in flib_netconn_onTeamAddCb"); + flib_log_e("null parameter in flib_netconn_onTeamAdd"); } else { conn->onTeamAddCb = callback ? callback : &defaultCallback_onTeamAdd; conn->onTeamAddCtx = context; } } +void flib_netconn_onTeamDelete(flib_netconn *conn, void (*callback)(void *context, const char *teamname), void *context) { + if(!conn) { + flib_log_e("null parameter in flib_netconn_onTeamDelete"); + } else { + conn->onTeamDeleteCb = callback ? callback : &defaultCallback_str; + conn->onTeamDeleteCtx = context; + } +} + +void flib_netconn_onRunGame(flib_netconn *conn, void (*callback)(void *context), void *context) { + if(!conn) { + flib_log_e("null parameter in flib_netconn_onRunGame"); + } else { + conn->onRunGameCb = callback ? callback : &defaultCallback_void; + conn->onRunGameCtx = context; + } +} + +void flib_netconn_onTeamAccepted(flib_netconn *conn, void (*callback)(void *context, const char *teamName), void *context) { + if(!conn) { + flib_log_e("null parameter in flib_netconn_onTeamAccepted"); + } else { + conn->onTeamAcceptedCb = callback ? callback : &defaultCallback_str; + conn->onTeamAcceptedCtx = context; + } +} + +void flib_netconn_onHogCountChanged(flib_netconn *conn, void (*callback)(void *context, const char *teamName, int hogs), void *context) { + if(!conn) { + flib_log_e("null parameter in flib_netconn_onHogCountChanged"); + } else { + conn->onHogCountChangedCb = callback ? callback : &defaultCallback_str_int; + conn->onHogCountChangedCtx = context; + } +} + +void flib_netconn_onTeamColorChanged(flib_netconn *conn, void (*callback)(void *context, const char *teamName, uint32_t colorARGB), void *context) { + if(!conn) { + flib_log_e("null parameter in flib_netconn_onTeamColorChanged"); + } else { + conn->onTeamColorChangedCb = callback ? callback : &defaultCallback_onTeamColorChanged; + conn->onTeamColorChangedCtx = context; + } +} + +void flib_netconn_onEngineMessage(flib_netconn *conn, void (*callback)(void *context, const char *message, int size), void *context) { + if(!conn) { + flib_log_e("null parameter in flib_netconn_onEngineMessage"); + } else { + conn->onEngineMessageCb = callback ? callback : &defaultCallback_str_int; + conn->onEngineMessageCtx = context; + } +} + +void flib_netconn_onCfgScheme(flib_netconn *conn, void (*callback)(void *context, flib_cfg *scheme), void *context) { + if(!conn) { + flib_log_e("null parameter in flib_netconn_onCfgScheme"); + } else { + conn->onCfgSchemeCb = callback ? callback : &defaultCallback_onCfgScheme; + conn->onCfgSchemeCtx = context; + } +} + +void flib_netconn_onMapChanged(flib_netconn *conn, void (*callback)(void *context, const flib_map *map, int changetype), void *context) { + if(!conn) { + flib_log_e("null parameter in flib_netconn_onMapChanged"); + } else { + conn->onMapChangedCb = callback ? callback : &defaultCallback_onMapChanged; + conn->onMapChangedCtx = context; + } +} + +void flib_netconn_onScriptChanged(flib_netconn *conn, void (*callback)(void *context, const char *script), void *context) { + if(!conn) { + flib_log_e("null parameter in flib_netconn_onScriptChanged"); + } else { + conn->onScriptChangedCb = callback ? callback : &defaultCallback_str; + conn->onScriptChangedCtx = context; + } +} + +void flib_netconn_onWeaponsetChanged(flib_netconn *conn, void (*callback)(void *context, flib_weaponset *weaponset), void *context) { + if(!conn) { + flib_log_e("null parameter in flib_netconn_onWeaponsetChanged"); + } else { + conn->onWeaponsetChangedCb = callback ? callback : &defaultCallback_onWeaponsetChanged; + conn->onWeaponsetChangedCtx = context; + } +} + +void flib_netconn_onAdminAccess(flib_netconn *conn, void (*callback)(void *context), void *context) { + if(!conn) { + flib_log_e("null parameter in flib_netconn_onAdminAccess"); + } else { + conn->onAdminAccessCb = callback ? callback : &defaultCallback_void; + conn->onAdminAccessCtx = context; + } +} + +void flib_netconn_onServerVar(flib_netconn *conn, void (*callback)(void *context, const char *name, const char *value), void *context) { + if(!conn) { + flib_log_e("null parameter in flib_netconn_onServerVar"); + } else { + conn->onServerVarCb = callback ? callback : &defaultCallback_str_str; + conn->onServerVarCtx = context; + } +} + +void leaveRoom(flib_netconn *conn) { + conn->netconnState = NETCONN_STATE_LOBBY; + conn->isChief = false; + flib_map *map = flib_map_create_named("", "NoSuchMap"); + if(map) { + flib_map_release(conn->map); + conn->map = map; + } else { + flib_log_e("Error resetting netconn.map"); + } +} + static void flib_netconn_wrappedtick(flib_netconn *conn) { flib_netmsg *netmsg; flib_netbase *net = conn->netBase; @@ -503,14 +502,14 @@ if(netmsg->partCount<2) { flib_log_w("Net: Malformed NICK message"); } else { - free(conn->playerName); - conn->playerName = flib_strdupnull(netmsg->parts[1]); - if(!conn->playerName) { + char *nick = flib_strdupnull(netmsg->parts[1]); + if(nick) { + free(conn->playerName); + conn->playerName = nick; + } else { conn->netconnState = NETCONN_STATE_DISCONNECTED; conn->onDisconnectedCb(conn->onDisconnectedCtx, NETCONN_DISCONNECT_INTERNAL_ERROR, "Out of memory"); exit = true; - } else { - conn->onNickAcceptCb(conn->onNickAcceptCtx, conn->playerName); } } } else if (!strcmp(cmd, "PROTO")) { @@ -583,18 +582,9 @@ free(joined); } } 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(); -// } + for(int offset=1; offset+2partCount; offset+=2) { + conn->onServerVarCb(conn->onServerVarCtx, netmsg->parts[offset], netmsg->parts[offset+1]); + } } else if (!strcmp(cmd, "CLIENT_FLAGS")) { if(netmsg->partCount < 3 || strlen(netmsg->parts[1]) < 2) { flib_log_w("Net: Malformed CLIENT_FLAGS message"); @@ -606,12 +596,6 @@ switch(flags[i]) { case 'r': for(int j = 2; j < netmsg->partCount; ++j) { - if (!strcmp(conn->playerName, netmsg->parts[i])) { - // TODO what is the reason behind this (copied from QtFrontend)? - if (conn->isChief && !setFlag) { - flib_netbase_sendf(conn->netBase, "%s\n\n", "TOGGLE_READY"); - } - } conn->onReadyStateCb(conn->onReadyStateCtx, netmsg->parts[i], setFlag); } break; @@ -631,21 +615,22 @@ conn->onDisconnectedCb(conn->onDisconnectedCtx, NETCONN_DISCONNECT_INTERNAL_ERROR, "Internal error"); exit = true; } else { + team->remoteDriven = true; conn->onTeamAddCb(conn->onTeamAddCtx, team); } + flib_team_release(team); } } else if (!strcmp(cmd, "REMOVE_TEAM")) { if(netmsg->partCount != 2) { flib_log_w("Net: Bad REMOVETEAM message"); } else { - // TODO - // emit RemoveNetTeam(HWTeam(lst[1])); + conn->onTeamDeleteCb(conn->onTeamDeleteCtx, netmsg->parts[1]); } } else if(!strcmp(cmd, "ROOMABANDONED")) { - conn->netconnState = NETCONN_STATE_LOBBY; + leaveRoom(conn); conn->onLeaveRoomCb(conn->onLeaveRoomCtx, NETCONN_ROOMLEAVE_ABANDONED, "Room destroyed"); } else if(!strcmp(cmd, "KICKED")) { - conn->netconnState = NETCONN_STATE_LOBBY; + leaveRoom(conn); conn->onLeaveRoomCb(conn->onLeaveRoomCtx, NETCONN_ROOMLEAVE_KICKED, "You got kicked"); } else if(!strcmp(cmd, "JOINED")) { if(netmsg->partCount < 2) { @@ -718,8 +703,7 @@ } } else if (!strcmp(cmd, "RUN_GAME")) { conn->netconnState = NETCONN_STATE_INGAME; - // TODO - // emit AskForRunGame(); + conn->onRunGameCb(conn->onRunGameCtx); } else if (!strcmp(cmd, "ASKPASSWORD")) { conn->onPasswordRequestCb(conn->onPasswordRequestCtx, conn->playerName); } else if (!strcmp(cmd, "NOTICE")) { @@ -740,49 +724,128 @@ if (netmsg->partCount != 2) { flib_log_w("Net: Bad TEAM_ACCEPTED message"); } else { - // TODO - // emit TeamAccepted(lst[1]); + conn->onTeamAcceptedCb(conn->onTeamAcceptedCtx, netmsg->parts[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); + const char *subcmd = netmsg->parts[1]; + if(!strcmp(subcmd, "SCHEME") && netmsg->partCount == conn->metaCfg->modCount + conn->metaCfg->settingCount + 3) { + flib_cfg *cfg = flib_netmsg_to_cfg(conn->metaCfg, netmsg->parts+2); + if(cfg) { + conn->onCfgSchemeCb(conn->onCfgSchemeCtx, cfg); + } else { + flib_log_e("Error processing CFG SCHEME message"); + } + flib_cfg_release(cfg); + } else if(!strcmp(subcmd, "FULLMAPCONFIG") && netmsg->partCount == 7) { + flib_map *map = flib_netmsg_to_map(netmsg->parts+2); + if(map) { + flib_map_release(conn->map); + conn->map = map; + conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_FULL); + } else { + flib_log_e("Error processing CFG FULLMAPCONFIG message"); + } + } else if(!strcmp(subcmd, "MAP") && netmsg->partCount == 3) { + char *mapname = flib_strdupnull(netmsg->parts[2]); + if(mapname) { + free(conn->map->name); + conn->map->name = mapname; + conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_MAP); + } else { + flib_log_e("Error processing CFG MAP message"); + } + } else if(!strcmp(subcmd, "THEME") && netmsg->partCount == 3) { + char *themename = flib_strdupnull(netmsg->parts[2]); + if(themename) { + free(conn->map->theme); + conn->map->theme = themename; + conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_THEME); + } else { + flib_log_e("Error processing CFG THEME message"); + } + } else if(!strcmp(subcmd, "SEED") && netmsg->partCount == 3) { + char *seed = flib_strdupnull(netmsg->parts[2]); + if(seed) { + free(conn->map->seed); + conn->map->seed = seed; + conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_SEED); + } else { + flib_log_e("Error processing CFG SEED message"); + } + } else if(!strcmp(subcmd, "TEMPLATE") && netmsg->partCount == 3) { + conn->map->templateFilter = atoi(netmsg->parts[2]); + conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_TEMPLATE); + } else if(!strcmp(subcmd, "MAPGEN") && netmsg->partCount == 3) { + conn->map->mapgen = atoi(netmsg->parts[2]); + conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_MAPGEN); + } else if(!strcmp(subcmd, "MAZE_SIZE") && netmsg->partCount == 3) { + conn->map->mazeSize = atoi(netmsg->parts[2]); + conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_MAZE_SIZE); + } else if(!strcmp(subcmd, "DRAWNMAP") && netmsg->partCount == 3) { + size_t drawnMapSize = 0; + uint8_t *drawnMapData = flib_netmsg_to_drawnmapdata(&drawnMapSize, netmsg->parts[2]); + if(drawnMapData) { + free(conn->map->drawData); + conn->map->drawData = drawnMapData; + conn->map->drawDataSize = drawnMapSize; + conn->onMapChangedCb(conn->onMapChangedCtx, conn->map, NETCONN_MAPCHANGE_DRAWNMAP); + } else { + flib_log_e("Error processing CFG DRAWNMAP message"); + } + } else if(!strcmp(subcmd, "SCRIPT") && netmsg->partCount == 3) { + conn->onScriptChangedCb(conn->onScriptChangedCtx, netmsg->parts[2]); + } else if(!strcmp(subcmd, "AMMO") && netmsg->partCount == 4) { + flib_weaponset *weapons = flib_weaponset_from_ammostring(netmsg->parts[2], netmsg->parts[3]); + if(weapons) { + conn->onWeaponsetChangedCb(conn->onWeaponsetChangedCtx, weapons); + } else { + flib_log_e("Error processing CFG AMMO message"); + } + flib_weaponset_release(weapons); + } else { + flib_log_w("Net: Unknown or malformed CFG subcommand: %s", subcmd); + } } } else if (!strcmp(cmd, "HH_NUM")) { if (netmsg->partCount != 3) { - flib_log_w("Net: Bad TEAM_ACCEPTED message"); + flib_log_w("Net: Bad HH_NUM message"); } else { - // TODO -// HWTeam tmptm(lst[1]); -// tmptm.setNumHedgehogs(lst[2].toUInt()); -// emit hhnumChanged(tmptm); + int hogs = atoi(netmsg->parts[2]); + if(hogs<=0 || hogs>HEDGEHOGS_PER_TEAM) { + flib_log_w("Net: Bad HH_NUM message: %s hogs", netmsg->parts[2]); + } else { + conn->onHogCountChangedCb(conn->onHogCountChangedCtx, netmsg->parts[1], hogs); + } } } 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); + long color; + if(sscanf(netmsg->parts[2], "#%lx", &color)) { + conn->onTeamColorChangedCb(conn->onTeamColorChangedCtx, netmsg->parts[1], (uint32_t)color); + } else { + flib_log_w("Net: Bad TEAM_COLOR message: Color %s", netmsg->parts[2]); + } } } 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); -// } + for(int i = 1; i < netmsg->partCount; ++i) { + char *out = NULL; + size_t outlen; + bool ok = base64_decode_alloc(netmsg->parts[i], strlen(netmsg->parts[i]), &out, &outlen); + if(ok && outlen) { + conn->onEngineMessageCb(conn->onEngineMessageCtx, out, outlen); + } else { + flib_log_e("Net: Malformed engine message: %s", netmsg->parts[i]); + } + free(out); + } } } else if (!strcmp(cmd, "BYE")) { if (netmsg->partCount < 2) { @@ -797,7 +860,7 @@ exit = true; } } else if (!strcmp(cmd, "ADMIN_ACCESS")) { - // TODO callback? + conn->onAdminAccessCb(conn->onAdminAccessCtx); conn->isAdmin = true; } else if (!strcmp(cmd, "ROOM_CONTROL_ACCESS")) { if (netmsg->partCount < 2) { diff -r 5b0aeef8ba2a -r 5608ac657362 project_files/frontlib/net/netconn.h --- a/project_files/frontlib/net/netconn.h Thu Jun 21 21:32:12 2012 +0200 +++ b/project_files/frontlib/net/netconn.h Mon Jun 25 00:42:07 2012 +0200 @@ -28,8 +28,16 @@ #define NETCONN_MSG_TYPE_WARNING 2 #define NETCONN_MSG_TYPE_ERROR 3 +#define NETCONN_MAPCHANGE_FULL 0 +#define NETCONN_MAPCHANGE_MAP 1 +#define NETCONN_MAPCHANGE_MAPGEN 2 +#define NETCONN_MAPCHANGE_DRAWNMAP 3 +#define NETCONN_MAPCHANGE_MAZE_SIZE 4 +#define NETCONN_MAPCHANGE_TEMPLATE 5 +#define NETCONN_MAPCHANGE_THEME 6 +#define NETCONN_MAPCHANGE_SEED 7 -struct _flib_netconn; +// TODO: Order of functions, and match the order in netconn.c typedef struct _flib_netconn flib_netconn; flib_netconn *flib_netconn_create(const char *playerName, flib_cfg_meta *metacfg, const char *host, uint16_t port); @@ -60,6 +68,12 @@ int flib_netconn_send_chat(flib_netconn *conn, const char *chat); /** + * Send a teamchat message, forwarded from the engine. Only makes sense ingame. + * The server does not send a reply. TODO figure out details + */ +int flib_netconn_send_teamchat(flib_netconn *conn, const char *msg); + +/** * Note: Most other functions in this lib accept UTF-8, but the password needs to be * sent as latin1 */ @@ -68,16 +82,212 @@ /** * Request a different nickname. * This function only makes sense in reaction to an onNickTaken callback, because the netconn automatically - * requests the nickname you provide on creation, and once the server accepts the nickname (onNickAccept) - * it can no longer be changed. - * - * As a response to the nick change request, the server will either reply with a confirmation (onNickAccept) - * or a rejection (onNickTaken). Note that the server confirms a nick even if it is password protected, the - * password request happens afterwards. + * requests the nickname you provide on creation, and once the server accepts the nickname it can no longer + * be changed. */ int flib_netconn_send_nick(flib_netconn *conn, const char *nick); /** + * Join a room as guest (not chief). Only makes sense when in lobby state. If the action succeeds, you will + * receive an onEnterRoom callback with chief=false. + */ +int flib_netconn_send_joinRoom(flib_netconn *conn, const char *room); + +/** + * Create and join a new room. Only makes sense when in lobby state. If the action succeeds, you will + * receive an onEnterRoom callback with chief=true. + */ +int flib_netconn_send_createRoom(flib_netconn *conn, const char *room); + +/** + * Rename the current room. Only makes sense in room state and if you are chief. + * TODO: reply + */ +int flib_netconn_send_renameRoom(flib_netconn *conn, const char *roomName); + +/** + * Leave the room for the lobby. Only makes sense in room state. + * TODO: reply, TODO can you send a message? + */ +int flib_netconn_send_leaveRoom(flib_netconn *conn); + +/** + * Change your "ready" status in the room. Only makes sense when in room state. + * TODO: reply + */ +int flib_netconn_send_toggleReady(flib_netconn *conn); + +/** + * Add a team to the current room. The message includes the team color, but not + * the number of hogs. Only makes sense when in room state. If the action succeeds, you will + * receive an onTeamAccepted callback with the name of the team. + */ +int flib_netconn_send_addTeam(flib_netconn *conn, const flib_team *team); + +/** + * Remove the team with the name teamname. Only makes sense when in room state. + * TODO: reply + */ +int flib_netconn_send_removeTeam(flib_netconn *conn, const char *teamname); + +/** + * Send an engine message. Only makes sense when ingame. + * TODO: reply + */ +int flib_netconn_send_engineMessage(flib_netconn *conn, const uint8_t *message, size_t size); + +/** + * Set the number of hogs for a team. Only makes sense in room state and if you are chief. + * The server does not send a reply. + */ +int flib_netconn_send_teamHogCount(flib_netconn *conn, const char *teamname, int hogcount); + +/** + * Set the teamcolor of a team. Only makes sense in room state and if you are chief. + * The server does not send a reply. + */ +int flib_netconn_send_teamColor(flib_netconn *conn, const char *teamname, uint32_t colorRGB); + +/** + * Set the weaponset for the room. Only makes sense in room state and if you are chief. + * The server does not send a reply. + */ +int flib_netconn_send_weaponset(flib_netconn *conn, const flib_weaponset *weaponset); + +/** + * Set the map for the room. Only makes sense in room state and if you are chief. + * The server does not send a reply. + */ +int flib_netconn_send_map(flib_netconn *conn, const flib_map *map); + +/** + * Set the mapname. Only makes sense in room state and if you are chief. + * The server does not send a reply. + */ +int flib_netconn_send_mapName(flib_netconn *conn, const char *mapName); + +/** + * Set the map generator. Only makes sense in room state and if you are chief. + * The server does not send a reply. + */ +int flib_netconn_send_mapGen(flib_netconn *conn, int mapGen); + +/** + * Set the map template for regular maps. Only makes sense in room state and if you are chief. + * The server does not send a reply. + */ +int flib_netconn_send_mapTemplate(flib_netconn *conn, int templateFilter); + +/** + * Set the maze template (maze size) for mazes. Only makes sense in room state and if you are chief. + * The server does not send a reply. + */ +int flib_netconn_send_mapMazeSize(flib_netconn *conn, int mazeSize); + +/** + * Set the seed for the map. Only makes sense in room state and if you are chief. + * The server does not send a reply. + */ +int flib_netconn_send_mapSeed(flib_netconn *conn, const char *seed); + +/** + * Set the theme for the map. Only makes sense in room state and if you are chief. + * The server does not send a reply. + */ +int flib_netconn_send_mapTheme(flib_netconn *conn, const char *theme); + +/** + * Set the draw data for the drawn map. Only makes sense in room state and if you are chief. + * The server does not send a reply. + */ +int flib_netconn_send_mapDrawdata(flib_netconn *conn, const uint8_t *drawData, size_t size); + +/** + * Set the script (game style). Only makes sense in room state and if you are chief. + * The server does not send a reply. + */ +int flib_netconn_send_script(flib_netconn *conn, const char *scriptName); + +/** + * Set the scheme. Only makes sense in room state and if you are chief. + * The server does not send a reply. + */ +int flib_netconn_send_scheme(flib_netconn *conn, const flib_cfg *scheme); + +/** + * Inform the server that the round has ended. TODO: Figure out details + */ +int flib_netconn_send_roundfinished(flib_netconn *conn, bool withoutError); + +/** + * Ban a player. TODO: Figure out details + */ +int flib_netconn_send_ban(flib_netconn *conn, const char *playerName); + +/** + * Kick a player. TODO: Figure out details + */ +int flib_netconn_send_kick(flib_netconn *conn, const char *playerName); + +/** + * Request information about a player. If the action succeeds, you will + * receive an onMessage callback with NETCONN_MSG_TYPE_PLAYERINFO containing + * the requested information. + */ +int flib_netconn_send_playerInfo(flib_netconn *conn, const char *playerName); + +/** + * Follow a player. TODO figure out details + */ +int flib_netconn_send_playerFollow(flib_netconn *conn, const char *playerName); + +/** + * Signal that you want to start the game. Only makes sense in room state and if you are chief. + * TODO figure out details + */ +int flib_netconn_send_startGame(flib_netconn *conn); + +/** + * Allow/forbid players to join the room. Only makes sense in room state and if you are chief. + * The server does not send a reply. + */ +int flib_netconn_send_toggleRestrictJoins(flib_netconn *conn); + +/** + * Allow/forbid adding teams to the room. Only makes sense in room state and if you are chief. + * The server does not send a reply. + */ +int flib_netconn_send_toggleRestrictTeams(flib_netconn *conn); + +/** + * Probably does something administrator-y. + */ +int flib_netconn_send_clearAccountsCache(flib_netconn *conn); + +/** + * Sets a server variable to the indicated value. Only makes sense if you are server admin. + * Known variables are MOTD_NEW, MOTD_OLD and LATEST_PROTO. + * TODO reply? + */ +int flib_netconn_send_setServerVar(flib_netconn *conn, const char *name, const char *value); + +/** + * Queries all server variables. Only makes sense if you are server admin. (TODO: try) + * If the action succeeds, you will receive several onServerVar callbacks with the + * current values of all server variables. + */ +int flib_netconn_send_getServerVars(flib_netconn *conn); + + + + + + + + + + +/** * Callback for several informational messages that should be displayed to the user * (e.g. in the chat window), but do not require a reaction. If a game is running, you might * want to redirect some of these messages to the engine as well so the user will see them. @@ -113,13 +323,11 @@ void flib_netconn_onRoomUpdate(flib_netconn *conn, void (*callback)(void *context, const char *oldName, const flib_roomlist_room *room), void* context); /** - * Callbacks for players joining or leaving a room or the lobby. If join is true it's a join, otherwise a leave. + * Callbacks for players joining or leaving the lobby. If join is true it's a join, otherwise a leave. * NOTE: partMessage is null if no parting message was given. */ void flib_netconn_onLobbyJoin(flib_netconn *conn, void (*callback)(void *context, const char *nick), void* context); void flib_netconn_onLobbyLeave(flib_netconn *conn, void (*callback)(void *context, const char *nick, const char *partMessage), void* context); -void flib_netconn_onRoomJoin(flib_netconn *conn, void (*callback)(void *context, const char *nick), void* context); -void flib_netconn_onRoomLeave(flib_netconn *conn, void (*callback)(void *context, const char *nick, const char *partMessage), void* context); /** * onNickTaken is called on connecting to the server, if it turns out that there is already a player with the same nick. @@ -130,14 +338,6 @@ void flib_netconn_onNickTaken(flib_netconn *conn, void (*callback)(void *context, const char *nick), void* context); /** - * onNickAccept informs that your nickname has been accepted by the server, i.e. there was nobody with that nick already - * on the server. - * Note that a nick request is sent automatically by the netconn when you join the server, so you should receive this - * callback shortly after connecting. - */ -void flib_netconn_onNickAccept(flib_netconn *conn, void (*callback)(void *context, const char *nick), void* context); - -/** * When connecting with a registered nickname, the server will ask for a password before admitting you in. * This callback is called when that happens. As a reaction, you can send the password using * flib_netconn_send_password or choose a different nick. If you don't register a callback, @@ -146,6 +346,18 @@ void flib_netconn_onPasswordRequest(flib_netconn *conn, void (*callback)(void *context, const char *nick), void* context); /** + * You just left the lobby and entered a room. + * If chief is true, you can and should send a full configuration for the room now. + * This consists of TODO + */ +void flib_netconn_onEnterRoom(flib_netconn *conn, void (*callback)(void *context, bool chief), void *context); + + +/** + * The following callbacks are only relevant in room state. + */ + +/** * This callback informs about changes to your room chief status, i.e. whether you are allowed to * modify the current room. Generally when you create a room you start out being room chief, and * when you join an existing room you are not. However, in some situations room ownership can change, @@ -159,21 +371,91 @@ /** * One of the players in the room (possibly you!) changed their ready state. */ -void flib_netconn_onReadyStateCb(flib_netconn *conn, void (*callback)(void *context, const char *nick, bool ready), void* context); - -/** - * You just left the lobby and entered a room. - * If chief is true, you can and should send a full configuration for the room now. - */ -void flib_netconn_onEnterRoomCb(flib_netconn *conn, void (*callback)(void *context, bool chief), void *context); +void flib_netconn_onReadyState(flib_netconn *conn, void (*callback)(void *context, const char *nick, bool ready), void* context); /** * You just left a room and entered the lobby again. * reason is one of the NETCONN_ROOMLEAVE_ constants. * This will not be called when you actively leave a room using PART. */ -void flib_netconn_onLeaveRoomCb(flib_netconn *conn, void (*callback)(void *context, int reason, const char *message), void *context); +void flib_netconn_onLeaveRoom(flib_netconn *conn, void (*callback)(void *context, int reason, const char *message), void *context); + +/** + * A new team was added to the room. The person who adds a team does NOT receive this callback (he gets onTeamAccepted instead). + * The team does not contain bindings, stats, weaponset, color or the number of hogs. + */ +void flib_netconn_onTeamAdd(flib_netconn *conn, void (*callback)(void *context, flib_team *team), void *context); + +/** + * A team was removed from the room. + */ +void flib_netconn_onTeamDelete(flib_netconn *conn, void (*callback)(void *context, const char *teamname), void *context); + +void flib_netconn_onRoomJoin(flib_netconn *conn, void (*callback)(void *context, const char *nick), void* context); +void flib_netconn_onRoomLeave(flib_netconn *conn, void (*callback)(void *context, const char *nick, const char *partMessage), void* context); + +/** + * The game is starting. Fire up the engine and join in! + * TODO: How? + */ +void flib_netconn_onRunGame(flib_netconn *conn, void (*callback)(void *context), void *context); + +/** + * When you ask for a team to be added, the server might reject it for several reasons, e.g. because it has the same name + * as an existing team, or because the room chief restricted adding new teams. If the team is accepted by the server, + * this callback is fired. + */ +void flib_netconn_onTeamAccepted(flib_netconn *conn, void (*callback)(void *context, const char *teamName), void *context); + +/** + * The number of hogs in a team has been changed by the room chief. If you are the chief and change the number of hogs yourself, + * you will not receive this callback! + */ +void flib_netconn_onHogCountChanged(flib_netconn *conn, void (*callback)(void *context, const char *teamName, int hogs), void *context); -void flib_netconn_onTeamAddCb(flib_netconn *conn, void (*callback)(void *context, flib_team *team), void *context); +/** + * The color of a team has been changed by the room chief. If you are the chief and change the color yourself, + * you will not receive this callback! + */ +void flib_netconn_onTeamColorChanged(flib_netconn *conn, void (*callback)(void *context, const char *teamName, uint32_t colorARGB), void *context); + +void flib_netconn_onEngineMessage(flib_netconn *conn, void (*callback)(void *context, const char *message, int size), void *context); + +void flib_netconn_onCfgScheme(flib_netconn *conn, void (*callback)(void *context, flib_cfg *scheme), void *context); + +/** + * This is called when the map configuration in a room is changed (or first received). Only non-chiefs receive these messages. + * To reduce the number of callback functions, the netconn keeps track of the current map settings and always passes the entire + * current map config, but informs the callee about what has changed (see the NETCONN_MAPCHANGE_ constants). + * The map parameter passed to the callback is an internally held map config. If you want to keep it around, best make a copy + * or it may or may not change while you are not looking. + * + * Caution: Due to the way the protocol works, the map might not be complete at this point if it is a hand-drawn map, because + * the "full" map config does not include the drawn map data. + */ +void flib_netconn_onMapChanged(flib_netconn *conn, void (*callback)(void *context, const flib_map *map, int changetype), void *context); + +/** + * The "game style" script has been changed by the room chief. If you are the chief and change the script yourself, + * you will not receive this callback! + */ +void flib_netconn_onScriptChanged(flib_netconn *conn, void (*callback)(void *context, const char *script), void *context); + +/** + * The weaponset has been changed by the room chief. If you are the chief and change the weaponset yourself, + * you will not receive this callback! + */ +void flib_netconn_onWeaponsetChanged(flib_netconn *conn, void (*callback)(void *context, flib_weaponset *weaponset), void *context); + +/** + * This callback is called if the server informs us that we have admin rights. + */ +void flib_netconn_onAdminAccess(flib_netconn *conn, void (*callback)(void *context), void *context); + +/** + * When you query the server vars with GET_SERVER_VAR (TODO probably only works as admin), the server + * replies with a list of them. This callback is called for each entry in that list. + */ +void flib_netconn_onServerVar(flib_netconn *conn, void (*callback)(void *context, const char *name, const char *value), void *context); #endif diff -r 5b0aeef8ba2a -r 5608ac657362 project_files/frontlib/net/netconn_internal.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/net/netconn_internal.h Mon Jun 25 00:42:07 2012 +0200 @@ -0,0 +1,126 @@ +/** + * Common definitions needed by netconn functions, to allow splitting them into several files. + */ + +#ifndef NETCONN_INTERNAL_H_ +#define NETCONN_INTERNAL_H_ + +#include "netconn.h" +#include "netbase.h" +#include "../model/cfg.h" +#include "../model/roomlist.h" +#include "../model/map.h" +#include "../model/team.h" +#include "../model/weapon.h" + +#include +#include +#include + +struct _flib_netconn { + flib_netbase *netBase; + char *playerName; + flib_cfg_meta *metaCfg; + flib_roomlist *roomList; + + int netconnState; // One of the NETCONN_STATE constants + + bool isAdmin; // Player is server administrator + bool isChief; // Player can modify the current room + flib_map *map; // Map settings in the current room. + + void (*onMessageCb)(void *context, int msgtype, const char *msg); + void *onMessageCtx; + + void (*onConnectedCb)(void *context); + void *onConnectedCtx; + + void (*onDisconnectedCb)(void *context, int reason, const char *message); + void *onDisconnectedCtx; + + void (*onRoomAddCb)(void *context, const flib_roomlist_room *room); + void *onRoomAddCtx; + + void (*onRoomDeleteCb)(void *context, const char *name); + void *onRoomDeleteCtx; + + void (*onRoomUpdateCb)(void *context, const char *oldName, const flib_roomlist_room *room); + void *onRoomUpdateCtx; + + void (*onChatCb)(void *context, const char *nick, const char *msg); + void *onChatCtx; + + void (*onLobbyJoinCb)(void *context, const char *nick); + void *onLobbyJoinCtx; + + void (*onLobbyLeaveCb)(void *context, const char *nick, const char *partMessage); + void *onLobbyLeaveCtx; + + void (*onRoomJoinCb)(void *context, const char *nick); + void *onRoomJoinCtx; + + void (*onRoomLeaveCb)(void *context, const char *nick, const char *partMessage); + void *onRoomLeaveCtx; + + void (*onNickTakenCb)(void *context, const char *nick); + void *onNickTakenCtx; + + void (*onPasswordRequestCb)(void *context, const char *nick); + void *onPasswordRequestCtx; + + void (*onRoomChiefStatusCb)(void *context, bool isChief); + void *onRoomChiefStatusCtx; + + void (*onReadyStateCb)(void *context, const char *nick, bool ready); + void *onReadyStateCtx; + + void (*onEnterRoomCb)(void *context, bool chief); + void *onEnterRoomCtx; + + void (*onLeaveRoomCb)(void *context, int reason, const char *message); + void *onLeaveRoomCtx; + + void (*onTeamAddCb)(void *context, flib_team *team); + void *onTeamAddCtx; + + void (*onTeamDeleteCb)(void *context, const char *teamname); + void *onTeamDeleteCtx; + + void (*onRunGameCb)(void *context); + void *onRunGameCtx; + + void (*onTeamAcceptedCb)(void *context, const char *teamName); + void *onTeamAcceptedCtx; + + void (*onHogCountChangedCb)(void *context, const char *teamName, int hogs); + void *onHogCountChangedCtx; + + void (*onTeamColorChangedCb)(void *context, const char *teamName, uint32_t colorRGB); + void *onTeamColorChangedCtx; + + void (*onEngineMessageCb)(void *context, const char *message, int size); + void *onEngineMessageCtx; + + void (*onCfgSchemeCb)(void *context, flib_cfg *scheme); + void *onCfgSchemeCtx; + + void (*onMapChangedCb)(void *context, const flib_map *map, int changetype); + void *onMapChangedCtx; + + void (*onScriptChangedCb)(void *context, const char *script); + void *onScriptChangedCtx; + + void (*onWeaponsetChangedCb)(void *context, flib_weaponset *weaponset); + void *onWeaponsetChangedCtx; + + void (*onAdminAccessCb)(void *context); + void *onAdminAccessCtx; + + void (*onServerVarCb)(void *context, const char *name, const char *value); + void *onServerVarCtx; + + bool running; + bool destroyRequested; +}; + +#endif diff -r 5b0aeef8ba2a -r 5608ac657362 project_files/frontlib/net/netconn_send.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/net/netconn_send.c Mon Jun 25 00:42:07 2012 +0200 @@ -0,0 +1,346 @@ +#include "netconn_internal.h" + +#include "../util/logging.h" +#include "../util/util.h" +#include "../util/buffer.h" +#include "../md5/md5.h" +#include "../base64/base64.h" + +#include +#include +#include + +// TODO state changes + +// cmdname is always given as literal from functions in this file, so it is never null. +static int sendVoid(flib_netconn *conn, const char *cmdname) { + if(!conn) { + flib_log_e("null parameter trying to send %s command.", cmdname); + return -1; + } + return flib_netbase_sendf(conn->netBase, "%s\n\n", cmdname); +} + +static int sendStr(flib_netconn *conn, const char *cmdname, const char *str) { + if(!conn || !str) { + flib_log_e("null parameter trying to send %s command.", cmdname); + return -1; + } + return flib_netbase_sendf(conn->netBase, "%s\n%s\n\n", cmdname, str); +} + +static int sendInt(flib_netconn *conn, const char *cmdname, int param) { + if(!conn) { + flib_log_e("null parameter trying to send %s command.", cmdname); + return -1; + } + return flib_netbase_sendf(conn->netBase, "%s\n%i\n\n", cmdname, param); +} + +int flib_netconn_send_quit(flib_netconn *conn, const char *quitmsg) { + return sendStr(conn, "QUIT", quitmsg ? quitmsg : "User quit"); +} + +int flib_netconn_send_chat(flib_netconn *conn, const char *chat) { + return sendStr(conn, "CHAT", chat); +} + +int flib_netconn_send_teamchat(flib_netconn *conn, const char *chat) { + return sendStr(conn, "TEAMCHAT", chat); +} + +int flib_netconn_send_nick(flib_netconn *conn, const char *nick) { + int result = -1; + if(!conn || !nick) { + flib_log_e("null parameter in flib_netconn_send_nick"); + } else { + char *tmpName = flib_strdupnull(nick); + if(tmpName) { + if(!flib_netbase_sendf(conn->netBase, "%s\n%s\n\n", "NICK", nick)) { + free(conn->playerName); + conn->playerName = tmpName; + tmpName = NULL; + result = 0; + } + } + free(tmpName); + } + return result; +} + +int flib_netconn_send_password(flib_netconn *conn, const char *latin1Passwd) { + int result = -1; + if(!conn || !latin1Passwd) { + flib_log_e("null parameter in flib_netconn_send_password"); + } else { + md5_state_t md5state; + uint8_t md5bytes[16]; + char md5hex[33]; + md5_init(&md5state); + md5_append(&md5state, (unsigned char*)latin1Passwd, strlen(latin1Passwd)); + md5_finish(&md5state, md5bytes); + for(int i=0;inetBase, "%s\n%s\n\n", "PASSWORD", md5hex); + } + return result; +} + +int flib_netconn_send_joinRoom(flib_netconn *conn, const char *room) { + return sendStr(conn, "JOIN_ROOM", room); +} + +int flib_netconn_send_createRoom(flib_netconn *conn, const char *room) { + return sendStr(conn, "CREATE_ROOM", room); +} + +int flib_netconn_send_renameRoom(flib_netconn *conn, const char *roomName) { + return sendStr(conn, "ROOM_NAME", roomName); +} + +int flib_netconn_send_leaveRoom(flib_netconn *conn) { + return sendVoid(conn, "PART"); +} + +int flib_netconn_send_toggleReady(flib_netconn *conn) { + return sendVoid(conn, "TOGGLE_READY"); +} + +int flib_netconn_send_addTeam(flib_netconn *conn, const flib_team *team) { + int result = -1; + if(!conn || !team) { + flib_log_e("null parameter in flib_netconn_send_addTeam"); + } else { + bool missingInfo = !team->name || !team->color || !team->grave || !team->fort || !team->voicepack || !team->flag; + for(int i=0; ihogs[i].name || !team->hogs[i].hat; + } + if(missingInfo) { + flib_log_e("Incomplete team definition for flib_netconn_send_addTeam"); + } else { + flib_vector *vec = flib_vector_create(); + if(vec) { + bool error = false; + error |= flib_vector_appendf(vec, "ADD_TEAM\n%s\n%lu\n%s\n%s\n%s\n%s\n%i\n", team->name, (unsigned long)team->color, team->grave, team->fort, team->voicepack, team->flag, team->hogs[0].difficulty); + for(int i=0; ihogs[i].name, team->hogs[i].hat); + } + error |= flib_vector_appendf(vec, "\n"); + if(!error) { + result = flib_netbase_send_raw(conn->netBase, flib_vector_data(vec), flib_vector_size(vec)); + } + } + flib_vector_destroy(vec); + } + } + return result; +} + +int flib_netconn_send_removeTeam(flib_netconn *conn, const char *teamname) { + return sendStr(conn, "REMOVE_TEAM", teamname); +} + +int flib_netconn_send_engineMessage(flib_netconn *conn, const uint8_t *message, size_t size) { + int result = -1; + if(!conn || (!message && size>0)) { + flib_log_e("null parameter in flib_netconn_send_engineMessage"); + } else { + char *base64encout = NULL; + base64_encode_alloc((const char*)message, size, &base64encout); + if(base64encout) { + result = flib_netbase_sendf(conn->netBase, "EM\n%s\n\n", base64encout); + } + free(base64encout); + } + return result; +} + +int flib_netconn_send_teamHogCount(flib_netconn *conn, const char *teamname, int hogcount) { + if(!conn || !teamname || hogcount<1 || hogcount>HEDGEHOGS_PER_TEAM) { + flib_log_e("invalid parameter in flib_netconn_send_teamHogCount"); + return -1; + } + return flib_netbase_sendf(conn->netBase, "HH_NUM\n%s\n%i\n\n", teamname, hogcount); +} + +int flib_netconn_send_teamColor(flib_netconn *conn, const char *teamname, uint32_t colorRGB) { + if(!conn || !teamname) { + flib_log_e("null parameter in flib_netconn_send_teamColor"); + return -1; + } + return flib_netbase_sendf(conn->netBase, "TEAM_COLOR\n%s\n%lu\n\n", teamname, (unsigned long)colorRGB); +} + +int flib_netconn_send_weaponset(flib_netconn *conn, const flib_weaponset *weaponset) { + if(!conn || !weaponset) { + flib_log_e("null parameter in flib_netconn_send_weaponset"); + return -1; + } + + char ammostring[WEAPONS_COUNT*4+1]; + strcpy(ammostring, weaponset->loadout); + strcat(ammostring, weaponset->crateprob); + strcat(ammostring, weaponset->delay); + strcat(ammostring, weaponset->crateammo); + return flib_netbase_sendf(conn->netBase, "CFG\nAMMO\n%s\n%s\n\n", weaponset->name, ammostring); +} + +int flib_netconn_send_map(flib_netconn *conn, const flib_map *map) { + if(!conn || !map) { + flib_log_e("null parameter in flib_netconn_send_map"); + return -1; + } + bool error = false; + + if(map->seed) { + error |= flib_netconn_send_mapSeed(conn, map->seed); + } + error |= flib_netconn_send_mapTemplate(conn, map->templateFilter); + if(map->theme) { + error |= flib_netconn_send_mapTheme(conn, map->theme); + } + error |= flib_netconn_send_mapGen(conn, map->mapgen); + error |= flib_netconn_send_mapMazeSize(conn, map->mazeSize); + if(map->name) { + error |= flib_netconn_send_mapName(conn, map->name); + } + if(map->drawData && map->drawDataSize>0) { + error |= flib_netconn_send_mapDrawdata(conn, map->drawData, map->drawDataSize); + } + return error; +} + +int flib_netconn_send_mapName(flib_netconn *conn, const char *mapName) { + return sendStr(conn, "CFG\nMAP", mapName); +} + +int flib_netconn_send_mapGen(flib_netconn *conn, int mapGen) { + return sendInt(conn, "CFG\nMAPGEN", mapGen); +} + +int flib_netconn_send_mapTemplate(flib_netconn *conn, int templateFilter) { + return sendInt(conn, "CFG\nTEMPLATE", templateFilter); +} + +int flib_netconn_send_mapMazeSize(flib_netconn *conn, int mazeSize) { + return sendInt(conn, "CFG\nMAZE_SIZE", mazeSize); +} + +int flib_netconn_send_mapSeed(flib_netconn *conn, const char *seed) { + return sendStr(conn, "CFG\nSEED", seed); +} + +int flib_netconn_send_mapTheme(flib_netconn *conn, const char *theme) { + return sendStr(conn, "CFG\nTHEME", theme); +} + +int flib_netconn_send_mapDrawdata(flib_netconn *conn, const uint8_t *drawData, size_t size) { + int result = -1; + if(!conn || (!drawData && size>0) || size>SIZE_MAX/2) { + flib_log_e("invalid parameter in flib_netconn_send_map"); + } else { + uLongf zippedSize = compressBound(size); + uint8_t *zipped = flib_malloc(zippedSize+4); // 4 extra bytes for header + if(zipped) { + // Create the QCompress size header (uint32 big endian) + zipped[0] = (size>>24) & 0xff; + zipped[1] = (size>>16) & 0xff; + zipped[2] = (size>>8) & 0xff; + zipped[3] = (size) & 0xff; + + if(compress(zipped+4, &zippedSize, drawData, size) != Z_OK) { + flib_log_e("Error compressing drawn map data."); + } else { + char *base64encout = NULL; + base64_encode_alloc((const char*)zipped, zippedSize+4, &base64encout); + if(!base64encout) { + flib_log_e("Error base64-encoding drawn map data."); + } else { + result = flib_netbase_sendf(conn->netBase, "CFG\nDRAWNMAP\n%s\n\n", base64encout); + } + free(base64encout); + } + } + free(zipped); + } + return result; +} + +int flib_netconn_send_script(flib_netconn *conn, const char *scriptName) { + return sendStr(conn, "CFG\nSCRIPT", scriptName); +} + +int flib_netconn_send_scheme(flib_netconn *conn, const flib_cfg *scheme) { + int result = -1; + if(!conn || !scheme) { + flib_log_e("null parameter in flib_netconn_send_scheme"); + } else { + flib_vector *vec = flib_vector_create(); + if(vec) { + bool error = false; + error |= flib_vector_appendf(vec, "CFG\nSCHEME\n%s\n", scheme->name); + for(int i=0; imeta->modCount; i++) { + error |= flib_vector_appendf(vec, "%s\n", scheme->mods[i] ? "true" : "false"); + } + for(int i=0; imeta->settingCount; i++) { + error |= flib_vector_appendf(vec, "%i\n", scheme->settings[i]); + } + error |= flib_vector_appendf(vec, "\n"); + if(!error) { + result = flib_netbase_send_raw(conn->netBase, flib_vector_data(vec), flib_vector_size(vec)); + } + } + flib_vector_destroy(vec); + } + return result; +} + +int flib_netconn_send_roundfinished(flib_netconn *conn, bool withoutError) { + return sendInt(conn, "ROUNDFINISHED", withoutError ? 1 : 0); +} + +int flib_netconn_send_ban(flib_netconn *conn, const char *playerName) { + return sendStr(conn, "BAN", playerName); +} + +int flib_netconn_send_kick(flib_netconn *conn, const char *playerName) { + return sendStr(conn, "KICK", playerName); +} + +int flib_netconn_send_playerInfo(flib_netconn *conn, const char *playerName) { + return sendStr(conn, "INFO", playerName); +} + +int flib_netconn_send_playerFollow(flib_netconn *conn, const char *playerName) { + return sendStr(conn, "FOLLOW", playerName); +} + +int flib_netconn_send_startGame(flib_netconn *conn) { + return sendVoid(conn, "START_GAME"); +} + +int flib_netconn_send_toggleRestrictJoins(flib_netconn *conn) { + return sendVoid(conn, "TOGGLE_RESTRICT_JOINS"); +} + +int flib_netconn_send_toggleRestrictTeams(flib_netconn *conn) { + return sendVoid(conn, "TOGGLE_RESTRICT_TEAMS"); +} + +int flib_netconn_send_clearAccountsCache(flib_netconn *conn) { + return sendVoid(conn, "CLEAR_ACCOUNTS_CACHE"); +} + +int flib_netconn_send_setServerVar(flib_netconn *conn, const char *name, const char *value) { + if(!conn || !name || !value) { + flib_log_e("null parameter trying to send SET_SERVER_VAR command."); + return -1; + } + return flib_netbase_sendf(conn->netBase, "%s\n%s\n%s\n\n", "SET_SERVER_VAR", name, value); +} + +int flib_netconn_send_getServerVars(flib_netconn *conn) { + return sendVoid(conn, "GET_SERVER_VAR"); +} diff -r 5b0aeef8ba2a -r 5608ac657362 project_files/frontlib/net/netprotocol.c --- a/project_files/frontlib/net/netprotocol.c Thu Jun 21 21:32:12 2012 +0200 +++ b/project_files/frontlib/net/netprotocol.c Mon Jun 25 00:42:07 2012 +0200 @@ -3,6 +3,10 @@ #include "../util/util.h" #include "../util/logging.h" +#include "../base64/base64.h" + +#include + #include #include #include @@ -10,18 +14,12 @@ static int fillTeamFromMsg(flib_team *team, char **parts) { team->name = flib_strdupnull(parts[0]); - team->grave = flib_strdupnull(parts[2]); - team->fort = flib_strdupnull(parts[3]); - team->voicepack = flib_strdupnull(parts[4]); - team->flag = flib_strdupnull(parts[5]); - if(!team->name || !team->grave || !team->fort || !team->voicepack || !team->flag) { - return -1; - } - - long color; - if(sscanf(parts[1], "#%lx", &color)) { - team->color = color; - } else { + team->grave = flib_strdupnull(parts[1]); + team->fort = flib_strdupnull(parts[2]); + team->voicepack = flib_strdupnull(parts[3]); + team->flag = flib_strdupnull(parts[4]); + team->ownerName = flib_strdupnull(parts[5]); + if(!team->name || !team->grave || !team->fort || !team->voicepack || !team->flag || !team->ownerName) { return -1; } @@ -45,7 +43,7 @@ flib_team *flib_team_from_netmsg(char **parts) { flib_team *result = NULL; - flib_team *tmpTeam = flib_calloc(1, sizeof(flib_team)); + flib_team *tmpTeam = flib_team_retain(flib_calloc(1, sizeof(flib_team))); if(tmpTeam) { if(!fillTeamFromMsg(tmpTeam, parts)) { result = tmpTeam; @@ -54,6 +52,65 @@ flib_log_e("Error parsing team from net."); } } - flib_team_destroy(tmpTeam); + flib_team_release(tmpTeam); + return result; +} + +flib_cfg *flib_netmsg_to_cfg(flib_cfg_meta *meta, char **parts) { + flib_cfg *result = flib_cfg_create(meta, parts[0]); + if(result) { + for(int i=0; imodCount; i++) { + result->mods[i] = !strcmp(parts[i+1], "true"); + } + for(int i=0; isettingCount; i++) { + result->settings[i] = atoi(parts[i+meta->modCount+1]); + } + } + return result; +} + +flib_map *flib_netmsg_to_map(char **parts) { + flib_map *result = flib_map_create_named(parts[3], parts[0]); + if(result) { + result->mapgen = atoi(parts[1]); + result->mazeSize = atoi(parts[2]); + result->templateFilter = atoi(parts[4]); + } return result; } + +// TODO: Test with empty map +uint8_t *flib_netmsg_to_drawnmapdata(size_t *outlen, char *netmsg) { + uint8_t *result = NULL; + + // First step: base64 decoding + char *base64decout = NULL; + size_t base64declen; + bool ok = base64_decode_alloc(netmsg, strlen(netmsg), &base64decout, &base64declen); + if(ok && base64declen>3) { + // Second step: unzip with the QCompress header. That header is just a big-endian + // uint32 indicating the length of the uncompressed data. + uint32_t unzipLen = + (((uint32_t)base64decout[0])<<24) + + (((uint32_t)base64decout[1])<<16) + + (((uint32_t)base64decout[2])<<8) + + base64decout[3]; + uint8_t *out = flib_malloc(unzipLen); + if(out) { + uLongf actualUnzipLen = unzipLen; + int resultcode = uncompress(out, &actualUnzipLen, (Bytef*)(base64decout+4), base64declen-4); + if(resultcode == Z_OK) { + result = out; + *outlen = actualUnzipLen; + out = NULL; + } else { + flib_log_e("Uncompressing drawn map failed. Code: %i", resultcode); + } + } + free(out); + } else { + flib_log_e("base64 decoding of drawn map failed."); + } + free(base64decout); + return result; +} diff -r 5b0aeef8ba2a -r 5608ac657362 project_files/frontlib/net/netprotocol.h --- a/project_files/frontlib/net/netprotocol.h Thu Jun 21 21:32:12 2012 +0200 +++ b/project_files/frontlib/net/netprotocol.h Mon Jun 25 00:42:07 2012 +0200 @@ -2,11 +2,35 @@ #define NETPROTOCOL_H_ #include "../model/team.h" +#include "../model/cfg.h" +#include "../model/map.h" + +#include /** * Create a new team from this 23-part net message */ flib_team *flib_team_from_netmsg(char **parts); +/** + * Create a new scheme from this net message, which must have + * meta->modCount+meta->settingCount+1 parts. + */ +flib_cfg *flib_netmsg_to_cfg(flib_cfg_meta *meta, char **parts); + +/** + * Create a new map from this five-part netmsg + */ +flib_map *flib_netmsg_to_map(char **parts); + +/** + * Decode the drawn map data from this netmessage line. + * + * The data is first base64 decoded and then quncompress()ed. + * The return value is a newly allocated byte buffer, the length + * is written to the variable pointed to by outlen. + * Returns NULL on error. + */ +uint8_t *flib_netmsg_to_drawnmapdata(size_t *outlen, char *netmsg); #endif /* NETPROTOCOL_H_ */ diff -r 5b0aeef8ba2a -r 5608ac657362 project_files/frontlib/socket.c --- a/project_files/frontlib/socket.c Thu Jun 21 21:32:12 2012 +0200 +++ b/project_files/frontlib/socket.c Mon Jun 25 00:42:07 2012 +0200 @@ -5,84 +5,77 @@ #include #include -typedef struct _flib_tcpsocket { +struct _flib_tcpsocket { TCPsocket sock; SDLNet_SocketSet sockset; -} _flib_tcpsocket; +}; -typedef struct _flib_acceptor { +struct _flib_acceptor { TCPsocket sock; uint16_t port; -} _flib_acceptor; +}; -static uint32_t get_peer_ip(TCPsocket sock) { +static uint32_t getPeerIp(TCPsocket sock) { IPaddress *addr = SDLNet_TCP_GetPeerAddress(sock); return SDLNet_Read32(&addr->host); } -static bool connection_is_local(TCPsocket sock) { - return get_peer_ip(sock) == (uint32_t)((127UL<<24)+1); // 127.0.0.1 +static bool connectionIsLocal(TCPsocket sock) { + return getPeerIp(sock) == (uint32_t)((127UL<<24)+1); // 127.0.0.1 } -static flib_tcpsocket *flib_socket_create(TCPsocket sdlsock) { - flib_tcpsocket *result = flib_calloc(1, sizeof(_flib_tcpsocket)); - if(!result) { - return NULL; - } - result->sock = sdlsock; - result->sockset = SDLNet_AllocSocketSet(1); +static flib_tcpsocket *createSocket(TCPsocket sdlsock) { + flib_tcpsocket *result = flib_calloc(1, sizeof(flib_tcpsocket)); + if(result) { + result->sock = sdlsock; + result->sockset = SDLNet_AllocSocketSet(1); - if(!result->sockset) { - flib_log_e("Can't allocate socket: Out of memory!"); - SDLNet_FreeSocketSet(result->sockset); - free(result); - return NULL; + if(!result->sockset) { + flib_log_e("Can't allocate socket: Out of memory!"); + SDLNet_FreeSocketSet(result->sockset); + free(result); + result = NULL; + } else { + SDLNet_AddSocket(result->sockset, (SDLNet_GenericSocket)result->sock); + } } + return result; +} - SDLNet_AddSocket(result->sockset, (SDLNet_GenericSocket)result->sock); - return result; +TCPsocket listen(uint16_t port) { + IPaddress addr; + addr.host = INADDR_ANY; + SDLNet_Write16(port, &addr.port); + TCPsocket sock = SDLNet_TCP_Open(&addr); + if(!sock) { + flib_log_w("Unable to listen on port %u: %s", (unsigned)port, SDLNet_GetError()); + } + return sock; } flib_acceptor *flib_acceptor_create(uint16_t port) { - flib_acceptor *result = flib_calloc(1, sizeof(_flib_acceptor)); - if(!result) { - return NULL; - } - - IPaddress addr; - addr.host = INADDR_ANY; - - if(port > 0) { - result->port = port; - SDLNet_Write16(port, &addr.port); - result->sock = SDLNet_TCP_Open(&addr); - if(result->sock) { - return result; + flib_acceptor *result = flib_calloc(1, sizeof(flib_acceptor)); + if(result) { + if(port > 0) { + result->port = port; + result->sock = listen(result->port); } else { - flib_log_e("Unable to listen on port %u: %s", (unsigned)port, SDLNet_GetError()); - free(result); - return NULL; - } - } else { - /* SDL_net does not seem to have a way to listen on a random unused port - and find out which port that is, so let's try to find one ourselves. */ - srand(time(NULL)); - rand(); - for(int i=0; i<1000; i++) { - // IANA suggests using ports in the range 49152-65535 for things like this - result->port = 49152+(rand()%(65535-49152)); - SDLNet_Write16(result->port, &addr.port); - result->sock = SDLNet_TCP_Open(&addr); - if(result->sock) { - return result; - } else { - flib_log_w("Unable to listen on port %u: %s", (unsigned)result->port, SDLNet_GetError()); + /* SDL_net does not seem to have a way to listen on a random unused port + and find out which port that is, so let's try to find one ourselves. */ + srand(time(NULL)); + for(int i=0; !result->sock && i<1000; i++) { + // IANA suggests using ports in the range 49152-65535 for things like this + result->port = 49152+(rand()%(65535-49152)); + result->sock = listen(result->port); } } - flib_log_e("Unable to listen on a random unused port."); - free(result); - return NULL; + if(!result->sock) { + flib_log_e("Failed to create acceptor."); + free(result); + result = NULL; + } } + return result; } uint16_t flib_acceptor_listenport(flib_acceptor *acceptor) { @@ -101,18 +94,17 @@ } flib_tcpsocket *flib_socket_accept(flib_acceptor *acceptor, bool localOnly) { + flib_tcpsocket *result = NULL; if(!acceptor) { flib_log_e("Call to flib_socket_accept with acceptor==null"); - return NULL; - } - flib_tcpsocket *result = NULL; - TCPsocket sock = NULL; - while(!result && (sock = SDLNet_TCP_Accept(acceptor->sock))) { - if(localOnly && !connection_is_local(sock)) { - flib_log_i("Rejected nonlocal connection attempt from %s", flib_format_ip(get_peer_ip(sock))); - SDLNet_TCP_Close(sock); - } else { - result = flib_socket_create(sock); + } else { + TCPsocket sock = NULL; + while(!result && (sock = SDLNet_TCP_Accept(acceptor->sock))) { + if(localOnly && !connectionIsLocal(sock)) { + flib_log_i("Rejected nonlocal connection attempt from %s", flib_format_ip(getPeerIp(sock))); + } else { + result = createSocket(sock); + } if(!result) { SDLNet_TCP_Close(sock); } @@ -134,7 +126,7 @@ if(!sock) { flib_log_e("SDLNet_TCP_Open: %s\n", SDLNet_GetError()); } else { - result = flib_socket_create(sock); + result = createSocket(sock); if(result) { sock = NULL; } diff -r 5b0aeef8ba2a -r 5608ac657362 project_files/frontlib/socket.h --- a/project_files/frontlib/socket.h Thu Jun 21 21:32:12 2012 +0200 +++ b/project_files/frontlib/socket.h Mon Jun 25 00:42:07 2012 +0200 @@ -15,10 +15,7 @@ #include #include -struct _flib_tcpsocket; typedef struct _flib_tcpsocket flib_tcpsocket; - -struct _flib_acceptor; typedef struct _flib_acceptor flib_acceptor; /** @@ -26,7 +23,7 @@ * on the given port. If port is 0, this will listen on a random * unused port which can then be queried with flib_acceptor_listenport. * - * Can return NULL on error. + * Returns NULL on error. */ flib_acceptor *flib_acceptor_create(uint16_t port); @@ -36,8 +33,7 @@ uint16_t flib_acceptor_listenport(flib_acceptor *acceptor); /** - * Close the acceptor and free its memory. - * If the acceptor is already NULL, nothing happens. + * Close the acceptor and free its memory. NULL-safe. */ void flib_acceptor_close(flib_acceptor *acceptor); @@ -54,8 +50,7 @@ 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. + * Close the socket and free its memory. NULL-safe. */ void flib_socket_close(flib_tcpsocket *socket); @@ -67,6 +62,11 @@ */ int flib_socket_nbrecv(flib_tcpsocket *sock, void *data, int maxlen); +/** + * Blocking send all the data in the data buffer. Returns the actual ammount + * of data sent, or a negative value on error. If the value returned here + * is less than len, either the connection closed or an error occurred. + */ int flib_socket_send(flib_tcpsocket *sock, const void *data, int len); #endif /* SOCKET_H_ */ diff -r 5b0aeef8ba2a -r 5608ac657362 project_files/frontlib/test.c --- a/project_files/frontlib/test.c Thu Jun 21 21:32:12 2012 +0200 +++ b/project_files/frontlib/test.c Mon Jun 25 00:42:07 2012 +0200 @@ -2,6 +2,7 @@ #include "util/logging.h" #include "util/buffer.h" #include "util/util.h" +#include "util/list.h" #include "model/map.h" #include "model/weapon.h" #include "model/schemelist.h" @@ -78,11 +79,11 @@ void testMapPreview() { // Create a map description and check that there was no error - flib_map *map = flib_map_create_maze("Jungle", MAZE_SIZE_SMALL_TUNNELS); + flib_map *map = flib_map_create_maze("This is the seed value", "Jungle", MAZE_SIZE_SMALL_TUNNELS); assert(map); // Create a new connection to the engine and check that there was no error - flib_mapconn *mapConnection = flib_mapconn_create("This is the seed value", map); + flib_mapconn *mapConnection = flib_mapconn_create(map); assert(mapConnection); // We don't need the map description anymore @@ -112,10 +113,9 @@ flib_gamesetup setup; setup.gamescheme = flib_schemelist_find(schemelist, "Default"); - setup.map = flib_map_create_maze("Jungle", MAZE_SIZE_MEDIUM_TUNNELS); - setup.seed = "asparagus"; + setup.map = flib_map_create_maze("asparagus", "Jungle", MAZE_SIZE_MEDIUM_TUNNELS); setup.script = NULL; - setup.teamcount = 2; + setup.teamCount = 2; setup.teams = calloc(2, sizeof(flib_team*)); setup.teams[0] = calloc(1, sizeof(flib_team)); setup.teams[0]->color = 0xffff0000; @@ -250,12 +250,54 @@ } free(text); } + } else if(!memcmp("join ", command, strlen("join "))) { + const char *roomname = command+strlen("join "); + flib_netconn_send_joinRoom(*(flib_netconn**)context, roomname); + } else if(!memcmp("ready", command, strlen("ready"))) { + flib_netconn_send_toggleReady(*(flib_netconn**)context); } } } -void handleAskPass(void *context, const char *nick) { - flib_netconn_send_password((flib_netconn*)context, "Lorem"); +static flib_gamesetup gGamesetup = {0}; +static flib_weaponset *gWeaponset = NULL; + +void handleEnterRoom(void *context, bool isChief) { + flib_netconn_send_toggleReady(*(flib_netconn**)context); +} + +void handleMap(void *context, const flib_map *map, int changeType) { + flib_map_release(gGamesetup.map); + gGamesetup.map = flib_map_copy(map); +} + +void handleCfgScheme(void *context, flib_cfg *cfg) { + flib_cfg_release(gGamesetup.gamescheme); + gGamesetup.gamescheme = flib_cfg_retain(cfg); +} + +void handleWeaponset(void *context, flib_weaponset *weaponset) { + flib_weaponset_release(gWeaponset); + gWeaponset = flib_weaponset_retain(weaponset); +} + +void handleScript(void *context, const char *script) { + free(gGamesetup.script); + gGamesetup.script = flib_strdupnull(script); +} + +void handleTeamAdd(void *context, flib_team *team) { + flib_team *teamptr = flib_team_retain(team); + gGamesetup.teams = flib_list_insert(gGamesetup.teams, &gGamesetup.teamCount, sizeof(*gGamesetup.teams), &teamptr, 0); +} + +void handleTeamRemove(void *context, const char *team) { + for(int i=0; iname)) { + flib_team_release(gGamesetup.teams[i]); + gGamesetup.teams = flib_list_delete(gGamesetup.teams, &gGamesetup.teamCount, sizeof(*gGamesetup.teams), i); + } + } } int main(int argc, char *argv[]) { @@ -269,7 +311,7 @@ flib_cfg_meta *meta = flib_cfg_meta_from_ini("metasettings.ini"); assert(meta); - flib_netconn *conn = flib_netconn_create("Medo42", meta, "140.247.62.101", 46631); + flib_netconn *conn = flib_netconn_create("frontbot", meta, "140.247.62.101", 46631); assert(conn); flib_cfg_meta_release(meta); @@ -277,7 +319,14 @@ flib_netconn_onDisconnected(conn, handleNetDisconnect, &conn); flib_netconn_onLobbyJoin(conn, handleLobbyJoin, &conn); flib_netconn_onChat(conn, handleChat, &conn); - flib_netconn_onPasswordRequest(conn, handleAskPass, conn); + flib_netconn_onMapChanged(conn, handleMap, conn); + flib_netconn_onEnterRoom(conn, handleEnterRoom, conn); + flib_netconn_onCfgScheme(conn, handleCfgScheme, conn); + flib_netconn_onWeaponsetChanged(conn, handleWeaponset, conn); + flib_netconn_onScriptChanged(conn, handleScript, conn); + flib_netconn_onTeamAdd(conn, handleTeamAdd, conn); + flib_netconn_onTeamRemove(conn, handleTeamRemove, conn); + flib_netconn_onHogCountChanged(conn, handleHogCountChanged, conn); while(conn) { flib_netconn_tick(conn); diff -r 5b0aeef8ba2a -r 5608ac657362 project_files/frontlib/util/buffer.c --- a/project_files/frontlib/util/buffer.c Thu Jun 21 21:32:12 2012 +0200 +++ b/project_files/frontlib/util/buffer.c Mon Jun 25 00:42:07 2012 +0200 @@ -8,15 +8,15 @@ #define MIN_VECTOR_CAPACITY 16 -typedef struct _flib_vector { +struct _flib_vector { void *data; size_t size; size_t capacity; -} _flib_vector; +}; flib_vector *flib_vector_create() { flib_vector *result = NULL; - flib_vector *tmpVector = flib_calloc(1, sizeof(_flib_vector)); + flib_vector *tmpVector = flib_calloc(1, sizeof(flib_vector)); if(tmpVector) { tmpVector->data = flib_malloc(MIN_VECTOR_CAPACITY); if(tmpVector->data) { @@ -67,8 +67,7 @@ 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... + // But at least by as much as we need of course size_t extraCapacity = (vec->capacity)/2; size_t minExtraCapacity = newSize - vec->capacity; if(extraCapacity < minExtraCapacity) { @@ -113,6 +112,27 @@ return len; } +int flib_vector_appendf(flib_vector *vec, const char *fmt, ...) { + int result = -1; + if(!vec || !fmt) { + flib_log_e("null parameter in flib_vector_appendf"); + } else { + va_list argp; + va_start(argp, fmt); + char *formatted = flib_vasprintf(fmt, argp); + va_end(argp); + + + if(formatted) { + size_t len = strlen(formatted); + if(flib_vector_append(vec, formatted, len) == len) { + result = 0; + } + } + } + return result; +} + flib_buffer flib_vector_as_buffer(flib_vector *vec) { if(!vec) { flib_log_e("null parameter in flib_vector_as_buffer"); diff -r 5b0aeef8ba2a -r 5608ac657362 project_files/frontlib/util/buffer.h --- a/project_files/frontlib/util/buffer.h Thu Jun 21 21:32:12 2012 +0200 +++ b/project_files/frontlib/util/buffer.h Mon Jun 25 00:42:07 2012 +0200 @@ -26,7 +26,6 @@ /** * Simple variable-capacity data structure that can be efficiently appended to. */ -struct _flib_vector; typedef struct _flib_vector flib_vector; /** @@ -55,6 +54,12 @@ int flib_vector_append(flib_vector *vec, const void *data, size_t len); /** + * Append data from a format string to the buffer (without trailing 0) + * Returns 0 on success. + */ +int flib_vector_appendf(flib_vector *vec, const char *template, ...); + +/** * Return a pointer to the current data buffer of the vector. This pointer can * become invalid if the vector size or capacity is changed. */ diff -r 5b0aeef8ba2a -r 5608ac657362 project_files/frontlib/util/inihelper.h --- a/project_files/frontlib/util/inihelper.h Thu Jun 21 21:32:12 2012 +0200 +++ b/project_files/frontlib/util/inihelper.h Mon Jun 25 00:42:07 2012 +0200 @@ -15,7 +15,6 @@ #define INI_ERROR_FORMAT -2 #define INI_ERROR_OTHER -100 -struct _flib_ini; typedef struct _flib_ini flib_ini; /** diff -r 5b0aeef8ba2a -r 5608ac657362 project_files/frontlib/util/refcounter.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/project_files/frontlib/util/refcounter.c Mon Jun 25 00:42:07 2012 +0200 @@ -0,0 +1,37 @@ +#include "refcounter.h" + +#include "logging.h" + +void flib_retain(int *referenceCountPtr, const char *objName) { + if(!referenceCountPtr || !objName) { + flib_log_e("null parameter to flib_retain"); + } else { + if((*referenceCountPtr) >= 0) { + (*referenceCountPtr)++; + flib_log_d("retaining %s, now %i references", objName, (*referenceCountPtr)); + } + if((*referenceCountPtr) < 0) { + flib_log_e("Memory leak: Reference count overflow in %s object!", objName); + } + } +} + +/** + * Returns true if the struct should be freed. + */ +bool flib_release(int *referenceCountPtr, const char *objName) { + bool result = false; + if(!referenceCountPtr) { + flib_log_e("null parameter to flib_release"); + } else if((*referenceCountPtr) > 0) { + if(--(*referenceCountPtr) == 0) { + flib_log_d("releasing and destroying %s", objName); + result = true; + } else { + flib_log_d("releasing %s, now %i references", objName, (*referenceCountPtr)); + } + } else if((*referenceCountPtr) == 0) { + flib_log_e("Attempt to release a %s with zero references!", objName); + } + return result; +} diff -r 5b0aeef8ba2a -r 5608ac657362 project_files/frontlib/util/refcounter.h --- a/project_files/frontlib/util/refcounter.h Thu Jun 21 21:32:12 2012 +0200 +++ b/project_files/frontlib/util/refcounter.h Mon Jun 25 00:42:07 2012 +0200 @@ -11,41 +11,20 @@ #ifndef REFCOUNTER_H_ #define REFCOUNTER_H_ -#include "logging.h" #include -static inline void flib_retain(int *referenceCountPtr, const char *objName) { - if(!referenceCountPtr || !objName) { - flib_log_e("null parameter to flib_retain"); - } else { - if((*referenceCountPtr) >= 0) { - (*referenceCountPtr)++; - flib_log_d("retaining %s, now %i references", objName, (*referenceCountPtr)); - } - if((*referenceCountPtr) < 0) { - flib_log_e("Memory leak: Reference count overflow in %s object!", objName); - } - } -} - /** - * Returns true if the struct should be freed. + * Pass a pointer to the counter variable to be incremented, and the name of the + * object for logging purposes. On overflow an error will be logged and the + * counter will get "stuck" so neither retain nor release will modify it anymore. */ -static inline bool flib_release(int *referenceCountPtr, const char *objName) { - bool result = false; - if(!referenceCountPtr) { - flib_log_e("null parameter to flib_release"); - } else if((*referenceCountPtr) > 0) { - if(--(*referenceCountPtr) == 0) { - flib_log_d("releasing and destroying %s", objName); - result = true; - } else { - flib_log_d("releasing %s, now %i references", objName, (*referenceCountPtr)); - } - } else if((*referenceCountPtr) == 0) { - flib_log_e("Attempt to release a %s with zero references!", objName); - } - return result; -} +void flib_retain(int *referenceCountPtr, const char *objName); + +/** + * Pass a pointer to the counter variable to be decremented and the name + * of the object for logging purposes. + * Returns true if the object should be freed. + */ +bool flib_release(int *referenceCountPtr, const char *objName); #endif /* REFCOUNTER_H_ */