ref: 0c01f247e31b2cc808392f32ae00691dedad9796
dir: /srv.c/
/* * glendy (unix) server, based on cli version. * * it appears to my mind that socket.h and stdio.h have are in use * since medieval times, which is really impressive of a design; * ...for a torture device. */ #include <stdio.h> #include <string.h> #include <stdlib.h> #include <stdarg.h> #include <unistd.h> #include <time.h> #include <pthread.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include "unix.h" #include "engine.h" #define playersock sockfd[turn % 2] int pcount = 0; int sockfd[2]; int debug = 1; char syncmsg[8]; pthread_mutex_t pcount_mutex; // pthread_mutex_t print_mutex; /* * it might feel odd why it starts from 10, * becase then we can just read two bytes to see what's wrong. */ enum { GLNDY_CONN = 10, GLNDY_INIT = 11, GLNDY_SYNC, GLNDY_OK, GLNDY_WAIT, GLNDY_TURN, GLNDY_NOTTURN, GLNDY_CANTM, GLNDY_CANTP, GLNDY_INVALIDINPUT, GLNDY_WALL, GLNDY_GWON, GLNDY_GLOST, }; static void error(const char *msg) { perror(msg); pthread_exit(NULL); } static void vprint(int fd, char *fmt, va_list arg) { char *s = malloc(1024); int n; n = vsprintf(s, fmt, arg); if(write(fd, s, n) < n) error("couldn't write\n"); free(s); } static void print(int fd, char *fmt, ...) { va_list arg; va_start(arg, fmt); vprint(fd, fmt, arg); va_end(arg); } static void printclients(char *fmt, ...) { /* it seems arg gets changed during the first call to vprint, thus we need two */ va_list arg, arg2; va_start(arg, fmt); va_start(arg2, fmt); vprint(sockfd[0], fmt, arg); vprint(sockfd[1], fmt, arg2); va_end(arg); va_end(arg2); } static int dprint(char *fmt, ...) { va_list va; int n; if(!debug) return 0; va_start(va, fmt); n = vfprintf(stderr, fmt, va); va_end(va); return n; } int setuplistener(int portno) { int sockfd; struct sockaddr_in serv_addr; sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) error("ERROR opening listener socket."); memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = INADDR_ANY; serv_addr.sin_port = htons(portno); if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) error("ERROR binding listener socket."); dprint("listener on\n"); return sockfd; } void getclients(int lis_sockfd) { int conns = 0; socklen_t clilen; struct sockaddr_in servaddr, clientaddr; while(conns < 2) { listen(lis_sockfd, 253 - pcount); memset(&clientaddr, 0, sizeof(clientaddr)); clilen = sizeof(clientaddr); sockfd[conns] = accept(lis_sockfd, (struct sockaddr *) &clientaddr, &clilen); if(sockfd[conns] < 0) error("ERROR accepting a connection from a client."); dprint("got connection %d\n", conns); print(sockfd[conns], "CONN %d\n", conns); dprint("client %d connected\n", conns); pthread_mutex_lock(&pcount_mutex); pcount++; dprint("pcount = %d\n", pcount); pthread_mutex_unlock(&pcount_mutex); if(conns == 0) { print(sockfd[conns], "WAIT\n"); dprint("someone is gonna wait until they friend comes\n"); } conns++; } } void drawlevel(void) { /* prints first row, this assumes SzX = SzY, is there a better way? */ printf("T%2d|", turn); for(int i = 0 ; i < SzX ; i++) printf("%2d |", i); printf("\n"); for(int x = 0; x < SzX; x++) { for(int i = 0 ; i < SzY+1 ; i++) printf("----"); printf("\n"); /* show column number and have a zig-zag effect */ printf("%2d%s |", x, x % 2 ? "" : "| "); for(int y = 0; y < SzY; y++) { /* it's [y][x], not [x][y] */ switch(grid[y][x]) { case Wall: printf(" * |"); break; case Glenda: /* fancy effect for glenda's face */ printf(" g |"); break; default: printf(" |"); break; } } printf("\n"); } } void sendlevel(void) { if(state == Start) { printclients("INIT\n"); for(int x = 0; x < SzX; x++) { for(int y = 0; y < SzY; y++) { /* it's [y][x], not [x][y] */ switch(grid[y][x]) { case Wall: printclients("w %d %d\n", x, y); break; case Glenda: printclients("g %d %d\n", x, y); break; } } } printclients("SENT\n"); } else if(state == Playing) { dprint("turn: %d\n", turn); printclients("SYNC %d %s\n", turn, syncmsg); } else { if(state == Won) printclients("WON\n"); else if(state == Lost) printclients("LOST\n"); restart(); /* will it execute twice? */ } } /* p x y */ void proc_put(char *s) { unsigned int x, y, r; sscanf(s, "%u %n", &x, &r); sscanf(s+r, "%u", &y); dprint("put %d %d\n", x, y); if(x >= SzX || x < 0 || y >= SzY || y < 0) { print(playersock, "ERR invalidinput proc_put(): %d %d\n", x, y); return; } r = doput(Pt(x, y)); if(r == Wall) print(playersock, "WALL %d %d\n", x, y); else { strncpy(syncmsg, s, 8); dprint("syncmsg = %s\n", s); } } /* m x y */ void proc_move(char *s) { int d; Point p; p = findglenda(); if(strcmp(s, "NE") == 0) d = NE; else if(strcmp(s, "E") == 0) d = E; else if(strcmp(s, "SE") == 0) d = SE; else if(strcmp(s, "W") == 0) d = W; else if(strcmp(s, "SW") == 0) d = SW; else if(strcmp(s, "NW") == 0) d = NW; else { print(playersock, "ERR invalidinput proc_move(): %s\n", s); return; } if(domove(d, p) == Wall) { print(playersock, "WALL %s %d %d\n", s, p.x, p.y); return; } else { strncpy(syncmsg, s, 8); dprint("syncmsg = %s\n", syncmsg); } } /* * handle input, which is in the form of * p1> p xx yy * which puts a wall in xx yy * p2> m {NE, E, SE, W, SW, NW} * which moves the bunny * > r * restarts the game * > u * undos last move * > q * quits the game */ void proc(char *s) { char *t; int oturn, n; /* skip \n, so we don't need to process it later */ t = strchr(s, '\n'); if(t != nil) *t = '\0'; oturn = turn; /* s+2 skips command and first space after it */ switch(*s) { case 'p': if(turn % 2 == 0) proc_put(s+2); else if(turn % 2 == 1) print(playersock, "CANTP\n"); break; case 'm': if(turn % 2 == 0) print(playersock, "CANTM\n"); else if(turn % 2 == 1) proc_move(s+2); break; case 'u': undo(); break; case 'r': /* maybe we need to put a confirm message here */ restart(); break; case 'q': case '\0': /* should we end the game at this point? XXX important */ break; default: print(playersock, "ERR invalidinput %c\n", *s); } /* only print the map if turn have changed */ if(turn != oturn) { if(debug) drawlevel(); sendlevel(); } } int input(void) { char *s, c; int n = 0; /* sang bozorg */ s = malloc(1024); print(playersock, "TURN\n"); // while((c = read(playersock, s + n++, 1)) != '\n' && c != '\0' && n < 1023) memset(s, 0, 1024); while(read(playersock, s+n, 1) == 1 && n < 1024) { if(s[n] == '\n' || s[n] == '\0') { s[n] = '\0'; break; } n++; } dprint("got input: %s\n", s); proc(s); return Ok; } int main(int argc, char **argv) { int listenfd, port, result; char r; // pthread_t thread; /* it might not be a real human */ ptype[0] = Human; ptype[1] = Human; if(argc != 2) { fprintf(stderr, "usage: %s <port>\n", argv[0]); exit(1); } port = atoi(argv[1]); listenfd = setuplistener(port); pthread_mutex_init(&pcount_mutex, NULL); // pthread_mutex_init(&print_mutex, NULL); /* OpenBSD ignores this */ srand(time(nil)); getclients(listenfd); initlevel(); /* result = pthread_create(&thread, NULL, (void*)input, NULL); // if(result){ // printf("Thread creation failed with return code %d\n", result); // exit(-1); // } */ if(debug) drawlevel(); sendlevel(); while(input() != Err) ; close(listenfd); // pthread_mutex_destroy(&pcount_mutex); // pthread_mutex_destroy(&print_mutex); return 0; }