#include #include #include #include #include #include #include #ifdef ENABLE_SSL #include #endif enum { CREWMATE, IMPOSTER, DIED, COUNT }; enum { LOBBY, GAME, DISCUSSION }; typedef struct { char *nick; int num; int type; int admin; int map; int votes; } CLIENT; CLIENT *players; size_t now_players; size_t size_players = 10; time_t timer; time_t voting_timer; int current_mode; int GetByNick(const char *nick, size_t *ret) { for (size_t i = 0; i < now_players; i++) if (!strcmp(nick, players[i].nick)) { *ret = i; return 0; } return 1; } void sort(size_t ptr){ for (size_t i = ptr; i < now_players; i++) if (i + 1 <= now_players) players[i] = players[i + 1]; } void clear(void) { for (size_t i = 0; i < size_players; i++) if (players[i].nick) free(players[i].nick); memset(players, 0, sizeof(CLIENT) * size_players); current_mode = LOBBY; now_players = 0; } void broadcast(IRCC_client *irc, const char *ign, const char *msg) { for (size_t i = 0; i < now_players; i++) if (strcmp(ign, players[i].nick)) IRCC_send(irc, players[i].nick, msg); } int test_player(IRCC_client *irc, const char *nick, size_t *res) { size_t ret = 0; if (GetByNick(nick, &ret)) { IRCC_send(irc, nick, "Firstly join!"); return 1; } *res = ret; return 0; } void player_quit(IRCC_client *irc, char *buf, size_t size, const char *nick) { size_t ret = 0; if (test_player(irc, nick, &ret)) return; snprintf(buf, size, "Player [%d] left", players[ret].num); broadcast(irc, nick, buf); if (players[ret].admin) { CLIENT *client; if (ret + 1 < now_players) client = &players[ret + 1]; else client = &players[ret - 1]; client->admin = 1; snprintf(buf, size, "New admin is [%d]", client->num); broadcast(irc, nick, buf); } if (players[ret].type == IMPOSTER) { snprintf(buf, size, "[%d] was imposter. Game over!", players[ret].num); broadcast(irc, nick, buf); clear(); return; } free(players[ret].nick); sort(ret); now_players--; } int gameloop(IRCC_client *irc) { int irc_ret = IRCC_recv(irc); if (irc_ret == IRCC_DISCONNECTED) return 1; irc_ret = IRCC_parse(irc); if (irc->irc_msg == NULL || irc->irc_nick == NULL || irc->irc_channel == NULL) return 0; /* Common buffer */ char buf[512]; char *nick = strdup(irc->irc_nick); if (nick == NULL) return 0; if (irc_ret == IRCC_PRIVMSG) { if (!strncmp(irc->irc_msg, "!join", 5)) { if (current_mode != LOBBY) { IRCC_send(irc, nick, "Game started"); goto END; } /* Check player */ if (now_players == size_players) { IRCC_send(irc, nick, "Sorry, max players"); goto END; } size_t ret = 0; if (!GetByNick(nick, &ret)) { IRCC_send(irc, nick, "You already joined"); goto END; } /* Register */ players[now_players].nick = strdup(nick); if (players[now_players].nick == NULL) goto END; /* In game nickname */ players[now_players].num = rand(); /* Give admin permission */ int admin_flag = 0; if (now_players == 0) admin_flag = 1; players[now_players].admin = admin_flag; /* Other */ players[now_players].map = 0; players[now_players].type = 0; /* Inform all */ snprintf(buf, sizeof(buf), "New player [%d]! Total: %zu/%zu", players[now_players].num, now_players + 1, size_players); broadcast(irc, nick, buf); snprintf(buf, sizeof(buf), "You joined with id [%d]", players[now_players].num); IRCC_send(irc, nick, buf); now_players++; } else if (!strncmp(irc->irc_msg, "!say", 4)) { if (current_mode == GAME) goto END; size_t ret = 0; if (test_player(irc, nick, &ret)) goto END; if (players[ret].type == DIED) { IRCC_send(irc, nick, "You died! You cant send messages"); goto END; } snprintf(buf, sizeof(buf), "[%d]:%s", players[ret].num, irc->irc_msg + 4); broadcast(irc, nick, buf); } else if (!strncmp(irc->irc_msg, "!exit", 5)) { IRCC_send(irc, nick, "Goodbye!"); player_quit(irc, buf, sizeof(buf), nick); } else if (!strncmp(irc->irc_msg, "!status", 7)) { size_t ret = 0; if (test_player(irc, nick, &ret)) goto END; snprintf(buf, sizeof(buf), "Status: Your id [%d] | Players [%zu/%zu]", players[ret].num, now_players, size_players); IRCC_send(irc, nick, buf); } else if (!strncmp(irc->irc_msg, "!start", 6)) { if (current_mode != LOBBY) { IRCC_send(irc, nick, "Game already started"); goto END; } size_t ret = 0; if (GetByNick(nick, &ret)) { IRCC_send(irc, nick, "You are not admin"); goto END; } current_mode = GAME; timer = time(NULL); for (size_t i = 0; i < now_players; i++) players[i].type = CREWMATE; size_t im = rand() % now_players; players[im].type = IMPOSTER; /* Inform all */ broadcast(irc, players[im].nick, "You crewmate"); IRCC_send(irc, players[im].nick, "You imposter"); } else if (!strncmp(irc->irc_msg, "!emergency", 10)) { size_t ret = 0; if (test_player(irc, nick, &ret)) goto END; current_mode = DISCUSSION; snprintf(buf, sizeof(buf), "Player [%d] pressed emergency button! You have 60 second to vote or skip", players[ret].num); voting_timer = time(NULL); } else if (!strncmp(irc->irc_msg, "!help", 5)) IRCC_send(irc, nick, "[!start] [!help] [!say MSG] [!join] [!exit] [!status] InGame: [!kill] [!go NUM] [!emergency] [!vote NUM] [!skip]"); } else if (irc_ret == IRCC_QUIT) player_quit(irc, buf, sizeof(buf), nick); END: free(nick); return 0; } int main(int argc, char **argv) { char *server = "127.0.0.1"; char *nick = "amongrass"; unsigned int port = 6667; int usessl = 0; int opt; while ((opt = getopt(argc, argv, "h:n:p:m:s")) != -1) { switch (opt) { case 'm': sscanf(optarg, "%zu", &size_players); if (size_players < 1 || size_players > 100) { fprintf(stderr, "amongrass: failed to set players count\n"); return 1; } break; case 'h': server = optarg; break; case 'p': port = atoi(optarg); break; case 'n': nick = optarg; break; case 's': usessl = 1; break; default: printf("./amongrass [snpm]\n\t-h IP Server ip Default: %s\n\t-n NICK Bot nickname Default: %s\n\t-p PORT Server port Default: %d\n\t-m N Max players in game Default: %zu\n\t-s Use SSL Default: false\n", server, nick, port, size_players); return 0; } } players = malloc(sizeof(CLIENT) * size_players); if (players == NULL) { fprintf(stderr, "amongrass: malloc failed: %s", strerror(errno)); return 1; } IRCC_client irc; if (IRCC_connect(&irc, server, port) == IRCC_ERROR) { fprintf(stderr, "amongrass: failed connect to %s: %s\n", server, strerror(errno)); goto EXIT_MAIN; } if (usessl) { #ifdef ENABLE_SSL if (IRCC_initssl(&irc) == IRCC_ERROR) { fprintf(stderr, "amongrass: irc_ssl: %s\n", ERR_error_string(ERR_get_error(), NULL)); goto EXIT_MAIN; } #endif } if (IRCC_register(&irc, nick) == IRCC_DISCONNECTED) { fprintf(stderr, "amongrass: irc: disconnected\n"); goto EXIT_MAIN; } timer = time(NULL); current_mode = LOBBY; srand(getpid()); fprintf(stderr, "amongrass: Successful connected\namongrass: Nickname: %s\n", nick); while (1) { if (time(NULL) - timer >= 60) { broadcast(&irc, "", "Time is over!"); clear(); } if (gameloop(&irc)) break; } IRCC_close(&irc); for (size_t i = 0; i < size_players; i++) if (players[i].nick) free(players[i].nick); EXIT_MAIN: free(players); return 1; }