added nonblocking read for network games + minor build system fixes

Wed, 09 Apr 2014 12:07:47 +0200

author
Mike Becker <universe@uap-core.de>
date
Wed, 09 Apr 2014 12:07:47 +0200
changeset 34
c4d4b8a8f902
parent 33
866025982aa9
child 35
6c64b7a073af

added nonblocking read for network games + minor build system fixes

src/Makefile file | annotate | diff | comparison | revisions
src/chess/Makefile file | annotate | diff | comparison | revisions
src/client.c file | annotate | diff | comparison | revisions
src/game.c file | annotate | diff | comparison | revisions
src/main.c file | annotate | diff | comparison | revisions
src/network.c file | annotate | diff | comparison | revisions
src/network.h file | annotate | diff | comparison | revisions
src/server.c file | annotate | diff | comparison | revisions
--- a/src/Makefile	Wed Apr 09 11:12:04 2014 +0200
+++ b/src/Makefile	Wed Apr 09 12:07:47 2014 +0200
@@ -38,18 +38,18 @@
 OBJ = $(SRC:%.c=../build/release/%$(OBJ_EXT))
 OBJ_D = $(SRC:%.c=../build/debug/%$(OBJ_EXT))
 
-all: $(OBJ)
-	$(LD) -o ../build/release/$(BIN) $^ \
+all: ../build/release $(OBJ)
+	$(LD) -o ../build/release/$(BIN) $(OBJ) \
 	../build/release/chess$(LIB_EXT) $(LDFLAGS)
 
-debug: $(OBJ_D)
-	$(LD) -o ../build/debug/$(BIN) $^ \
+debug: ../build/debug $(OBJ_D)
+	$(LD) -o ../build/debug/$(BIN) $(OBJ_D) \
 	../build/debug/chess$(LIB_EXT) $(LDFLAGS)	
 
-../build/release/%$(OBJ_EXT): %.c ../build/release
+../build/release/%$(OBJ_EXT): %.c
 	$(CC) -o $@ $(CFLAGS) -c $<
 
-../build/debug/%$(OBJ_EXT): %.c ../build/debug
+../build/debug/%$(OBJ_EXT): %.c
 	$(CC) -o $@ $(CFLAGS_D) -c $<
 	
 ../build/release:
--- a/src/chess/Makefile	Wed Apr 09 11:12:04 2014 +0200
+++ b/src/chess/Makefile	Wed Apr 09 12:07:47 2014 +0200
@@ -42,16 +42,16 @@
 OBJ = $(SRC:%.c=$(BUILDDIR)/release/%$(OBJ_EXT))
 OBJ_D = $(SRC:%.c=$(BUILDDIR)/debug/%$(OBJ_EXT))
 
-all: $(OBJ)
+all: $(BUILDDIR)/release $(OBJ)
 	$(AR) $(ARFLAGS) $(BUILDDIR)/release/chess$(LIB_EXT) $(OBJ)
 
-debug: $(OBJ_D)
+debug: $(BUILDDIR)/debug $(OBJ_D)
 	$(AR) $(ARFLAGS) $(BUILDDIR)/debug/chess$(LIB_EXT) $(OBJ_D)	
 
-$(BUILDDIR)/release/%$(OBJ_EXT): %.c $(BUILDDIR)/release
+$(BUILDDIR)/release/%$(OBJ_EXT): %.c
 	$(CC) -o $@ $(CFLAGS) -c $<
 	
-$(BUILDDIR)/debug/%$(OBJ_EXT): %.c $(BUILDDIR)/debug
+$(BUILDDIR)/debug/%$(OBJ_EXT): %.c
 	$(CC) -o $@ $(CFLAGS_D) -c $<
 
 $(BUILDDIR):
--- a/src/client.c	Wed Apr 09 11:12:04 2014 +0200
+++ b/src/client.c	Wed Apr 09 12:07:47 2014 +0200
@@ -34,12 +34,12 @@
 
 static int client_connect(Server *server, char *host, char *port) {
     if (net_find(server, host, port)) {
-        fprintf(stderr, "Can't find server\n");
+        addstr("Can't find server");
         return 1;
     }
 
     if (net_connect(server)) {
-        perror("Can't connect to server");
+        addstr("Can't connect to server");
         return 1;
     }
     
@@ -48,7 +48,7 @@
 
 static int client_handshake(Server *server) {
     if (net_recieve_code(server->fd) != NETCODE_VERSION) {
-        fprintf(stderr, "Server uses an incompatible software version.\n");
+        addstr("Server uses an incompatible software version.");
         return 1;
     } else {
         net_send_code(server->fd, NETCODE_VERSION);
@@ -84,7 +84,7 @@
             net_send_code(server.fd, NETCODE_DECLINE);
         }
     } else {
-        fprintf(stderr, "Server sent invalid gameinfo.\n");
+        addstr("Server sent invalid gameinfo.");
         net_destroy(&server);
         return EXIT_FAILURE;
     }
--- a/src/game.c	Wed Apr 09 11:12:04 2014 +0200
+++ b/src/game.c	Wed Apr 09 12:07:47 2014 +0200
@@ -33,6 +33,7 @@
 #include <ncurses.h>
 #include <string.h>
 #include <inttypes.h>
+#include <sys/select.h>
 
 static const uint8_t boardx = 10, boardy = 10;
 static int inputy = 21; /* should be overridden on game startup */
@@ -315,6 +316,13 @@
 
 static int recvmove(GameState *gamestate, GameInfo *gameinfo, int opponent) {
     
+    if (net_setnonblocking(opponent, 1)) {
+        printw("Cannot setup nonblocking IO on network socket");
+        cbreak(); getch();
+        exit(EXIT_FAILURE);
+    }
+    
+    struct timeval timeout;
     while (1) {
         timecontrol(gamestate, gameinfo);
         
@@ -323,52 +331,66 @@
         clrtoeol();
         refresh();
 
-        // TODO: nonblocking
-        uint32_t code = net_recieve_code(opponent);
+        fd_set readfds;
+        
+        FD_ZERO(&readfds);
+        FD_SET(opponent, &readfds);
+        timeout.tv_sec = 0;
+        timeout.tv_usec = 1e5;
         
-        Move move;
-        switch (code) {
-        case NETCODE_TIMEOVER:
-            printw("\rYour opponent's time ran out - you win!");
-            clrtoeol();
-            return 1;
-        case NETCODE_SURRENDER:
-            printw("\rYour opponent surrendered!");
-            clrtoeol();
-            return 1;
-        case NETCODE_REMIS:
-            if (prompt_yesno(
-                "\rYour opponent offers remis - do you accept")) {
-                printw("\rRemis accepted!");
+        int result = select(opponent+1, &readfds, NULL, NULL, &timeout);
+        if (result == -1) {
+            printw("\rCannot perform asynchronous network IO");
+            cbreak(); getch();
+            exit(EXIT_FAILURE);
+        }
+        if (result > 0) {
+            uint32_t code = net_recieve_code(opponent);
+
+            Move move;
+            switch (code) {
+            case NETCODE_TIMEOVER:
+                printw("\rYour opponent's time ran out - you win!");
                 clrtoeol();
-                net_send_code(opponent, NETCODE_ACCEPT);
                 return 1;
-            } else {
-                net_send_code(opponent, NETCODE_DECLINE);
-            }
-            break;
-        case NETCODE_MOVE:
-            net_recieve_data(opponent, &move, sizeof(Move));
-            if (validate_move(gamestate, &move)) {
-                apply_move(gamestate, &move);
-                if (move.check) {
-                    net_send_code(opponent, NETCODE_CHECK);
-                } else if (gamestate->checkmate) {
-                    net_send_code(opponent, NETCODE_CHECKMATE);
-                    printw("\rCheckmate!");
+            case NETCODE_SURRENDER:
+                printw("\rYour opponent surrendered!");
+                clrtoeol();
+                return 1;
+            case NETCODE_REMIS:
+                if (prompt_yesno(
+                    "\rYour opponent offers remis - do you accept")) {
+                    printw("\rRemis accepted!");
                     clrtoeol();
-                    return 1;
-                } else if (gamestate->stalemate) {
-                    net_send_code(opponent, NETCODE_STALEMATE);
-                    printw("\rStalemate!");
-                    clrtoeol();
+                    net_send_code(opponent, NETCODE_ACCEPT);
                     return 1;
                 } else {
-                    net_send_code(opponent, NETCODE_ACCEPT);
+                    net_send_code(opponent, NETCODE_DECLINE);
                 }
-                return 0;
-            } else {
-                net_send_code(opponent, NETCODE_DECLINE);
+                break;
+            case NETCODE_MOVE:
+                net_recieve_data(opponent, &move, sizeof(Move));
+                if (validate_move(gamestate, &move)) {
+                    apply_move(gamestate, &move);
+                    if (move.check) {
+                        net_send_code(opponent, NETCODE_CHECK);
+                    } else if (gamestate->checkmate) {
+                        net_send_code(opponent, NETCODE_CHECKMATE);
+                        printw("\rCheckmate!");
+                        clrtoeol();
+                        return 1;
+                    } else if (gamestate->stalemate) {
+                        net_send_code(opponent, NETCODE_STALEMATE);
+                        printw("\rStalemate!");
+                        clrtoeol();
+                        return 1;
+                    } else {
+                        net_send_code(opponent, NETCODE_ACCEPT);
+                    }
+                    return 0;
+                } else {
+                    net_send_code(opponent, NETCODE_DECLINE);
+                }
             }
         }
     }
--- a/src/main.c	Wed Apr 09 11:12:04 2014 +0200
+++ b/src/main.c	Wed Apr 09 12:07:47 2014 +0200
@@ -165,7 +165,7 @@
         return EXIT_SUCCESS;
     }    
     initscr();
-    halfdelay(10);
+    halfdelay(1);
     keypad(stdscr, TRUE);
     if (has_colors()) {
         start_color();
@@ -181,8 +181,12 @@
     if (settings.singlemachine) {
         game_start_singlemachine(&settings);
     } else {
-        return is_server(&settings) ?
+        int exitcode = is_server(&settings) ?
             server_run(&settings) : client_run(&settings);
+        
+        if (exitcode != EXIT_SUCCESS) {
+            cbreak(); getch();
+        }
     }
 }
 
--- a/src/network.c	Wed Apr 09 11:12:04 2014 +0200
+++ b/src/network.c	Wed Apr 09 12:07:47 2014 +0200
@@ -29,6 +29,7 @@
 
 #include <stdlib.h>
 #include <string.h>
+#include <fcntl.h>
 #include "network.h"
 
 #define new_socket() socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
@@ -133,3 +134,21 @@
 void net_recieve_data(int socket, void *data, size_t len) {
     recv(socket, data, len, 0);
 }
+
+int net_setnonblocking(int socket, _Bool nonblocking) {
+    int opts = fcntl(socket, F_GETFL);
+	if (opts < 0) {
+		return 1;
+	}
+    
+    if (nonblocking) {
+        opts |= O_NONBLOCK;
+    } else {
+        opts &= ~O_NONBLOCK;
+    }
+	if (fcntl(socket, F_SETFL, opts) < 0) {
+		return 1;
+	}
+    
+    return 0;
+}
--- a/src/network.h	Wed Apr 09 11:12:04 2014 +0200
+++ b/src/network.h	Wed Apr 09 12:07:47 2014 +0200
@@ -74,6 +74,7 @@
 uint8_t net_recieve_code(int socket);
 void net_recieve_data(int socket, void *data, size_t len);
 
+int net_setnonblocking(int socket, _Bool nonblocking);
 
 #ifdef	__cplusplus
 }
--- a/src/server.c	Wed Apr 09 11:12:04 2014 +0200
+++ b/src/server.c	Wed Apr 09 12:07:47 2014 +0200
@@ -35,12 +35,12 @@
     printw("\nListening for client...\n");
     refresh();
     if (net_create(server, port)) {
-        perror("Server creation failed");
+        addstr("Server creation failed");
         return 1;
     }
 
     if (net_listen(server)) {
-        perror("Listening for client failed");
+        addstr("Listening for client failed");
         return 1;
     }
     
@@ -50,7 +50,7 @@
 static int server_handshake(Client *client) {
     net_send_code(client->fd, NETCODE_VERSION);
     if (net_recieve_code(client->fd) != NETCODE_VERSION) {
-        fprintf(stderr, "Client uses an incompatible software version.\n");
+        addstr("Client uses an incompatible software version.");
         return 1;
     }
 
@@ -90,7 +90,9 @@
         printw("\rClient connected - challenge declined.");
         clrtoeol();
     } else {
-        fprintf(stderr, "Invalid client response\n");
+        addstr("\rInvalid client response");
+        clrtoeol();
+        
         net_destroy(&server);
         return EXIT_FAILURE;
     }

mercurial