2020-10-17 21:10:38 +00:00
# include <arpa/inet.h>
2020-10-17 14:19:22 +00:00
# include <assert.h>
2020-10-17 21:10:38 +00:00
# include <ctype.h>
# include <errno.h>
2020-10-17 14:19:22 +00:00
# include <limits.h>
2020-10-17 21:10:38 +00:00
# include <netdb.h>
# include <netinet/in.h>
2020-10-19 02:56:21 +00:00
# include <stdbool.h>
2020-10-19 15:44:14 +00:00
# include <stdint.h>
2020-10-17 21:10:38 +00:00
# include <stdio.h>
# include <string.h>
# include <stdlib.h>
2020-10-16 22:11:52 +00:00
# include <sys/types.h>
2020-10-17 21:10:38 +00:00
# include <sys/select.h>
2020-10-16 22:11:52 +00:00
# include <sys/socket.h>
# include <time.h>
2020-10-17 21:10:38 +00:00
# include <unistd.h>
2020-11-07 22:21:04 +00:00
# include <getopt.h>
2020-10-16 22:11:52 +00:00
2020-12-02 19:47:22 +00:00
# ifndef VERSION
# define VERSION "unknown"
# endif
2020-10-16 22:11:52 +00:00
# define NUM_PLAYERS 10
2020-10-16 23:55:27 +00:00
# define NUM_SHORT 6
# define NUM_LONG 2
2020-10-18 00:16:39 +00:00
# define NUM_CHATS 50
2020-10-18 03:45:50 +00:00
# define MIN_NAME 2
# define MAX_NAME 10
2020-10-16 22:11:52 +00:00
2020-10-22 17:52:23 +00:00
# ifndef MOVEMENT_NOTIFICATIONS
2020-10-18 21:03:27 +00:00
# define MOVEMENT_NOTIFICATIONS 1
2020-10-22 17:52:23 +00:00
# endif
2020-10-18 21:03:27 +00:00
2020-10-16 22:11:52 +00:00
enum game_stage {
STAGE_LOBBY ,
STAGE_PLAYING ,
STAGE_DISCUSS ,
} ;
enum player_stage {
PLAYER_STAGE_NAME ,
PLAYER_STAGE_LOBBY ,
PLAYER_STAGE_MAIN ,
PLAYER_STAGE_DISCUSS ,
2020-11-09 22:22:32 +00:00
PLAYER_STAGE_WAITING ,
2020-10-16 22:11:52 +00:00
} ;
2020-10-16 23:55:27 +00:00
enum player_task_short {
TASK_CAFE_TRASH ,
TASK_CAFE_COFFEE ,
TASK_CAFE_WIRES ,
TASK_STORAGE_TRASH ,
2020-10-19 13:43:10 +00:00
TASK_STORAGE_WIRES ,
TASK_STORAGE_CLEAN ,
2020-10-16 23:55:27 +00:00
TASK_ELECTRICAL_WIRES ,
TASK_ELECTRICAL_BREAKERS ,
TASK_ADMIN_WIRES ,
2020-10-19 13:43:10 +00:00
TASK_ADMIN_CLEAN ,
2020-10-16 23:55:27 +00:00
TASK_NAVIGATION_WIRES ,
2020-10-19 13:43:10 +00:00
TASK_NAVIGATION_COURSE ,
TASK_NAVIGATION_HEADING ,
2020-10-16 23:55:27 +00:00
TASK_WEAPONS_WIRES ,
2020-10-19 13:43:10 +00:00
TASK_WEAPONS_CALIBRATE ,
2020-10-16 23:55:27 +00:00
TASK_SHIELDS_WIRES ,
TASK_O2_WIRES ,
TASK_O2_CLEAN ,
2020-10-19 13:43:10 +00:00
TASK_OS_WATER ,
2020-10-16 23:55:27 +00:00
TASK_MEDBAY_WIRES ,
TASK_UPPER_CATALYZER ,
TASK_LOWER_CATALYZER ,
TASK_UPPER_COMPRESSION_COIL ,
TASK_LOWER_COMPRESSION_COIL ,
2020-10-18 03:45:50 +00:00
TASK_SHORT_COUNT ,
2020-10-16 23:55:27 +00:00
} ;
2020-10-17 09:44:48 +00:00
const char short_task_descriptions [ ] [ 45 ] = {
" Empty the cafeteria trash " ,
" Start the coffee maker in the cafeteria " ,
2020-10-18 15:37:35 +00:00
" Fix wiring in cafeteria " ,
2020-10-18 03:45:50 +00:00
" Empty the storage trash chute " ,
2020-10-19 13:43:10 +00:00
" Fix wiring in storage " ,
" Clean the floor in storage " ,
2020-10-17 09:44:48 +00:00
" Fix wiring in electrical " ,
" Reset breakers in electrical " ,
" Fix wiring in admin " ,
2020-10-19 13:43:10 +00:00
" Clean the floor in admin " ,
2020-10-17 09:44:48 +00:00
" Fix wiring in navigation " ,
2020-10-19 13:43:10 +00:00
" Adjust course in navigation " ,
" Check headings in navigation " ,
2020-10-17 09:44:48 +00:00
" Fix wiring in weapons " ,
2020-10-19 13:43:10 +00:00
" Calibrate targeting system in weapons " ,
2020-10-17 09:44:48 +00:00
" Fix wiring in shields " ,
" Fix wiring in o2 " ,
2020-10-19 13:43:10 +00:00
" Clean oxygenator filter in o2 " ,
" Water plants in o2 " ,
2020-10-17 09:44:48 +00:00
" Fix wiring in medbay " ,
" Check catalyzer in upper engine " ,
" Check catalyzer in lower engine " ,
" Replace compression coil in upper engine " ,
2020-10-18 03:45:50 +00:00
" Replace compression coil in lower engine " ,
2020-10-17 09:44:48 +00:00
} ;
2020-10-16 23:55:27 +00:00
enum player_task_long {
2020-10-19 13:43:10 +00:00
TASK_SHIELDS_POWER ,
TASK_WEAPONS_POWER ,
TASK_NAV_LOG ,
TASK_SHIELD_LOG ,
TASK_REACTOR_FUEL ,
TASK_POTATO ,
2020-10-16 23:55:27 +00:00
TASK_LONG_COUNT
} ;
2020-10-19 13:43:10 +00:00
const char long_task_descriptions [ ] [ 2 ] [ 45 ] = {
{ " Route power to defence in electrical " , " Accept rerouted power in shields " } ,
{ " Route power to attack in electrical " , " Accept rerouted power in weapons " } ,
2020-10-29 20:25:52 +00:00
{ " Download the latest navigation data " , " Upload data in admin " } ,
{ " Download the latest shields data " , " Upload data in admin " } ,
2020-10-19 13:43:10 +00:00
{ " Pick up nuclear fuel in storage " , " Insert fuel into reactor " } ,
{ " Pick up potato in cafeteria " , " Plant the potato in o2 " } ,
{ " Get radio log from communications " , " Deliver communications log to admin " } ,
2020-10-17 09:52:35 +00:00
} ;
2020-10-16 22:11:52 +00:00
enum player_location {
2020-10-17 09:29:47 +00:00
LOC_CAFETERIA ,
2020-10-16 22:11:52 +00:00
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 ,
2020-10-18 03:45:50 +00:00
LOC_COUNT ,
2020-10-16 22:11:52 +00:00
} ;
2020-10-17 10:24:15 +00:00
const char locations [ ] [ 45 ] = {
2020-10-18 03:45:50 +00:00
[ LOC_CAFETERIA ] = " cafeteria " ,
[ LOC_REACTOR ] = " reactor " ,
[ LOC_UPPER_ENGINE ] = " upper " ,
[ LOC_LOWER_ENGINE ] = " lower " ,
[ LOC_SECURITY ] = " security " ,
[ LOC_MEDBAY ] = " medbay " ,
[ LOC_ELECTRICAL ] = " electrical " ,
[ LOC_STORAGE ] = " storage " ,
[ LOC_ADMIN ] = " admin " ,
[ LOC_COMMUNICATIONS ] = " communications " ,
[ LOC_O2 ] = " o2 " ,
[ LOC_WEAPONS ] = " weapons " ,
[ LOC_SHIELDS ] = " shields " ,
[ LOC_NAVIGATION ] = " navigation " ,
} ;
enum player_location doors [ ] [ 10 ] = {
2020-10-19 06:21:59 +00:00
[ LOC_CAFETERIA ] = { LOC_MEDBAY , LOC_ADMIN , LOC_WEAPONS , LOC_COUNT } ,
[ LOC_REACTOR ] = { LOC_UPPER_ENGINE , LOC_SECURITY , LOC_LOWER_ENGINE , LOC_COUNT } ,
2020-10-21 14:05:49 +00:00
[ LOC_UPPER_ENGINE ] = { LOC_REACTOR , LOC_SECURITY , LOC_MEDBAY , LOC_COUNT } ,
2020-10-20 18:39:45 +00:00
[ LOC_LOWER_ENGINE ] = { LOC_REACTOR , LOC_SECURITY , LOC_ELECTRICAL , LOC_COUNT } ,
2020-10-19 06:21:59 +00:00
[ LOC_SECURITY ] = { LOC_UPPER_ENGINE , LOC_REACTOR , LOC_LOWER_ENGINE , LOC_COUNT } ,
[ LOC_MEDBAY ] = { LOC_UPPER_ENGINE , LOC_CAFETERIA , LOC_COUNT } ,
[ LOC_ELECTRICAL ] = { LOC_LOWER_ENGINE , LOC_STORAGE , LOC_COUNT } ,
[ LOC_STORAGE ] = { LOC_ELECTRICAL , LOC_ADMIN , LOC_COMMUNICATIONS , LOC_SHIELDS , LOC_COUNT } ,
[ LOC_ADMIN ] = { LOC_CAFETERIA , LOC_STORAGE , LOC_COUNT } ,
[ LOC_COMMUNICATIONS ] = { LOC_STORAGE , LOC_SHIELDS , LOC_COUNT } ,
[ LOC_O2 ] = { LOC_SHIELDS , LOC_WEAPONS , LOC_NAVIGATION , LOC_COUNT } ,
[ LOC_WEAPONS ] = { LOC_CAFETERIA , LOC_O2 , LOC_NAVIGATION , LOC_COUNT } ,
[ LOC_SHIELDS ] = { LOC_STORAGE , LOC_COMMUNICATIONS , LOC_O2 , LOC_NAVIGATION , LOC_COUNT } ,
[ LOC_NAVIGATION ] = { LOC_WEAPONS , LOC_O2 , LOC_SHIELDS , LOC_COUNT } ,
2020-10-18 03:45:50 +00:00
} ;
const char descriptions [ ] [ 256 ] = {
[ LOC_CAFETERIA ] = " You are standing in the middle of the cafeteria, in the center there's an emergency button \n " ,
[ LOC_REACTOR ] = " You are in the reactor room, it seems to be running normally \n " ,
[ LOC_UPPER_ENGINE ] = " You are in a small room, mostly filled up by an engine. \n " ,
[ LOC_LOWER_ENGINE ] = " You are in a small room, mostly filled up by an engine. \n " ,
[ LOC_SECURITY ] = " You are in a small room filled with monitors, the monitors are displaying camera images showing an overview of the ship \n " ,
[ LOC_MEDBAY ] = " You are in a room with beds and a medical scanner. \n " ,
[ LOC_ELECTRICAL ] = " You are in a room filled with equipment racks. Some of them have wires sticking out of them \n " ,
[ LOC_STORAGE ] = " You are in a large room filled with boxes. One of the walls has a large door to the outside \n " ,
[ LOC_ADMIN ] = " You are in a nice carpeted room with a holographic map in the middle \n " ,
[ LOC_COMMUNICATIONS ] = " You are in a small room with what looks like radio equipment \n " ,
[ LOC_O2 ] = " You are in a room with plants in terrariums and life support equipment \n " ,
[ LOC_WEAPONS ] = " You are in a circular room with a targeting system in the middle and a view of outer space \n " ,
[ LOC_SHIELDS ] = " You are in a circular room with glowing tubes and a control panel for the shields \n " ,
[ LOC_NAVIGATION ] = " You are all the way in the front of the ship in a room with the ship controls and a great view of space \n " ,
2020-10-17 10:24:15 +00:00
} ;
2020-11-07 16:54:13 +00:00
const char map [ ] =
" | \\ ----------------|--------------|----------------|-------------- \\ \n "
" | \\ \n "
" | UPPER ENGINE CAFETERIA WEAPONS \\ \n "
" | |- --------| | \\ \n "
" |/--------| |--| MEDBAY | | \\ \n "
" | | | | \\ ------ \\ \n "
" /---------| |------- \\ | |----------| | \\ \n "
" | | | \\ |---| |------| | | \n "
" | \\ | | | \n "
" | REACTOR SECURITY | | ADMIN OFFICE | O2 NAVIGATION | \n "
" | | | | | | \n "
" | | | | |---| |----|-|----------| | \n "
" \\ ---------| |----------|------| | | / \n "
" | | | /------/ \n "
" | \\ --------| |--| | / \n "
" | | | |-- --| / \n "
" | LOWER ENGINE ELECTRICAL STORAGE | COMMS | SHIELDS / \n "
" | | | / \n "
" |/----------------|--------------|--------------|--------|-------/ \n "
;
2020-10-18 15:35:24 +00:00
2020-11-07 22:21:04 +00:00
const char usage [ ] =
" Usage: among-sus [-p <port>] [-h] \n "
" among-sus: \t Among Us, but it's a text adventure \n "
" \n "
" -p, \t \t Set port number \n "
" -h, \t \t Display this message \n "
;
2020-10-18 23:44:41 +00:00
enum player_state {
PLAYER_STATE_ALIVE ,
PLAYER_STATE_VENT , // TODO: implement vents
PLAYER_STATE_DEAD ,
PLAYER_STATE_FOUND ,
PLAYER_STATE_EJECTED ,
PLAYER_STATE_KICKED ,
} ;
2020-10-16 22:11:52 +00:00
struct player {
int fd ;
enum player_stage stage ;
2020-10-18 03:45:50 +00:00
char name [ MAX_NAME + 1 ] ;
2020-10-16 22:11:52 +00:00
int is_admin ;
2020-10-21 17:58:46 +00:00
int is_impostor ;
2020-10-16 22:11:52 +00:00
enum player_location location ;
2020-10-18 23:44:41 +00:00
enum player_state state ;
2020-10-16 22:11:52 +00:00
int has_cooldown ;
2020-10-17 14:19:22 +00:00
int voted ;
int votes ;
2020-10-16 23:55:27 +00:00
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 ] ;
2020-10-16 22:11:52 +00:00
} ;
struct gamestate {
enum game_stage stage ;
int has_admin ;
int players ;
int is_reactor_meltdown ;
2020-10-18 00:16:39 +00:00
int chats_left ;
2020-10-18 13:51:36 +00:00
int skips ;
2020-10-21 14:34:46 +00:00
unsigned int impostor_cooldown ;
2020-10-16 22:11:52 +00:00
} ;
struct gamestate state ;
2020-10-18 03:45:50 +00:00
struct player players [ NUM_PLAYERS ] ;
2020-10-18 13:51:36 +00:00
fd_set rfds , afds ;
2020-10-16 22:11:52 +00:00
2020-10-19 02:56:21 +00:00
int
random_num ( int upper_bound )
{
int ret ;
do {
ret = rand ( ) ;
} while ( ret > = ( RAND_MAX - RAND_MAX % upper_bound ) ) ;
ret % = upper_bound ;
return ret ;
}
2020-10-19 22:53:47 +00:00
int
alive ( struct player player )
{
return player . state = = PLAYER_STATE_ALIVE | | player . state = = PLAYER_STATE_VENT ;
}
2020-10-16 22:11:52 +00:00
void
broadcast ( char * message , int notfd )
{
char buf [ 1024 ] ;
int pid ;
printf ( " *> %s \n " , message ) ;
2020-10-18 03:45:50 +00:00
snprintf ( buf , sizeof ( buf ) , " %s \n " , message ) ;
2020-10-16 22:11:52 +00:00
for ( pid = 0 ; pid < NUM_PLAYERS ; pid + + ) {
2020-10-18 03:45:50 +00:00
if ( players [ pid ] . fd = = - 1 | | players [ pid ] . fd = = notfd
2020-11-09 22:22:32 +00:00
| | players [ pid ] . stage = = PLAYER_STAGE_NAME
| | players [ pid ] . stage = = PLAYER_STAGE_WAITING )
2020-10-17 22:54:55 +00:00
continue ;
2020-10-16 22:11:52 +00:00
write ( players [ pid ] . fd , buf , strlen ( buf ) ) ;
}
}
2020-10-19 22:53:47 +00:00
void
broadcast_ghosts ( char * message , int notfd )
2020-10-18 23:44:41 +00:00
{
2020-10-19 22:53:47 +00:00
char buf [ 1024 ] ;
int pid ;
printf ( " *> %s \n " , message ) ;
snprintf ( buf , sizeof ( buf ) , " %s \n " , message ) ;
for ( pid = 0 ; pid < NUM_PLAYERS ; pid + + ) {
2020-10-20 19:31:31 +00:00
if ( players [ pid ] . fd = = - 1
2020-10-19 22:53:47 +00:00
| | players [ pid ] . fd = = notfd
| | players [ pid ] . stage = = PLAYER_STAGE_NAME
2020-11-09 22:22:32 +00:00
| | players [ pid ] . stage = = PLAYER_STAGE_WAITING
2020-10-19 22:53:47 +00:00
| | alive ( players [ pid ] ) )
continue ;
write ( players [ pid ] . fd , buf , strlen ( buf ) ) ;
}
2020-10-18 23:44:41 +00:00
}
2020-10-16 22:11:52 +00:00
void
2020-10-19 06:21:59 +00:00
player_move ( size_t pid , enum player_location location )
2020-10-16 22:11:52 +00:00
{
char buf [ 100 ] ;
2020-10-18 21:03:27 +00:00
enum player_location old_location = players [ pid ] . location ;
2020-10-19 06:21:59 +00:00
printf ( " Moving player %zu to %d \n " , pid , location ) ;
2020-10-16 22:11:52 +00:00
players [ pid ] . location = location ;
2020-10-21 14:34:46 +00:00
if ( players [ pid ] . has_cooldown ! = 0 )
- - players [ pid ] . has_cooldown ;
2020-10-16 22:11:52 +00:00
// body detection
2020-10-19 06:21:59 +00:00
for ( size_t i = 0 ; i < NUM_PLAYERS ; i + + ) {
2020-10-18 03:45:50 +00:00
if ( players [ i ] . location ! = players [ pid ] . location | | i = = pid
| | players [ i ] . fd = = - 1
2020-11-09 22:22:32 +00:00
| | players [ i ] . state ! = PLAYER_STATE_DEAD
| | players [ i ] . stage ! = PLAYER_STAGE_WAITING )
2020-10-16 22:11:52 +00:00
continue ;
2020-10-18 03:45:50 +00:00
snprintf ( buf , sizeof ( buf ) , " you enter the room and see the body of [%s] laying on the floor \n " , players [ i ] . name ) ;
write ( players [ pid ] . fd , buf , strlen ( buf ) ) ;
}
2020-10-18 21:03:27 +00:00
// Notify players you're moving
if ( MOVEMENT_NOTIFICATIONS ) {
2020-10-19 06:21:59 +00:00
for ( size_t i = 0 ; i < NUM_PLAYERS ; i + + ) {
2020-10-19 21:59:46 +00:00
if ( players [ i ] . fd = = - 1 | | ! alive ( players [ i ] )
| | ! alive ( players [ pid ] ) | | i = = pid )
2020-10-18 21:03:27 +00:00
continue ;
if ( players [ i ] . location = = players [ pid ] . location ) {
snprintf ( buf , sizeof ( buf ) , " [%s] just walked into the room \n " , players [ pid ] . name ) ;
write ( players [ i ] . fd , buf , strlen ( buf ) ) ;
}
if ( players [ i ] . location = = old_location ) {
snprintf ( buf , sizeof ( buf ) , " [%s] just left the room \n " , players [ pid ] . name ) ;
write ( players [ i ] . fd , buf , strlen ( buf ) ) ;
}
}
}
2020-10-18 03:45:50 +00:00
}
void
end_game ( )
{
2020-11-09 22:22:32 +00:00
char buf [ 100 ] ;
2020-10-18 03:45:50 +00:00
broadcast ( " ------------------------ " , - 1 ) ;
broadcast ( " The game has ended, returning to lobby " , - 1 ) ;
state . stage = STAGE_LOBBY ;
2020-10-16 22:11:52 +00:00
2020-10-18 03:45:50 +00:00
for ( int i = 0 ; i < NUM_PLAYERS ; i + + ) {
if ( players [ i ] . fd = = - 1 )
2020-10-16 22:11:52 +00:00
continue ;
2020-11-09 22:22:32 +00:00
if ( players [ i ] . stage = = PLAYER_STAGE_WAITING ) {
snprintf ( buf , sizeof ( buf ) , " Game ended, sending you to lobby. \n \a " ) ;
write ( players [ i ] . fd , buf , strlen ( buf ) ) ;
snprintf ( buf , sizeof ( buf ) , " [%s] has joined the lobby. " , players [ i ] . name ) ;
broadcast ( buf , players [ i ] . fd ) ;
}
2020-10-18 03:45:50 +00:00
players [ i ] . stage = PLAYER_STAGE_LOBBY ;
}
}
2020-10-16 22:11:52 +00:00
2020-10-18 21:14:54 +00:00
int
2020-10-18 03:45:50 +00:00
check_win_condition ( void )
{
2020-10-18 13:51:36 +00:00
char buf [ 100 ] ;
2020-10-19 06:21:59 +00:00
size_t nalive = 0 , iid = 0 , tasks = 1 ;
2020-10-18 13:51:36 +00:00
2020-10-18 15:37:35 +00:00
for ( size_t i = 0 ; i < NUM_PLAYERS ; i + + ) {
2020-10-21 17:58:46 +00:00
if ( players [ i ] . fd ! = - 1 & & players [ i ] . is_impostor )
2020-10-18 13:51:36 +00:00
iid = i ;
2020-10-21 17:58:46 +00:00
if ( players [ i ] . is_impostor = = 1
2020-10-18 23:44:41 +00:00
& & ! alive ( players [ i ] ) ) {
2020-10-29 20:26:59 +00:00
broadcast ( " The crew won, the impostor died " , - 1 ) ;
2020-10-18 03:45:50 +00:00
end_game ( ) ;
2020-10-18 21:14:54 +00:00
return 1 ;
2020-10-18 03:45:50 +00:00
}
2020-10-18 13:51:36 +00:00
2020-10-18 23:44:41 +00:00
if ( players [ i ] . fd ! = - 1 & & alive ( players [ i ] )
2020-11-09 22:22:32 +00:00
& & players [ i ] . is_impostor = = 0
& & players [ i ] . stage ! = PLAYER_STAGE_WAITING )
2020-10-18 23:44:41 +00:00
nalive + + ;
2020-10-18 15:37:35 +00:00
2020-11-09 22:22:32 +00:00
if ( players [ i ] . fd ! = - 1 & & ! players [ i ] . is_impostor
& & players [ i ] . stage ! = PLAYER_STAGE_WAITING ) {
2020-10-18 15:37:35 +00:00
for ( size_t j = 0 ; j < NUM_SHORT ; j + + ) {
if ( ! players [ i ] . short_tasks_done [ j ] ) {
tasks = 0 ;
break ;
}
}
for ( size_t j = 0 ; j < NUM_LONG ; j + + ) {
2020-10-19 13:43:10 +00:00
if ( players [ i ] . long_tasks_done [ j ] ! = 2 ) {
2020-10-18 15:37:35 +00:00
tasks = 0 ;
break ;
}
}
}
}
if ( tasks = = 1 ) {
2020-10-29 20:26:59 +00:00
broadcast ( " The crew won, all tasks completed " , - 1 ) ;
2020-10-18 15:37:35 +00:00
end_game ( ) ;
2020-10-18 21:14:54 +00:00
return 1 ;
2020-10-18 03:45:50 +00:00
}
2020-10-18 23:44:41 +00:00
if ( nalive = = 1 ) {
2020-12-02 05:11:16 +00:00
broadcast ( " The impostor is alone with the last crewmate and murders them " , - 1 ) ;
2020-10-21 17:58:46 +00:00
snprintf ( buf , sizeof ( buf ) , " The impostor was [%s] all along... " , players [ iid ] . name ) ;
2020-10-18 13:51:36 +00:00
broadcast ( buf , - 1 ) ;
2020-10-18 03:45:50 +00:00
end_game ( ) ;
2020-10-18 21:14:54 +00:00
return 1 ;
2020-10-16 22:11:52 +00:00
}
2020-10-18 21:14:54 +00:00
return 0 ;
2020-10-16 22:11:52 +00:00
}
2020-10-17 10:24:15 +00:00
void
2020-10-19 06:21:59 +00:00
task_completed ( size_t pid , size_t task_id , int long_task )
2020-10-17 10:24:15 +00:00
{
// Mark task completed for player
if ( ! long_task ) {
2020-10-19 06:21:59 +00:00
for ( size_t i = 0 ; i < NUM_SHORT ; i + + ) {
2020-10-17 10:24:15 +00:00
if ( players [ pid ] . short_tasks [ i ] = = task_id ) {
players [ pid ] . short_tasks_done [ i ] = 1 ;
}
}
} else {
2020-10-19 06:21:59 +00:00
for ( size_t i = 0 ; i < NUM_LONG ; i + + ) {
2020-10-17 10:24:15 +00:00
if ( players [ pid ] . long_tasks [ i ] = = task_id ) {
2020-10-19 13:43:10 +00:00
players [ pid ] . long_tasks_done [ i ] + + ;
2020-10-17 10:24:15 +00:00
}
}
}
2020-10-18 03:45:50 +00:00
check_win_condition ( ) ;
2020-10-17 10:24:15 +00:00
}
2020-10-20 18:39:45 +00:00
void
list_rooms_with_players ( size_t pid ) {
int count [ LOC_COUNT ] = { 0 } ;
char buf [ 100 ] ;
for ( size_t i = 0 ; i < NUM_PLAYERS ; i + + ) {
if ( players [ i ] . fd ! = - 1 & & alive ( players [ i ] ) )
count [ players [ i ] . location ] + + ;
}
for ( int i = 0 ; i < LOC_COUNT ; i + + ) {
if ( count [ i ] > 0 ) {
snprintf ( buf , sizeof ( buf ) , " * There are %d players in %s \n " ,
count [ i ] , locations [ i ] ) ;
write ( players [ pid ] . fd , buf , strlen ( buf ) ) ;
}
}
}
2020-10-16 23:55:27 +00:00
void
2020-10-19 06:21:59 +00:00
player_list_tasks ( size_t pid )
2020-10-16 23:55:27 +00:00
{
char buf [ 100 ] ;
2020-10-19 13:43:10 +00:00
int task_desc ;
2020-10-30 23:39:36 +00:00
int done = 1 ;
2020-10-16 23:55:27 +00:00
2020-10-19 06:21:59 +00:00
for ( size_t i = 0 ; i < TASK_SHORT_COUNT ; i + + ) {
for ( size_t j = 0 ; j < NUM_SHORT ; j + + ) {
2020-10-16 23:55:27 +00:00
if ( players [ pid ] . short_tasks [ j ] = = i ) {
2020-10-18 03:45:50 +00:00
const char * cm ;
2020-10-17 00:03:24 +00:00
if ( players [ pid ] . short_tasks_done [ j ] ) {
2020-10-18 03:45:50 +00:00
cm = " * " ;
2020-10-17 00:03:24 +00:00
} else {
2020-10-18 03:45:50 +00:00
cm = " " ;
2020-10-30 23:39:36 +00:00
done = 0 ;
2020-10-17 00:03:24 +00:00
}
2020-10-18 03:45:50 +00:00
snprintf ( buf , sizeof ( buf ) , " [%s] %s \n " , cm ,
short_task_descriptions [ i ] ) ;
2020-10-16 23:55:27 +00:00
write ( players [ pid ] . fd , buf , strlen ( buf ) ) ;
}
}
}
2020-10-19 06:21:59 +00:00
for ( size_t i = 0 ; i < TASK_LONG_COUNT ; i + + ) {
for ( size_t j = 0 ; j < NUM_LONG ; j + + ) {
2020-10-17 09:52:35 +00:00
if ( players [ pid ] . long_tasks [ j ] = = i ) {
2020-10-18 03:45:50 +00:00
const char * cm ;
2020-10-19 13:43:10 +00:00
if ( players [ pid ] . long_tasks_done [ j ] = = 2 ) {
2020-10-18 03:45:50 +00:00
cm = " * " ;
2020-10-19 13:43:10 +00:00
task_desc = 0 ;
} else if ( players [ pid ] . long_tasks_done [ j ] = = 1 ) {
cm = " - " ;
task_desc = 1 ;
2020-10-30 23:39:36 +00:00
done = 0 ;
2020-10-17 09:52:35 +00:00
} else {
2020-10-18 03:45:50 +00:00
cm = " " ;
2020-10-19 13:43:10 +00:00
task_desc = 0 ;
2020-10-30 23:39:36 +00:00
done = 0 ;
2020-10-17 09:52:35 +00:00
}
2020-10-18 03:45:50 +00:00
snprintf ( buf , sizeof ( buf ) , " [%s] %s \n " , cm ,
2020-10-19 13:43:10 +00:00
long_task_descriptions [ i ] [ task_desc ] ) ;
2020-10-17 09:52:35 +00:00
write ( players [ pid ] . fd , buf , strlen ( buf ) ) ;
}
}
}
2020-10-30 23:39:36 +00:00
if ( done ) {
snprintf ( buf , sizeof ( buf ) , " All your tasks are completed! \n # " ) ;
} else {
snprintf ( buf , sizeof ( buf ) , " Complete the tasks by typing the full task name in the correct location \n # " ) ;
}
2020-10-17 10:24:15 +00:00
write ( players [ pid ] . fd , buf , strlen ( buf ) ) ;
2020-10-16 23:55:27 +00:00
}
2020-11-09 22:22:32 +00:00
bool
2020-10-19 06:21:59 +00:00
player_kill ( size_t pid , size_t tid )
2020-10-16 22:11:52 +00:00
{
char buf [ 100 ] ;
2020-10-18 03:45:50 +00:00
if ( players [ pid ] . location ! = players [ tid ] . location
2020-11-09 22:22:32 +00:00
| | players [ tid ] . is_impostor
| | players [ tid ] . stage ! = PLAYER_STAGE_MAIN )
return false ;
2020-10-16 22:11:52 +00:00
// so sad
2020-10-18 23:44:41 +00:00
players [ tid ] . state = PLAYER_STATE_DEAD ;
2020-10-16 22:11:52 +00:00
// less murdering, reset by movement
2020-10-21 14:34:46 +00:00
players [ pid ] . has_cooldown = state . impostor_cooldown ;
2020-10-19 17:41:48 +00:00
2020-10-16 22:11:52 +00:00
// notify player of their recent death
2020-10-21 17:58:46 +00:00
snprintf ( buf , sizeof ( buf ) , " It turns out %s is the impostor, sadly the way you know is that you died. \n " ,
2020-10-18 03:45:50 +00:00
players [ pid ] . name ) ;
2020-10-16 22:11:52 +00:00
write ( players [ tid ] . fd , buf , strlen ( buf ) ) ;
// notify bystanders
2020-10-19 06:21:59 +00:00
for ( size_t i = 0 ; i < NUM_PLAYERS ; i + + ) {
2020-10-18 23:44:41 +00:00
if ( i = = pid | | players [ i ] . fd = = - 1 | | ! alive ( players [ i ] )
2020-11-09 22:22:32 +00:00
| | players [ i ] . location ! = players [ pid ] . location
| | players [ i ] . stage ! = PLAYER_STAGE_MAIN )
2020-10-16 22:11:52 +00:00
continue ;
2020-10-18 03:45:50 +00:00
snprintf ( buf , sizeof ( buf ) , " someone killed [%s] while you were in the room \n " ,
players [ tid ] . name ) ;
2020-10-16 22:11:52 +00:00
write ( players [ i ] . fd , buf , strlen ( buf ) ) ;
}
2020-10-19 17:41:48 +00:00
2020-10-18 03:45:50 +00:00
check_win_condition ( ) ;
2020-11-09 22:22:32 +00:00
return true ;
2020-10-16 22:11:52 +00:00
}
void
2020-10-19 06:21:59 +00:00
start_discussion ( size_t pid , size_t bid )
2020-10-16 22:11:52 +00:00
{
2020-10-16 23:55:27 +00:00
char buf [ 100 ] ;
2020-10-16 22:11:52 +00:00
state . stage = STAGE_DISCUSS ;
2020-10-18 13:51:36 +00:00
state . skips = 0 ;
2020-10-16 22:11:52 +00:00
2020-10-18 13:51:36 +00:00
// switch everyone to the discussion state and mark bodies found
2020-10-16 22:11:52 +00:00
for ( int i = 0 ; i < NUM_PLAYERS ; i + + ) {
2020-11-09 22:22:32 +00:00
if ( players [ i ] . fd = = - 1
| | players [ i ] . stage ! = PLAYER_STAGE_MAIN )
2020-10-16 22:11:52 +00:00
continue ;
players [ i ] . stage = PLAYER_STAGE_DISCUSS ;
2020-10-18 03:45:50 +00:00
players [ i ] . voted = 0 ;
players [ i ] . votes = 0 ;
2020-10-16 22:11:52 +00:00
}
2020-10-18 00:16:39 +00:00
broadcast ( " ------------------------ " , - 1 ) ;
2020-10-16 22:11:52 +00:00
// Inform everyone
2020-10-19 06:21:59 +00:00
if ( bid = = SIZE_MAX ) {
2020-10-16 22:11:52 +00:00
// Emergency button was pressed
2020-10-18 03:45:50 +00:00
snprintf ( buf , sizeof ( buf ) , " \n An emergency meeting was called by [%s] " , players [ pid ] . name ) ;
2020-10-16 22:11:52 +00:00
} else {
// Body was reported
2020-10-18 03:45:50 +00:00
snprintf ( buf , sizeof ( buf ) , " \n The body of [%s] was found by [%s] " , players [ bid ] . name , players [ pid ] . name ) ;
2020-10-18 23:44:41 +00:00
players [ bid ] . state = PLAYER_STATE_FOUND ;
2020-10-16 23:55:27 +00:00
}
broadcast ( buf , - 1 ) ;
// List the state of the players
broadcast ( " Players: " , - 1 ) ;
for ( int i = 0 ; i < NUM_PLAYERS ; i + + ) {
2020-11-09 22:22:32 +00:00
if ( players [ i ] . fd = = - 1
| | players [ i ] . stage ! = PLAYER_STAGE_DISCUSS )
2020-10-16 23:55:27 +00:00
continue ;
2020-10-18 23:44:41 +00:00
switch ( players [ i ] . state ) {
case PLAYER_STATE_ALIVE :
2020-10-18 03:45:50 +00:00
snprintf ( buf , sizeof ( buf ) , " * %d [%s] " , i , players [ i ] . name ) ;
2020-10-18 23:44:41 +00:00
break ;
case PLAYER_STATE_DEAD :
2020-10-18 03:45:50 +00:00
snprintf ( buf , sizeof ( buf ) , " * %d [%s] (dead) " , i , players [ i ] . name ) ;
2020-10-18 23:44:41 +00:00
break ;
case PLAYER_STATE_FOUND :
snprintf ( buf , sizeof ( buf ) , " * %d [%s] (dead, reported) " , i , players [ i ] . name ) ;
break ;
2020-10-19 06:21:59 +00:00
case PLAYER_STATE_VENT :
case PLAYER_STATE_EJECTED :
case PLAYER_STATE_KICKED :
2020-10-18 23:44:41 +00:00
continue ;
2020-10-16 23:55:27 +00:00
}
broadcast ( buf , - 1 ) ;
2020-10-16 22:11:52 +00:00
}
2020-10-18 00:16:39 +00:00
// Inform people of the chat limit
2020-10-18 03:45:50 +00:00
snprintf ( buf , sizeof ( buf ) , " Discuss, there are %d messages left " , NUM_CHATS ) ;
2020-10-18 00:16:39 +00:00
state . chats_left = NUM_CHATS ;
broadcast ( buf , - 1 ) ;
2020-10-16 22:11:52 +00:00
}
2020-10-17 21:25:39 +00:00
void
back_to_playing ( )
{
state . stage = STAGE_PLAYING ;
// switch everyone to the playing state
for ( int i = 0 ; i < NUM_PLAYERS ; i + + ) {
2020-11-09 22:22:32 +00:00
if ( players [ i ] . fd = = - 1
| | players [ i ] . stage ! = PLAYER_STAGE_DISCUSS )
2020-10-17 21:25:39 +00:00
continue ;
players [ i ] . stage = PLAYER_STAGE_MAIN ;
2020-10-18 19:05:23 +00:00
players [ i ] . location = LOC_CAFETERIA ;
2020-10-17 21:25:39 +00:00
}
broadcast ( " -- Voting has ended, back to the ship -- \n \n # " , - 1 ) ;
}
2020-10-16 22:11:52 +00:00
void
2020-10-19 06:21:59 +00:00
discussion ( size_t pid , char * input )
2020-10-16 22:11:52 +00:00
{
2020-10-16 23:55:27 +00:00
char buf [ 300 ] ;
2020-10-24 23:40:36 +00:00
intmax_t vote = 0 , max_votes = 0 , tie = 0 , winner = - 1 , hasvalidchar = 0 ;
2020-10-19 22:53:47 +00:00
for ( size_t i = 0 ; i < strlen ( input ) ; i + + ) {
2020-10-24 23:40:36 +00:00
if ( ! isprint ( input [ i ] ) )
2020-10-19 22:53:47 +00:00
input [ i ] = ' \0 ' ;
2020-10-24 23:40:36 +00:00
else if ( ! isspace ( input [ i ] ) )
hasvalidchar = 1 ;
2020-10-19 22:53:47 +00:00
}
2020-10-17 14:19:22 +00:00
2020-10-18 03:45:50 +00:00
if ( input [ 0 ] = = ' / ' & & input [ 1 ] ! = ' / ' ) {
2020-10-19 22:53:47 +00:00
if ( ( strncmp ( input , " /vote " , 6 ) = = 0
2020-10-18 23:44:41 +00:00
| | strncmp ( input , " /yeet " , 6 ) = = 0
2020-10-19 22:53:47 +00:00
| | strncmp ( input , " /skip " , 6 ) = = 0 )
& & alive ( players [ pid ] ) ) {
2020-10-17 14:19:22 +00:00
if ( players [ pid ] . voted ) {
2020-10-18 03:45:50 +00:00
snprintf ( buf , sizeof ( buf ) , " You can only vote once \n " ) ;
2020-10-17 14:19:22 +00:00
write ( players [ pid ] . fd , buf , strlen ( buf ) ) ;
return ;
}
2020-10-18 13:51:36 +00:00
if ( input [ 1 ] = = ' s ' ) {
vote = - 1 ;
} else {
char * endptr = NULL ;
2020-10-19 16:28:48 +00:00
vote = strtol ( & input [ 6 ] , & endptr , 10 ) ;
2020-10-18 13:51:36 +00:00
if ( ! endptr | | endptr [ 0 ] ! = ' \0 ' ) {
snprintf ( buf , sizeof ( buf ) , " Invalid vote, not an integer \n " ) ;
write ( players [ pid ] . fd , buf , strlen ( buf ) ) ;
return ;
}
2020-10-18 03:45:50 +00:00
}
2020-10-17 14:19:22 +00:00
2020-12-02 05:10:24 +00:00
if ( vote ! = - 1 & & ( vote < - 1 | | vote > NUM_PLAYERS - 1
| | players [ vote ] . fd = = - 1
| | players [ vote ] . stage ! = PLAYER_STAGE_DISCUSS ) ) {
2020-10-18 03:45:50 +00:00
snprintf ( buf , sizeof ( buf ) , " Invalid vote, no such player \n " ) ;
2020-10-17 14:19:22 +00:00
write ( players [ pid ] . fd , buf , strlen ( buf ) ) ;
return ;
}
2020-11-08 16:24:23 +00:00
if ( ! alive ( players [ vote ] ) ) {
snprintf ( buf , sizeof ( buf ) , " Invalid vote, that person is dead \n " ) ;
write ( players [ pid ] . fd , buf , strlen ( buf ) ) ;
return ;
}
if ( vote = = - 1 ) {
printf ( " [%s] voted to skip \n " , players [ pid ] . name ) ;
} else {
printf ( " [%s] voted for %jd \n " , players [ pid ] . name , vote ) ;
}
2020-10-17 14:19:22 +00:00
players [ pid ] . voted = 1 ;
2020-10-18 13:51:36 +00:00
if ( vote = = - 1 ) {
state . skips + + ;
} else {
players [ vote ] . votes + + ;
}
check_votes :
2020-10-17 14:19:22 +00:00
// Check if voting is complete
2020-10-19 06:21:59 +00:00
for ( size_t i = 0 ; i < NUM_PLAYERS ; i + + ) {
2020-10-18 23:44:41 +00:00
if ( players [ i ] . fd ! = - 1 & & players [ i ] . voted = = 0
2020-11-09 22:22:32 +00:00
& & alive ( players [ i ] )
& & players [ i ] . stage = = PLAYER_STAGE_DISCUSS ) {
2020-10-17 14:19:22 +00:00
printf ( " No vote from [%s] yet \n " , players [ i ] . name ) ;
goto not_yet ;
}
}
printf ( " Voting complete \n " ) ;
// Count votes
2020-10-18 13:51:36 +00:00
max_votes = state . skips ;
2020-10-19 06:21:59 +00:00
for ( size_t i = 0 ; i < NUM_PLAYERS ; i + + ) {
2020-11-09 22:22:32 +00:00
if ( players [ i ] . fd = = - 1 | | players [ i ] . stage ! = PLAYER_STAGE_DISCUSS )
2020-10-17 14:19:22 +00:00
continue ;
if ( players [ i ] . votes > max_votes ) {
max_votes = players [ i ] . votes ;
tie = 0 ;
2020-10-19 06:21:59 +00:00
winner = ( intmax_t ) i ;
2020-10-17 14:19:22 +00:00
continue ;
}
if ( players [ i ] . votes = = max_votes ) {
tie = 1 ;
}
}
2020-10-19 06:21:59 +00:00
printf ( " Vote winner: %jd \n " , winner ) ;
2020-10-18 03:45:50 +00:00
2020-10-17 14:19:22 +00:00
if ( tie ) {
broadcast ( " The voting ended in a tie " , - 1 ) ;
2020-10-17 21:25:39 +00:00
back_to_playing ( ) ;
return ;
2020-10-18 13:51:36 +00:00
} else if ( winner = = - 1 ) {
snprintf ( buf , sizeof ( buf ) , " The crew voted to skip \n " ) ;
broadcast ( buf , - 1 ) ;
back_to_playing ( ) ;
return ;
2020-10-17 14:19:22 +00:00
} else {
2020-10-18 03:45:50 +00:00
snprintf ( buf , sizeof ( buf ) , " The crew voted to eject [%s] \n " , players [ winner ] . name ) ;
2020-10-17 14:19:22 +00:00
broadcast ( buf , - 1 ) ;
}
// dramatic pause
for ( int i = 0 ; i < 5 ; i + + ) {
sleep ( 1 ) ;
broadcast ( " . " , - 1 ) ;
}
2020-10-18 23:44:41 +00:00
players [ winner ] . state = PLAYER_STATE_EJECTED ;
2020-10-21 17:58:46 +00:00
if ( players [ winner ] . is_impostor ) {
snprintf ( buf , sizeof ( buf ) , " It turns out [%s] was an impostor " , players [ winner ] . name ) ;
2020-10-17 14:19:22 +00:00
broadcast ( buf , - 1 ) ;
} else {
2020-10-21 17:58:46 +00:00
snprintf ( buf , sizeof ( buf ) , " Sadly, [%s] was not an impostor " , players [ winner ] . name ) ;
2020-10-17 14:19:22 +00:00
broadcast ( buf , - 1 ) ;
}
2020-10-18 21:14:54 +00:00
if ( ! check_win_condition ( ) ) {
back_to_playing ( ) ;
}
2020-10-18 03:45:50 +00:00
return ;
2020-10-17 14:19:22 +00:00
not_yet :
broadcast ( " A vote has been cast " , - 1 ) ;
2020-10-18 13:51:36 +00:00
} else if ( strncmp ( input , " /help " , 6 ) = = 0 ) {
snprintf ( buf , sizeof ( buf ) , " Commands: /vote [id], /skip, /list \n " ) ;
write ( players [ pid ] . fd , buf , strlen ( buf ) ) ;
} else if ( strncmp ( input , " /list " , 6 ) = = 0 ) {
for ( int i = 0 ; i < NUM_PLAYERS ; i + + ) {
2020-11-09 22:22:32 +00:00
if ( players [ i ] . fd = = - 1
| | players [ i ] . stage ! = PLAYER_STAGE_DISCUSS )
2020-10-18 13:51:36 +00:00
continue ;
2020-10-18 23:44:41 +00:00
if ( alive ( players [ i ] ) & & players [ i ] . voted ) {
2020-10-18 13:51:36 +00:00
snprintf ( buf , sizeof ( buf ) , " * %d [%s] (voted) \n " , i , players [ i ] . name ) ;
2020-10-18 23:44:41 +00:00
} else if ( alive ( players [ i ] ) ) {
2020-10-18 13:51:36 +00:00
snprintf ( buf , sizeof ( buf ) , " * %d [%s] \n " , i , players [ i ] . name ) ;
} else {
snprintf ( buf , sizeof ( buf ) , " * %d [%s] (dead) \n " , i , players [ i ] . name ) ;
}
write ( players [ pid ] . fd , buf , strlen ( buf ) ) ;
}
snprintf ( buf , sizeof ( buf ) , " Commands: /vote [id], /skip, /list \n " ) ;
write ( players [ pid ] . fd , buf , strlen ( buf ) ) ;
} else if ( strncmp ( input , " /kick " , 5 ) = = 0 ) {
if ( ! players [ pid ] . is_admin ) {
snprintf ( buf , sizeof ( buf ) , " You have no kicking permission \n " ) ;
write ( players [ pid ] . fd , buf , strlen ( buf ) ) ;
}
char * endptr = NULL ;
2020-10-19 18:39:27 +00:00
vote = strtol ( & input [ 6 ] , & endptr , 10 ) ;
2020-10-18 13:51:36 +00:00
if ( ! endptr | | endptr [ 0 ] ! = ' \0 ' ) {
snprintf ( buf , sizeof ( buf ) , " Invalid kick, not an integer \n " ) ;
write ( players [ pid ] . fd , buf , strlen ( buf ) ) ;
return ;
}
snprintf ( buf , sizeof ( buf ) , " Admin kicked [%s] \n " , players [ vote ] . name ) ;
broadcast ( buf , - 1 ) ;
2020-10-19 17:41:48 +00:00
2020-10-18 13:51:36 +00:00
close ( players [ vote ] . fd ) ;
FD_CLR ( players [ vote ] . fd , & afds ) ;
players [ vote ] . fd = - 1 ;
2020-10-18 23:44:41 +00:00
players [ vote ] . state = PLAYER_STATE_KICKED ;
2020-10-18 13:51:36 +00:00
goto check_votes ;
2020-10-20 19:31:31 +00:00
} else if ( strncmp ( input , " /me " , 4 ) = = 0 ) {
2020-10-22 18:58:00 +00:00
if ( state . chats_left > 0 & & alive ( players [ pid ] ) ) {
2020-10-19 22:53:47 +00:00
snprintf ( buf , sizeof ( buf ) , " (%d) * [%s] %s " , state . chats_left , players [ pid ] . name , & input [ 4 ] ) ;
broadcast ( buf , - 1 ) ;
state . chats_left - - ;
2020-10-22 18:58:00 +00:00
} else if ( alive ( players [ pid ] ) ) {
snprintf ( buf , sizeof ( buf ) , " No chats left, you can only vote now \n " ) ;
write ( players [ pid ] . fd , buf , strlen ( buf ) ) ;
return ;
2020-10-19 22:53:47 +00:00
} else {
snprintf ( buf , sizeof ( buf ) , " (dead) * [%s] %s " , players [ pid ] . name , & input [ 4 ] ) ;
broadcast_ghosts ( buf , - 1 ) ;
}
2020-10-20 19:31:31 +00:00
} else if ( strncmp ( input , " /shrug " , 6 ) = = 0 ) {
2020-10-22 18:58:00 +00:00
if ( state . chats_left > 0 & & alive ( players [ pid ] ) ) {
2020-10-19 22:53:47 +00:00
snprintf ( buf , sizeof ( buf ) , " (%d) [%s]: ¯ \\ _(ツ)_/¯ " , state . chats_left , players [ pid ] . name ) ;
broadcast ( buf , - 1 ) ;
state . chats_left - - ;
2020-10-22 18:58:00 +00:00
} else if ( alive ( players [ pid ] ) ) {
snprintf ( buf , sizeof ( buf ) , " No chats left, you can only vote now \n " ) ;
write ( players [ pid ] . fd , buf , strlen ( buf ) ) ;
return ;
2020-10-19 22:53:47 +00:00
} else {
snprintf ( buf , sizeof ( buf ) , " (dead) [%s]: ¯ \\ _(ツ)_/¯ " , players [ pid ] . name ) ;
broadcast_ghosts ( buf , - 1 ) ;
}
2020-10-18 03:45:50 +00:00
} else {
snprintf ( buf , sizeof ( buf ) , " Invalid command \n " ) ;
write ( players [ pid ] . fd , buf , strlen ( buf ) ) ;
}
} else if ( input [ 0 ] = = ' / ' ) {
2020-10-19 22:53:47 +00:00
if ( state . chats_left = = 0 & & alive ( players [ pid ] ) ) {
2020-10-18 03:45:50 +00:00
snprintf ( buf , sizeof ( buf ) , " No chats left, you can only vote now \n " ) ;
write ( players [ pid ] . fd , buf , strlen ( buf ) ) ;
return ;
2020-10-16 22:11:52 +00:00
}
2020-10-19 22:53:47 +00:00
if ( alive ( players [ pid ] ) ) {
snprintf ( buf , sizeof ( buf ) , " (%d) [%s]: %s " , state . chats_left , players [ pid ] . name , & input [ 1 ] ) ;
broadcast ( buf , - 1 ) ;
state . chats_left - - ;
} else {
snprintf ( buf , sizeof ( buf ) , " (dead) [%s]: %s " , players [ pid ] . name , & input [ 1 ] ) ;
broadcast_ghosts ( buf , - 1 ) ;
}
2020-10-24 23:40:36 +00:00
} else if ( hasvalidchar ) {
2020-10-22 18:58:00 +00:00
if ( state . chats_left < = 0 & & alive ( players [ pid ] ) ) {
2020-10-18 03:45:50 +00:00
snprintf ( buf , sizeof ( buf ) , " No chats left, you can only vote now \n " ) ;
2020-10-18 00:16:39 +00:00
write ( players [ pid ] . fd , buf , strlen ( buf ) ) ;
return ;
}
2020-10-19 22:53:47 +00:00
if ( alive ( players [ pid ] ) ) {
snprintf ( buf , sizeof ( buf ) , " (%d) [%s]: %s " , state . chats_left , players [ pid ] . name , input ) ;
broadcast ( buf , - 1 ) ;
state . chats_left - - ;
} else {
snprintf ( buf , sizeof ( buf ) , " (dead) [%s]: %s " , players [ pid ] . name , input ) ;
broadcast_ghosts ( buf , - 1 ) ;
}
2020-10-16 22:11:52 +00:00
}
}
void
2020-10-19 06:21:59 +00:00
adventure ( size_t pid , char * input )
2020-10-16 22:11:52 +00:00
{
char buf [ 1024 ] ;
2020-10-18 03:45:50 +00:00
const char * location ;
2020-10-19 06:21:59 +00:00
size_t task_id ;
2020-10-17 10:24:15 +00:00
int task_is_long ;
2020-10-18 13:51:36 +00:00
if ( input [ 0 ] = = ' e ' | | strncmp ( input , " ls " , 3 ) = = 0 ) {
2020-10-18 03:45:50 +00:00
enum player_location loc = players [ pid ] . location ;
strcpy ( buf , " you can move to: " ) ;
2020-10-19 06:21:59 +00:00
assert ( doors [ loc ] [ 0 ] ! = LOC_COUNT ) ;
2020-10-18 03:45:50 +00:00
strncat ( buf , locations [ doors [ loc ] [ 0 ] ] , sizeof ( buf ) - 1 ) ;
2020-10-19 06:21:59 +00:00
for ( size_t i = 1 ; doors [ loc ] [ i ] ! = LOC_COUNT ; i + + ) {
2020-10-18 03:45:50 +00:00
strncat ( strncat ( buf , " , " , sizeof ( buf ) - 1 ) ,
locations [ doors [ loc ] [ i ] ] , sizeof ( buf ) - 1 ) ;
}
strncat ( buf , " \n " , sizeof ( buf ) - 1 ) ;
location = descriptions [ loc ] ;
if ( loc = = LOC_REACTOR & & state . is_reactor_meltdown ) {
location = " You are in the reactor room, there are red warning lights on and a siren is going off. \n " ;
2020-10-16 22:11:52 +00:00
}
write ( players [ pid ] . fd , location , strlen ( location ) ) ;
2020-10-18 03:45:50 +00:00
write ( players [ pid ] . fd , buf , strlen ( buf ) ) ;
2020-10-19 06:21:59 +00:00
for ( size_t i = 0 ; i < NUM_PLAYERS ; i + + ) {
2020-10-18 03:45:50 +00:00
if ( players [ i ] . location ! = players [ pid ] . location
2020-11-09 22:22:32 +00:00
| | players [ i ] . fd = = - 1 | | i = = pid
| | players [ i ] . stage ! = PLAYER_STAGE_MAIN )
2020-10-16 22:11:52 +00:00
continue ;
2020-10-18 23:44:41 +00:00
switch ( players [ i ] . state ) {
case PLAYER_STATE_ALIVE :
2020-10-18 18:42:31 +00:00
snprintf ( buf , sizeof ( buf ) ,
" you also see %s in the room with you \n " ,
players [ i ] . name ) ;
2020-10-18 23:44:41 +00:00
break ;
case PLAYER_STATE_DEAD :
2020-10-18 18:42:31 +00:00
snprintf ( buf , sizeof ( buf ) ,
" you also see %s's corpse in the room with you \n " ,
players [ i ] . name ) ;
2020-10-18 23:44:41 +00:00
break ;
case PLAYER_STATE_FOUND :
snprintf ( buf , sizeof ( buf ) ,
" you also see %s's reported corpse in the room with you \n " ,
players [ i ] . name ) ;
break ;
2020-10-19 06:21:59 +00:00
case PLAYER_STATE_VENT :
case PLAYER_STATE_EJECTED :
case PLAYER_STATE_KICKED :
2020-10-18 23:44:41 +00:00
buf [ 0 ] = ' \0 ' ;
}
2020-10-18 03:45:50 +00:00
write ( players [ pid ] . fd , buf , strlen ( buf ) ) ;
2020-10-16 22:11:52 +00:00
}
2020-10-18 03:45:50 +00:00
snprintf ( buf , sizeof ( buf ) , " # " ) ;
2020-10-18 13:51:36 +00:00
} else if ( strncmp ( input , " go " , 3 ) = = 0 | | strncmp ( input , " cd " , 3 ) = = 0 ) {
2020-10-18 03:45:50 +00:00
enum player_location new ;
for ( new = 0 ; new < LOC_COUNT ; new + + ) {
if ( strcmp ( locations [ new ] , & input [ 3 ] ) = = 0 ) {
break ;
}
}
if ( new = = LOC_COUNT ) {
snprintf ( buf , sizeof ( buf ) , " INVALID MOVEMENT \n # " ) ;
} else {
2020-10-19 06:21:59 +00:00
for ( size_t i = 0 ; doors [ players [ pid ] . location ] [ i ] ! = LOC_COUNT ; i + + ) {
2020-10-18 03:45:50 +00:00
if ( doors [ players [ pid ] . location ] [ i ] = = new ) {
player_move ( pid , new ) ;
snprintf ( buf , sizeof ( buf ) ,
" successfully moved \n # " ) ;
new = LOC_COUNT ;
break ;
}
}
if ( new ! = LOC_COUNT ) {
snprintf ( buf , sizeof ( buf ) , " INVALID MOVEMENT \n # " ) ;
}
2020-10-16 22:11:52 +00:00
}
2020-10-18 03:45:50 +00:00
} else if ( strcmp ( input , " murder crewmate " ) = = 0 ) {
2020-10-21 17:58:46 +00:00
if ( players [ pid ] . is_impostor = = 0 ) {
2020-10-18 03:45:50 +00:00
snprintf ( buf , sizeof ( buf ) , " you might dislike them, but you can't kill them without weapon \n # " ) ;
2020-10-16 22:11:52 +00:00
} else if ( players [ pid ] . has_cooldown ) {
2020-10-18 03:45:50 +00:00
snprintf ( buf , sizeof ( buf ) , " you can't kill that quickly \n # " ) ;
2020-10-16 22:11:52 +00:00
} else {
2020-11-09 22:22:32 +00:00
snprintf ( buf , sizeof ( buf ) , " no one to kill here \n # " ) ;
2020-10-19 06:21:59 +00:00
for ( size_t i = 0 ; i < NUM_PLAYERS ; i + + ) {
2020-10-18 03:45:50 +00:00
if ( players [ i ] . location ! = players [ pid ] . location
| | i = = pid | | players [ i ] . fd = = - 1
2020-11-09 22:22:32 +00:00
| | ! alive ( players [ i ] )
| | players [ i ] . stage ! = PLAYER_STAGE_MAIN )
2020-10-16 22:11:52 +00:00
continue ;
// TODO: kill more randomly
2020-11-09 22:22:32 +00:00
if ( player_kill ( pid , i ) )
snprintf ( buf , sizeof ( buf ) , " you draw your weapon and brutally murder %s \n # " ,
players [ i ] . name ) ;
2020-10-16 22:11:52 +00:00
break ;
}
}
2020-10-18 03:45:50 +00:00
} else if ( strcmp ( input , " report " ) = = 0 ) {
2020-10-19 06:21:59 +00:00
for ( size_t i = 0 ; i < NUM_PLAYERS ; i + + ) {
2020-10-18 03:45:50 +00:00
if ( players [ i ] . location ! = players [ pid ] . location
| | i = = pid | | players [ i ] . fd = = - 1
2020-10-18 23:44:41 +00:00
| | players [ i ] . state ! = PLAYER_STATE_DEAD
| | ! alive ( players [ pid ] ) )
2020-10-16 22:11:52 +00:00
continue ;
start_discussion ( pid , i ) ;
return ;
}
2020-10-18 03:45:50 +00:00
snprintf ( buf , sizeof ( buf ) , " Nothing to report here \n # " ) ;
} else if ( strcmp ( input , " press emergency button " ) = = 0 ) {
2020-10-17 22:51:22 +00:00
if ( players [ pid ] . location ! = LOC_CAFETERIA ) {
2020-11-08 17:45:27 +00:00
snprintf ( buf , sizeof ( buf ) , " You can't do that here \n # " ) ;
2020-10-19 18:29:57 +00:00
} else if ( ! alive ( players [ pid ] ) ) {
2020-11-08 17:45:27 +00:00
snprintf ( buf , sizeof ( buf ) , " Ghosts can't call emergencies \n # " ) ;
2020-10-17 22:51:22 +00:00
} else {
2020-10-19 06:21:59 +00:00
start_discussion ( pid , SIZE_MAX ) ;
2020-10-18 00:16:39 +00:00
return ;
2020-10-17 22:51:22 +00:00
}
2020-10-18 03:45:50 +00:00
} else if ( strcmp ( input , " check tasks " ) = = 0 ) {
2020-10-16 23:55:27 +00:00
player_list_tasks ( pid ) ;
return ;
2020-10-20 18:39:45 +00:00
} else if ( strcmp ( input , " look at monitors " ) = = 0 ) {
list_rooms_with_players ( pid ) ;
return ;
2020-10-18 03:45:50 +00:00
} else if ( strcmp ( input , " help " ) = = 0 ) {
2020-10-30 23:39:36 +00:00
snprintf ( buf , sizeof ( buf ) , " Commands: help, examine room, go [room], murder crewmate, report, check tasks " ) ;
write ( players [ pid ] . fd , buf , strlen ( buf ) ) ;
switch ( players [ pid ] . location ) {
case LOC_CAFETERIA :
snprintf ( buf , sizeof ( buf ) , " \n commands in this room: press emergency button \n # " ) ;
break ;
case LOC_SECURITY :
snprintf ( buf , sizeof ( buf ) , " \n commands in this room: look at monitor \n # " ) ;
break ;
default :
snprintf ( buf , sizeof ( buf ) , " \n # " ) ;
break ;
}
2020-10-18 15:35:24 +00:00
} else if ( strncmp ( input , " map " , 3 ) = = 0 ) {
2020-11-07 16:54:13 +00:00
write ( players [ pid ] . fd , map , strlen ( map ) ) ;
2020-10-18 15:35:24 +00:00
snprintf ( buf , sizeof ( buf ) , " # " ) ;
2020-10-17 10:24:15 +00:00
} else {
// check if it was a task
2020-10-19 06:21:59 +00:00
task_id = TASK_SHORT_COUNT + TASK_LONG_COUNT ;
2020-10-17 10:24:15 +00:00
task_is_long = 0 ;
2020-10-19 06:21:59 +00:00
for ( size_t i = 0 ; i < TASK_SHORT_COUNT ; i + + ) {
2020-10-17 10:24:15 +00:00
if ( strcmp ( input , short_task_descriptions [ i ] ) = = 0 ) {
task_id = i ;
break ;
}
}
2020-10-19 06:21:59 +00:00
for ( size_t i = 0 ; i < TASK_LONG_COUNT ; i + + ) {
2020-10-19 13:43:10 +00:00
for ( int k = 0 ; k < 2 ; k + + ) {
if ( strcmp ( input , long_task_descriptions [ i ] [ k ] ) = = 0 ) {
// Check if player has the task
for ( int l = 0 ; l < NUM_LONG ; l + + ) {
if ( players [ pid ] . long_tasks [ l ] = = i & &
players [ pid ] . long_tasks_done [ l ] = = k ) {
task_id = i ;
task_is_long = 1 ;
}
}
}
2020-10-17 10:24:15 +00:00
}
}
2020-10-19 06:21:59 +00:00
if ( task_id = = TASK_SHORT_COUNT + TASK_LONG_COUNT ) {
2020-10-18 03:45:50 +00:00
snprintf ( buf , sizeof ( buf ) , " Invalid instruction \n # " ) ;
2020-10-17 10:24:15 +00:00
} else {
// check it was in the right room
if ( strstr ( input , locations [ players [ pid ] . location ] ) ! = NULL ) {
task_completed ( pid , task_id , task_is_long ) ;
2020-10-18 15:37:35 +00:00
if ( state . stage = = STAGE_PLAYING ) {
snprintf ( buf , sizeof ( buf ) , " Completed task \n # " ) ;
} else {
buf [ 0 ] = ' \0 ' ;
}
2020-10-17 10:24:15 +00:00
} else {
2020-10-18 03:45:50 +00:00
snprintf ( buf , sizeof ( buf ) , " You're in the wrong place for that \n # " ) ;
2020-10-17 10:24:15 +00:00
}
}
2020-10-16 22:11:52 +00:00
}
write ( players [ pid ] . fd , buf , strlen ( buf ) ) ;
}
void
start_game ( )
{
2020-10-21 17:58:46 +00:00
int impostornum , assigned ;
2020-10-16 22:11:52 +00:00
char buf [ 200 ] ;
2020-10-19 06:21:59 +00:00
unsigned temp ;
2020-10-16 23:55:27 +00:00
2020-10-16 22:11:52 +00:00
broadcast ( " ---------- [ Game is starting ] ---------- " , - 1 ) ;
2020-10-20 18:35:59 +00:00
broadcast ( " \a " , - 1 ) ; /* Alarm beep for y'all multitaskers */
2020-10-19 17:41:48 +00:00
state . stage = STAGE_PLAYING ;
2020-10-16 22:11:52 +00:00
state . players = 0 ;
for ( int i = 0 ; i < NUM_PLAYERS ; i + + ) {
if ( players [ i ] . fd ! = - 1 ) {
state . players + + ;
}
}
2020-10-21 17:58:46 +00:00
// Pick an impostor
impostornum = random_num ( state . players ) ;
2020-10-16 22:11:52 +00:00
assigned = 0 ;
for ( int i = 0 ; i < NUM_PLAYERS ; i + + ) {
if ( players [ i ] . fd = = - 1 )
continue ;
players [ i ] . stage = PLAYER_STAGE_MAIN ;
2020-10-17 09:29:47 +00:00
players [ i ] . location = LOC_CAFETERIA ;
2020-10-18 23:44:41 +00:00
players [ i ] . state = PLAYER_STATE_ALIVE ;
2020-10-16 23:55:27 +00:00
// Assign NUM_SHORT random short tasks
for ( int j = 0 ; j < NUM_SHORT ; j + + ) {
2020-10-18 03:45:50 +00:00
retry :
2020-10-19 06:21:59 +00:00
temp = ( unsigned ) random_num ( TASK_SHORT_COUNT ) ;
2020-10-16 23:55:27 +00:00
for ( int k = 0 ; k < NUM_SHORT ; k + + ) {
if ( players [ i ] . short_tasks [ k ] = = temp )
goto retry ;
}
players [ i ] . short_tasks [ j ] = temp ;
players [ i ] . short_tasks_done [ j ] = 0 ;
}
2020-10-17 09:52:35 +00:00
// Assign NUM_LONG random long tasks
2020-10-19 06:21:59 +00:00
for ( size_t j = 0 ; j < NUM_LONG ; j + + ) {
2020-10-18 03:45:50 +00:00
retry2 :
2020-10-19 06:21:59 +00:00
temp = ( unsigned ) random_num ( TASK_LONG_COUNT ) ;
for ( size_t k = 0 ; k < NUM_LONG ; k + + ) {
2020-10-17 09:52:35 +00:00
if ( players [ i ] . long_tasks [ k ] = = temp )
goto retry2 ;
}
players [ i ] . long_tasks [ j ] = temp ;
players [ i ] . long_tasks_done [ j ] = 0 ;
}
2020-10-21 17:58:46 +00:00
if ( assigned = = impostornum ) {
players [ i ] . is_impostor = 1 ;
snprintf ( buf , sizeof ( buf ) , " You are the impostor, kill everyone without getting noticed. \n " ) ;
2020-10-16 22:11:52 +00:00
} else {
2020-10-21 17:58:46 +00:00
players [ i ] . is_impostor = 0 ;
2020-10-18 03:45:50 +00:00
snprintf ( buf , sizeof ( buf ) , " You are a crewmate, complete your tasks before everyone is killed. \n " ) ;
2020-10-16 22:11:52 +00:00
}
write ( players [ i ] . fd , buf , strlen ( buf ) ) ;
assigned + + ;
}
2020-10-17 23:53:25 +00:00
// dramatic pause
for ( int i = 0 ; i < 5 ; i + + ) {
sleep ( 1 ) ;
broadcast ( " . " , - 1 ) ;
}
for ( int i = 0 ; i < NUM_PLAYERS ; i + + ) {
if ( players [ i ] . fd = = - 1 )
continue ;
2020-10-21 17:58:46 +00:00
if ( players [ i ] . is_impostor ) {
2020-11-13 23:54:10 +00:00
snprintf ( buf , sizeof ( buf ) , " You are in a spaceship, the other %d crew members think you're one of them \n # " , assigned - 1 ) ;
2020-10-17 23:53:25 +00:00
write ( players [ i ] . fd , buf , strlen ( buf ) ) ;
} else {
2020-10-18 03:45:50 +00:00
snprintf ( buf , sizeof ( buf ) , " You are in a spaceship, one of the crew of %d people \n " , assigned ) ;
2020-10-17 23:53:25 +00:00
write ( players [ i ] . fd , buf , strlen ( buf ) ) ;
2020-10-18 03:45:50 +00:00
snprintf ( buf , sizeof ( buf ) , " The tasks have been handed out and the daily routine is starting up, but there are rumors one of your fellow crewmates isn't a crewmate at all. \n # " ) ;
2020-10-17 23:53:25 +00:00
write ( players [ i ] . fd , buf , strlen ( buf ) ) ;
}
}
2020-10-16 22:11:52 +00:00
}
2020-10-18 21:33:43 +00:00
void reassign_admin ( ) {
char buf [ 100 ] ;
for ( int i = 0 ; i < NUM_PLAYERS ; i + + ) {
if ( players [ i ] . fd = = - 1 | | players [ i ] . stage = = PLAYER_STAGE_NAME )
continue ;
players [ i ] . is_admin = 1 ;
2020-10-20 19:05:08 +00:00
state . has_admin = 1 ;
2020-10-18 21:33:43 +00:00
snprintf ( buf , sizeof ( buf ) , " ** Admin left, new admin is %s ** \n " , players [ i ] . name ) ;
broadcast ( buf , - 1 ) ;
return ;
}
}
2020-10-21 14:34:46 +00:00
void
set ( char * buf , size_t buf_len , int fd , int pid )
{
if ( strncmp ( & buf [ 5 ] , " kill-cooldown " , 13 ) = = 0 ) {
char * nextptr = NULL ;
int value = strtol ( & buf [ 19 ] , & nextptr , 10 ) ;
if ( nextptr = = & buf [ 19 ] ) {
const char * msg = " Error: you didn't enter any number. Leaving current value... \n " ;
write ( fd , msg , strlen ( msg ) ) ;
} else if ( value < 0 ) {
const char * msg = " Error: negative numbers aren't allowed. Leaving current value... \n " ;
write ( fd , msg , strlen ( msg ) ) ;
} else if ( nextptr ! = NULL & & nextptr [ 0 ] = = ' \0 ' ) {
state . impostor_cooldown = value ;
2020-10-21 17:58:46 +00:00
snprintf ( buf , buf_len , " %s changed impostor cooldown to %d. " , players [ pid ] . name , state . impostor_cooldown ) ;
2020-10-21 14:34:46 +00:00
broadcast ( buf , - 1 ) ;
} else {
const char * msg = " Error: invalid input. Leaving current value... \n " ;
write ( fd , msg , strlen ( msg ) ) ;
}
} else {
const char * msg = " Error: you didn't write a valid property. \n " ;
write ( fd , msg , strlen ( msg ) ) ;
}
}
2020-10-21 17:57:14 +00:00
void
list_set ( int pid )
{
char buf [ 100 ] ;
snprintf ( buf , sizeof ( buf ) , " kill-cooldown = %d \n " , state . impostor_cooldown ) ;
write ( players [ pid ] . fd , buf , strlen ( buf ) ) ;
}
2020-10-16 22:11:52 +00:00
int
handle_input ( int fd )
{
2020-10-21 17:52:26 +00:00
char buf [ 200 ] = { 0 } ;
2020-10-16 22:11:52 +00:00
char buf2 [ 300 ] ;
2020-10-19 06:21:59 +00:00
ssize_t len ;
size_t pid ;
2020-10-16 22:11:52 +00:00
// Find player for fd
for ( pid = 0 ; pid < NUM_PLAYERS ; pid + + ) {
if ( players [ pid ] . fd = = fd ) {
break ;
}
}
// Get the input
2020-10-21 17:52:26 +00:00
len = read ( fd , buf , 199 ) ;
2020-10-16 22:11:52 +00:00
if ( len < 0 ) {
2020-10-19 06:21:59 +00:00
printf ( " Read error from player %zu \n " , pid ) ;
2020-10-17 14:19:22 +00:00
players [ pid ] . fd = - 1 ;
2020-10-18 21:33:43 +00:00
if ( players [ pid ] . stage ! = PLAYER_STAGE_NAME ) {
snprintf ( buf , sizeof ( buf ) , " Player [%s] disconnected. " , players [ pid ] . name ) ;
2021-01-17 20:41:35 +00:00
players [ pid ] . name [ 0 ] = ' \0 ' ;
2020-10-18 21:33:43 +00:00
printf ( " Sending disconnection message \n " ) ;
broadcast ( buf , - 1 ) ;
}
if ( players [ pid ] . is_admin ) {
2020-10-20 17:24:10 +00:00
state . has_admin = 0 ;
2020-10-18 21:33:43 +00:00
reassign_admin ( ) ;
}
2020-10-16 22:11:52 +00:00
return - 1 ;
}
if ( len = = 0 ) {
2020-10-19 06:21:59 +00:00
printf ( " Received EOF from player %zu \n " , pid ) ;
2020-10-17 14:19:22 +00:00
players [ pid ] . fd = - 1 ;
2020-10-17 23:25:27 +00:00
if ( players [ pid ] . stage ! = PLAYER_STAGE_NAME ) {
2020-10-18 03:45:50 +00:00
snprintf ( buf , sizeof ( buf ) , " Player [%s] left the game. " , players [ pid ] . name ) ;
2021-01-17 20:41:35 +00:00
players [ pid ] . name [ 0 ] = ' \0 ' ;
2020-10-17 23:25:27 +00:00
printf ( " Sending parting message \n " ) ;
broadcast ( buf , - 1 ) ;
}
2020-10-18 21:33:43 +00:00
if ( players [ pid ] . is_admin ) {
2020-10-20 17:24:10 +00:00
state . has_admin = 0 ;
2020-10-18 21:33:43 +00:00
reassign_admin ( ) ;
}
2020-10-16 22:11:52 +00:00
return - 2 ;
}
2020-10-19 06:21:59 +00:00
for ( size_t i = 0 ; i < sizeof ( buf ) ; i + + ) {
2020-10-17 10:49:03 +00:00
if ( buf [ i ] = = ' \n ' | | buf [ i ] = = ' \r ' ) {
2020-10-16 22:11:52 +00:00
buf [ i ] = ' \0 ' ;
break ;
}
}
2020-10-19 17:41:48 +00:00
2020-10-19 06:21:59 +00:00
printf ( " %zu: %s \n " , pid , buf ) ;
2020-10-16 22:11:52 +00:00
switch ( players [ pid ] . stage ) {
case PLAYER_STAGE_NAME :
// Setting the name after connection and informing the lobby
2020-10-18 03:45:50 +00:00
if ( strlen ( buf ) < MIN_NAME ) {
2020-10-19 06:21:59 +00:00
snprintf ( buf , sizeof ( buf ) , " Too short, pick another name \n > " ) ;
2020-10-17 14:19:22 +00:00
write ( fd , buf , strlen ( buf ) ) ;
return 0 ;
}
2020-10-18 03:45:50 +00:00
if ( strlen ( buf ) > MAX_NAME ) {
2020-10-19 06:21:59 +00:00
snprintf ( buf , sizeof ( buf ) , " Too long, pick another name \n > " ) ;
2020-10-17 14:19:22 +00:00
write ( fd , buf , strlen ( buf ) ) ;
return 0 ;
}
2021-01-17 20:41:35 +00:00
for ( size_t i = 0 ; i < NUM_PLAYERS ; i + + ) {
if ( strcmp ( players [ i ] . name , buf ) = = 0 ) {
snprintf ( buf , sizeof ( buf ) , " Taken, pick another name \n > " ) ;
write ( fd , buf , strlen ( buf ) ) ;
return 0 ;
}
}
2020-10-19 06:21:59 +00:00
for ( size_t i = 0 ; i < strlen ( buf ) ; i + + ) {
if ( ! isprint ( buf [ i ] ) ) {
snprintf ( buf , sizeof ( buf ) , " Invalid char, pick another name \n > " ) ;
2020-10-17 14:19:22 +00:00
write ( fd , buf , strlen ( buf ) ) ;
return 0 ;
}
}
2020-10-16 22:11:52 +00:00
strcpy ( players [ pid ] . name , buf ) ;
2020-10-17 14:19:22 +00:00
2020-11-09 22:22:32 +00:00
if ( state . stage = = STAGE_LOBBY ) {
snprintf ( buf , sizeof ( buf ) , " [%s] has joined the lobby " , players [ pid ] . name ) ;
broadcast ( buf , fd ) ;
players [ pid ] . stage = PLAYER_STAGE_LOBBY ;
} else
players [ pid ] . stage = PLAYER_STAGE_WAITING ;
break ;
case PLAYER_STAGE_WAITING :
2020-10-16 22:11:52 +00:00
break ;
case PLAYER_STAGE_LOBBY :
// Chat message in the lobby
if ( buf [ 0 ] = = ' / ' ) {
2020-10-18 03:45:50 +00:00
if ( strcmp ( buf , " /start " ) = = 0 ) {
2020-10-16 22:11:52 +00:00
if ( players [ pid ] . is_admin ) {
start_game ( ) ;
} else {
2020-10-18 03:45:50 +00:00
const char * msg = " You don't have permission to /start \n " ;
write ( fd , msg , strlen ( msg ) ) ;
2020-10-16 22:11:52 +00:00
}
2020-10-18 03:45:50 +00:00
} else if ( strcmp ( buf , " /shrug " ) = = 0 ) {
snprintf ( buf2 , sizeof ( buf2 ) , " [%s] ¯ \\ _(ツ)_/¯ " , players [ pid ] . name ) ;
2020-10-17 22:20:56 +00:00
broadcast ( buf2 , fd ) ;
2020-10-18 03:45:50 +00:00
} else if ( strncmp ( buf , " /me " , 4 ) = = 0 ) {
snprintf ( buf2 , sizeof ( buf2 ) , " * [%s] %s " , players [ pid ] . name , & buf [ 4 ] ) ;
2020-10-17 22:28:31 +00:00
broadcast ( buf2 , fd ) ;
2020-10-18 03:45:50 +00:00
} else if ( strcmp ( buf , " /help " ) = = 0 ) {
snprintf ( buf , sizeof ( buf ) , " Commands: /start, /list and more \n " ) ;
2020-10-17 22:44:19 +00:00
write ( fd , buf , strlen ( buf ) ) ;
2020-10-18 03:45:50 +00:00
} else if ( strcmp ( buf , " /list " ) = = 0 ) {
2020-10-17 22:44:19 +00:00
for ( int i = 0 ; i < NUM_PLAYERS ; i + + ) {
if ( players [ i ] . fd = = - 1 )
continue ;
if ( players [ i ] . stage = = PLAYER_STAGE_NAME ) {
2020-10-18 03:45:50 +00:00
snprintf ( buf , sizeof ( buf ) , " %d: -[ setting name ]- \n " , i ) ;
2020-10-17 22:44:19 +00:00
} else if ( players [ i ] . is_admin ) {
2020-10-18 03:45:50 +00:00
snprintf ( buf , sizeof ( buf ) , " %d: %s (admin) \n " , i , players [ i ] . name ) ;
2020-10-17 22:44:19 +00:00
} else {
2020-10-18 03:45:50 +00:00
snprintf ( buf , sizeof ( buf ) , " %d: %s \n " , i , players [ i ] . name ) ;
2020-10-17 22:44:19 +00:00
}
write ( fd , buf , strlen ( buf ) ) ;
}
2020-10-21 17:57:14 +00:00
} else if ( strncmp ( buf , " /set " , 4 ) = = 0 ) {
if ( strlen ( buf ) = = 4 ) {
list_set ( pid ) ;
return 0 ;
}
2020-10-21 14:34:46 +00:00
if ( players [ pid ] . is_admin )
set ( buf , sizeof ( buf ) , fd , pid ) ;
else {
const char * msg = " You don't have permission to /set \n " ;
write ( fd , msg , strlen ( msg ) ) ;
}
2020-10-16 22:11:52 +00:00
}
} else {
2020-10-24 23:40:36 +00:00
int hasvalidchar = 0 ;
for ( size_t i = 0 ; i < strlen ( buf ) ; i + + ) {
if ( ! isprint ( buf [ i ] ) )
2020-10-17 22:16:20 +00:00
buf [ i ] = ' \0 ' ;
2020-10-24 23:40:36 +00:00
else if ( ! isspace ( buf [ i ] ) )
hasvalidchar = 1 ;
}
if ( hasvalidchar ) {
snprintf ( buf2 , sizeof ( buf2 ) , " [%s]: %s " , players [ pid ] . name , buf ) ;
broadcast ( buf2 , fd ) ;
2020-10-17 22:16:20 +00:00
}
2020-10-16 22:11:52 +00:00
}
break ;
case PLAYER_STAGE_MAIN :
// Main game adventure loop
adventure ( pid , buf ) ;
break ;
case PLAYER_STAGE_DISCUSS :
// Main discussion loop
discussion ( pid , buf ) ;
break ;
}
return 0 ;
}
2020-10-17 11:37:10 +00:00
int
2020-10-18 03:45:50 +00:00
welcome_player ( int fd )
2020-10-16 22:11:52 +00:00
{
char buf [ 100 ] ;
2020-10-19 06:21:59 +00:00
for ( size_t i = 0 ; i < sizeof ( players ) ; i + + ) {
2020-10-16 22:11:52 +00:00
if ( players [ i ] . fd > 0 ) {
continue ;
}
2020-11-09 22:22:32 +00:00
2020-11-15 20:22:01 +00:00
snprintf ( buf , sizeof ( buf ) , " among-sus server: version %s \n " , VERSION ) ;
write ( fd , buf , strlen ( buf ) ) ;
2020-11-09 22:22:32 +00:00
if ( state . stage ! = STAGE_LOBBY ) {
snprintf ( buf , sizeof ( buf ) , " There is a game in progress, waiting for the match to finish... \n " ) ;
write ( fd , buf , strlen ( buf ) ) ;
}
2020-10-18 03:45:50 +00:00
players [ i ] . fd = fd ;
2020-10-20 17:24:10 +00:00
players [ i ] . is_admin = 0 ;
2020-10-18 03:45:50 +00:00
if ( ! state . has_admin ) {
state . has_admin = 1 ;
players [ i ] . is_admin = 1 ;
}
players [ i ] . stage = PLAYER_STAGE_NAME ;
2020-10-19 06:21:59 +00:00
snprintf ( buf , sizeof ( buf ) , " Welcome player %zu! \n \n Enter your name: \n > " , i ) ;
2020-10-18 03:45:50 +00:00
write ( fd , buf , strlen ( buf ) ) ;
2020-10-19 06:21:59 +00:00
printf ( " Assigned player to spot %zu \n " , i ) ;
2020-10-18 03:45:50 +00:00
return 0 ;
2020-10-16 22:11:52 +00:00
}
2020-10-18 03:45:50 +00:00
snprintf ( buf , sizeof ( buf ) , " There are no spots available, goodbye! \n " ) ;
2020-10-16 22:11:52 +00:00
write ( fd , buf , strlen ( buf ) ) ;
close ( fd ) ;
2020-10-17 11:37:10 +00:00
return - 1 ;
2020-10-16 22:11:52 +00:00
}
int
2020-11-07 22:21:04 +00:00
main ( int argc , char * argv [ ] )
2020-10-16 22:11:52 +00:00
{
2020-10-21 14:34:46 +00:00
// Set default settings
state . impostor_cooldown = 1 ;
2020-10-19 06:21:59 +00:00
uint16_t port = 1234 ;
2020-11-07 22:21:04 +00:00
char * endptr = NULL ;
int opt ;
while ( ( opt = getopt ( argc , argv , " hp: " ) ) ! = - 1 ) {
switch ( opt ) {
2020-12-06 17:55:30 +00:00
case ' p ' : ;
errno = 0 ;
long _port = strtol ( optarg , & endptr , 10 ) ;
if ( * endptr ! = ' \0 ' | | errno ! = 0 | |
_port < = 0 | | _port > = 65536 ) {
fprintf ( stderr , " Invalid port: %s \n " , optarg ) ;
exit ( EXIT_FAILURE ) ;
}
port = ( uint16_t ) _port ;
2020-11-07 22:21:04 +00:00
break ;
case ' h ' :
printf ( " %s " , usage ) ;
exit ( EXIT_SUCCESS ) ;
2020-12-06 13:36:13 +00:00
default :
2020-11-07 22:21:04 +00:00
printf ( " %s " , usage ) ;
exit ( EXIT_FAILURE ) ;
}
}
int listen_fd , listen6_fd , new_fd , i ;
2020-10-17 21:41:26 +00:00
socklen_t client_size ;
2020-11-09 21:43:47 +00:00
struct sockaddr_in listen_addr = { 0 } , client_addr = { 0 } ;
struct sockaddr_in6 listen6_addr = { 0 } ;
2020-10-16 22:11:52 +00:00
for ( i = 0 ; i < NUM_PLAYERS ; i + + ) {
players [ i ] . fd = - 1 ;
2020-10-19 06:21:59 +00:00
}
srand ( ( unsigned ) time ( NULL ) ) ;
2020-10-16 22:11:52 +00:00
2020-12-09 17:39:59 +00:00
if ( ( listen_fd = socket ( AF_INET , SOCK_STREAM , 0 ) ) = = - 1 ) {
perror ( " IPv4 socket " ) ;
exit ( EXIT_FAILURE ) ;
}
if ( ( listen6_fd = socket ( AF_INET6 , SOCK_STREAM , 0 ) ) = = - 1 ) {
perror ( " IPv6 socket " ) ;
exit ( EXIT_FAILURE ) ;
}
2020-10-16 22:11:52 +00:00
2020-10-19 02:56:22 +00:00
i = 1 ;
if ( setsockopt ( listen_fd , SOL_SOCKET , SO_REUSEADDR ,
& i , sizeof ( i ) ) ) {
2020-12-09 17:39:59 +00:00
perror ( " IPv4 setsockopt " ) ;
2020-10-19 02:56:22 +00:00
exit ( EXIT_FAILURE ) ;
}
2020-10-29 20:28:37 +00:00
if ( setsockopt ( listen6_fd , SOL_SOCKET , SO_REUSEADDR ,
& i , sizeof ( i ) ) ) {
2020-12-09 17:39:59 +00:00
perror ( " IPv6 setsockopt " ) ;
2020-10-29 20:28:37 +00:00
exit ( EXIT_FAILURE ) ;
}
2020-10-16 22:11:52 +00:00
listen_addr . sin_family = AF_INET ;
listen_addr . sin_addr . s_addr = htonl ( INADDR_ANY ) ;
listen_addr . sin_port = htons ( port ) ;
2020-10-19 03:40:31 +00:00
listen6_addr . sin6_family = AF_INET6 ;
listen6_addr . sin6_addr = in6addr_any ;
listen6_addr . sin6_port = htons ( port ) ;
2020-10-19 14:44:20 +00:00
if ( setsockopt ( listen6_fd , IPPROTO_IPV6 , IPV6_V6ONLY ,
& i , sizeof ( i ) ) ) {
perror ( " setsockopt " ) ;
exit ( EXIT_FAILURE ) ;
}
if ( setsockopt ( listen6_fd , IPPROTO_IPV6 , IPV6_V6ONLY ,
& i , sizeof ( i ) ) ) {
perror ( " setsockopt " ) ;
exit ( EXIT_FAILURE ) ;
}
2020-10-19 03:40:31 +00:00
2020-10-16 22:11:52 +00:00
if ( bind ( listen_fd , ( struct sockaddr * ) & listen_addr , sizeof ( listen_addr ) ) < 0 ) {
2020-10-19 03:40:31 +00:00
perror ( " ipv4 bind " ) ;
return - 1 ;
}
if ( bind ( listen6_fd , ( struct sockaddr * ) & listen6_addr , sizeof ( listen6_addr ) ) < 0 ) {
perror ( " ipv6 bind " ) ;
2020-10-16 22:11:52 +00:00
return - 1 ;
}
listen ( listen_fd , 5 ) ;
2020-10-19 03:40:31 +00:00
listen ( listen6_fd , 5 ) ;
2020-10-16 22:11:52 +00:00
printf ( " Listening on :%d \n " , port ) ;
state . stage = STAGE_LOBBY ;
FD_ZERO ( & afds ) ;
FD_SET ( listen_fd , & afds ) ;
2020-10-19 03:40:31 +00:00
FD_SET ( listen6_fd , & afds ) ;
2020-10-16 22:11:52 +00:00
while ( 1 ) {
rfds = afds ;
if ( select ( FD_SETSIZE , & rfds , NULL , NULL , NULL ) < 0 ) {
2020-10-18 03:45:50 +00:00
perror ( " select " ) ;
2020-10-16 22:11:52 +00:00
exit ( EXIT_FAILURE ) ;
}
for ( i = 0 ; i < FD_SETSIZE ; + + i ) {
if ( FD_ISSET ( i , & rfds ) ) {
2020-10-19 03:40:31 +00:00
if ( i = = listen_fd | | i = = listen6_fd ) {
2020-10-16 22:11:52 +00:00
printf ( " welcome client! \n " ) ;
client_size = sizeof ( client_addr ) ;
2020-10-19 03:40:31 +00:00
new_fd = accept ( i , ( struct sockaddr * ) & client_addr , & client_size ) ;
2020-10-16 22:11:52 +00:00
if ( new_fd < 0 ) {
2020-10-18 03:45:50 +00:00
perror ( " accept " ) ;
2020-10-16 22:11:52 +00:00
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 ) ;
2020-10-18 03:45:50 +00:00
if ( welcome_player ( new_fd ) < 0 ) {
2020-10-17 11:37:10 +00:00
FD_CLR ( new_fd , & afds ) ;
}
2020-10-16 22:11:52 +00:00
} else {
if ( handle_input ( i ) < 0 ) {
close ( i ) ;
FD_CLR ( i , & afds ) ;
}
}
}
}
}
}