From a2d696dea797faaa3157046c8ae89cd70e965bff Mon Sep 17 00:00:00 2001 From: scratko Date: Sat, 10 Aug 2024 02:46:56 +0300 Subject: Prefinal version Added client. Moved files to directories. --- server/server.c | 759 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 759 insertions(+) create mode 100644 server/server.c (limited to 'server/server.c') diff --git a/server/server.c b/server/server.c new file mode 100644 index 0000000..0a2bd0f --- /dev/null +++ b/server/server.c @@ -0,0 +1,759 @@ +#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_card = + extract_trump_card(serv->shuffled_deck, &serv->shuffled_deck_size); + serv->trump_suit = serv->trump_card + strlen(serv->trump_card) - 1; +#if 0 + serv->trump_suit = find_trump_suit(serv->shuffled_deck, + &serv->shuffled_deck_size); +#endif + serv->turn_queue = malloc(sizeof(struct session*) * + serv->connected_players_counter); + /* number of cards each player holds */ + serv->cc.number_arr_idx = serv->connected_players_counter - 1; + serv->cc.number_arr = malloc(sizeof(int) * serv->connected_players_counter); + serv->position_whose_turn = 0; + serv->cot.card_arr_idx = -1; + /* + * 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->gi = get_new_game_info(serv->connected_players_counter, + serv->cc.number_arr, + serv->shuffled_deck_size, + serv->trump_card, 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); +} +/* + * TODO refactoring + */ +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 { + serv->turn_queue[1]->defense_lost = 1; + put_all_cards_from_queue_to_table(&serv->cot, &serv->cq); + if(is_receiving_cards_limit(&serv->cot, serv->turn_queue[1]->deck, + &serv->cq)) { + set_state_for_client(serv, display_only_table); + serv->state = table; + } else { + set_state_for_client(serv, tossing); + set_whose_turn(serv, tossing); + 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); + /* go to tossing cards */ + if(!is_receiving_cards_limit(&serv->cot, serv->turn_queue[1]->deck, + &serv->cq) && + 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 { + /* the defender has beaten the attack or tossing phase completely */ + if(is_empty_queue(&serv->cq)) { + if(!is_receiving_cards_limit(&serv->cot, serv->turn_queue[0]->deck, + &serv->cq) && + 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(is_receiving_cards_limit(&serv->cot, serv->turn_queue[1]->deck, + &serv->cq) && + 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(is_receiving_cards_limit(&serv->cot, + serv->turn_queue[1]->deck, + &serv->cq) && + 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->cc.number_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->cot, + serv->turn_queue[1]->deck, + &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, select_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_result = select(maxfd + 1, &readfds, &writefds, NULL, NULL); + if(select_result == -1) + return 3; + + 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->cc.number_arr = 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 +} -- cgit v1.2.3