Merge remote-tracking branch 'origin/development'
authorpk910 <philipp@zoelle1.de>
Mon, 12 Nov 2012 15:45:37 +0000 (16:45 +0100)
committerpk910 <philipp@zoelle1.de>
Mon, 12 Nov 2012 15:45:37 +0000 (16:45 +0100)
23 files changed:
Makefile.am
configure.ac
src/IOEngine.h
src/IRCParser.c
src/main.c
src/memoryDebug.c
src/modules.c
src/modules/NeonFun.mod/bot_NeonFun.c
src/modules/NeonFun.mod/bot_NeonFun.h
src/modules/NeonFun.mod/cmd_neonfun.c
src/modules/NeonFun.mod/cmd_neonfun.h
src/modules/NeonFun.mod/cmd_neonfun_bjenough.c [new file with mode: 0644]
src/modules/NeonFun.mod/cmd_neonfun_bjtake.c [new file with mode: 0644]
src/modules/NeonFun.mod/cmd_neonfun_blackjack.c [new file with mode: 0644]
src/modules/NeonFun.mod/cmd_neonfun_uno.c
src/modules/NeonFun.mod/game_blackjack.c [new file with mode: 0644]
src/modules/NeonFun.mod/game_blackjack.h [new file with mode: 0644]
src/modules/NeonFun.mod/game_uno.c
src/modules/funcmd.mod/cmd_funcmds.c
src/modules/module.h
src/mutexDebug.c [new file with mode: 0644]
src/mutexDebug.h [new file with mode: 0644]
src/overall.h

index e0105d6d5997131236477125d25ac3194c05d0de..b03b830b321dfa2fdb866ef9b5986d4ae4f8ebde 100644 (file)
@@ -168,6 +168,10 @@ libNeonFun_la_SOURCES = src/modules/NeonFun.mod/bot_NeonFun.c \
       src/modules/NeonFun.mod/cmd_neonfun_4stone.c \
       src/modules/NeonFun.mod/cmd_neonfun_4view.c \
       src/modules/NeonFun.mod/game_4wins.c \
+      src/modules/NeonFun.mod/cmd_neonfun_blackjack.c \
+      src/modules/NeonFun.mod/cmd_neonfun_bjtake.c \
+      src/modules/NeonFun.mod/cmd_neonfun_bjenough.c \
+      src/modules/NeonFun.mod/game_blackjack.c \
       src/modules/NeonFun.mod/module.c
 libNeonFun_la_LDFLAGS = -module -rpath /nowhere -avoid-version -no-undefined
 libNeonFun_la_LIBADD = $(MYSQL_LIBS)
@@ -213,7 +217,8 @@ neonserv_SOURCES = src/version.c \
       src/ModuleFunctions.c \
       src/statistics.c \
       src/log.c \
-      src/memoryDebug.c
+      src/memoryDebug.c \
+      src/mutexDebug.c
 
 neonserv_LDADD = $(MYSQL_LIBS) $(SYSTEM_LIBS)
 
index 56d1e7f1ec3209fdf73519ff8ec9511c4db809a3..0a6c9d97fed62c693d182de37660b2d8bed28938 100644 (file)
@@ -68,6 +68,13 @@ AC_ARG_ENABLE([memory-debug],
   ],
   [])
 
+AC_ARG_ENABLE([mutex-debug],
+  [AS_HELP_STRING([--enable-mutex-debug], [run mutex debugger])],
+  [
+    AC_DEFINE([ENABLE_MUTEX_DEBUG], 1, [Define if you enable mutexDebug.c])
+  ],
+  [])
+
 AC_ARG_ENABLE([debug],
   [AS_HELP_STRING([--enable-debug], [debug mode (compile using -O0 -Wall -Wshadow -Werror)])],
   [CFLAGS='-g -O0 -Wall -Wshadow -Werror'],
index 5f68240617f1304da64ddbf9a1f6a9e1f84be53e..411173a82c443283ec77f3f9ac460777be73fc3b 100644 (file)
     pthread_mutexattr_settype(&mutex_attr, PTHREAD_MUTEX_RECURSIVE_VAL);\
     pthread_mutex_init(&var, &mutex_attr); \
 }
+#ifdef ENABLE_MUTEX_DEBUG
+#include "mutexDebug.h"
+#define IOSYNCHRONIZE(var) xmutex(1, &var, __FILE__, __LINE__); pthread_mutex_lock(&var)
+#define IODESYNCHRONIZE(var) xmutex(0, &var, __FILE__, __LINE__); pthread_mutex_unlock(&var)
+#else
 #define IOSYNCHRONIZE(var) pthread_mutex_lock(&var)
 #define IODESYNCHRONIZE(var) pthread_mutex_unlock(&var)
+#endif
 #else
 #define IOTHREAD_MUTEX_INIT(var)
 #define IOSYNCHRONIZE(var)
index 143148b5e12ea71145b6529390ac1be167de6823..89047f8402648cae2289a74003a3ad998da9caba 100644 (file)
@@ -481,7 +481,8 @@ static IRC_CMD(raw_kick) {
     chanuser->visCount--;
     if(chanuser->visCount == 0) {
         delChanUser(chanuser, 0); //not free, yet!
-        event_kick(user, chanuser, argv[2]);
+        if(user)
+            event_kick(user, chanuser, argv[2]);
         freeChanUser(chanuser);
     }
     
index c88c02cbfcefc866a8b82526d2e3a65809cc1414..fb16300da97ac5697e13adfcc59b85811a8468d3 100644 (file)
@@ -305,6 +305,11 @@ int main(int argc, char *argv[]) {
     initMemoryDebug();
     #endif
     
+    //initialize mutex debugger BEFORE using any mutexes
+    #ifdef ENABLE_MUTEX_DEBUG
+    initMutexDebug();
+    #endif
+    
     //deny root startup
     #ifndef WIN32
     if(geteuid() == 0 || getuid() == 0) {
index c42559233023cc0c7e013c29f2974423082bc9af..d4e828f1aeb127ce84a8c00c41059e49a12518d2 100644 (file)
@@ -81,9 +81,10 @@ void xfree(void *mem_ref) {
     free(mem_ref);
 }
 
-
 static void add_mem_info(void *mem_ref, unsigned int size, const char *file, unsigned int line) {
-    SYNCHRONIZE(synchronized);
+    #ifdef HAVE_THREADS
+    pthread_mutex_lock(&synchronized);
+    #endif
     struct MemoryLeak *mem_leak_info = malloc(sizeof(*mem_leak_info));
     own_allocated_memleaks++;
     mem_leak_info->mem_info.address = mem_ref;
@@ -92,11 +93,15 @@ static void add_mem_info(void *mem_ref, unsigned int size, const char *file, uns
     mem_leak_info->mem_info.line = line;
     mem_leak_info->next = ptr_start;
     ptr_start = mem_leak_info;
-    DESYNCHRONIZE(synchronized);
+    #ifdef HAVE_THREADS
+    pthread_mutex_unlock(&synchronized);
+    #endif
 }
 
 static void remove_mem_info(void *mem_ref) {
-    SYNCHRONIZE(synchronized);
+    #ifdef HAVE_THREADS
+    pthread_mutex_lock(&synchronized);
+    #endif
     struct MemoryLeak *leak_info, *next, *prev = NULL;
     for(leak_info = ptr_start; leak_info; leak_info = next) {
         next = leak_info->next;
@@ -111,7 +116,9 @@ static void remove_mem_info(void *mem_ref) {
         } else 
             prev = leak_info;
     }
-    DESYNCHRONIZE(synchronized);
+    #ifdef HAVE_THREADS
+    pthread_mutex_unlock(&synchronized);
+    #endif
 }
 
 void initMemoryDebug() {
@@ -119,7 +126,9 @@ void initMemoryDebug() {
 }
 
 struct memoryInfoFiles *getMemoryInfoFiles() {
-    SYNCHRONIZE(synchronized);
+    #ifdef HAVE_THREADS
+    pthread_mutex_lock(&synchronized);
+    #endif
     struct MemoryLeak *leak_info;
     struct memoryInfoFiles *list = NULL, *element;
     for(leak_info = ptr_start; leak_info != NULL; leak_info = leak_info->next) {
@@ -145,7 +154,9 @@ struct memoryInfoFiles *getMemoryInfoFiles() {
     element->allocated = own_allocated_memleaks * sizeof(struct MemoryLeak);
     element->next = list;
     list = element;
-    DESYNCHRONIZE(synchronized);
+    #ifdef HAVE_THREADS
+    pthread_mutex_unlock(&synchronized);
+    #endif
     return list;
 }
 
@@ -159,7 +170,9 @@ void freeMemoryInfoFiles(struct memoryInfoFiles *files) {
 }
 
 struct memoryInfoLines *getMemoryInfoLines(const char *filename) {
-    SYNCHRONIZE(synchronized);
+    #ifdef HAVE_THREADS
+    pthread_mutex_lock(&synchronized);
+    #endif
     struct MemoryLeak *leak_info;
     struct memoryInfoLines *list = NULL, *element;
     for(leak_info = ptr_start; leak_info != NULL; leak_info = leak_info->next) {
@@ -187,7 +200,9 @@ struct memoryInfoLines *getMemoryInfoLines(const char *filename) {
         element->next = list;
         list = element;
     }
-    DESYNCHRONIZE(synchronized);
+    #ifdef HAVE_THREADS
+    pthread_mutex_unlock(&synchronized);
+    #endif
     return list;
 }
 
index 325621e9da06820c4e47a371608cafa262b15bf4..f48c41a0b2b0eda7a55987eda7522398057f807f 100644 (file)
@@ -21,7 +21,9 @@
 
 /* 000-001 */ #include "main.h"
 /* 002-004 */ #include "tools.h"
-/* 005-006 */ /* deprecated */
+#ifdef ENABLE_MUTEX_DEBUG
+/* 005-006 */ #include "mutexDebug.h"
+#endif
 /* 007-009 */ /* main.h */
 /* 010     */ #include "log.h"
 /* 011     */ /* main.h */
@@ -41,7 +43,9 @@
 /* 099-102 */ #include "memoryDebug.h"
 #endif
 /* 103-106 */ #include "memoryInfo.h"
-/* 107-125 */ #include "modcmd.h"
+/* 107-122 */ #include "modcmd.h"
+/* 123     */ /* deprecated */
+/* 124-125 */ /* modcmd.h */
 /* 126-136 */ #include "ModeNode.h"
 /* 137-142 */ #include "mysqlConn.h"
 /* 143-149 */ #include "timeq.h"
@@ -64,8 +68,13 @@ void *global_functions[] = {
 /* 002 */ (Function) getCurrentSecondsOfDay,
 /* 003 */ (Function) stricmp,
 /* 004 */ (Function) stricmplen,
-/* 005 */ (Function) NULL, /* deprecated */
-/* 006 */ (Function) NULL, /* deprecated */
+#ifdef ENABLE_MUTEX_DEBUG
+/* 005 */ (Function) xmutex,
+/* 006 */ (Function) mutex_debug,
+#else
+/* 005 */ (Function) NULL,
+/* 006 */ (Function) NULL,
+#endif
 /* 007 */ (Function) restart_bot,
 /* 008 */ (Function) stop_bot,
 /* 009 */ (Function) reload_config,
index ab78474da05b67357dab33a89bf32677fcd1936e..44df0c31d078c17ad3d871bfe81577049b7e17e4 100644 (file)
@@ -39,6 +39,7 @@
 #include "../../bots.h"
 #include "game_uno.h"
 #include "game_4wins.h"
+#include "game_blackjack.h"
 
 #define BOTID NEONFUN_BOTID
 #define BOTALIAS "NeonFun"
@@ -132,11 +133,13 @@ static void start_bots(int type) {
 static void neonfun_parted(struct ChanUser *chanuser, int quit, char *reason) {
     uno_event_part(chanuser);
     fourwins_event_part(chanuser);
+    bj_event_part(chanuser);
 }
 
 static int neonfun_freechan(struct ChanNode *chan) {
     uno_event_freechan(chan);
     fourwins_event_freechan(chan);
+    bj_event_freechan(chan);
     return 0;
 }
 
@@ -301,5 +304,26 @@ void fourwins_reply(struct fourwins_game *game, const char *text, ...) {
     }
 }
 
+void bj_reply(struct bj_game *game, struct UserNode *user, const char *text, ...) {
+    struct ClientSocket *client = game->textbot;
+    const char *reply_format = get_language_string(user, text);
+    if(reply_format == NULL)
+        reply_format = text;
+    char formatBuf[MAXLEN];
+    sprintf(formatBuf, "PRIVMSG %s :[BJ] %s", game->channel->name, reply_format);
+    va_list arg_list;
+    char sendBuf[MAXLEN];
+    int pos;
+    if (!(client->flags & SOCKET_FLAG_CONNECTED)) return;
+    sendBuf[0] = '\0';
+    va_start(arg_list, text);
+    pos = vsnprintf(sendBuf, MAXLEN - 2, formatBuf, arg_list);
+    va_end(arg_list);
+    if (pos < 0 || pos > (MAXLEN - 2)) pos = MAXLEN - 2;
+    sendBuf[pos] = '\n';
+    sendBuf[pos+1] = '\0';
+    write_socket(client, sendBuf, pos+1);
+}
+
 #undef BOTID
 #undef BOTALIAS
index 291c456f9896e66aefe04ce67c9a976d0630dedf..a2a9ea49adaca9c83d6de775e2bf8c505e472061 100644 (file)
@@ -24,6 +24,7 @@ void free_NeonFun(int type);
 
 struct uno_game;
 struct fourwins_game;
+struct bj_game;
 struct ChanNode;
 struct UserNode;
 
@@ -31,5 +32,6 @@ char* getSetting(struct UserNode *user, struct ChanNode *chan, const char *setti
 void setSetting(struct UserNode *user, struct ChanNode *chan, const char *setting, const char *value);
 void uno_reply(struct uno_game *game, struct UserNode *user, const char *text, ...);
 void fourwins_reply(struct fourwins_game *game, const char *text, ...);
+void bj_reply(struct bj_game *game, struct UserNode *user, const char *text, ...);
 
 #endif
\ No newline at end of file
index cb4e784e823825d74b1aed6ac520c95bf1f783d0..9bf3b4b728747babd3a009ba835c4c5fdbbd5d22 100644 (file)
@@ -61,7 +61,7 @@ static const struct default_language_entry msgtab[] = {
     {"NF_4WINS_USER_WON", "$b%s$b won the game!"}, /* {ARGS: "TestUser"} */
     {"NF_4WINS_ENTER_OPPONENT", "Please enter the nick of the opponent you want to play against as a parameter of 4wins (eg. $b4wins TestUser$b)"},
     {"NF_4WINS_OPPONENT_NOT_IN_CHAN", "$b%s$b needs to be in this channel to start a game against him/her."}, /* {ARGS: "TestUser"} */
-    {"NF_4WINS_DRAW", "Noone has won this game."},
+    {"NF_4WINS_DRAW", "Nobody has won this game."},
     {"NF_4WINS_REQUEST", "$b%s$b wants to play a game against you. use $b4wins$b to start the game."}, /* {ARGS: "TestUser"} */
     {"NF_4WINS_REQUESTED", "waiting for $b%s$b accepting the game..."}, /* {ARGS: "TestUser"} */
     {"NF_4WINS_START", "%s accepted the challenge! game starts:"}, /* {ARGS: "TestUser"} */
@@ -72,6 +72,26 @@ static const struct default_language_entry msgtab[] = {
     {"NF_4WINS_GAME_CLOSED", "Game aborted."},
     {"NF_4WINS_TIMEOUT", "Game aborted (timeout)."},
     {"NF_4WINS_SELF", "You may not play against yourself."},
+    
+    {"NF_BJ_ALREADY_RUNNING", "There is already a BlackJack game running in %s."}, /* {ARGS: "#channel"} */
+    {"NF_BJ_ALREADY_JOINED", "You have already joined this BlackJack game."},
+    {"NF_BJ_USER_JOINED", "$b%s$b has joined the game."}, /* {ARGS: "TestUser"} */
+    {"NF_BJ_CREATED", "$b%s$b started a BlackJack game. Type $b+bj$b to join the game."}, /* {ARGS: "TestUser"} */
+    {"NF_BJ_ERROR", "BlackJack program error. Please contact a bot administrator. (ErrCode: %d)"}, /* {ARGS: 0} */
+    {"NF_BJ_NOT_YOUR_TURN", "Wait your turn!"},
+    {"NF_BJ_START", "GAME START"},
+    {"NF_BJ_USER_HURRY_UP", "$b%s$b, it's your turn! You can take a card with $bbjtake$b or stop playing with $bbjenough$b"}, /* {ARGS: "TestUser"} */
+    {"NF_BJ_TAKE", "You took another card: %s (%d points)."}, /* {ARGS: "[&#9824;A]", 11} */
+    {"NF_BJ_YOUR_CARDS", "You have %d points! Your cards: %s"}, /* {ARGS: 18, "[&#9824;A] [&#9827;7] ..."} */
+    {"NF_BJ_LESS_PLAYERS", "There are not enough players to start the game."},
+    {"NF_BJ_USER_TIMEOUT", "It seems that %s doesn't want another card... (Timeout)"}, /* {ARGS: "TestUser"} */
+    {"NF_BJ_POINTS_EXCEEDED", "You have more than 21 points... please use $bbjenough$b to finish."},
+    {"NF_BJ_GAME_FINISHED", "Game Finished! Showdown:"},
+    {"NF_BJ_RANK", "Rank"},
+    {"NF_BJ_NAME", "Name"},
+    {"NF_BJ_POINTS", "Points"},
+    {"NF_BJ_CARDS", "Cards"},
+    
     {NULL, NULL}
 };
 
@@ -90,6 +110,10 @@ void register_commands() {
     USER_COMMAND("4stone",       neonfun_cmd_4stone,   1,      NULL,     CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN);
     USER_COMMAND("4view",        neonfun_cmd_4view,    0,      NULL,     CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN);
     
+    USER_COMMAND("blackjack",    neonfun_cmd_blackjack,0,      NULL,     CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN);
+    USER_COMMAND("bjtake",       neonfun_cmd_bjtake,   0,      NULL,     CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN);
+    USER_COMMAND("bjenough",     neonfun_cmd_bjenough, 0,      NULL,     CMDFLAG_REQUIRE_CHAN | CMDFLAG_REGISTERED_CHAN);
+    
     #undef USER_COMMAND
     
 }
index 4537bb11ffeecda3c9331b8d254e4f1cc835ad1e..fdccf0fce12b0fb8fa9782ebef79f20a7b9de6f8 100644 (file)
@@ -50,4 +50,8 @@ CMD_BIND(neonfun_cmd_4wins);
 CMD_BIND(neonfun_cmd_4stone);
 CMD_BIND(neonfun_cmd_4view);
 
+CMD_BIND(neonfun_cmd_blackjack);
+CMD_BIND(neonfun_cmd_bjtake);
+CMD_BIND(neonfun_cmd_bjenough);
+
 #endif
diff --git a/src/modules/NeonFun.mod/cmd_neonfun_bjenough.c b/src/modules/NeonFun.mod/cmd_neonfun_bjenough.c
new file mode 100644 (file)
index 0000000..da1ff9b
--- /dev/null
@@ -0,0 +1,41 @@
+/* cmd_neonfun_bjenough.c - NeonServ v5.6
+ * Copyright (C) 2011-2012  Philipp Kreil (pk910)
+ * 
+ * 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. 
+ */
+
+#include "cmd_neonfun.h"
+#include "game_blackjack.h"
+
+CMD_BIND(neonfun_cmd_bjenough) {
+    struct ChanUser *chanuser = getChanUser(user, chan);
+    if(!chanuser) return;
+    struct bj_game *game;
+    for(game = bj_active_games; game; game = game->next) {
+        if(chan == game->channel) {
+            if(game->state == BJ_STATE_WAITING)
+                return;
+            else 
+                break;
+        }
+    }
+    if(game) {
+        //check if it's the player's turn
+        if(game->active_player->chanuser != chanuser) {
+            reply(textclient, user, "NF_BJ_NOT_YOUR_TURN");
+            return;
+        }
+        bj_action_next_player(game, game->active_player);
+    }
+}
diff --git a/src/modules/NeonFun.mod/cmd_neonfun_bjtake.c b/src/modules/NeonFun.mod/cmd_neonfun_bjtake.c
new file mode 100644 (file)
index 0000000..91b3f09
--- /dev/null
@@ -0,0 +1,41 @@
+/* cmd_neonfun_bjtake.c - NeonServ v5.6
+ * Copyright (C) 2011-2012  Philipp Kreil (pk910)
+ * 
+ * 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. 
+ */
+
+#include "cmd_neonfun.h"
+#include "game_blackjack.h"
+
+CMD_BIND(neonfun_cmd_bjtake) {
+    struct ChanUser *chanuser = getChanUser(user, chan);
+    if(!chanuser) return;
+    struct bj_game *game;
+    for(game = bj_active_games; game; game = game->next) {
+        if(chan == game->channel) {
+            if(game->state == BJ_STATE_WAITING)
+                return;
+            else 
+                break;
+        }
+    }
+    if(game) {
+        //check if it's the player's turn
+        if(game->active_player->chanuser != chanuser) {
+            reply(textclient, user, "NF_BJ_NOT_YOUR_TURN");
+            return;
+        }
+        bj_action_take_card(game, game->active_player);
+    }
+}
diff --git a/src/modules/NeonFun.mod/cmd_neonfun_blackjack.c b/src/modules/NeonFun.mod/cmd_neonfun_blackjack.c
new file mode 100644 (file)
index 0000000..31fe45c
--- /dev/null
@@ -0,0 +1,69 @@
+/* cmd_neonfun_blackjack.c - NeonServ v5.6
+ * Copyright (C) 2011-2012  Philipp Kreil (pk910)
+ * 
+ * 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. 
+ */
+
+#include "cmd_neonfun.h"
+#include "game_blackjack.h"
+
+CMD_BIND(neonfun_cmd_blackjack) {
+    struct ChanUser *chanuser = getChanUser(user, chan);
+    if(!chanuser) return;
+    struct bj_game *game;
+    for(game = bj_active_games; game; game = game->next) {
+        if(chan == game->channel) 
+            break;
+    }
+    if(game) {
+        //check if player has already joined
+        struct bj_player *player, *last_player = NULL;
+        for(player = game->player; player; player = player->next) {
+            if(player->chanuser == chanuser) {
+                reply(textclient, user, "NF_BJ_ALREADY_JOINED");
+                return;
+            } else
+                last_player = player;
+        }
+        if(!last_player) return; //error (game without players?)
+        player = malloc(sizeof(*player));
+        player->chanuser = chanuser;
+        player->count = 0;
+        player->cards = NULL;
+        player->prev = last_player;
+        player->next = NULL;
+        last_player->next = player;
+        game->players++;
+        bj_reply(game, user, "NF_BJ_USER_JOINED", user->nick);
+    } else {
+        game = malloc(sizeof(*game));
+        game->channel = chan;
+        game->textbot = textclient;
+        game->state = BJ_STATE_WAITING;
+        game->deck = NULL;
+        struct bj_player *player = malloc(sizeof(*player));
+        player->chanuser = chanuser;
+        player->count = 0;
+        player->cards = NULL;
+        player->prev = NULL;
+        player->next = NULL;
+        game->player = player;
+        game->active_player = NULL;
+        game->players = 1;
+        game->timer = timeq_add(30, module_id, bj_game_wait_timeout, game);
+        game->next = bj_active_games;
+        bj_active_games = game;
+        bj_reply(game, user, "NF_BJ_CREATED", user->nick);
+    }
+}
index 09a52678e28422e20d20c24bdf7f3d070aa33ec3..5742fa0076f61a1ab8d87e28e533c7ff973a23fd 100644 (file)
@@ -66,13 +66,13 @@ CMD_BIND(neonfun_cmd_uno) {
         player->chanuser = chanuser;
         player->count = 0;
         player->cards = NULL;
+        player->timeout = 0;
         player->prev = NULL;
         player->next = NULL;
         game->player = player;
         game->winner = NULL;
         game->active_player = NULL;
         game->players = 1;
-        game->active_player = 0;
         game->timer = timeq_add(30, module_id, uno_game_wait_timeout, game);
         game->next = uno_active_games;
         uno_active_games = game;
diff --git a/src/modules/NeonFun.mod/game_blackjack.c b/src/modules/NeonFun.mod/game_blackjack.c
new file mode 100644 (file)
index 0000000..f2576dc
--- /dev/null
@@ -0,0 +1,421 @@
+/* game_blackjack.c - NeonServ v5.6
+ * Copyright (C) 2011-2012  Philipp Kreil (pk910)
+ * 
+ * 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. 
+ */
+#include "../module.h"
+#include "game_blackjack.h"
+#include "bot_NeonFun.h"
+#include "../../IRCParser.h"
+#include "../../bots.h"
+#include "../../UserNode.h"
+#include "../../ChanUser.h"
+#include "../../tools.h"
+#include "../botid.h"
+
+struct bj_game *bj_active_games = NULL;
+
+static int bj_ctype[] =         {BJ_CTYPE_HEARTS, BJ_CTYPE_SPADES, BJ_CTYPE_DIAMONDS, BJ_CTYPE_CLUBS};
+static int bj_irc_colors[] =    {4,               1,               4,                 1};
+static char *bj_color_chars[] = {"\xE2\x9D\xA4",  "\xE2\x99\xA0",  "\xE2\x99\xA6",    "\xE2\x99\xA3"};
+
+static const struct {
+    const char *name;
+    unsigned char type;
+    unsigned int points : 8;
+} bj_card_types[] = {
+    {"2",  BJ_CARD_NUMBER_2,  2},
+    {"3",  BJ_CARD_NUMBER_3,  3},
+    {"4",  BJ_CARD_NUMBER_4,  4},
+    {"5",  BJ_CARD_NUMBER_5,  5},
+    {"6",  BJ_CARD_NUMBER_6,  6},
+    {"7",  BJ_CARD_NUMBER_7,  7},
+    {"8",  BJ_CARD_NUMBER_8,  8},
+    {"9",  BJ_CARD_NUMBER_9,  9},
+    {"10", BJ_CARD_NUMBER_10, 10},
+    {"J",  BJ_CARD_JACK,      10},
+    {"Q",  BJ_CARD_QUEEN,     10},
+    {"K",  BJ_CARD_KING,      10},
+    {"A",  BJ_CARD_ACE,       11},
+    {NULL, 0}
+};
+
+struct bj_card_deck *bj_shuffle_deck() {
+    struct bj_card *card, *last_card = NULL;
+    int card_count = 0;
+    #define ADD_CARD(ctype,ccard) \
+    card = malloc(sizeof(*card)); \
+    card->type = ctype; \
+    card->card = ccard; \
+    card->prev = NULL; \
+    card->next = last_card; \
+    if(last_card) \
+        last_card->prev = card; \
+    last_card = card; \
+    card_count++;
+    int typecount = BJ_CTYPE_COUNT;
+    for(typecount--; typecount >= 0; typecount--) {
+        ADD_CARD(bj_ctype[typecount], BJ_CARD_NUMBER_2);
+        ADD_CARD(bj_ctype[typecount], BJ_CARD_NUMBER_3);
+        ADD_CARD(bj_ctype[typecount], BJ_CARD_NUMBER_4);
+        ADD_CARD(bj_ctype[typecount], BJ_CARD_NUMBER_5);
+        ADD_CARD(bj_ctype[typecount], BJ_CARD_NUMBER_6);
+        ADD_CARD(bj_ctype[typecount], BJ_CARD_NUMBER_7);
+        ADD_CARD(bj_ctype[typecount], BJ_CARD_NUMBER_8);
+        ADD_CARD(bj_ctype[typecount], BJ_CARD_NUMBER_9);
+        ADD_CARD(bj_ctype[typecount], BJ_CARD_NUMBER_10);
+        ADD_CARD(bj_ctype[typecount], BJ_CARD_JACK);
+        ADD_CARD(bj_ctype[typecount], BJ_CARD_QUEEN);
+        ADD_CARD(bj_ctype[typecount], BJ_CARD_KING);
+        ADD_CARD(bj_ctype[typecount], BJ_CARD_ACE);
+    }
+    #undef ADD_CARD
+    struct bj_card_deck *deck = malloc(sizeof(*deck));
+    deck->cards = last_card;
+    deck->count = card_count;
+    return deck;
+}
+
+struct bj_card *bj_get_card(struct bj_card_deck *deck) {
+    if(!deck->count) return NULL;
+    int card_id = (rand() % deck->count);
+    int i = 0;
+    struct bj_card *card;
+    for(card = deck->cards; card; card = card->next) {
+        if(i == card_id) {
+            if(card->prev)
+                card->prev->next = card->next;
+            else
+                deck->cards = card->next;
+            if(card->next)
+                card->next->prev = card->prev;
+            deck->count--;
+            card->next = NULL;
+            card->prev = NULL;
+            return card;
+        }
+        i++;
+    }
+    return NULL;
+}
+
+void bj_free_deck(struct bj_card_deck *deck) {
+    struct bj_card *card, *next_card;
+    if(deck->count) {
+        for(card = deck->cards; card; card = next_card) {
+            next_card = card->next;
+            free(card);
+        }
+    }
+    free(deck);
+}
+
+void bj_free_player(struct bj_player *player, struct bj_card_deck *deck) {
+    if(player->count) {
+        struct bj_card *card, *next_card;
+        for(card = player->cards; card; card = next_card) {
+            next_card = card->next;
+            if(deck) {
+                card->next = deck->cards;
+                deck->cards->prev = card;
+                deck->cards = card;
+                deck->count++;
+            } else
+                free(card);
+        }
+    }
+    if(player->prev)
+        player->prev->next = player->next;
+    if(player->next)
+        player->next->prev = player->prev;
+    free(player);
+}
+
+void bj_free_game(struct bj_game *game) {
+    struct bj_player *player, *next_player;
+    for(player = game->player; player; player = next_player) {
+        next_player = player->next;
+        bj_free_player(player, NULL);
+    }
+    if(game->deck)
+        bj_free_deck(game->deck);
+    if(game->timer)
+        timeq_del(game->timer);
+    struct bj_game *cgame, *pgame = NULL;
+    for(cgame = bj_active_games; cgame; cgame = cgame->next) {
+        if(cgame == game) {
+            if(pgame)
+                pgame->next = game->next;
+            else
+                bj_active_games = game->next;
+            break;
+        } else
+            pgame = cgame;
+    }
+    free(game);
+}
+
+struct bj_player *bj_get_next_player(struct bj_game *game) {
+    struct bj_player *player = game->active_player;
+    player = player->next;
+    return player;
+}
+
+int bj_get_player_card_points(struct bj_player *player) {
+    int i, points = 0, ace_count = 0;
+    struct bj_card *card;
+    for(card = player->cards; card; card = card->next) {
+        if(card->card == BJ_CARD_ACE) {
+            ace_count++; //add these points later ;)
+            continue;
+        }
+        i = 0;
+        while(bj_card_types[i].name) {
+            if(bj_card_types[i].type == card->card) {
+                points += bj_card_types[i].points;
+                break;
+            }
+            i++;
+        }
+    }
+    while((ace_count--) > 0) {
+        if(points <= 10)
+            points += 11;
+        else
+            points += 1;
+    }
+    return points;
+}
+
+static void bj_print_player_cards(struct bj_player *player, char *cards_buf) {
+    struct bj_card *card;
+    int cards_bufpos = 0;
+    cards_buf[0] = '\0';
+    for(card = player->cards; card; card = card->next) {
+        int cardcolor = 1;
+        char *cardchar = "";
+        int i;
+        for(i = 0; i < BJ_CTYPE_COUNT; i++) {
+            if(bj_ctype[i] == card->type) {
+                cardcolor = bj_irc_colors[i];
+                cardchar = bj_color_chars[i];
+                break;
+            }
+        }
+        i = 0;
+        while(bj_card_types[i].name) {
+            if(bj_card_types[i].type == card->card) {
+                cards_bufpos += sprintf(cards_buf + cards_bufpos, "%s[\003%d%s%s\003]", (cards_bufpos ? " " : ""), cardcolor, cardchar, bj_card_types[i].name);
+                break;
+            }
+            i++;
+        }
+    }
+}
+
+void bj_show_player_cards(struct bj_game *game, struct bj_player *player) {
+    char cards_buf[MAXLEN];
+    bj_print_player_cards(player, cards_buf);
+    int card_points = bj_get_player_card_points(player);
+    reply(game->textbot, player->chanuser->user, "NF_BJ_YOUR_CARDS", card_points, cards_buf);
+}
+
+TIMEQ_CALLBACK(bj_game_wait_timeout) {
+    struct bj_game *game = data;
+    game->timer = NULL;
+    if(game->players == 1) {
+        bj_reply(game, NULL, "NF_BJ_LESS_PLAYERS");
+        bj_free_game(game);
+        return;
+    }
+    game->deck = bj_shuffle_deck();
+    bj_reply(game, NULL, "NF_BJ_START");
+    game->state = BJ_STATE_RUNNING;
+    game->active_player = game->player; //active player
+    bj_reply(game, NULL, "NF_BJ_USER_HURRY_UP", game->active_player->chanuser->user->nick);
+    game->timer = timeq_add(30, module_id, bj_player_timeout, game);
+}
+
+TIMEQ_CALLBACK(bj_player_timeout) {
+    struct bj_game *game = data;
+    game->timer = NULL;
+    //player timeout (next player)
+    struct bj_player *player = game->active_player;
+    bj_reply(game, NULL, "NF_BJ_USER_TIMEOUT", player->chanuser->user->nick);
+    bj_action_next_player(game, player);
+}
+
+void bj_action_take_card(struct bj_game *game, struct bj_player *player) {
+    int points = bj_get_player_card_points(player);
+    if(points > 21) {
+        reply(game->textbot, player->chanuser->user, "NF_BJ_POINTS_EXCEEDED");
+        return;
+    }
+    if(game->timer) {
+        timeq_del(game->timer);
+        game->timer = NULL;
+    }
+    struct bj_card *card = bj_get_card(game->deck);
+    if(!card) {
+        bj_free_deck(game->deck);
+        game->deck = bj_shuffle_deck();
+        card = bj_get_card(game->deck);
+    }
+    struct bj_card *last_card;
+    for(last_card = player->cards; last_card; last_card = last_card->next) {
+        if(last_card->next == NULL)
+            break;
+    }
+    card->prev = last_card;
+    if(last_card)
+        last_card->next = card;
+    else
+        player->cards = card;
+    player->count++;
+    char cardbuf[16];
+    int cardcolor = 1;
+    char *cardchar = "";
+    int i, cardpoints = 0;
+    cardbuf[0] = '\0';
+    for(i = 0; i < BJ_CTYPE_COUNT; i++) {
+        if(bj_ctype[i] == card->type) {
+            cardcolor = bj_irc_colors[i];
+            cardchar = bj_color_chars[i];
+            break;
+        }
+    }
+    i = 0;
+    while(bj_card_types[i].name) {
+        if(bj_card_types[i].type == card->card) {
+            sprintf(cardbuf, "[\003%d%s%s\003]", cardcolor, cardchar, bj_card_types[i].name);
+            cardpoints = bj_card_types[i].points;
+            if(bj_card_types[i].type == BJ_CARD_ACE && points > 10)
+                cardpoints = 1;
+            break;
+        }
+        i++;
+    }
+    reply(game->textbot, player->chanuser->user, "NF_BJ_TAKE", cardbuf, cardpoints);
+    bj_show_player_cards(game, player);
+    points = bj_get_player_card_points(player);
+    if(points > 21)
+        reply(game->textbot, player->chanuser->user, "NF_BJ_POINTS_EXCEEDED");
+    game->timer = timeq_add(30, module_id, bj_player_timeout, game);
+}
+
+void bj_action_next_player(struct bj_game *game, struct bj_player *player) {
+    if(game->timer) {
+        timeq_del(game->timer);
+        game->timer = NULL;
+    }
+    if(player)
+        bj_show_player_cards(game, player);
+    game->active_player = bj_get_next_player(game);
+    if(game->active_player) {
+        bj_reply(game, NULL, "NF_BJ_USER_HURRY_UP", game->active_player->chanuser->user->nick);
+        game->timer = timeq_add(30, module_id, bj_player_timeout, game);
+    } else {
+        bj_game_end(game);
+        bj_free_game(game);
+    }
+}
+
+void bj_event_part(struct ChanUser *chanuser) {
+    struct bj_game *game;
+    for(game = bj_active_games; game; game = game->next) {
+        if(chanuser->chan == game->channel) {
+            struct bj_player *player;
+            for(player = game->player; player; player = player->next) {
+                if(player->chanuser == chanuser) {
+                    if(game->active_player == player) {
+                        if(bj_get_next_player(game) == NULL) {
+                            bj_game_end(game);
+                            bj_free_game(game);
+                            return;
+                        } else
+                            bj_action_next_player(game, NULL);
+                    }
+                    game->players--;
+                    bj_free_player(player, game->deck);
+                    return;
+                }
+            }
+        }
+    }
+}
+
+void bj_event_freechan(struct ChanNode *chan) {
+    struct bj_game *game;
+    for(game = bj_active_games; game; game = game->next) {
+        if(game->channel == chan) {
+            bj_free_game(game);
+            return;
+        }
+    }
+}
+
+static int bj_highscore_sort(const void *a, const void *b) {
+    const struct bj_player *player_a = *((struct bj_player * const *) a);
+    const struct bj_player *player_b = *((struct bj_player * const *) b); 
+    int points_a, points_b;
+    if(player_a->count > 21)
+        points_a = player_a->count * -1;
+    else
+        points_a = player_a->count;
+    if(player_b->count > 21)
+        points_b = player_b->count * -1;
+    else
+        points_b = player_b->count;
+    return points_b - points_a;
+}
+
+void bj_game_end(struct bj_game *game) {
+    bj_reply(game, NULL, "NF_BJ_GAME_FINISHED");
+    struct Table *table;
+    table = table_init(4, game->players+1, 0);
+    char *content[4];
+    content[0] = get_language_string(NULL, "NF_BJ_RANK");
+    content[1] = get_language_string(NULL, "NF_BJ_NAME");
+    content[2] = get_language_string(NULL, "NF_BJ_POINTS");
+    content[3] = get_language_string(NULL, "NF_BJ_CARDS");
+    table_add(table, content);
+    //sort users
+    struct bj_player *players[game->players];
+    struct bj_player *player;
+    int i = 0;
+    for(player = game->player; player; player = player->next) {
+        player->count = bj_get_player_card_points(player);
+        players[i++] = player;
+    }
+    qsort(players, game->players, sizeof(struct bj_player *), bj_highscore_sort);
+    char rankbuf[12];
+    char pointbuf[12];
+    char cardsbuf[MAXLEN];
+    for(i = 0; i < game->players; i++) {
+        player = players[i];
+        sprintf(rankbuf, "#%d", i+1);
+        content[0] = rankbuf;
+        content[1] = player->chanuser->user->nick;
+        sprintf(pointbuf, "%d", player->count);
+        content[2] = pointbuf;
+        bj_print_player_cards(player, cardsbuf);
+        content[3] = cardsbuf;
+        table_add(table, content);
+    }
+    char **table_lines = table_end(table);
+    for(i = 0; i < table->entrys; i++) {
+        bj_reply(game, NULL, table_lines[i]);
+    }
+    table_free(table);
+}
diff --git a/src/modules/NeonFun.mod/game_blackjack.h b/src/modules/NeonFun.mod/game_blackjack.h
new file mode 100644 (file)
index 0000000..0e643ae
--- /dev/null
@@ -0,0 +1,102 @@
+/* game_blackjack.h - NeonServ v5.6
+ * Copyright (C) 2011-2012  Philipp Kreil (pk910)
+ * 
+ * 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. 
+ */
+#ifndef _game_blackjack_h
+#define _game_blackjack_h
+#include "../../timeq.h"
+
+#define BJ_STATE_WAITING 1
+#define BJ_STATE_RUNNING 2
+
+#define BJ_CTYPE_HEARTS   0
+#define BJ_CTYPE_SPADES   1
+#define BJ_CTYPE_DIAMONDS 2
+#define BJ_CTYPE_CLUBS    3
+
+#define BJ_CTYPE_COUNT    4
+
+#define BJ_CARD_NUMBER_2  0
+#define BJ_CARD_NUMBER_3  1
+#define BJ_CARD_NUMBER_4  2
+#define BJ_CARD_NUMBER_5  3
+#define BJ_CARD_NUMBER_6  4
+#define BJ_CARD_NUMBER_7  5
+#define BJ_CARD_NUMBER_8  6
+#define BJ_CARD_NUMBER_9  7
+#define BJ_CARD_NUMBER_10 8
+#define BJ_CARD_JACK      9
+#define BJ_CARD_QUEEN     10
+#define BJ_CARD_KING      11
+#define BJ_CARD_ACE       12
+
+struct UserNode;
+struct ChanNode;
+struct ChanUser;
+struct ClientSocket;
+extern struct bj_game *bj_active_games;
+
+struct bj_card {
+    unsigned char type;
+    unsigned char card;
+    struct bj_card *prev, *next;
+};
+
+struct bj_card_deck {
+    struct bj_card *cards;
+    int count;
+};
+
+struct bj_player {
+    struct ChanUser *chanuser;
+    struct bj_card *cards;
+    int count;
+    struct bj_player *prev, *next;
+};
+
+struct bj_game {
+    struct ChanNode *channel;
+    struct ClientSocket *textbot;
+    int state : 3;
+    struct bj_card_deck *deck;
+    struct bj_player *player;
+    struct bj_player *active_player;
+    int players;
+    
+    struct timeq_entry *timer;
+    struct bj_game *next;
+};
+
+struct bj_card_deck *bj_shuffle_deck();
+struct bj_card *bj_get_card(struct bj_card_deck *deck);
+void bj_free_deck(struct bj_card_deck *deck);
+void bj_free_player(struct bj_player *player, struct bj_card_deck *deck);
+void bj_free_game(struct bj_game *game);
+struct bj_player *bj_get_next_player(struct bj_game *game);
+int bj_get_player_card_points(struct bj_player *player);
+void bj_show_player_cards(struct bj_game *game, struct bj_player *player);
+
+TIMEQ_CALLBACK(bj_game_wait_timeout);
+TIMEQ_CALLBACK(bj_player_timeout);
+
+void bj_action_take_card(struct bj_game *game, struct bj_player *player);
+void bj_action_next_player(struct bj_game *game, struct bj_player *player);
+
+void bj_event_part(struct ChanUser *chanuser);
+void bj_event_freechan(struct ChanNode *chan);
+
+void bj_game_end(struct bj_game *game);
+
+#endif
index 99827c989039b1aef7141843ad62c720f407eb97..2e67edaa2b871febaf38b52300f1cfc4c8cd137a 100644 (file)
@@ -329,7 +329,7 @@ TIMEQ_CALLBACK(uno_game_wait_timeout) {
     uno_show_top_card(game);
     game->active_player = game->player; //active player
     uno_reply(game, NULL, "NF_UNO_USER_HURRY_UP", game->active_player->chanuser->user->nick);
-    game->timer = timeq_add(30, module_id, uno_player_timeout, game);
+    game->timer = timeq_add(40, module_id, uno_player_timeout, game);
 }
 
 TIMEQ_CALLBACK(uno_player_timeout) {
@@ -338,22 +338,42 @@ TIMEQ_CALLBACK(uno_player_timeout) {
     //player timeout (take another card)
     struct uno_player *player = game->active_player, *next_player = uno_get_next_player(game);
     //add a card to the players deck
-    if(!game->deck->count)
-        game->deck = uno_shuffle_deck();
-    struct uno_card *card = uno_get_card(game->deck);
-    if(!card) {
-        uno_reply(game, NULL, "NF_UNO_ERROR", 2);
-        uno_free_game(game);
-        return;
+    int ccount;
+    if(game->take_cards_pending) {
+        //count cards to take
+        struct uno_card *card;
+        ccount = 0;
+        for(card = game->top_card; card; card = card->prev) {
+            if(card->card == UNO_CARD_ADD_2)
+                ccount += 2;
+            else if(card->card == UNO_CARD_ADD_4)
+                ccount += 4;
+        }
+    } else
+        ccount = 1;
+    int i;
+    for(i = 0; i < ccount; i++) {
+        if(!game->deck->count)
+            game->deck = uno_shuffle_deck();
+        struct uno_card *card = uno_get_card(game->deck);
+        if(!card) {
+            uno_reply(game, NULL, "NF_UNO_ERROR", 2);
+            uno_free_game(game);
+            return;
+        }
+        card->next = player->cards;
+        if(player->cards)
+            player->cards->prev = card;
+        player->cards = card;
+        player->count++;
     }
-    card->next = player->cards;
-    if(player->cards)
-        player->cards->prev = card;
-    player->cards = card;
-    player->count++;
     player->timeout = 1;
     game->active_player = next_player;
-    uno_reply(game, NULL, "NF_UNO_USER_TOOK_CARD", player->chanuser->user->nick);
+    if(game->take_cards_pending) {
+        game->take_cards_pending = 0;
+        uno_reply(game, NULL, "NF_UNO_USER_TOOK_CARDS", player->chanuser->user->nick, ccount);
+    } else
+        uno_reply(game, NULL, "NF_UNO_USER_TOOK_CARD", player->chanuser->user->nick);
     struct uno_player *cplayer;
     for(cplayer = game->player; cplayer; cplayer = cplayer->next) {
         if(!cplayer->timeout)
@@ -368,7 +388,7 @@ TIMEQ_CALLBACK(uno_player_timeout) {
     uno_show_top_card(game);
     uno_reply(game, NULL, "NF_UNO_USER_HURRY_UP", game->active_player->chanuser->user->nick);
     uno_show_player_cards(game, game->active_player);
-    game->timer = timeq_add(30, module_id, uno_player_timeout, game);
+    game->timer = timeq_add(40, module_id, uno_player_timeout, game);
 }
 
 void uno_action_take_card(struct uno_game *game, struct uno_player *player) {
@@ -415,7 +435,7 @@ void uno_action_take_card(struct uno_game *game, struct uno_player *player) {
     uno_show_top_card(game);
     uno_reply(game, NULL, "NF_UNO_USER_HURRY_UP", game->active_player->chanuser->user->nick);
     uno_show_player_cards(game, game->active_player);
-    game->timer = timeq_add(30, module_id, uno_player_timeout, game);
+    game->timer = timeq_add(40, module_id, uno_player_timeout, game);
 }
 
 
@@ -619,7 +639,7 @@ void uno_play_card(struct uno_game *game, struct uno_player *player, struct uno_
     }
     uno_reply(game, NULL, "NF_UNO_USER_HURRY_UP", game->active_player->chanuser->user->nick);
     uno_show_player_cards(game, game->active_player);
-    game->timer = timeq_add(30, module_id, uno_player_timeout, game);
+    game->timer = timeq_add(40, module_id, uno_player_timeout, game);
 }
 
 void uno_event_part(struct ChanUser *chanuser) {
index 46a068e313e28c7cdd2819ac410c8de081b9a6f0..eda55cca075e18fcaa8fd2d369301abbb7078382 100644 (file)
@@ -127,6 +127,7 @@ free(header);
 #define REPLYTYPE_NOTICE 0x04
 static void funcmd_reply(struct funcmd_header_info *header, const char *text, int type, ...) {
     if (!(header->client->flags & SOCKET_FLAG_CONNECTED)) return;
+    struct ClientSocket *client = header->client;
     const char *reply_format = get_language_string((header->null_language ? NULL : header->user), text);
     if(reply_format)
         text = reply_format;
@@ -141,6 +142,8 @@ static void funcmd_reply(struct funcmd_header_info *header, const char *text, in
             sprintf(formatBuf, "PRIVMSG %s :\001ACTION %s\001", header->chan->name, text);
         else
             sprintf(formatBuf, "PRIVMSG %s :%s", header->chan->name, text);
+        if(!isUserOnChan(client->user, header->chan) && isModeSet(header->chan->modes, 'n'))
+            client = getChannelBot(header->chan, 0);
     }
     va_list arg_list;
     char sendBuf[MAXLEN];
@@ -152,7 +155,7 @@ static void funcmd_reply(struct funcmd_header_info *header, const char *text, in
     if (pos < 0 || pos > (MAXLEN - 2)) pos = MAXLEN - 2;
     sendBuf[pos] = '\n';
     sendBuf[pos+1] = '\0';
-    write_socket(header->client, sendBuf, pos+1);
+    write_socket(client, sendBuf, pos+1);
 }
 
 static char* getSetting(struct UserNode *user, struct ChanNode *chan, const char *setting) {
index 1b9dac7a55d75aa244c732b4ac012c004e57a235..4c55c40ef650cbc802e17dad1b183ee6b2f1c3af 100644 (file)
@@ -31,8 +31,10 @@ extern int module_id;
 /* 002 */ #define getCurrentSecondsOfDay ((int (*)(void))global[2])
 /* 003 */ #define stricmp ((int (*)(const char *, const char *))global[3])
 /* 004 */ #define stricmplen ((int (*)(const char *, const char *, int))global[4])
-/* 005 */ /* deprecated */
-/* 006 */ /* deprecated */
+#ifdef ENABLE_MUTEX_DEBUG
+/* 005 */ #define xmutex ((void (*)(int, pthread_mutex_t *, const char *, unsigned int))global[005])
+/* 006 */ #define mutex_debug ((void (*)(pthread_mutex_t *))global[006])
+#endif
 /* 007 */ #define restart_bot ((void (*)(int))global[7])
 /* 008 */ #define stop_bot ((void (*)(void))global[8])
 /* 009 */ #define reload_config ((void (*)(void))global[9])
diff --git a/src/mutexDebug.c b/src/mutexDebug.c
new file mode 100644 (file)
index 0000000..1b1e784
--- /dev/null
@@ -0,0 +1,190 @@
+/* mutexDebug.c - NeonServ v5.6
+ * Copyright (C) 2011-2012  Philipp Kreil (pk910)
+ * 
+ * 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. 
+ */
+#include "main.h"
+#include "mutexDebug.h"
+
+#ifdef ENABLE_MUTEX_DEBUG
+
+struct MutexLockEvent {
+    int locked;
+    const char *file;
+    unsigned int line;
+    struct MutexLockEvent *next;
+};
+
+struct MutexLock {
+    int thread;
+    int count;
+    struct MutexLockEvent *first_event, *last_event;
+    struct MutexLock *prev, *next;
+};
+
+struct MutexNode {
+    pthread_mutex_t *mutex;
+    struct MutexLock *first_lock, *last_lock;
+    struct MutexNode *next;
+};
+
+static pthread_mutex_t synchronized;
+static struct MutexNode *mutex_nodes = NULL;
+
+static struct MutexNode *getMutexNode(pthread_mutex_t *mutex, int create);
+static void lockMutex(struct MutexNode *node, const char *file, unsigned int line);
+static void unlockMutex(struct MutexNode *node, const char *file, unsigned int line);
+static void mutex_replay(struct MutexNode *node);
+
+void xmutex(int lock, pthread_mutex_t *mutex, const char *file, unsigned int line) {
+    pthread_mutex_lock(&synchronized);
+    struct MutexNode *node = getMutexNode(mutex, 1);
+    if(lock)
+        lockMutex(node, file, line);
+    else
+        unlockMutex(node, file, line);
+    pthread_mutex_unlock(&synchronized);
+}
+
+void initMutexDebug() {
+    THREAD_MUTEX_INIT(synchronized);
+}
+
+static struct MutexNode *getMutexNode(pthread_mutex_t *mutex, int create) {
+    struct MutexNode *node;
+    for(node = mutex_nodes; node; node = node->next) {
+        if(node->mutex == mutex)
+            return node;
+    }
+    if(!create)
+        return NULL;
+    node = malloc(sizeof(*node));
+    node->first_lock = NULL;
+    node->last_lock = NULL;
+    node->mutex = mutex;
+    node->next = mutex_nodes;
+    mutex_nodes = node;
+    return node;
+}
+
+static void lockMutex(struct MutexNode *node, const char *file, unsigned int line) {
+    struct MutexLock *lock;
+    int thread = getCurrentThreadID();
+    for(lock = node->first_lock; lock; lock = lock->next) {
+        if(lock->thread == thread)
+            break;
+    }
+    if(!lock) {
+        lock = malloc(sizeof(*lock));
+        lock->thread = thread;
+        lock->count = 0;
+        lock->first_event = NULL;
+        lock->last_event = NULL;
+        lock->prev = node->last_lock;
+        lock->next = NULL;
+        node->last_lock = lock;
+        if(!node->first_lock)
+            node->first_lock = lock;
+    }
+    lock->count++;
+    //add event
+    struct MutexLockEvent *event = malloc(sizeof(*event));
+    event->locked = 1;
+    event->file = file;
+    event->line = line;
+    event->next = NULL;
+    if(lock->last_event) {
+        lock->last_event->next = event;
+        lock->last_event = event;
+    } else {
+        lock->first_event = event;
+        lock->last_event = event;
+    }
+}
+
+static void unlockMutex(struct MutexNode *node, const char *file, unsigned int line) {
+    struct MutexLock *lock;
+    int thread = getCurrentThreadID();
+    for(lock = node->first_lock; lock; lock = lock->next) {
+        if(lock->thread == thread)
+            break;
+    }
+    if(!lock)
+        return;
+    lock->count--;
+    if(lock->count <= 0) {
+        //remove lock
+        if(lock->prev)
+            lock->prev->next = lock->next;
+        else
+            node->first_lock = lock->next;
+        if(lock->next)
+            lock->next->prev = lock->prev;
+        else
+            node->last_lock = lock->prev;
+        //recursive free all events
+        struct MutexLockEvent *event, *next_event;
+        for(event = lock->first_event; event; event = next_event) {
+            next_event = event->next;
+            free(event);
+        }
+        free(lock);
+    } else {
+        //add event
+        struct MutexLockEvent *event = malloc(sizeof(*event));
+        event->locked = 0;
+        event->file = file;
+        event->line = line;
+        event->next = NULL;
+        if(lock->last_event) {
+            lock->last_event->next = event;
+            lock->last_event = event;
+        } else {
+            lock->first_event = event;
+            lock->last_event = event;
+        }
+    }
+}
+
+void mutex_debug(pthread_mutex_t *mutex) {
+    //replay mutex events to stdout
+    struct MutexNode *node;
+    if(mutex) {
+        node = getMutexNode(mutex, 0);
+        if(!node) {
+            printf("[MUTEX_DEBUG] unknown mutex!\n");
+            return;
+        }
+        mutex_replay(node);
+    } else {
+        for(node = mutex_nodes; node; node = node->next) {
+            mutex_replay(node);
+        }
+    }
+    printf("[MUTEX_DEBUG] end of mutex replay.\n");
+}
+
+static void mutex_replay(struct MutexNode *node) {
+    printf("[MUTEX_DEBUG] mutex replay:\n");
+    struct MutexLock *lock;
+    struct MutexLockEvent *event;
+    for(lock = node->first_lock; lock; lock = lock->next) {
+        printf("[MUTEX_DEBUG]  THREAD %d (%d locks):\n", lock->thread, lock->count);
+        for(event = lock->first_event; event; event = event->next) {
+            printf("[MUTEX_DEBUG]   %s in %s:%d\n", (event->locked ? "lock  " : "unlock"), event->file, event->line);
+        }
+    }
+}
+
+#endif
diff --git a/src/mutexDebug.h b/src/mutexDebug.h
new file mode 100644 (file)
index 0000000..2b2a995
--- /dev/null
@@ -0,0 +1,29 @@
+/* mutexDebug.h - NeonServ v5.6
+ * Copyright (C) 2011-2012  Philipp Kreil (pk910)
+ * 
+ * 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 3 of the License, 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, see <http://www.gnu.org/licenses/>. 
+ */
+#ifndef _mutexdebug_h
+#define _mutexdebug_h
+
+#ifndef DND_FUNCTIONS
+#ifdef ENABLE_MUTEX_DEBUG
+/* MODULAR ACCESSIBLE */ void xmutex(int lock, pthread_mutex_t *mutex, const char *file, unsigned int line);
+/* MODULAR ACCESSIBLE */ void mutex_debug(pthread_mutex_t *mutex);
+
+void initMutexDebug();
+
+#endif
+#endif
+#endif
index d500e4771a47cd13d18d69c628ae49d16009da2a..ff6f40873ab71ee3e87d8cee6c06c4f104074e44 100644 (file)
     pthread_mutexattr_settype(&mutex_attr, type);\
     pthread_mutex_init(&var, &mutex_attr); \
 }
+#ifdef ENABLE_MUTEX_DEBUG
+#include "mutexDebug.h"
+#define SYNCHRONIZE(var) xmutex(1, &var, __FILE__, __LINE__); pthread_mutex_lock(&var)
+#define DESYNCHRONIZE(var) xmutex(0, &var, __FILE__, __LINE__); pthread_mutex_unlock(&var)
+#else
 #define SYNCHRONIZE(var) pthread_mutex_lock(&var)
-#define SET_SYNCHRONIZE(var) pthread_mutex_trylock(&var)
 #define DESYNCHRONIZE(var) pthread_mutex_unlock(&var)
+#endif
 #else
 #define THREAD_MUTEX_INIT(var)
 #define SYNCHRONIZE(var)
 #define DESYNCHRONIZE(var)
+#ifdef ENABLE_MUTEX_DEBUG
+#undef ENABLE_MUTEX_DEBUG
+#endif
 #endif
 
 #if __GNUC__