#define _POSIX_C_SOURCE 200112L #define _XOPEN_SOURCE 700 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define NUM_PLAYERS 10 #define NUM_SHORT 6 #define NUM_LONG 2 enum game_stage { STAGE_LOBBY, STAGE_PLAYING, STAGE_DISCUSS, }; enum player_stage { PLAYER_STAGE_NAME, PLAYER_STAGE_LOBBY, PLAYER_STAGE_MAIN, PLAYER_STAGE_DISCUSS, }; enum player_task_short { TASK_CAFE_TRASH, TASK_CAFE_COFFEE, TASK_CAFE_WIRES, TASK_STORAGE_TRASH, TASK_ELECTRICAL_WIRES, TASK_ELECTRICAL_BREAKERS, TASK_ADMIN_WIRES, TASK_NAVIGATION_WIRES, TASK_WEAPONS_WIRES, TASK_SHIELDS_WIRES, TASK_O2_WIRES, TASK_O2_CLEAN, TASK_MEDBAY_WIRES, TASK_UPPER_CATALYZER, TASK_LOWER_CATALYZER, TASK_UPPER_COMPRESSION_COIL, TASK_LOWER_COMPRESSION_COIL, TASK_SHORT_COUNT }; const char short_task_descriptions[][45] = { "Empty the cafeteria trash", "Start the coffee maker in the cafeteria", "Fix cafeteria wireing", "Empty the storage trash shute", "Fix wiring in electrical", "Reset breakers in electrical", "Fix wiring in admin", "Fix wiring in navigation", "Fix wiring in weapons", "Fix wiring in shields", "Fix wiring in o2", "Clean oxygenator output in o2", "Fix wiring in medbay", "Check catalyzer in upper engine", "Check catalyzer in lower engine", "Replace compression coil in upper engine", "Replace compression coil in lower engine" }; enum player_task_long { TASK_STORAGE_COUNT, TASK_O2_LOG, TASK_REACTOR_LOG, TASK_ADMIN_PIN, TASK_LONG_COUNT }; const char long_task_descriptions[][45] = { "Take count of the boxes in storage", "Log o2 numbers", "Log reactor numbers", "Enter pin at admin", }; enum player_location { LOC_CAFETERIA, LOC_REACTOR, LOC_UPPER_ENGINE, LOC_LOWER_ENGINE, LOC_SECURITY, LOC_MEDBAY, LOC_ELECTRICAL, LOC_STORAGE, LOC_ADMIN, LOC_COMMUNICATIONS, LOC_O2, LOC_WEAPONS, LOC_SHIELDS, LOC_NAVIGATION, }; const char locations[][45] = { "cafeteria", "reactor", "upper", "lower", "security", "medbay", "electrical", "storage", "admin", "communications", "o2", "weapons", "shields", "navigation" }; struct player { int fd; struct sockaddr_in *addr; enum player_stage stage; char name[20]; int is_admin; int is_imposter; enum player_location location; int in_vent; int is_alive; int has_cooldown; int voted; int votes; enum player_task_short short_tasks[NUM_SHORT]; int short_tasks_done[NUM_SHORT]; enum player_task_long long_tasks[NUM_LONG]; int long_tasks_done[NUM_LONG]; }; struct gamestate { enum game_stage stage; int has_admin; int players; int is_reactor_meltdown; }; struct gamestate state; struct player *players; void broadcast(char* message, int notfd) { char buf[1024]; int pid; printf("*> %s\n", message); sprintf(buf, "%s\n", message); for (pid = 0; pid < NUM_PLAYERS; pid++) { if (players[pid].fd == -1) continue; if (players[pid].fd == notfd) continue; write(players[pid].fd, buf, strlen(buf)); } } int startswith(const char *str, const char *prefix) { return strncmp(prefix, str, strlen(prefix)) == 0; } void player_move(int pid, enum player_location location) { char buf[100]; printf("Moving player %d to %d\n", pid, location); players[pid].location = location; players[pid].has_cooldown = 0; // body detection for(int i=0; i NUM_PLAYERS-1 || players[vote].fd == -1) { sprintf(buf, "Invalid vote, no such player\n"); write(players[pid].fd, buf, strlen(buf)); return; } players[pid].voted = 1; players[vote].votes++; // Check if voting is complete for(int i=0;i max_votes){ max_votes = players[i].votes; tie = 0; winner = i; continue; } if(players[i].votes == max_votes) { tie = 1; } } if (tie) { broadcast("The voting ended in a tie", -1); back_to_playing(); return; } else { sprintf(buf, "The crew voted to eject [%s]\n", players[winner].name); broadcast(buf, -1); } // dramatic pause for(int i=0;i<5;i++) { sleep(1); broadcast(".", -1); } if (players[winner].is_imposter) { sprintf(buf, "It turns out [%s] was an imposter", players[winner].name); broadcast(buf, -1); broadcast("It crew wins!", -1); end_game(); return; } else { sprintf(buf, "Sadly [%s] was not an imposter", players[winner].name); broadcast(buf, -1); players[winner].is_alive = 0; // count alive crew for(int i=0; i"); write(fd, buf, strlen(buf)); return 0; } if(strlen(buf) > 10) { sprintf(buf, "Too long, pick another name\n >"); write(fd, buf, strlen(buf)); return 0; } for(int i=0;i"); write(fd, buf, strlen(buf)); return 0; } } strcpy(players[pid].name, buf); sprintf(buf, "[%s] has joined the lobby", players[pid].name); broadcast(buf, fd); players[pid].stage = PLAYER_STAGE_LOBBY; break; case PLAYER_STAGE_LOBBY: // Chat message in the lobby if (buf[0] == '/') { if (strncmp(buf, "/start", 6) == 0) { if(players[pid].is_admin) { start_game(); } else { sprintf(buf2, "You don't have permission to /start\n"); write(fd, buf2, strlen(buf2)); } } else if (strncmp(buf, "/shrug", 6) == 0) { sprintf(buf2, "[%s] ¯\\_(ツ)_/¯", players[pid].name); broadcast(buf2, fd); } else if (strncmp(buf, "/me ", 3) == 0) { sprintf(buf2, " * [%s] %s", players[pid].name, &buf[4]); broadcast(buf2, fd); } } else { for(int i=0;i 0) { continue; } // Bonus points for goto usage goto found_spot; } sprintf(buf, "There are no spots available, goodbye!\n"); write(fd, buf, strlen(buf)); close(fd); return -1; found_spot: printf("Assigned player to spot %d\n", i); players[i].fd = fd; players[i].addr = malloc(sizeof(addr)); if (!state.has_admin) { state.has_admin = 1; players[i].is_admin = 1; } players[i].stage = PLAYER_STAGE_NAME; sprintf(buf, "Welcome player %d!\n\nEnter your name:\n> ", i); write(fd, buf, strlen(buf)); return 0; } int main() { int listen_fd; int new_fd; socklen_t client_size; struct sockaddr_in listen_addr, client_addr; int port = 1234; int i; fd_set rfds, afds; players = (struct player*)malloc(sizeof(struct player) * NUM_PLAYERS); for (i = 0; i < NUM_PLAYERS; i++) { players[i].fd = -1; }; srand(time(NULL)); listen_fd = socket(AF_INET, SOCK_STREAM, 0); listen_addr.sin_family = AF_INET; listen_addr.sin_addr.s_addr = htonl(INADDR_ANY); listen_addr.sin_port = htons(port); if (bind(listen_fd, (struct sockaddr *)&listen_addr, sizeof(listen_addr)) < 0) { printf("bind failed\n"); return -1; } listen(listen_fd, 5); printf("Listening on :%d\n", port); state.stage = STAGE_LOBBY; FD_ZERO(&afds); FD_SET(listen_fd, &afds); while (1) { rfds = afds; if (select(FD_SETSIZE, &rfds, NULL, NULL, NULL) < 0) { printf("select oops\n"); exit(EXIT_FAILURE); } for (i = 0; i < FD_SETSIZE; ++i) { if (FD_ISSET (i, &rfds)) { if (i == listen_fd) { printf("welcome client!\n"); client_size = sizeof(client_addr); new_fd = accept(listen_fd, (struct sockaddr *)&client_addr, &client_size); if (new_fd < 0) { printf("new client oops\n"); exit(EXIT_FAILURE); } printf("New connection from %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); FD_SET(new_fd, &afds); if(welcome_player(new_fd, client_addr)<0){ FD_CLR(new_fd, &afds); } } else { if(handle_input(i) < 0) { close(i); FD_CLR(i, &afds); } } } } } return 0; }