summaryrefslogtreecommitdiff
path: root/src/net
diff options
context:
space:
mode:
Diffstat (limited to 'src/net')
-rw-r--r--src/net/client.c156
-rw-r--r--src/net/client.h15
-rw-r--r--src/net/comm.c106
-rw-r--r--src/net/comm.h43
-rw-r--r--src/net/server.c202
-rw-r--r--src/net/server.h25
6 files changed, 547 insertions, 0 deletions
diff --git a/src/net/client.c b/src/net/client.c
new file mode 100644
index 0000000..2606bd4
--- /dev/null
+++ b/src/net/client.c
@@ -0,0 +1,156 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <assert.h>
+#include "client.h"
+#include "../global.h"
+#include "../player.h"
+#include "../game.h"
+
+/**
+ * Client side function; connects to specified host:port
+ * @param[in] host Hostname of server
+ * @param[in] port Port of server
+ * @return Socket with open connection to server
+ */
+int client_connect_server(const char* host, const char* port)
+{
+ assert(host != NULL);
+ assert(port != NULL);
+
+ int status;
+ int sock;
+ struct addrinfo hints, *result, *tmp;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC; // IPv4 or IPv6
+ hints.ai_socktype = SOCK_STREAM; // TCP socket
+
+ status = getaddrinfo(host, port, &hints, &result);
+ if(status != 0)
+ {
+ printf("getaddrinfo: %s\n", gai_strerror(status));
+ exit(EXIT_FAILURE);
+ }
+
+ // connect to first result in linked addrinfo list
+ for(tmp = result; tmp != NULL; tmp = tmp->ai_next)
+ {
+ // create socket
+ sock = socket(tmp->ai_family, tmp->ai_socktype, tmp->ai_protocol);
+ if(sock == -1)
+ continue;
+
+ // connect!
+ if(connect(sock, tmp->ai_addr, tmp->ai_addrlen) != -1)
+ break; // Success!
+
+ close(sock);
+ }
+
+ if(tmp == NULL)
+ {
+ printf("failed to connect\n");
+ exit(EXIT_FAILURE);
+ }
+ freeaddrinfo(result);
+
+ return sock;
+}
+
+bool client_recv_player_list(const uint8_t* payload, const uint8_t data_len)
+{
+ assert(payload != NULL);
+
+ data_store* ds = datamodel();
+ uint32_t pos = 0;
+
+ ds->players.count = payload[pos++];
+
+ // read usernames from buffer
+ for(int i=0; i<ds->players.count; i++)
+ {
+ uint8_t namelen;
+ ds->players.players[i].player_id = payload[pos++];
+ namelen = payload[pos++];
+ assert(namelen <= MAX_PLAYER_NAME_LENGTH);
+ strncpy(ds->players.players[i].player_name, (const char*) payload+pos, namelen);
+ ds->players.players[i].player_name[namelen] = '\0';
+ pos += namelen;
+ }
+
+ return true;
+}
+
+bool client_recv_deal_cards(const uint8_t* payload, const uint8_t payload_len)
+{
+ assert(payload != NULL);
+ assert(payload_len == MAX_HAND_CARDS); // deal_cards packet have fixed size
+
+ data_store* ds = datamodel();
+
+ for(int i=0; i<MAX_HAND_CARDS; i++)
+ ds->hand.cards[i] = payload[i];
+
+ return true;
+}
+
+bool client_recv_selected_stack(const uint8_t* payload, const uint8_t payload_len)
+{
+ assert(payload != NULL && payload_len == 1);
+
+ data_store* ds = datamodel();
+ ds->stack_index = payload[0];
+
+ assert(ds->stack_index <= NUM_TABLE_STACKS);
+
+ return true;
+}
+
+bool client_recv_initial_stacks(const uint8_t* payload, const uint8_t payload_len)
+{
+ assert(payload != NULL && payload_len == NUM_TABLE_STACKS);
+
+ data_store* ds = datamodel();
+ for(int i=0; i<NUM_TABLE_STACKS; i++)
+ ds->table_stacks.stacks[i].cards[0] = payload[i];
+
+ return true;
+}
+
+uint8_t client_send_hello(uint8_t* payload, const uint8_t payload_len)
+{
+ data_store* ds = datamodel();
+ uint8_t namelen = strlen(ds->nickname);
+
+ memcpy(payload, ds->nickname, namelen);
+
+ return namelen;
+}
+
+uint8_t client_send_selected_card(uint8_t* payload, const uint8_t payload_len)
+{
+ data_store* ds = datamodel();
+ card c = ds->selected_card;
+
+ assert(c >= MIN_CARD && c <= MAX_CARD);
+
+ payload[0] = c;
+
+ return 1;
+}
+
+uint8_t client_send_selected_stack(uint8_t* payload, const uint8_t payload_len)
+{
+ data_store* ds = datamodel();
+
+ payload[0] = ds->stack_index;
+ assert(ds->stack_index <= NUM_TABLE_STACKS);
+
+ return 1;
+}
+
diff --git a/src/net/client.h b/src/net/client.h
new file mode 100644
index 0000000..31b3d30
--- /dev/null
+++ b/src/net/client.h
@@ -0,0 +1,15 @@
+#ifndef OXEN_CLIENT_H
+#define OXEN_CLIENT_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+int client_connect_server(const char* host, const char* port);
+uint8_t client_send_hello(uint8_t* payload, const uint8_t payload_len);
+uint8_t client_send_selected_card(uint8_t* payload, const uint8_t payload_len);
+uint8_t client_send_selected_stack(uint8_t* payload, const uint8_t payload_len);
+bool client_recv_player_list(const uint8_t* payload, const uint8_t data_len);
+bool client_recv_deal_cards(const uint8_t* payload, const uint8_t payload_len);
+bool client_recv_selected_stack(const uint8_t* payload, const uint8_t payload_len);
+
+#endif // OXEN_CLIENT_H
diff --git a/src/net/comm.c b/src/net/comm.c
new file mode 100644
index 0000000..5a283db
--- /dev/null
+++ b/src/net/comm.c
@@ -0,0 +1,106 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include "comm.h"
+#include "client.h"
+#include "server.h"
+
+bool net_recv(int sock, const msg_type_t type)
+{
+ msg_t m;
+ bool result;
+ ssize_t len = recv(sock, &m.hdr, sizeof(msg_header_t), MSG_PEEK); // just peek into packet to determine message header
+
+ assert(len != -1);
+
+ if(m.hdr.type != type)
+ {
+ printf("net_recv: received message type %d instead of %d", m.hdr.type, type);
+ return false;
+ }
+
+ m.payload = malloc(m.hdr.payload_length); // Allocate space for message payload
+ recv(sock, &m.hdr, sizeof(msg_header_t), 0); // Remove message header from socket
+ recv(sock, m.payload, m.hdr.payload_length, 0);// And then receive the payload
+
+ switch(type)
+ {
+ case msg_type_hello:
+ result = server_recv_hello(m.payload, m.hdr.payload_length);
+ break;
+ case msg_type_start_game:
+ result = client_recv_player_list(m.payload, m.hdr.payload_length);
+ break;
+ case msg_type_deal_cards:
+ result = client_recv_deal_cards(m.payload, m.hdr.payload_length);
+ break;
+ case msg_type_init_stacks:
+ printf("not yet implemented: msg_type_init_stacks\n");
+ exit(EXIT_FAILURE);
+ break;
+ case msg_type_selected_card:
+ result = server_recv_selected_card(m.payload, m.hdr.payload_length);
+ break;
+ case msg_type_selected_stack_c:
+ result = server_recv_selected_stack(m.payload, m.hdr.payload_length);
+ break;
+ case msg_type_selected_stack_s:
+ result = client_recv_selected_stack(m.payload, m.hdr.payload_length);
+ break;
+ default:
+ printf("net_recv: Unknown message type %d received!\n", type);
+ exit(EXIT_FAILURE);
+ break;
+ }
+
+ free(m.payload);
+
+ return result;
+}
+
+bool net_send(int sock, const msg_type_t type, void* param)
+{
+ bool result = true;
+ uint8_t payload_len = 255;
+ uint8_t buf[payload_len+2]; // should be enough for all packet types
+
+ buf[0] = type;
+
+ switch(type)
+ {
+ case msg_type_hello:
+ payload_len = client_send_hello(&buf[2], payload_len);
+ break;
+ case msg_type_selected_card:
+ payload_len = client_send_selected_card(&buf[2], payload_len);
+ break;
+ case msg_type_selected_stack_c:
+ payload_len = client_send_selected_stack(&buf[2], payload_len);
+ break;
+ case msg_type_start_game:
+ payload_len = server_send_start_game(&buf[2], payload_len);
+ break;
+ case msg_type_selected_stack_s:
+ payload_len = server_send_selected_stack(&buf[2], payload_len);
+ break;
+ case msg_type_deal_cards:
+ payload_len = server_send_deal_cards(&buf[2], payload_len, param);
+ break;
+ case msg_type_initial_stack:
+ payload_len = server_send_initial_stacks(&buf[2], payload_len);
+ break;
+ default:
+ printf("net_send: Unknown message type %d received\n", type);
+ exit(EXIT_FAILURE);
+ break;
+ }
+
+ buf[1] = payload_len;
+
+ send(sock, buf, payload_len+2, 0);
+
+ return result;
+}
+
diff --git a/src/net/comm.h b/src/net/comm.h
new file mode 100644
index 0000000..03816c8
--- /dev/null
+++ b/src/net/comm.h
@@ -0,0 +1,43 @@
+#ifndef OXEN_NET_H
+#define OXEN_NET_H
+
+#include <stdint.h>
+#include <stdbool.h>
+
+// Offsets within the receive buffer to easily access the data fields of the received message
+#define NET_MSG_OFFSET_TYPE 0
+#define NET_MSG_OFFSET_PAYLOAD_LENGTH 1
+#define NET_MSG_OFFSET_PAYLOAD 2
+
+typedef enum
+{
+ // Specify message type identifiers here
+ msg_type_hello = 0x0,
+ msg_type_start_game = 0x1,
+ msg_type_deal_cards = 0x2,
+ msg_type_init_stacks = 0x3,
+ msg_type_selected_card = 0x4,
+ msg_type_selected_stack_c = 0x5,
+ msg_type_selected_stack_s = 0x6,
+ msg_type_initial_stack = 0x7
+} msg_type_t;
+
+// Header format
+typedef struct
+{
+ uint8_t type;
+ uint8_t payload_length;
+} msg_header_t;
+
+// Message format
+typedef struct
+{
+ msg_header_t hdr;
+ uint8_t *payload;
+} msg_t;
+
+// generic network functions
+bool net_recv(int sock, msg_type_t wanted);
+bool net_send(int sock, const msg_type_t type, void* param);
+
+#endif // OXEN_NET_H
diff --git a/src/net/server.c b/src/net/server.c
new file mode 100644
index 0000000..10d5d11
--- /dev/null
+++ b/src/net/server.c
@@ -0,0 +1,202 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <assert.h>
+#include "server.h"
+#include "../global.h"
+
+/**
+ * Server side function; start server on specified port
+ * @param[in] port Port on which server should listen
+ * @return Listening socket
+ */
+int server_start(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(NULL, 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;
+}
+
+/**
+ * Server side function; accepts connections from clients
+ * @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; i<count; i++)
+ {
+ struct sockaddr_storage addr;
+ socklen_t addrlen = sizeof(addr);
+ client_socks->sockets[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;
+}
+
+/**
+ * Server side function; receive hello message from client and read username
+ * @param[in] sock Socket to use
+ * @return Username of client
+ */
+bool server_recv_hello(const uint8_t* payload, const uint8_t payload_len)
+{
+ assert(payload != NULL && payload_len < MAX_PLAYER_NAME_LENGTH && payload_len > 0);
+
+ data_store* ds = datamodel();
+
+ for(int i=0; i<ds->players.count; i++)
+ {
+ if(strlen(ds->players.players[i].player_name) > 0) // search for first empty (not yet assigned) slot
+ continue;
+ memcpy(ds->players.players[i].player_name, payload, payload_len);
+ ds->players.players[i].player_name[payload_len] = '\0';
+ }
+
+ return true;
+}
+
+card* server_recv_selected_card(const uint8_t* payload, const uint8_t payload_len)
+{
+ assert(payload != NULL && payload_len == 1);
+
+ card* c = malloc(sizeof(card));
+ assert(c != NULL);
+ *c = payload[0];
+
+ return c;
+}
+
+uint8_t* server_recv_selected_stack(const uint8_t* payload, const uint8_t payload_len)
+{
+ assert(payload != NULL && payload_len == 1);
+
+ uint8_t* index = malloc(sizeof(uint8_t));
+ assert(index != NULL);
+ *index = payload[0];
+
+ return index;
+}
+
+uint8_t server_send_start_game(uint8_t* payload, const uint8_t payload_len)
+{
+ uint16_t pos = 0;
+ data_store* ds = datamodel();
+ player_list* players = &ds->players;
+
+ payload[pos++] = players->count;
+
+ // copy player_ids, length and nicknames to buffer
+ for(int i=0; i<players->count; i++)
+ {
+ pnoc_t* pl = &players->players[i];
+ payload[pos++] = pl->player_id;
+ uint8_t len = strlen(pl->player_name);
+ payload[pos++] = len;
+ memcpy(payload+pos, pl->player_name, len);
+ payload += len;
+ }
+
+ assert(pos <= payload_len);
+
+ return pos;
+}
+
+uint8_t server_send_selected_stack(uint8_t* payload, const uint8_t payload_len)
+{
+ data_store* ds = datamodel();
+
+ payload[0] = ds->stack_index;
+
+ return 1;
+}
+
+uint8_t server_send_deal_cards(uint8_t* payload, const uint8_t payload_len, const void* param)
+{
+ const hand_t* hand = (hand_t*) param;
+
+ for(int i=0; i<MAX_HAND_CARDS; i++)
+ payload[i] = hand->cards[i];
+
+ return MAX_HAND_CARDS;
+}
+
+
+uint8_t server_send_initial_stacks(uint8_t* payload, const uint8_t payload_len)
+{
+ data_store* ds = datamodel();
+
+ for(int i=0; i<NUM_TABLE_STACKS; i++)
+ payload[i] = ds->table_stacks.stacks[i].cards[0];
+
+ return NUM_TABLE_STACKS;
+}
+
diff --git a/src/net/server.h b/src/net/server.h
new file mode 100644
index 0000000..66bce9b
--- /dev/null
+++ b/src/net/server.h
@@ -0,0 +1,25 @@
+#ifndef OXEN_SERVER_H
+#define OXEN_SERVER_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "../player.h"
+
+// Socket list
+typedef struct
+{
+ uint8_t count;
+ int sockets[MAX_PLAYERS];
+} socket_list_t;
+
+int server_start(const char* port);
+void server_get_players(int serversock, socket_list_t* client_socks, const uint8_t count);
+bool server_recv_hello(const uint8_t* payload, const uint8_t payload_len);
+card* server_recv_selected_card(const uint8_t* payload, const uint8_t payload_len);
+uint8_t* server_recv_selected_stack(const uint8_t* payload, const uint8_t payload_len);
+uint8_t server_send_start_game(uint8_t* payload, const uint8_t payload_len);
+uint8_t server_send_deal_cards(uint8_t* payload, const uint8_t payload_len, const void* param);
+uint8_t server_send_selected_stack(uint8_t* payload, const uint8_t payload_len);
+uint8_t server_send_initial_stacks(uint8_t* payload, const uint8_t payload_len);
+
+#endif // OXEN_SERVER_H