Newer
Older
gnyognyo / gnyoserv.c
@yuuji yuuji on 17 Jan 1997 9 KB *** empty log message ***
/*
 * gnyognyo server
 * (c)1994,1995 by HIROSE Yuuji [pcs39334@asciinet.or.jp]
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/signal.h>
#include <sys/param.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <fcntl.h>
#include <memory.h>

/* ちょっとうまく動かないかも知れないです。直して下さい ^^; */

#define error(x)	{fprintf(stderr, "gnyoserv: "); perror(x); exit(1);}
#define GNYOPORT	9969
#define BUFLEN		256
#define MAXCL		3

static int sofd, clisofd[MAXCL+1], clients;
static char *clinames[MAXCL], start=0;

/* $Id$ */

char* savestr(char* str)
{
    char *p;
    if (NULL == (p=(char*)malloc(strlen(str)+1)))
        error("Virtual memory exhausted.");
    strcpy(p, str);
    return p;
}

int mypoint(int me, int score[][MAXCL])
{
    int i, j, point=0;
    for (i=0; i<MAXCL; i++) {
        if (i != me) {
            point += score[i][me];
        }
    }
    return point;
}

int sendstr(int fd, char* str, int mode)
{
    return send(fd, str, strlen(str), mode);
}

/* 番号clientのクライアントから受け取った buffer を処理する */
int gnyoquery(int client, int fd, char* buffer)
{
    /* 以下のコマンドを受け付ける:
     * 
     * '!'	接続者が全員(2人以上)から ! が来たら go! を返す
     * '?'	接続者がのIDを返す
     * 'a'	続けて得点を書いてそれを加算 (例. a500)
     * 'b'	後続するメッセージを自分以外の全員に送る(例. baka)
     *		blose! を受け取ると敗戦処理
     * 'i'	全てのクライアントの得点をリセット
     * 'n'	お名前を更新する (例. n太武郎);
     * 'r'	全ての敵のスコアの合計を受け取って清算する
     * 'v'	接続クライアントの表示
     * 'q'	抜ける
     */
    int i, j, point;
    static int score[MAXCL][MAXCL], ready[MAXCL], nentry=0;
    /* score[X][Y] は クライアントX の クライアントY に対する得点 */
    static char sendbuf[BUFLEN];
    switch (buffer[0]) {
    case '!':
        if (ready[client] == 0) {
            if (start) {
                sendstr(fd, "AHO-! おめーはげーむおーばーだろーが!\n", 0);
            } else {
                nentry++;
                ready[client] = 1;
            }
        }
#ifdef DEBUG
        fprintf(stderr, "Client %d ready - c=%d, n=%d\n", client, clients, nentry);
#endif
        j = 1;
        
        if (clients==nentry && clients!=1) {  /* all are ready */
            sprintf(sendbuf, "Go! %d clients exist.\n", clients);
            for (i=0; i<MAXCL; i++) {
                if (clisofd[i]) {
                    /* gnyoquery(i, clisofd[i], "v\n"); */
                    sendstr(clisofd[i], sendbuf, 0);
                    start = 1;  /* set start-flag */
                }
            }
        }
        break;
    case '?':
        sprintf(sendbuf, "Your id is %d.", client);
        sendstr(fd, sendbuf, 0);
        break;
    case 'a':
        for (j=0; j<MAXCL; j++)
            score[client][j] += atoi(buffer+1);
#ifdef DEBUG
        fprintf(stderr, "Client %d's score=%d\n", client, score[client][j-1]);
#endif
        break;
    case 'b':
        if ((0==strncmp(buffer+1, "lose!", 5)) && ready[client]) {
            ready[client] = 0;
            nentry--;
            sprintf(sendbuf, "Lost=%s\n", clinames[client]);
            j=strlen(sendbuf);
            for (i=0; i<MAXCL; i++) {
                if (i!=client && clisofd[i]) send(clisofd[i], sendbuf, j, 0);
            }
            if (nentry == 1) {  /* Won! */
                start = 0;      /* clear start-flag */
                for (j=0; ready[j]==0 && j<MAXCL; j++);
                if (j==MAXCL) break;
                ready[j] = nentry = 0;
                sendstr(clisofd[j], "You won!!\n", 0);
            }
        } else {
            sprintf(sendbuf, "m%d%s\r\n", client, 1+buffer);
            j=strlen(sendbuf);
            for (i=0; i<MAXCL; i++) {
                if (i!=client && clisofd[i]) {
                    send(clisofd[i], sendbuf, j, 0);
                }
            }
        }
        break;
    case 'i':
        for (i=0; i<MAXCL; i++) {
            for (j=0; j<MAXCL; j++)
                score[i][j] = 0;
        }
        break;
    case 'n':
        if (clinames[client]) free(clinames[client]);
        if (strpbrk(buffer, "\r\n")) *((char*)strpbrk(buffer, "\r\n")) = '\0';
        clinames[client] = savestr(1+buffer);
        break;
    case 'r':
        point = mypoint(client, score);
        for (i=0; i<MAXCL; i++) {
            score[i][client] = 0;
        }
        sprintf(sendbuf, "r=%d\n", point);
        sendstr(fd, sendbuf, 0);
        break;
    case 'v':
        for (i=0; i<MAXCL; i++) {
            if (clisofd[i]) {
                sprintf(sendbuf, "Client%d=%-40s (ready=%d)\n",
                        i, clinames[i], /*mypoint(i, score),*/ ready[i]);
                sendstr(fd, sendbuf, 0);
            }
        }
        break;
    case 'q':
        ready[client] = 0;
        for (j=0; j<MAXCL; j++)
            score[client][j] = 0;
        sprintf(sendbuf, "またのお越しをお待ちしております.\n");
        sendstr(fd, sendbuf, 0);
        return 1;
    }
    return 0;
}

void gnyoserv()
{
    int done=0, readable, c, rfd, i;
    fd_set fds;
    char buffer[BUFLEN], tmpbuf[BUFLEN], *p;
    static struct sockaddr_in cliaddr;
    int cliaddrlen = sizeof(cliaddr);
#define SAD struct sockaddr
    while (!done) {
        FD_ZERO(&fds);
        FD_SET(sofd, &fds);
        for (i=0; i<MAXCL; i++) FD_SET(clisofd[i], &fds);
        readable = select(32, &fds, 0, 0, 0);
        /* accept a new client */
        if (FD_ISSET(sofd, &fds)) {
            for (i=0; i<MAXCL && clisofd[i]; i++);
            if ((clisofd[i] = accept(sofd, (SAD*)&cliaddr, &cliaddrlen)) < 0)
                error("Couldn't accept");
            if (i==MAXCL) {
                sprintf(buffer,  "There are over %d users.  Connect later.\n",
                        MAXCL);
                sendstr(clisofd[i], buffer, 0);
                shutdown(clisofd[i], 2);
                close(clisofd[i]);
            } else {
                struct hostent *host;
                sprintf(buffer, "Welcome to gnyoserver. Your id is %d.\n", i);
                sendstr(clisofd[i], buffer, 0);
                host = gethostbyaddr((char*)&cliaddr.sin_addr,
                                     sizeof(cliaddr.sin_addr), AF_INET);
#ifdef DEBUG
                fprintf(stderr, "Connection from %s\n", host->h_name);
#endif
                sprintf(buffer, "%s", host->h_name);
                clinames[i] = savestr(buffer);
                clients++;
            }
            readable--;
        }
        for (c=0; readable>0 && c<MAXCL; readable--) {
            while (c<MAXCL && !FD_ISSET(clisofd[c], &fds)) c++;
            rfd = clisofd[c];		/* is the desc. of sender */
            memset(buffer, '\0', BUFLEN);
            if (recv(rfd, buffer, BUFLEN, 0) < 0) error("Receive failed");
            if (buffer[0] == '.') {
                sprintf(buffer, "Bye bye!\n");
                done = 1;
                rfd = 0;
            }
#ifdef WRITE
            for (i=0; i<MAXCL; i++) {
                if (clisofd[i]!=rfd)
                  send(clisofd[i], buffer, strlen(buffer), 0);
            }
            if ((p=strchr(buffer, '\r'))) /* chop */
                *p = '\0';
            if ((p=strchr(buffer, '\n')))
                *p = '\0';
#else
            p = buffer;
            while (*p) {        /* 一行ごとにちぎって処理 */
                register int len;
                if (strpbrk(p, "\r\n")) {
                    len = (1+(char*)strpbrk(p, "\n") - p);
                } else {
                    len = strlen(p);
                }
                strncpy(tmpbuf, p, len);
                p += len;
                tmpbuf[len] = '\0';
                switch (gnyoquery(c, rfd, tmpbuf)) {
                case 1:
                    shutdown(rfd, 2);
                    close(rfd, 2);
                    clisofd[c] = 0;
                    if (clinames[c]) free(clinames[c]);
                    if (--clients == 0) done=1;
                    break;
                case 99:
                    done = 1;
                }
            }
#endif /* WRITE */
        }
    }
}

void trap()
{
    int i;
    for (i=0; i<MAXCL; i++) {
        shutdown(clisofd[i], 2);
        close(clisofd[i]);
    }
    shutdown(sofd, 2);
    close(sofd);
    fputs("Terminated by user\n", stderr);
    exit(0);
}

main(int argc, char* argv[])
{
    char hostname[MAXHOSTNAMELEN];
    static struct hostent *host;
    static struct sockaddr_in servaddr;
    int pid, clients=0, port=GNYOPORT;
    fd_set readfds;
    sofd = socket(AF_INET, SOCK_STREAM, 0);
    if (sofd < 0) error("Failed to open socket");
    if (gethostname(hostname, MAXHOSTNAMELEN) < 0)
      error("Couldn't get hostname");
    if (0 == (host = gethostbyname(hostname)))
      error("Couldn't get host id");
    if (argc>1 && strpbrk(argv[1], "0123456789")) {
        port = atoi(argv[1]);
        fprintf(stderr, "Port number set to %d\n", port);
    }
    memset((char*)&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = INADDR_ANY;
    servaddr.sin_port = htons(port);
    memcpy((char*)&servaddr.sin_addr, (char*)host->h_addr, host->h_length);

    if (bind(sofd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
      error("Failed to bind");

    if (listen(sofd, 5) == -1) error("Failed to listen");

    /* connection request */
    signal(SIGINT, trap);
    
    /* begin communication */
    FD_ZERO(&readfds);
    FD_SET(sofd, &readfds);
    gnyoserv();
    fputs("Session ended\n", stderr);
    for (clients=0; clients<MAXCL; clients++) {
        shutdown(clisofd[clients], 2);
        close(clisofd[clients]);
    }
    shutdown(sofd, 2);
    close(sofd);
    exit(0);
}