#include #include #include #include #include #include #include #include #include #include "server.h" #include "server_data_processing.h" #include "server_game_process.h" #include "card_stack.h" #include "card_queue.h" static void init_session(struct session *new_session, struct sockaddr_in *from, int fd, int player_position) { new_session->fd = fd; new_session->record = 1; new_session->state = display_only_table; new_session->player_position = player_position; new_session->tm = none; new_session->defense_lost = 0; init_stack(&new_session->deck); } static int accept_client(struct server *serv) { int fd, i; struct sockaddr_in addr; struct session *new_session; socklen_t len = sizeof(addr); fd = accept(serv->listen_socket, (struct sockaddr*) &addr, &len); if(fd == -1) return -1; if(fd >= serv->max_sess_arr_size) { int newlen = serv->max_sess_arr_size; while(fd >= newlen) newlen += init_sess_arr_size; serv->sess_arr = realloc(serv->sess_arr, newlen * sizeof(struct session*)); for(i = serv->max_sess_arr_size; i < newlen; i++) serv->sess_arr[i] = NULL; serv->max_sess_arr_size = newlen; } new_session = malloc(sizeof(struct session)); serv->sess_arr[fd] = new_session; ++serv->connected_players_counter; init_session(new_session, &addr, fd, serv->connected_players_counter); return 1; } static int check_playable_player_number(struct server *serv) { return serv->connected_players_counter >= 2 && serv->connected_players_counter < 8; } static int is_max_playable_player_number(struct server *serv) { return serv->connected_players_counter == 8; } static void init_new_game(struct server *serv) { int i,j, lowest_trump, current_trump, who_attack; /* idx in serv->turn_queue */ who_attack = -1; lowest_trump = 0; serv->shuffled_deck = get_shuffled_deck(); serv->trump_suit = find_trump_suit(serv->shuffled_deck, &serv->shuffled_deck_size); serv->turn_queue = malloc(sizeof(struct session*) * serv->connected_players_counter); serv->card_quantity_arr = malloc(sizeof(int) * serv->connected_players_counter); serv->position_whose_turn = 0; serv->cq = malloc(sizeof(struct card_queue)); serv->cot = malloc(sizeof(struct cards_on_table)); /* * dealing cards to all and * determining who moves first */ for(i = 0, j = 0; i < serv->max_sess_arr_size; ++i) { if(serv->sess_arr[i]) { serv->turn_queue[j] = serv->sess_arr[i]; deal_first_cards(serv->shuffled_deck, &serv->shuffled_deck_size, &serv->sess_arr[i]->deck); current_trump = find_lowest_trump(serv->sess_arr[i]->deck, serv->trump_suit); if(current_trump) if(!lowest_trump || current_trump < lowest_trump) { lowest_trump = current_trump; who_attack = j; } ++j; } } /* random selection of the first player */ if(who_attack == -1) { who_attack = 0 + (int)((double)serv->connected_players_counter*rand()/ (RAND_MAX + 1.0)); } for(i = 0; i < who_attack; ++i) shift_turn_queue_by_one(serv->turn_queue, serv->connected_players_counter); update_card_quantity_arr(serv); init_queue(serv->cq); serv->cot->card_arr_idx = -1; serv->gi = get_new_game_info(serv->connected_players_counter, serv->card_quantity_arr, serv->shuffled_deck_size, serv->trump_suit, serv->position_whose_turn, serv->cq, serv->cot); } static void set_whose_turn(struct server *serv, enum client_game_states state) { switch(state) { case attack: serv->position_whose_turn = serv->turn_queue[0]->player_position; break; case defense: serv->position_whose_turn = serv->turn_queue[1]->player_position; break; case tossing: default: serv->position_whose_turn = 0; } } static void set_state_for_client(struct server *serv, enum client_game_states state) { int i; switch(state) { case attack: serv->turn_queue[0]->state = attack; break; case defense: serv->turn_queue[1]->state = defense; break; case tossing: serv->turn_queue[1]->state = tossing_expectation; default: {} } for(i = 0; i < serv->connected_players_counter; ++i) { switch(state) { case attack: if(serv->turn_queue[i] && serv->turn_queue[i] != serv->turn_queue[0]) serv->turn_queue[i]->state = attack_expectation; break; case defense: if(serv->turn_queue[i] && serv->turn_queue[i] != serv->turn_queue[1]) serv->turn_queue[i]->state = defense_expectation; break; case tossing: if(serv->turn_queue[i] && serv->turn_queue[i] != serv->turn_queue[1]) { if(check_player_can_toss_card(serv->turn_queue[i]->deck, serv->cot)) { serv->turn_queue[i]->tm = answer_wait; serv->turn_queue[i]->state = tossing; } else { serv->turn_queue[i]->tm = none; serv->turn_queue[i]->state = tossing_expectation; } } break; case display_only_table: if(serv->turn_queue[i]) serv->turn_queue[i]->state = display_only_table; break; default: {} } } } static void set_record_status_for_all(struct server *serv) { int i; for(i = 0; i < serv->max_sess_arr_size; ++i) if(serv->sess_arr[i]) serv->sess_arr[i]->record = 1; } static int print_table_condition(struct server *serv) { return !check_defender_can_defend(serv->cq, serv->turn_queue[1]->deck, serv->trump_suit) && !check_someone_can_toss_card(serv->turn_queue, serv->connected_players_counter, serv->cq, serv->cot); } static int defense_condition(struct server *serv) { return check_defender_can_defend(serv->cq, serv->turn_queue[1]->deck, serv->trump_suit); } static void define_phase_after_attack(struct server *serv) { if(print_table_condition(serv)) { set_state_for_client(serv, display_only_table); put_all_cards_from_queue_to_table(serv->cot, serv->cq); serv->turn_queue[1]->defense_lost = 1; serv->state = table; } else if(defense_condition(serv)) { set_state_for_client(serv, defense); set_whose_turn(serv, defense); put_one_card_from_queue_to_table(serv->cot, serv->cq); serv->state = defense_phase_out; } else { set_state_for_client(serv, tossing); set_whose_turn(serv, tossing); put_all_cards_from_queue_to_table(serv->cot, serv->cq); serv->turn_queue[1]->defense_lost = 1; serv->state = tossing_phase_out; } } static void define_phase_after_defense(struct server *serv) { /* card wasn't beated */ if(serv->cot->card_arr[serv->cot->card_arr_idx][0] == '-') { serv->turn_queue[1]->defense_lost = 1; put_all_cards_from_queue_to_table(serv->cot, serv->cq); if(check_someone_can_toss_card(serv->turn_queue, serv->connected_players_counter, serv->cq, serv->cot)) { set_state_for_client(serv, tossing); set_whose_turn(serv, tossing); serv->state = tossing_phase_out; /* then begin new attack */ } else { set_state_for_client(serv, display_only_table); serv->state = table; } /* can defender continue defense? */ } else { if(is_empty_queue(serv->cq)) { if(check_someone_can_toss_card(serv->turn_queue, serv->connected_players_counter, serv->cq, serv->cot)) { set_state_for_client(serv, tossing); set_whose_turn(serv, tossing); serv->state = tossing_phase_out; } else { set_state_for_client(serv, display_only_table); serv->state = table; } /* card queue contains some cards for defender */ } else { /* defender can defense */ if(defense_condition(serv)) { put_one_card_from_queue_to_table(serv->cot, serv->cq); set_state_for_client(serv, defense); set_whose_turn(serv, defense); serv->state = defense_phase_out; } /* defender can't defense */ #if 0 } else { serv->turn_queue[1]->defense_lost = 1; put_all_cards_from_queue_to_table(serv->cot, serv->cq); if(check_someone_can_toss_card(serv->turn_queue, serv->connected_players_counter, serv->cq, serv->cot)) { set_state_for_client(serv, tossing); set_whose_turn(serv, tossing); serv->state = tossing_phase_out; } else { move_all_cards_to_defender(serv->cot, &serv->turn_queue[1]->deck); /* add shifting of turn arr*/ set_state_for_client(serv, attack); set_whose_turn(serv, attack); serv->state = attack_phase_out; } } #endif } } } static void define_phase_after_tossing(struct server *serv) { if(serv->turn_queue[1]->defense_lost) { if(is_empty_queue(serv->cq)) { /* nobody's put any cards on the table */ set_state_for_client(serv, display_only_table); serv->state = table; } else { put_all_cards_from_queue_to_table(serv->cot, serv->cq); if(check_someone_can_toss_card(serv->turn_queue, serv->connected_players_counter, serv->cq, serv->cot)) { set_state_for_client(serv, tossing); set_whose_turn(serv, tossing); serv->state = tossing_phase_out; } else { set_state_for_client(serv, display_only_table); serv->state = table; } } /* defense continue? */ } else { if(is_empty_queue(serv->cq)) { set_state_for_client(serv, display_only_table); serv->state = table; } else { if(check_defender_can_defend(serv->cq, serv->turn_queue[1]->deck, serv->trump_suit)) { put_one_card_from_queue_to_table(serv->cot, serv->cq); set_state_for_client(serv, defense); set_whose_turn(serv, defense); serv->state = defense_phase_out; } else { serv->turn_queue[1]->defense_lost = 1; put_all_cards_from_queue_to_table(serv->cot, serv->cq); if(check_someone_can_toss_card(serv->turn_queue, serv->connected_players_counter, serv->cq, serv->cot)) { set_state_for_client(serv, tossing); set_whose_turn(serv, tossing); serv->state = tossing_phase_out; } else { set_state_for_client(serv, display_only_table); serv->state = table; } } } } } static void determine_server_state(struct server *serv) { switch(serv->state) { case no_players: serv->state = first_player; break; case first_player: if(check_playable_player_number(serv)) serv->state = confirmation_waiting; break; case confirmation_waiting: set_record_status_for_all(serv); init_new_game(serv); serv->state = start_game; break; case start_game: set_state_for_client(serv, attack); set_whose_turn(serv, attack); set_record_status_for_all(serv); serv->state = attack_phase_out; break; case attack_phase_out: serv->state = attack_phase_in; break; case attack_phase_in: case defense_phase_in: case tossing_phase_in: if(serv->state == attack_phase_in) define_phase_after_attack(serv); else if(serv->state == defense_phase_in) define_phase_after_defense(serv); else /* all answers received or the limit on card tossing was reached */ define_phase_after_tossing(serv); set_record_status_for_all(serv); update_card_quantity_arr(serv); update_game_info(serv->gi, serv->connected_players_counter, serv->shuffled_deck_size, serv->position_whose_turn); break; case table: if(serv->turn_queue[1]->defense_lost) { move_all_cards_to_defender(serv->cot, &serv->turn_queue[1]->deck); move_turn_queue_two_players_ahead(serv->turn_queue, serv->connected_players_counter); } else { remove_cards_from_table(serv->cot); shift_turn_queue_by_one(serv->turn_queue, serv->connected_players_counter); } set_record_status_for_all(serv); set_state_for_client(serv, attack); set_whose_turn(serv, attack); update_card_quantity_arr(serv); deal_cards(serv->shuffled_deck, &serv->shuffled_deck_size, serv->turn_queue, serv->connected_players_counter, serv->card_quantity_arr); serv->state = attack_phase_out; break; case defense_phase_out: serv->state = defense_phase_in; break; case tossing_phase_out: serv->state = tossing_phase_in; break; } serv->change_server_state = 0; } static int new_player_tracking_condition(enum server_states state) { return state == no_players || state == first_player || state == confirmation_waiting; } /* * when accepting new players */ static int server_state_change_condition(struct server *serv) { return serv->state == no_players || (serv->state == first_player && check_playable_player_number(serv)) || (serv->state == confirmation_waiting && is_max_playable_player_number(serv)); } static int accept_new_player(struct server *serv) { int accept_result; accept_result = accept_client(serv); if(accept_result == -1) { syslog(LOG_ERR, "accept error"); return -1; } /* update info about connected players */ set_record_status_for_all(serv); if(server_state_change_condition(serv)) serv->change_server_state = 1; return 1; } static void set_up_player_tracking(struct server *serv, fd_set *readfds, fd_set *writefds) { int i; for(i = 0; i < serv->max_sess_arr_size; ++i) { if(!serv->sess_arr[i]) continue; switch(serv->state) { case first_player: if(serv->sess_arr[i]->record) FD_SET(i, writefds); break; case confirmation_waiting: FD_SET(i, readfds); if(serv->sess_arr[i]->record) FD_SET(i, writefds); break; case start_game: case attack_phase_out: case table: case defense_phase_out: case tossing_phase_out: if(serv->sess_arr[i]->record) FD_SET(i, writefds); break; case attack_phase_in: if(serv->sess_arr[i]->state == attack) FD_SET(i, readfds); break; case defense_phase_in: if(serv->sess_arr[i]->state == defense) FD_SET(i, readfds); break; case tossing_phase_in: if(serv->sess_arr[i]->state == tossing && serv->sess_arr[i]->tm == answer_wait) FD_SET(i, readfds); break; case no_players: {} } } } static int check_server_state_change_conditions(int is_tossing_limit, struct server *serv) { switch(serv->state) { case start_game: case attack_phase_out: case attack_phase_in: case defense_phase_out: case defense_phase_in: case tossing_phase_out: case table: return 1; case tossing_phase_in: if(is_tossing_limit || check_all_answers_were_received((const struct session**) serv->turn_queue, serv->connected_players_counter)) return 1; default: return 0; } return 0; } static void close_connection() { } static void make_data_transfer(struct server *serv, fd_set *readfds, fd_set *writefds) { int i, result, tossing_limit = 0; for(i = 0; i < serv->max_sess_arr_size; ++i) { if(!serv->sess_arr[i]) continue; switch(serv->state) { case first_player: if(FD_ISSET(i, writefds)) { result = print_message_for_first_player(i); serv->sess_arr[i]->record = 0; } break; case confirmation_waiting: if(FD_ISSET(i, writefds) && serv->sess_arr[i]->record) { result = print_connected_players(i, serv->connected_players_counter); serv->sess_arr[i]->record = 0; } if(FD_ISSET(i, readfds)) { result = check_readiness(serv->sess_arr[i]); if(result == 1) serv->change_server_state = 1; } break; case start_game: case attack_phase_out: case defense_phase_out: case tossing_phase_out: case table: if(FD_ISSET(i, writefds) && serv->sess_arr[i]->record) result = print_game_part(serv->sess_arr[i], serv->gi); serv->sess_arr[i]->record = 0; break; case attack_phase_in: if(FD_ISSET(i, readfds)) result = get_cards_from_attacker(serv->sess_arr[i], serv->cq); break; case defense_phase_in: if(FD_ISSET(i, readfds)) result = get_card_from_defender(serv->sess_arr[i], serv->cot, serv->trump_suit); break; case tossing_phase_in: if(FD_ISSET(i, readfds)) { /* to determine that the player can no longer tossing */ if(tossing_limit) serv->sess_arr[i]->tm = cancel; result = get_cards_from_tossing_player(serv->sess_arr[i], serv->turn_queue[1]->deck, serv->cot, serv->cq); if(result == 1 || result == 2) serv->sess_arr[i]->tm = answer_got; if(result == 2) tossing_limit = 1; else if(result == 3) serv->sess_arr[i]->tm = cancel; } break; case no_players: {} } } if(serv->state == start_game || serv->state == table) sleep(2); if(check_server_state_change_conditions(tossing_limit, serv)) serv->change_server_state = 1; /* connection is closed */ if(!result) close_connection(); } int main_loop(struct server *serv) { int maxfd, accept_result; fd_set readfds, writefds; for(;;) { if(serv->change_server_state) determine_server_state(serv); FD_ZERO(&readfds); FD_ZERO(&writefds); if(new_player_tracking_condition(serv->state)) { FD_SET(serv->listen_socket, &readfds); maxfd = serv->listen_socket; } set_up_player_tracking(serv, &readfds, &writefds); select(maxfd + 1, &readfds, &writefds, NULL, NULL); if(FD_ISSET(serv->listen_socket, &readfds)) { accept_result = accept_new_player(serv); if(accept_result == -1) return 4; } make_data_transfer(serv, &readfds, &writefds); } } /* * 0 - error * 1 - success */ static int init_server(struct server *serv, int port) { int opt, i; struct sockaddr_in addr; serv->listen_socket = socket(AF_INET, SOCK_STREAM, 0); if(serv->listen_socket == -1) return 0; opt = 1; setsockopt(serv->listen_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_port = htons(port); if(bind(serv->listen_socket, (struct sockaddr*) &addr, sizeof(addr)) == -1) return 0; serv->sess_arr = malloc(sizeof(*serv->sess_arr) * init_sess_arr_size); serv->max_sess_arr_size = init_sess_arr_size; for(i = 0; i < init_sess_arr_size; i++) serv->sess_arr[i] = NULL; serv->state = no_players; serv->change_server_state = 0; serv->connected_players_counter = 0; serv->shuffled_deck = NULL; serv->shuffled_deck_size = max_shuffled_deck_size; serv->gi = NULL; serv->card_quantity_arr = NULL; serv->cq = NULL; serv->cot = NULL; listen(serv->listen_socket, listen_qlen); return 1; } #if 0 static void demonization() { int pid; close(0); close(1); close(2); open("/dev/null", O_RDONLY); open("/dev/null", O_WRONLY); open("/dev/null", O_WRONLY); chdir("/"); pid = fork(); if(pid) exit(0); setsid(); pid = fork(); if(pid) exit(0); } #endif int main(int argc, char **argv) { unsigned short port; struct server serv; if(argc != 2) { fprintf(stderr, "Usage: \n"); return 1; } #if 0 demonization(); #endif srand(time(NULL)); #if 0 openlog("durak server", 0, LOG_USER); syslog(LOG_INFO, "daemon started"); #endif port = strtol(argv[1], NULL, 10); if(!init_server(&serv, port)) { #if 0 syslog(LOG_ERR, "server initialization error"); #endif return 2; } return main_loop(&serv); #if 0 syslog(LOG_INFO, "server terminated"); closelog(); #endif }