back to scratko.xyz
aboutsummaryrefslogtreecommitdiff
path: root/windows_client/client.c
diff options
context:
space:
mode:
authorscratko <m@scratko.xyz>2024-08-30 12:46:56 +0300
committerscratko <m@scratko.xyz>2024-08-30 14:59:44 +0300
commit831f9f01fbe4088eb6bd378c0e417d9996b676fd (patch)
tree53297459d35ad795618c351a79b1829776e5e1f4 /windows_client/client.c
parent4b6c15f780d59895f067383a5041edcfe86f504e (diff)
downloaddurak-831f9f01fbe4088eb6bd378c0e417d9996b676fd.tar.gz
durak-831f9f01fbe4088eb6bd378c0e417d9996b676fd.tar.bz2
durak-831f9f01fbe4088eb6bd378c0e417d9996b676fd.zip
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.
Diffstat (limited to 'windows_client/client.c')
-rw-r--r--windows_client/client.c440
1 files changed, 440 insertions, 0 deletions
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 <winsock2.h>
+#include <windows.h>
+#include <conio.h> /* _getch() */
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h> /* 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);
+}