From 831f9f01fbe4088eb6bd378c0e417d9996b676fd Mon Sep 17 00:00:00 2001 From: scratko Date: Fri, 30 Aug 2024 12:46:56 +0300 Subject: Final version v2.0 Added windows client. SIGPIPE signal was being sent to the server when the client was disconnected. Now there is handling of this signal. Added a delay when displaying some informational messages. --- windows_client/client.c | 440 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 440 insertions(+) create mode 100644 windows_client/client.c (limited to 'windows_client/client.c') diff --git a/windows_client/client.c b/windows_client/client.c new file mode 100644 index 0000000..521dd0f --- /dev/null +++ b/windows_client/client.c @@ -0,0 +1,440 @@ +#include +#include +#include /* _getch() */ +#include +#include +#include +#include /* for debug */ + +#include "client.h" +#include "data_decryption.h" +#include "printing_game_frames.h" +#include "verification_client_input.h" + +static const char *ip = "109.107.161.30"; +static const int port = 1025; + +static void clean_up_resources(struct client *cl) +{ + free(cl->cc.number_arr); + clear_stack(&cl->deck); +} + +static void get_data_from_server(struct client *cl, fd_set *readfds) +{ + int i; + int update_info = 0; + /* pointer to delimeter (:) that is behind extract data */ + char *end_p; + + if(FD_ISSET(cl->fd, readfds) && !cl->read_data) { + if(!cl->data_left_in_buffer) { + cl->data_left_in_buffer = + recv(cl->fd, cl->buffer, max_buffer_size, 0); + /* end of file -- closed connection (from server) */ + if(!cl->data_left_in_buffer) { + clean_up_resources(cl); + WSACleanup(); + exit(0); + } +#ifdef DEBUG + printf("%d\n", cl->data_left_in_buffer); + for(i = 0; i < cl->data_left_in_buffer; ++i) + putchar(cl->buffer[i]); + putchar('\n'); +#endif + } + decrypt_set_state(cl, &end_p); + update_info = 1; + cl->display_new_frame = 1; + cl->pending_server_response = 0; + } + if(update_info) { + switch(cl->state) { + case first_player: + break; + case confirmation_waiting: + cl->total_players = decrypt_get_number(end_p+1, &end_p); + break; + case display_only_table: + case tossing_expectation: + case tossing: + /* last arg will contain pointer to '\n' */ + decrypt_set_base_info(cl, end_p+1, &end_p); + break; + case attack: + case defense: + case attack_expectation: + decrypt_set_base_info(cl, end_p+1, &end_p); + decrypt_set_position_whose_turn(cl, end_p+1, &end_p); + break; + case defense_expectation: + case spectator: + decrypt_set_base_info(cl, end_p+1, &end_p); + decrypt_set_position_whose_turn(cl, end_p+1, &end_p); + decrypt_set_card_queue(cl, end_p+1, &end_p); + if(cl->state == spectator) + decrypt_set_spectator_mode(cl, end_p+1); + break; + case card_acceptance_status: + decrypt_set_card_acceptance_status(cl, end_p+1); + break; + case result: + decrypt_set_durak_position(cl, end_p+1); + break; + /* no data to extract */ + case tossing_limit_status: + case disconnect: + default: + {} + } + for(i = 0; cl->buffer[i] != '\n'; ++i) + {} + if((cl->buffer+i - cl->buffer + 1) != cl->data_left_in_buffer) { + cl->data_left_in_buffer -= i + 1; + memmove(cl->buffer, cl->buffer+i+1, cl->data_left_in_buffer); + } else + cl->data_left_in_buffer = 0; + } +} + +static void prepare_tips_for_client(struct client *cl) +{ + char *trump_suit = NULL; + + if(!is_empty_stack(cl->deck)) + add_hint_letters_stack(cl->deck); + switch(cl->state) { + case attack: + mark_card_for_attackers_stack(cl->deck); + break; + case defense: + trump_suit = cl->trump_card + strlen(cl->trump_card) - 1; + mark_card_for_defenders_stack(cl->deck, &cl->cot, trump_suit); + break; + case tossing: + mark_card_for_tossing_stack(cl->deck, &cl->cot); + break; + default: + {} + } +} + +static void change_client_frame(struct client *cl) +{ + if(cl->display_new_frame) { + pgf_new_frame(); + switch(cl->state) { + case first_player: + pgf_first_player(); + break; + case confirmation_waiting: + pgf_confirmation_waiting(cl->total_players); + break; + case display_only_table: + case attack_expectation: + case defense_expectation: + case tossing_expectation: + pgf_table(cl); + if(cl->state == attack_expectation || + cl->state == defense_expectation || + cl->state == tossing_expectation) + { + pgf_select_idle_mode_message(cl->state); + } + break; + case spectator: + pgf_table(cl); + pgf_spectator_mode(cl->sp_mode); + break; + case attack: + case defense: + case tossing: + pgf_table(cl); + pgf_suggestions(cl); + break; + case card_acceptance_status: + pgf_card_acceptance_status(cl->all_input_cards_accepted); + break; + case tossing_limit_status: + pgf_tossing_limit_status(); + break; + case result: + pgf_game_result(cl->durak_position); + break; + case disconnect: + pgf_disconnect(); + break; + case none: + {} + } + cl->display_new_frame = 0; + } +} + +static int check_users_exit(enum client_states state, const char *buffer, + int size) +{ + if((state == first_player || state == confirmation_waiting) && + size == 2 && (buffer[0] == 'q' && buffer[1] == '\n')) + return 1; + + return size == 3 && !strncmp(buffer, "qq", 2) && buffer[2] == '\n'; +} + +static int fill_in_buffer(struct client *cl, int *data_size, + INPUT_RECORD *irInBuf) +{ + int i, buffer_idx, end_status = 0, input_status = 0; + int key; + + for(i = 0, buffer_idx = 0; i < *data_size; ++i) { + if(irInBuf[i].Event.KeyEvent.bKeyDown) { + key = irInBuf[i].Event.KeyEvent.uChar.AsciiChar; + if(key != '\n' && key != '\r' && (key < 'A' || key > 'z' )) + { + return 0; + } + cl->buffer[buffer_idx] = key; + putchar(key); + if(key == '\n' || key == '\r') { + cl->buffer[buffer_idx] = '\n'; + end_status = 1; + } + ++buffer_idx; + input_status = 1; + } + } + /* no data was input */ + if(!input_status) + return 0; + /* the data has not yet been entered up to the newline character */ + if(!end_status && input_status) { + for(; (key = _getch()) != '\r'; ) { + if(key == back && buffer_idx > 0) { + --buffer_idx; + cl->buffer[buffer_idx] = '\0'; + putchar(back); + putchar(' '); + putchar(back); + continue; + } else if(key == back) + continue; + cl->buffer[buffer_idx] = key; + ++buffer_idx; + putchar(key); + } + cl->buffer[buffer_idx] = '\n'; + putchar('\n'); + ++buffer_idx; /* like size of buffer */ + } + *data_size = buffer_idx; + return 1; +} + +static void send_data_to_server(struct client *cl) +{ + DWORD cNumRead; + int input_result = 0, data_size; + INPUT_RECORD irInBuf[128]; + + if(cl->read_data) { + ReadConsoleInput(GetStdHandle(STD_INPUT_HANDLE), irInBuf, 128, + &cNumRead); + data_size = (int) cNumRead; + /* data_size will be changed */ + if(!fill_in_buffer(cl, &data_size, irInBuf)) { + cl->read_data = 0; + return; + } + if(check_users_exit(cl->state, cl->buffer, data_size)) { + closesocket(cl->fd); + clean_up_resources(cl); + pgf_disconnect(); + WSACleanup(); + exit(0); + } + + switch(cl->state) { + case confirmation_waiting: + input_result = vci_confirmation_waiting(cl->fd, cl->buffer); + break; + case attack: + case tossing: + input_result = vci_attack_or_tossing(cl->fd, cl->buffer, cl->deck, + cl->state); + break; + case defense: + input_result = vci_defense(cl->fd, cl->buffer, cl->deck); + break; + default: + {} + } + /* if 0, then re-tracking the input client */ + if(input_result) + cl->pending_server_response = 1; + cl->read_data = 0; + } +} + +static int check_tracking_client_input(enum client_states state, + int pending_server_response) +{ + if(pending_server_response) + return 0; + + switch(state) { + case first_player: + case confirmation_waiting: + case attack: + case defense: + case tossing: + return 1; + default: + return 0; + } + return 0; +} + +static int key_was_pressed() +{ + return + WaitForSingleObject(GetStdHandle(STD_INPUT_HANDLE), 0) == WAIT_OBJECT_0; +} + +static void wait_for_key_press() +{ + WaitForSingleObject(GetStdHandle(STD_INPUT_HANDLE), INFINITE); +} + +int main_loop(struct client *cl) +{ + struct timeval timeout; + int select_result, error; + fd_set readfds; + DWORD fdwMode; + + fdwMode = + ENABLE_WINDOW_INPUT | ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | + ENABLE_PROCESSED_INPUT; + FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE)); + SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), fdwMode); + + for(;;) { + FD_ZERO(&readfds); + + FD_SET(cl->fd, &readfds); + if(check_tracking_client_input(cl->state, cl->pending_server_response)) { + if(cl->state == attack || cl->state == defense || + cl->state == tossing) + { + wait_for_key_press(); + if(key_was_pressed()) + cl->read_data = 1; + } else if(key_was_pressed()) { + cl->read_data = 1; + } + } + + if(!cl->data_left_in_buffer && !cl->read_data) { + if(cl->state == first_player || cl->state == confirmation_waiting) { + timeout.tv_sec = 1; + timeout.tv_usec = 0; + } + select_result = + select(cl->fd + 1, &readfds, NULL, NULL, + cl->state == first_player || + cl->state == confirmation_waiting ? &timeout : NULL); + if(select_result == SOCKET_ERROR) { + error = WSAGetLastError(); + if(error == WSANOTINITIALISED) + printf("WSANOTINITIALISED\n"); + else if(error == WSAEFAULT) + printf("WSAEFAULT\n"); + else if(error == WSAENETDOWN) + printf("WSAENETDOWN\n"); + else if(error == WSAEINVAL) + printf("WSAEINVAL\n"); + else if(error == WSAEINTR) + printf("WSAEINTR\n"); + else if(error == WSAEINPROGRESS) + printf("WSAEINPROGRESS\n"); + else if(error == WSAENOTSOCK) + printf("WSAENOTSOCK\n"); + return 2; + } + } + get_data_from_server(cl, &readfds); + prepare_tips_for_client(cl); + if(cl->display_new_frame) + change_client_frame(cl); + send_data_to_server(cl); + } +} + +static void init_client(struct client *cl) +{ + cl->fd = -1; + cl->state = none; + cl->total_players = 0; + cl->total_cards_left = 0; + cl->player_position = 0; + cl->cc.number_arr = NULL; + cl->cc.number_arr_idx = -1; + cl->cot.card_arr_idx = -1; + cl->deck = NULL; + cl->pending_server_response = 0; + cl->cq.card_arr_idx = -1; + cl->position_whose_turn = 0; + cl->display_new_frame = 0; + cl->all_input_cards_accepted = 0; + cl->data_left_in_buffer = 0; + cl->sp_mode = spectator_table; + cl->durak_position = 0; + cl->read_data = 0; +} + +/* + * 0 - failure + * 1 - success + */ +static int connect_to_server(struct client *cl) +{ + int connect_result; + struct sockaddr_in addr; + + cl->fd = socket(AF_INET, SOCK_STREAM, 0); + + if(cl->fd == -1) + return 0; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = inet_addr(ip); + addr.sin_port = htons(port); + + connect_result = connect(cl->fd, (struct sockaddr*) &addr, sizeof(addr)); + if(connect_result == -1) + return 0; + return 1; +} + +int main() +{ + struct client cl; + static WSADATA wsaData; + int wsaerr = WSAStartup(MAKEWORD(2, 0), &wsaData); + if (wsaerr) + exit(1); + + pgf_new_frame(); + pgf_welcome(); + sleep(2); + init_client(&cl); + if(!connect_to_server(&cl)) { + pgf_new_frame(); + pgf_connection(0); + return 1; + } + pgf_new_frame(); + pgf_connection(1); + return main_loop(&cl); +} -- cgit v1.2.3