#include #include #include #include #include #include #include #include #include "server.h" #include "../data_store.h" /** * Return socket with connection to player with specified player id */ int socket_for_player_id(const socket_list_t *client_socks, const player_id_t pid) { for(int i=0; icount; i++) { if(client_socks->player_ids[i] == pid) return client_socks->sockets[i]; } assert(false); return 0; } /** * Starts the server. The server will listen on a specified port. * @param[in] port Port on which server should listen * @return Listening socket */ int server_start(const char* addr, const char* port) { assert(port != NULL); int status; int serversock; struct addrinfo hints, *result, *tmp; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; // IPv4 or IPv6 hints.ai_socktype = SOCK_STREAM; // TCP socket hints.ai_flags = AI_PASSIVE; // wildcard IP hints.ai_protocol = 0; // any protocol status = getaddrinfo(addr, port, &hints, &result); if(status != 0) { printf("getaddrinfo: %s\n", gai_strerror(status)); exit(EXIT_FAILURE); } // iterate over linked addrinfo list and use first useable entry for(tmp = result; tmp != NULL; tmp = tmp->ai_next) { int yes=1; // create socket serversock = socket(tmp->ai_family, tmp->ai_socktype, tmp->ai_protocol); if(serversock == -1) continue; // try to reuse still open sockets in TIME_WAIT state setsockopt(serversock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); // try to bind to address/port if(bind(serversock, tmp->ai_addr, tmp->ai_addrlen) == 0) break; // Success! close(serversock); } if(tmp == NULL) { printf("failed to bind\n"); exit(EXIT_FAILURE); } freeaddrinfo(result); // start listening status = listen(serversock, 1); if(status == -1) { printf("listen: %s\n", strerror(status)); exit(EXIT_FAILURE); } return serversock; } /** * Waits for and accepts connections from clients. Each client connection is represented by a socket that will be stored in a list for further communication. * @param[in] serversock Socket on which server is listening * @param[out] client_socks Socket list in which to store open client connections * @param[in] count Number of clients that should connect */ void server_get_players(int serversock, socket_list_t* client_socks, const uint8_t count) { int i; assert(count <= MAX_PLAYERS && count > 0); // accept connections for(i=0; isockets[i] = accept(serversock, (struct sockaddr*) &addr, &addrlen); if(client_socks->sockets[i] == -1) { printf("accept: %s\n", strerror(client_socks->sockets[i])); exit(EXIT_FAILURE); } //printf("new client connected: %s\n", inet_ntop(sock.ss_family, get_in_addr((struct sockaddr*)&sock), INET6_ADDRSTRLEN)); printf("new client connected (%d/%d)\n", i+1, count); } client_socks->count = count; } /** * Parses hello message from client and store username in the global data store. * @param[in] msg The message to parse * @return true */ bool server_parse_hello(const msg_t *m) { assert(m != NULL); assert(m->hdr.payload_length > 0); assert(m->hdr.payload_length < MAX_PLAYER_NAME_LENGTH); data_store_t *ds = data_store(); for(int i=0; iplayer_list.count; i++) { if(ds->player_list.players[i].player_id != 0) // search for first empty (not yet assigned) slot continue; memcpy(ds->player_list.players[i].player_name, m->payload, m->hdr.payload_length); ds->player_list.players[i].player_name[m->hdr.payload_length] = '\0'; break; } return true; } /** * Parses open card message from client and stores the selected card in the global data store. * @param[in] msg The message to parse * @return true */ bool server_parse_selected_card(const msg_t *m) { assert(m != NULL); assert(m->hdr.payload_length == 1); data_store_t *ds = data_store(); ds->selected_card = m->payload[0]; return true; } /** * Parses selected stack message from client and stores the stack index in the global data store. * @param[in] msg The message to parse * @return true */ bool server_parse_selected_stack(const msg_t *m) { assert(m != NULL); assert(m->hdr.payload_length == 1); data_store_t *ds = data_store(); ds->stack_index = m->payload[0]; assert(ds->stack_index <= NUM_TABLE_STACKS); return true; } /** * Prepares start game message. All player IDs, player names and name lengths will be saved in the message payload. * @param[out] msg A preallocated message object to store header and payload information */ void server_prep_start_game(msg_t *m) { uint16_t pos = 0; data_store_t *ds = data_store(); player_list_t *player_list = &ds->player_list; m->hdr.type = msg_type_start_game; m->payload[pos++] = player_list->count; // copy player_ids, length and nicknames to message payload for(int i = 0; i < player_list->count; i++) { player_list_entry_t *ple = &player_list->players[i]; m->payload[pos++] = ple->player_id; uint8_t len = strlen(ple->player_name); m->payload[pos++] = len; memcpy(m->payload+pos, ple->player_name, len); pos += len; } m->hdr.payload_length = pos; } void server_prep_hello(msg_t *m, const player_list_entry_t* player) { m->hdr.type = msg_type_hello_s; m->payload[0] = player->player_id; m->hdr.payload_length = 1; } void server_prep_selected_stack(msg_t *m) { data_store_t *ds = data_store(); m->hdr.type = msg_type_selected_stack_s; m->payload[0] = ds->stack_index; m->hdr.payload_length = 1; } void server_prep_deal_hand(msg_t *m, const hand_t *h) { assert(h != NULL); m->hdr.type = msg_type_deal_hand; for(int i=0; ipayload[i] = h->cards[i]; m->hdr.payload_length = MAX_HAND_CARDS; } void server_prep_initial_stacks(msg_t *m) { data_store_t *ds = data_store(); m->hdr.type = msg_type_initial_stacks; for(int i=0; ipayload[i] = ds->table_stacks.stacks[i].cards[0]; m->hdr.payload_length = NUM_TABLE_STACKS; } void server_prep_selected_card_all(msg_t *m) { uint8_t pos = 0; data_store_t *ds = data_store(); m->hdr.type = msg_type_selected_card_all; for(int i = 0; i < ds->player_list.count; i++) { player_list_entry_t *ple = &ds->player_list.players[i]; if(ple->player_id == 0) // invalid player continue; m->payload[pos++] = ple->player_id; m->payload[pos++] = ple->open_card; } m->hdr.payload_length = pos; } void server_prep_next_action(msg_t *m) { data_store_t *ds = data_store(); m->hdr.type = msg_type_next_action; m->payload[0] = ds->game_finished; m->hdr.payload_length = 1; }