From: pk910 Date: Mon, 12 Nov 2012 15:45:37 +0000 (+0100) Subject: Merge remote-tracking branch 'origin/development' X-Git-Url: http://git.pk910.de/?p=NeonServV5.git;a=commitdiff_plain;h=58b5a3c9d422799f1a5d612cc6ea8afb6fdecca5;hp=346d93c001239ddd9a73b573e4aadd8eeb872681 Merge remote-tracking branch 'origin/development' --- diff --git a/Makefile.am b/Makefile.am index e0105d6..b03b830 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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) diff --git a/configure.ac b/configure.ac index 56d1e7f..0a6c9d9 100644 --- a/configure.ac +++ b/configure.ac @@ -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'], diff --git a/src/IOEngine.h b/src/IOEngine.h index 5f68240..411173a 100644 --- a/src/IOEngine.h +++ b/src/IOEngine.h @@ -31,8 +31,14 @@ 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) diff --git a/src/IRCParser.c b/src/IRCParser.c index 143148b..89047f8 100644 --- a/src/IRCParser.c +++ b/src/IRCParser.c @@ -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); } diff --git a/src/main.c b/src/main.c index c88c02c..fb16300 100644 --- a/src/main.c +++ b/src/main.c @@ -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) { diff --git a/src/memoryDebug.c b/src/memoryDebug.c index c425592..d4e828f 100644 --- a/src/memoryDebug.c +++ b/src/memoryDebug.c @@ -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; } diff --git a/src/modules.c b/src/modules.c index 325621e..f48c41a 100644 --- a/src/modules.c +++ b/src/modules.c @@ -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, diff --git a/src/modules/NeonFun.mod/bot_NeonFun.c b/src/modules/NeonFun.mod/bot_NeonFun.c index ab78474..44df0c3 100644 --- a/src/modules/NeonFun.mod/bot_NeonFun.c +++ b/src/modules/NeonFun.mod/bot_NeonFun.c @@ -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 diff --git a/src/modules/NeonFun.mod/bot_NeonFun.h b/src/modules/NeonFun.mod/bot_NeonFun.h index 291c456..a2a9ea4 100644 --- a/src/modules/NeonFun.mod/bot_NeonFun.h +++ b/src/modules/NeonFun.mod/bot_NeonFun.h @@ -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 diff --git a/src/modules/NeonFun.mod/cmd_neonfun.c b/src/modules/NeonFun.mod/cmd_neonfun.c index cb4e784..9bf3b4b 100644 --- a/src/modules/NeonFun.mod/cmd_neonfun.c +++ b/src/modules/NeonFun.mod/cmd_neonfun.c @@ -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: "[♠A]", 11} */ + {"NF_BJ_YOUR_CARDS", "You have %d points! Your cards: %s"}, /* {ARGS: 18, "[♠A] [♣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 } diff --git a/src/modules/NeonFun.mod/cmd_neonfun.h b/src/modules/NeonFun.mod/cmd_neonfun.h index 4537bb1..fdccf0f 100644 --- a/src/modules/NeonFun.mod/cmd_neonfun.h +++ b/src/modules/NeonFun.mod/cmd_neonfun.h @@ -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 index 0000000..da1ff9b --- /dev/null +++ b/src/modules/NeonFun.mod/cmd_neonfun_bjenough.c @@ -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 . + */ + +#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 index 0000000..91b3f09 --- /dev/null +++ b/src/modules/NeonFun.mod/cmd_neonfun_bjtake.c @@ -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 . + */ + +#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 index 0000000..31fe45c --- /dev/null +++ b/src/modules/NeonFun.mod/cmd_neonfun_blackjack.c @@ -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 . + */ + +#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); + } +} diff --git a/src/modules/NeonFun.mod/cmd_neonfun_uno.c b/src/modules/NeonFun.mod/cmd_neonfun_uno.c index 09a5267..5742fa0 100644 --- a/src/modules/NeonFun.mod/cmd_neonfun_uno.c +++ b/src/modules/NeonFun.mod/cmd_neonfun_uno.c @@ -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 index 0000000..f2576dc --- /dev/null +++ b/src/modules/NeonFun.mod/game_blackjack.c @@ -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 . + */ +#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 index 0000000..0e643ae --- /dev/null +++ b/src/modules/NeonFun.mod/game_blackjack.h @@ -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 . + */ +#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 diff --git a/src/modules/NeonFun.mod/game_uno.c b/src/modules/NeonFun.mod/game_uno.c index 99827c9..2e67eda 100644 --- a/src/modules/NeonFun.mod/game_uno.c +++ b/src/modules/NeonFun.mod/game_uno.c @@ -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) { diff --git a/src/modules/funcmd.mod/cmd_funcmds.c b/src/modules/funcmd.mod/cmd_funcmds.c index 46a068e..eda55cc 100644 --- a/src/modules/funcmd.mod/cmd_funcmds.c +++ b/src/modules/funcmd.mod/cmd_funcmds.c @@ -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) { diff --git a/src/modules/module.h b/src/modules/module.h index 1b9dac7..4c55c40 100644 --- a/src/modules/module.h +++ b/src/modules/module.h @@ -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 index 0000000..1b1e784 --- /dev/null +++ b/src/mutexDebug.c @@ -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 . + */ +#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 index 0000000..2b2a995 --- /dev/null +++ b/src/mutexDebug.h @@ -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 . + */ +#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 diff --git a/src/overall.h b/src/overall.h index d500e47..ff6f408 100644 --- a/src/overall.h +++ b/src/overall.h @@ -77,13 +77,21 @@ 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__