code duplication
[netris.git] / server.c
index 7e0aca1eb91de6d4df5d8b30e79093d4d19a28e0..70406a3b4daec99a11f2226fd91fd05302e77f6d 100644 (file)
--- a/server.c
+++ b/server.c
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * $Id: game.c,v 1.39 1999/05/16 06:56:27 mhw Exp $
  */
 
 #define NOEXT
 #include "netris.h"
+
 #include <stdlib.h>
+#include <stdbool.h>
 #include <ctype.h>
 #include <string.h>
 #include <sys/types.h>
 #include <errno.h>
 #include <setjmp.h>
 
-#define HEADER_SIZE sizeof(netint4[3])
-#define MAX_CONNECTIONS 3
+#include "util.h"
+#include "msg.h"
 
-char *version_string = "0.5.89";
+#define HEADER_SIZE sizeof(netint4[3])
 
 static struct option options[] = {
-       { "wait",               0, 0, 'w' },
-       { "port",               1, 0, 'p' },
-       { "speed",              1, 0, 'i' },
-       { "seed",               1, 0, 's' },
-       { "info",               0, 0, 'H' },
-       { "help",               0, 0, 'h' },
-       { 0,                    0, 0,  0 }
+       { "wait",        0, 0, 'w' },
+       { "port",        1, 0, 'p' },
+       { "quadra",      1, 0, 'q' },
+       { "min-players", 1, 0, 'm' },
+       { "max-players", 1, 0, 'x' },
+       { "continuous",  1, 0, 'c' },
+       { "speed",       1, 0, 'i' },
+       { "seed",        1, 0, 's' },
+       { "verbose",     0, 0, 'v' },
+       { "info",        0, 0, 'H' },
+       { "help",        0, 0, 'h' },
+       { 0,             0, 0,  0  }
 };
 
-static char *gameNames[GT_len] = { "OnePlayer", "ClassicTwo" };
+static char minplayers = 2;
+static char maxplayers = 8;
+static char playercount;
+static char verbose = 0;
 
-ExtFunc MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event);
+struct sockaddr_in addr;
 
-EventGenRec netGen[MAX_SCREENS] = {
-       { NULL, 0, FT_read, -1, NetGenFunc, EM_net, 0, "\0", 0, HEADER_SIZE } };
+static MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event);
+static EventGenRec netGen[MAX_SCREENS] = {
+       { NULL, 0, FT_read, -1, NetGenFunc, EM_net, 0, "\0", 0, HEADER_SIZE }
+};
 
 static MyEventType AlarmGenFunc(EventGenRec *gen, MyEvent *event);
-static EventGenRec alarmGen =
-               { &alarmGen, 0, FT_read, -1, AlarmGenFunc, EM_alarm };
+static EventGenRec alarmGen = {
+       &alarmGen, 0, FT_read, -1, AlarmGenFunc, EM_alarm
+};
+
+static MyEventType ConnGenFunc(EventGenRec *gen, MyEvent *event);
+static EventGenRec connGen = {
+       NULL, 0, FT_read, -1, ConnGenFunc, EM_connect
+};
+
 static EventGenRec *nextGen = &alarmGen;
 
 static sigjmp_buf close_env;
 
-ExtFunc volatile void die(char *msg)
-{
-       perror(msg);
-       exit(1);
-} //die
 
-ExtFunc int MyRead(int fd, void *data, int len)
-{
-       int result, left;
-
-       left = len;
-       while (left > 0) {
-               result = read(fd, data, left);
-               if (result > 0) {
-                       data = ((char *)data) + result;
-                       left -= result;
-               }
-               else if (errno != EINTR)
-                       return result;
-       }
-       return len;
-} //MyRead
+static void SendPacketTo(short player, short uid, NetPacketType type, int size, void *data)
+{ //send to someone
+       netint4 header[3];
 
-ExtFunc int MyWrite(int fd, void *data, int len)
-{
-       int result, left;
-
-       left = len;
-       while (left > 0) {
-               result = write(fd, data, left);
-               if (result > 0) {
-                       data = ((char *)data) + result;
-                       left -= result;
-               }
-               else if (errno != EINTR)
-                       return result;
+       if (netGen[player].fd >= 0) {
+               if (verbose)
+                       fprintf(stderr, MSG_SERVER_DBG_SEND "\n", type, uid, player);
+               header[0] = hton4(uid);
+               header[1] = hton4(type);
+               header[2] = hton4(size + HEADER_SIZE);
+               if (MyWrite(netGen[player].fd, header, HEADER_SIZE) != HEADER_SIZE)
+                       die("write (header)");
+               if (size > 0 && data && MyWrite(netGen[player].fd, data, size) != size)
+                       die("write");
        }
-       return len;
-} //MyWrite
+}
 
 static MyEventType AlarmGenFunc(EventGenRec *gen, MyEvent *event)
 {
        return E_alarm;
 }
 
-ExtFunc void AddEventGen(EventGenRec *gen)
-{
-       assert(gen->next == NULL);
-       assert(nextGen->next != (void*)0xffffffff);
-       gen->next = nextGen->next;
-       nextGen->next = gen;
-} //AddEventGen
+static void SCloseNet(short player)
+{ //kick some connection's ass!
+       MyEvent event;
 
-ExtFunc void RemoveEventGen(EventGenRec *gen)
-{
-       // assert(gen->next != NULL);   /* Be more forgiving, for SIGINTs */
-       if (gen->next) {
-               while (nextGen->next != gen)
-                       nextGen = nextGen->next;
-               nextGen->next = gen->next;
-               gen->next = NULL;
+       if (netGen[player].fd >= 0) {
+               if (Players[player].alive >= 0) {
+                       SendPacketTo(player, 0, NP_endConn, 0, NULL);
+                       do {} while (WaitMyEvent(&event, EM_net) != E_lostConn);
+               } //say bye to player
+               close(netGen[player].fd);
+               netGen[player].fd = -1;
        }
-} //RemoveEventGen
-
-ExtFunc MyEventType WaitMyEvent(MyEvent *event, int mask)
-{ //poll
-       int i, retry = 0;
-       fd_set fds[FT_len];
-       EventGenRec *gen;
-       int result, anyReady, anySet;
-       struct timeval tv;
-
-       for (;;) {
-               for (i = 0; i < FT_len; ++i)
-                       FD_ZERO(&fds[i]);
-               anyReady = anySet = 0;
-               gen = nextGen;
-               do {
-                       if (gen->mask & mask) {
-                               if (gen->ready)
-                                       anyReady = 1;
-                               if (gen->fd >= 0) {
-                                       FD_SET(gen->fd, &fds[gen->fdType]);
-                                       anySet = 1;
-                               }
-                       }
-                       gen = gen->next;
-               } while (gen != nextGen);
-               if (anySet) {
-                       tv.tv_sec = 0;
-                       tv.tv_usec = (retry && !anyReady) ? 500000 : 0;
-                       result = select(FD_SETSIZE, &fds[FT_read], &fds[FT_write],
-                                       &fds[FT_except], anyReady ? &tv : NULL);
-               }
-               else {
-                       if (retry && !anyReady)
-                               sleep(1);
-                       result = 0;
-               }
-               gen = nextGen;
-               do {
-                       if ((gen->mask & mask)
-                                       && (gen->ready || (result > 0 && gen->fd >= 0
-                                               && FD_ISSET(gen->fd, &fds[gen->fdType])))) {
-                               gen->ready = 0;
-                               event->type = gen->func(gen, event);
-                               if (event->type != E_none) {
-                                       nextGen = gen->next;
-                                       return event->type;
-                               }
-                       }
-                       gen = gen->next;
-               } while (gen != nextGen);
-               retry = 1;
-       }
-} //WaitMyEvent
+       if (netGen[player].next)
+               RemoveEventGen(&netGen[player]);
+}
 
-ExtFunc void ByeClient(int playa)
-{ //client went away
-       fprintf(stderr, "Close connection #%d\n", playa);
-       close(netGen[playa].fd);
-       netGen[playa].fd = -1;
-} //ByeClient
+static void CloseNets(void)
+{ //nou oogjes dicht en snaveltjes toe
+       int i;
 
-ExtFunc MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event)
+       fprintf(stderr, MSG_SERVER_CLOSE_ALL "\n");
+       for (i = 1; i < MAX_SCREENS; i++)
+               SCloseNet(i); // bye everybuddy
+       fprintf(stderr, MSG_SERVER_CLOSE_ALL_DONE "\n\n");
+}
+
+static MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event)
 { //receive
        int result;
        short uid, type, size;
@@ -194,15 +136,14 @@ ExtFunc MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event)
        result = MyRead(gen->fd, gen->buf + gen->bufSize,
                gen->bufGoal - gen->bufSize);
        if (result < 0) {
-               ByeClient(gen->player);
-               type = NP_endConn;
-               return E_net;
+               fprintf(stderr, MSG_SERVER_PLAYER_CLOSED "\n", gen->player);
+               return E_lostConn;
        }
        gen->bufSize += result;
        if (gen->bufSize < gen->bufGoal)
                return E_none;
        // *ugly* memcpy(data, gen->buf, sizeof(data));
-       uid = ntoh4(data[0]);
+       uid  = ntoh4(data[0]);
        type = ntoh4(data[1]);
        size = ntoh4(data[2]);
        gen->bufGoal = size;
@@ -215,287 +156,341 @@ ExtFunc MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event)
        event->u.net.type = type;
        event->u.net.size = size - HEADER_SIZE;
        event->u.net.data = gen->buf + HEADER_SIZE;
-       if (type == NP_endConn)
-               ByeClient(gen->player);
+       if (type == NP_endConn) {
+               fprintf(stderr, MSG_SERVER_PLAYER_QUIT "\n", gen->player);
+               return E_lostConn;
+       } //client sent quit signal
        return E_net;
-} //NetGenFunc
-
-ExtFunc void SendPacketTo(short playa, short uid, NetPacketType type, int size, void *data)
-{ //send to someone
-       netint4 header[3];
-
-       if (netGen[playa].fd >= 0) {
-               header[0] = hton4(uid);
-               header[1] = hton4(type);
-               header[2] = hton4(size + HEADER_SIZE);
-               if (MyWrite(netGen[playa].fd, header, HEADER_SIZE) != HEADER_SIZE)
-                       die("write (header)");
-               if (size > 0 && data && MyWrite(netGen[playa].fd, data, size) != size)
-                       die("write");
-       }
-} //SendPacketTo
-
-
-ExtFunc void AtExit(void (*handler)(void))
-{
-#ifdef HAS_ON_EXIT
-       on_exit((void *)handler, NULL);
-#else
-       atexit(handler);
-#endif
 }
 
-ExtFunc void SCloseNet(short playa)
-{ //kick some connection's ass!
-       MyEvent event;
-
-       if (netGen[playa].fd >= 0) {
-               SendPacketTo(playa, 0, NP_endConn, 0, NULL);
-               do{} while (WaitMyEvent(&event, EM_net) != E_lostConn);
-               close(netGen[playa].fd);
-               netGen[playa].fd = -1;
-       }
-       if (netGen[playa].next)
-               RemoveEventGen(&netGen[playa]);
-} //SCloseNet
-
-ExtFunc void CloseNets(void)
-{ //nou oogjes dicht en snaveltjes toe
-       int i;
-
-       for (i = 1; i <= totalPlayers; i++)
-               SCloseNet(i);
-} //CloseNets
 
-ExtFunc int WaitForConnection(short port)
+static MyEventType ConnGenFunc(EventGenRec *gen, MyEvent *event)
 {
-       struct sockaddr_in addr;
-       struct hostent *host;
-       int sockListen;
        int addrLen;
-       int val1;
        struct linger val2;
-       int i;
-
-       AtExit(CloseNets);
-       for (i = 1; i < MAX_SCREENS; i++)
-               memcpy(&netGen[i], &netGen[0], sizeof(EventGenRec));
-
-       memset(&addr, 0, sizeof(addr));
-       addr.sin_family = AF_INET;
-       addr.sin_port = htons(port);
-       addr.sin_addr.s_addr = htonl(INADDR_ANY);
-       if ((sockListen = socket(AF_INET, SOCK_STREAM, 0)) < 0)
-               die("socket");
-       val1 = 1;
-       setsockopt(sockListen, SOL_SOCKET, SO_REUSEADDR,
-                       (void *)&val1, sizeof(val1));
-       if (bind(sockListen, (struct sockaddr *)&addr, sizeof(addr)) < 0)
-               die("bind");
-       if (listen(sockListen, 1) < 0)
-               die("listen");
+       int new;
 
        addrLen = sizeof(addr);
-       for (i = 1; i <= MAX_CONNECTIONS; i++) {
-               if ((netGen[i].fd = accept(sockListen, (struct sockaddr *)&addr, &addrLen)) < 0)
-                       die("accept");
-               fprintf(stderr, "Connection: %s\n", inet_ntoa(addr.sin_addr));
-               val2.l_onoff = 1;
-               val2.l_linger = 0;
-               setsockopt(netGen[i].fd, SOL_SOCKET, SO_LINGER, (void *)&val2, sizeof(val2));
-               sprintf(Players[i].host, "%s", inet_ntoa(addr.sin_addr));
+       for (new = 1; new <= MAX_SCREENS; new++)
+               if (netGen[new].fd < 0) break;
+       if (new > maxplayers) return;
+
+       if ((
+               netGen[new].fd = accept(gen->fd, (struct sockaddr *)&addr, &addrLen)
+       ) < 0)
+               die("accept");
+       fprintf(stderr, MSG_SERVER_CONNECT "\n", inet_ntoa(addr.sin_addr));
+       val2.l_onoff = 1;
+       val2.l_linger = 0;
+       setsockopt(netGen[new].fd, SOL_SOCKET, SO_LINGER,(void *)&val2,
+               sizeof(val2));
+       AddEventGen(&netGen[new]);
+       netGen[new].player = event->u.net.uid = new;
+       { //new connection; netGen already initialized in GenFunc
+               struct hostent *host;
+
+               sprintf(Players[new].host, "%s", inet_ntoa(addr.sin_addr));
                if (addr.sin_family == AF_INET) {
-                       host = gethostbyaddr((void *)&addr.sin_addr,
-                                       sizeof(struct in_addr), AF_INET);
+                       host = gethostbyaddr(
+                               (void *)&addr.sin_addr, sizeof(struct in_addr), AF_INET
+                       );
                        if (host) {
-                               strncpy(Players[i].host, host->h_name, sizeof(Players[i].host) - 1);
-                               Players[i].host[sizeof(Players[i].host) - 1] = 0;
-                       }
+                               strncpy(Players[new].host, host->h_name,
+                                       sizeof(Players[new].host) - 1);
+                               Players[new].host[sizeof(Players[new].host) - 1] = 0;
+                       } //set player's hostname
                }
-               AddEventGen(&netGen[i]);
-               netGen[i].player = i;
-               totalPlayers++;
-               {
-                       MyEvent event;
-
-                       do {} while ((WaitMyEvent(&event, EM_net) != E_net) ||
-                                       (event.u.net.sender != i));
-                       if (event.u.net.type != NP_hello) ByeClient(i);
-                       else {
-                               netint4 versiondata[2];
-                               char data[255];
-                               int major;
-                               memcpy(versiondata, event.u.net.data, sizeof(versiondata));
-                               major = ntoh4(versiondata[0]);
-                               protocolVersion = ntoh4(versiondata[1]);
-                               if (major != MAJOR_VERSION || protocolVersion != PROTOCOL_VERSION) {
-                                       snprintf(data, sizeof(data), "Version mismatch: received %d.%d",
-                                               major, protocolVersion);
-                                       fprintf(stderr, "Byebye client #%d (%s)\n",
-                                               event.u.net.sender, data);
-                                       SendPacketTo(event.u.net.sender, 0, NP_error, strlen(data)+1, data);
-                                       SCloseNet(event.u.net.sender);
-                               } //version mismatch
-                               fprintf(stderr, "Accepted #%d\n", event.u.net.sender);
-                       } //NP_hello
+       } //E_connect
+       return E_connect;
+}
+
+static void CountPlayers(void)
+{ //count number of players/teams
+       int i, j;
+       playercount = 0;
+       for (i = 1; i < MAX_SCREENS; i++) if (Players[i].alive > 0) {
+               if (Players[i].team < 128) for (j = 1; j < i; j++) {
+                       if (Players[j].alive > 0 && Players[j].team == Players[i].team) {
+                               playercount--; // player of same team counted before
+                               break;
+                       }
                }
-       }
-       close(sockListen);
-       return 0;
-} //WaitForConnection
+               playercount++;
+       } //player alive
+}
 
-ExtFunc int StartServer(char *portStr)
+static int StartServer(void)
 {
        MyEvent event;
-       char serverdata[255];
-       int playercount;
+       netint2 currentpiece[MAX_SCREENS];
+       int playersReady = 0;
+       bool paused = 1;
        int i;
 
-       {
-               short port;
-
-               if (portStr)
-                       port = atoi(portStr);   /* XXX Error checking */
-               else
-                       port = DEFAULT_PORT;
-               WaitForConnection(port);
-       }
-
-       playercount = MAX_CONNECTIONS;
-
-       for (i = 1; i <= playercount; i++) {
-               sprintf(serverdata, "Netris server %s", version_string);
-               SendPacketTo(i, i, NP_hello, strlen(serverdata)+1, serverdata);
-       }
-
-       while(1) {
-               if (WaitMyEvent(&event, EM_net) == E_net) {
-//                     fprintf(stderr, "in %d: %d\n",
-//                             netGen[event.u.net.sender].fd, event.u.net.type);
-                       switch(event.u.net.type) {
-                               case NP_endConn:
-                               { //client went away :(
-                                       //tell the others! :)
-                                       break;
-                               } //NP_endConn
+       do {
+               switch (WaitMyEvent(&event, EM_any)) {
+                       case E_lostConn: //client went away :(
+                               Players[event.u.net.sender].alive = -1;
+                               for (i = 1; i < MAX_SCREENS; i++)
+                                       if (Players[i].alive >= 0)
+                                               SendPacketTo(i, event.u.net.sender,
+                                                       NP_part, sizeof(Players[0].alive),
+                                                       &Players[event.u.net.sender].alive);
+                               SCloseNet(event.u.net.sender);
+                               break; //NP_endConn
+                       case E_net:
+                               if (verbose) fprintf(stderr, MSG_SERVER_DBG_RECV "\n",
+                                       netGen[event.u.net.sender].fd, event.u.net.type);
+                               switch(event.u.net.type) {
                                case NP_hello:
+//                                     if (event.u.net.type != NP_hello) ByeClient(new);
+                               {
+                                       netint4 versiondata[2];
+                                       char data[255];
+                                       int major;
+                                       int protocolVersion;
+
+                                       memcpy(versiondata, event.u.net.data, sizeof(versiondata));
+                                       major = ntoh4(versiondata[0]);
+                                       protocolVersion = ntoh4(versiondata[1]);
+                                       if (major != MAJOR_VERSION
+                                        || protocolVersion != PROTOCOL_VERSION) {
+                                               snprintf(data, sizeof(data),
+                                                       "Version mismatch: received %d.%d",
+                                                       major, protocolVersion);
+                                               fprintf(stderr, MSG_SERVER_PLAYER_EVERSION "\n",
+                                                       event.u.net.sender, data);
+                                               SendPacketTo(event.u.net.sender, 0, NP_error,
+                                                       strlen(data)+1, data);
+                                               SCloseNet(event.u.net.sender);
+                                       } //version mismatch
+                                       fprintf(stderr, MSG_SERVER_PLAYER_ACCEPT "\n",
+                                               event.u.net.sender);
                                        break;
+                               } //NP_hello
                                case NP_newPlayer:
-                               //receive player details and return other players
+                               //receive player details and return other players
                                        memcpy(&Players[event.u.net.sender],
                                                event.u.net.data, event.u.net.size);
-                                       fprintf(stderr, "player %d: %s <%s>\n", event.u.net.sender,
-                                               event.u.net.data, //Players[event.u.net.sender].name
-                                               Players[event.u.net.sender].host);
-                                       for (i = 1; i <= totalPlayers; i++)
-                                               if (i != event.u.net.sender)
-                                                       SendPacketTo(i, event.u.net.sender, event.u.net.type,
-                                                               sizeof(Player) - sizeof(Players[0].spy),
-                                                               &Players[event.u.net.sender]);
-                                       if (--playercount == 0) {
-                                               fprintf(stderr, "Starting game\n");
-                                               for (i = 1; i <= totalPlayers; i++)
-                                                       SendPacketTo(i, 0, NP_goAhead, 0, NULL);
-                                               playercount++;
+                                       if (Players[event.u.net.sender].team < 1
+                                        || Players[event.u.net.sender].team > 7) {
+                                               int team;
+
+                                               for (team = 1; team < 7; team++) {
+                                                       for (i = 1; i < MAX_SCREENS; i++)
+                                                               if (Players[i].alive > 0 && Players[i].team == team)
+                                                                       break; //team in use
+                                                       if (i == MAX_SCREENS) break;
+                                               } //find unused team
+                                               Players[event.u.net.sender].team = team;
+                                               SendPacketTo(event.u.net.sender, event.u.net.sender, NP_team,
+                                                       sizeof(Players[event.u.net.sender].team),
+                                                       &Players[event.u.net.sender].team);
+                                       } //invalid team
+                                       if (Game.started < 2)
+                                               Players[event.u.net.sender].flags |= SCF_paused;
+                                       if (!Game.continuous && Game.started >= 2) {
+                                               char data[40];
+                                               strcpy(data, "Can't join: Game has already started");
+                                               fprintf(stderr, MSG_SERVER_PLAYER_JOIN_ESTARTED,
+                                                       event.u.net.sender);
+                                               SendPacketTo(event.u.net.sender, 0, NP_error,
+                                                       strlen(data)+1, data);
+//                                             SCloseNet(event.u.net.sender, 0);
+                                               break;
+                                       } //can't join started game
+                                       {
+                                               static struct {
+                                                       int playerflags;
+                                                       int gravity;    //1
+                                                       int started;    //2
+                                                       int continuous; //3
+                                                       long seed;              //4
+                                                       float shapes[7];
+                                                       int initspeed;  //5
+                                               } data;
+
+                                               memcpy(&data, &Players[event.u.net.sender].flags,
+                                                       sizeof(data.playerflags));
+                                               memcpy(&data.gravity, &Game.gravity,
+                                                       sizeof(data) - sizeof(data.playerflags));
+                                               SendPacketTo(event.u.net.sender, 0, NP_gamedata,
+                                                       sizeof(data), &data);
+                                       } //send game options
+                                       for (i = 1; i < MAX_SCREENS; i++)
+                                               if (netGen[i].fd >= 0 && i != event.u.net.sender) {
+                                                       SendPacketTo(event.u.net.sender, i, NP_newPlayer,
+                                                               sizeof(player_t), &Players[i]);
+                                                       SendPacketTo(event.u.net.sender, i, NP_newPiece,
+                                                               sizeof(Players[i].curShape), &Players[i].curShape);
+                                                       SendPacketTo(i, event.u.net.sender, NP_newPlayer,
+                                                               sizeof(player_t), &Players[event.u.net.sender]);
+                                               } //send (to) players
+                                       fprintf(stderr, MSG_SERVER_PLAYER_JOIN "\n",
+                                               event.u.net.sender,
+                                               Players[event.u.net.sender].name,
+                                               Players[event.u.net.sender].host,
+                                               teamname[Players[event.u.net.sender].team]);
+                                       if (++playersReady >= minplayers) {
+                                               if (Game.started > 1)
+                                                       SendPacketTo(event.u.net.sender, 0,
+                                                               NP_start, 0, NULL);
+/*                                             else {
+                                                       fprintf(stderr, "* Starting game (%010d)\n",
+                                                               Game.seed);
+                                                       for (i = 1; i < MAX_SCREENS; i++)
+                                                               SendPacketTo(i, 0, NP_start, 0, NULL);
+                                                       Game.started++;
+                                               } //first goahead (to all)*/
                                        } //give go ahead
-                                       break;
-                               } //NP_playerdata
-                               default:
-                               { //relay data to all players
-       //                              if (event.u.net.type >= NP_pause)
-                                       for (i = 1; i <= totalPlayers; i++)
+                                       break; //NP_playerdata
+                               case NP_newPiece:
+                                       memcpy(&Players[event.u.net.sender].curShape,
+                                               event.u.net.data, sizeof(Players[0].curShape));
+                                       goto sendtoall;
+                               case NP_argghhh:
+                                       Players[event.u.net.sender].alive = 0;
+                                       fprintf(stderr, MSG_SERVER_PLAYER_DIE "\n",
+                                               event.u.net.sender);
+                                       //check for unpaused game
+                               case NP_pause:
+                               {
+                                       Players[event.u.net.sender].flags ^= SCF_paused;
+                                       paused = Game.started < 1;
+                                       for (i = 1; i < MAX_SCREENS; i++)
+                                               if (Players[i].alive > 0)
+                                                       paused |= (Players[i].flags & SCF_paused) != 0;
+                                       fprintf(stderr, MSG_SERVER_PLAYER_PAUSE "\n",
+                                               event.u.net.sender, paused);
+                                       goto sendtoall;
+                               } //NP_pause
+                               default: //relay data to all players
+                               sendtoall:
+//                                     if (event.u.net.type >= NP_pause)
+                                       if (event.u.net.type >= NP_rotright
+                                        && Game.started < 2)
+                                               break;
+                                       for (i = 1; i < MAX_SCREENS; i++)
                                                if (i != event.u.net.sender)
-                                                       SendPacketTo(i, event.u.net.sender, event.u.net.type,
-                                                               event.u.net.size, event.u.net.data);
-                                       break;
+                                               if (event.u.net.type != NP_giveJunk
+                                                || Players[i].team != Players[event.u.net.sender].team)
+                                                       SendPacketTo(i, event.u.net.sender,
+                                                               event.u.net.type, event.u.net.size,
+                                                               event.u.net.data);
+                                       break; //>=NP_paused
                                }
-                       } //E_net
-               }
-       } //loop
-} //StartServer
+                               break; //E_net
+                       case E_connect:
+                       { //new connection; netGen already initialized in GenFunc
+                               char serverdata[255];
+
+                               sprintf(serverdata, "Netris server %s", version_string);
+                               SendPacketTo(event.u.net.uid, event.u.net.uid, NP_hello,
+                                       strlen(serverdata)+1, serverdata);
+                               break;
+                       } //E_connect
+               } //event
+               CountPlayers();
+               if (Game.started < 1) {
+                       if (playercount >= 2) {
+                               fprintf(stderr, MSG_SERVER_GAME_READY "\n", Game.seed);
+                               Game.started++;
+                       } //give goahead
+               } //game not yet started
+               else {
+                       if (playercount < 2) {
+                               fprintf(stderr, MSG_SERVER_GAME_STOP "\n");
+                               if (Game.seed) Game.seed++;
+                               if (Game.started > 1) for (i = 1; i < MAX_SCREENS; i++) {
+                                       if (Players[i].alive >= 0) {
+                                               Players[i].alive = 1;
+                                               Players[i].flags |= SCF_paused;
+                                               SendPacketTo(i, 0, NP_stop,
+                                                       sizeof(Game.seed), &Game.seed);
+                                       } //transmit game stop and set players not ready
+                               }
+                               paused = 1;
+                               Game.started = 0;
+                       } //too few players for game
+                       if (Game.started == 1 && !paused) {
+                               Game.started++;
+                               fprintf(stderr, MSG_SERVER_GAME_START "\n");
+                               for (i = 1; i < MAX_SCREENS; i++)
+                                       if (Players[i].alive > 0)
+                                               SendPacketTo(i, 0, NP_start, 0, NULL);
+                       } //everybody ready to start
+               } //game (ready to) start(ed)
+       } while (1);
+       fprintf(stderr, "* Exiting server\n");
+}
 
 
-ExtFunc void Header(void)
+static void SHeader(void)
 {
-       fprintf(stderr,
-         "NETRIS Server %s\t(c) 2002 Shiar <shiar@shiar.org>\n\n",
-         version_string);
-} //Header
-
-ExtFunc void Usage(void)
-{
-       Header();
-       fprintf(stderr,
-         "Usage: netris <options>\n"
-         "\n"
-         "  -h, --help\t\tPrint this usage information\n"
-         "  -H, --info\t\tShow distribution and warranty information\n"
-         "\n"
-         "  -p, --port <port>\tSet port number (default is %d)\n"
-         "\n"
-         "  -s, --seed <seed>\tStart with given random seed\n"
-         "  -i, --speed <sec>\tSet the initial step-down interval, in seconds\n"
-         "\n", DEFAULT_PORT);
+       fprintf(stderr, MSG_SERVER_TITLE "\n\n", version_string);
 }
 
-ExtFunc void DistInfo(void)
+static void SUsage(void)
 {
-       Header();
+       SHeader();
        fprintf(stderr,
-         "This program is free software; you can redistribute it and/or modify\n"
-         "it under the terms of the GNU General Public License as published by\n"
-         "the Free Software Foundation; either version 2 of the License, or\n"
-         "(at your option) any later version.\n"
-         "\n"
-         "This program is distributed in the hope that it will be useful,\n"
-         "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
-         "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
-         "GNU General Public License for more details.\n"
-         "\n"
-         "You should have received a copy of the GNU General Public License\n"
-         "along with this program; if not, write to the Free Software\n"
-         "Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n"
-         "\n");
-} //DistInfo
-
-ExtFunc void WriteConf(void)
-{
-       FILE *file_out;
-
-       file_out = fopen(CONFIG_FILE, "w");
-       if (file_out == NULL) {
-               perror("Error writing config file");
-               exit(1);
-       }
-
-       fprintf(file_out, "### NETRIS %s Config file ###\n\n", version_string);
-
-       fclose(file_out);
-       fprintf(stderr, "Wrote new game configuration to %s\n", CONFIG_FILE);
-} //WriteConf
+               "Usage: netris <options>\n"
+               "\n"
+               "  -h, --help\t\tPrint this usage information\n"
+               "  -H, --info\t\tShow distribution and warranty information\n"
+               "\n"
+               "  -p, --port %d\tSet port number\n"
+               "\n"
+               "  -s, --seed <seed>\tStart with given random seed\n"
+               "  -i, --speed %.2f\tSet the initial step-down interval, in seconds\n"
+               "  -m, --min-players %d\tNumber of players required before starting the game\n"
+               "  -x, --max-players %d\tMaximum number of players allowed in the game\n"
+               "  -c, --continuous\tDon'n quit the game\n"
+               "\n",
+               DEFAULT_PORT, DEFAULT_INTERVAL/1e6, minplayers, maxplayers
+       );
+}
 
-ExtFunc void HandleOption(char tag, char *value)
+static void HandleOption(char tag, char *value)
 {
        switch (tag) {
-               case 'p':       //port
-                       portStr = value; break;
-               case 'i':       //speed (of level 1)
-                       Game.initspeed = atof(value) * 1e6;
-                       break;
-               case 's':       //seed
-                       Game.seed = atoi(value);
-                       Players[0].flags |= SCF_setSeed;
-                       break;
-               case 'H':       //info
-                       DistInfo(); exit(0);
-               case 'h':       //help
-                       Usage(); exit(0);
-               default:
-                       break;
+       case 'v':  //verbose
+               verbose = 1;
+               break;
+       case 'p':  //port
+               port = atoi(value);
+               break;
+       case 'c':  //min-players
+               Game.continuous = atoi(value);
+               break;
+       case 'm':  //min-players
+               minplayers = atoi(value);
+               break;
+       case 'x':  //max-players
+               maxplayers = MIN(atoi(value), MAX_SCREENS);
+               break;
+       case 'q':  //quadra-style gravity
+               Game.gravity ^= 1;
+               break;
+       case 'i':  //speed (of level 1)
+               Game.initspeed = atof(value) * 1e6;
+               break;
+       case 's':  //seed
+               Game.seed = atoi(value);
+               break;
+       case 'H':  //info
+               SHeader();
+               DistInfo();
+               exit(0);
+       case 'h':  //help
+               SUsage();
+               exit(0);
+       default:
+               break;
        }
-} //HandleParam
+}
 
-ExtFunc void ReadConf(char *filename)
+static void ReadConf(char *filename)
 {
        FILE *file_in;
        char buf[513];
@@ -509,7 +504,8 @@ ExtFunc void ReadConf(char *filename)
                        if ((ch = strchr(buf, '#')))
                                *ch = '\0'; // truncate string from # char
                        for (i = strlen(buf)-1; i >= 0; i--)
-                               if (buf[i] == ' ' || buf[i] == '\n' || buf[i] == '\t' || buf[i] == 13)
+                               if (buf[i] == ' '  || buf[i] == '\t'
+                                || buf[i] == '\n' || buf[i] == 13)
                                        buf[i] = '\0';
                                else break;
 
@@ -527,41 +523,81 @@ ExtFunc void ReadConf(char *filename)
                fprintf(stderr, "Unable to open config file %s.\n", filename);
        } //defaults
 
-} //ReadConf
+}
 
-ExtFunc void CatchInt(int sig)
+static void CatchInt(int sig)
 {
        siglongjmp(close_env, 1);
 }
 
-ExtFunc int main(int argc, char **argv)
+int main(int argc, char **argv)
 {
+       int i;
        char ch;
 
        if (sigsetjmp(close_env, 1)) exit(0);
        signal(SIGINT, CatchInt);
-       Game.standout = Game.color = 1;
+       port = DEFAULT_PORT;
+       maxplayers = 8;
        Game.initspeed = DEFAULT_INTERVAL;
+       Game.seed = time(0);
+       for (i = 0; i < sizeof(Game.shapes) / sizeof(Game.shapes[0]); i++)
+               Game.shapes[i] = 1;
+       Game.gravity = 0;
+       {
+               int i;
+
+               for (i = 1; i < MAX_SCREENS; i++)
+                       Players[i].alive = -1;
+       }
 
 //     if (getopt(argc, argv, "f:") == 'f')
 //             ReadConf(optarg);
 //     else
        ReadConf(CONFIG_FILE);
-       while ((ch = getopt_long(argc, argv,
-                       "hHp:i:s:", options, NULL)) != -1)
+       while ((ch = getopt_long(
+               argc, argv, "hHvqp:i:s:c:m:x:", options, NULL
+       )) != -1)
                HandleOption(ch, optarg);
        if (optind < argc) {
-               Usage();
+               SUsage();
                exit(1);
        }
 //     WriteConf();
 
-       Header();
-       StartServer(portStr);
+       SHeader();
+
+       {
+               int i;
+
+               for (i = 1; i < MAX_SCREENS; i++)
+                       memcpy(&netGen[i], &netGen[0], sizeof(EventGenRec));
+       } //setup netGen var
+
+       AtExit(CloseNets);
+
+       {
+               int val1;
+
+               memset(&addr, 0, sizeof(addr));
+               addr.sin_family = AF_INET;
+               addr.sin_port = htons(port);
+               addr.sin_addr.s_addr = htonl(INADDR_ANY);
+               if ((connGen.fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+                       die("socket");
+               val1 = 1;
+               setsockopt(connGen.fd, SOL_SOCKET, SO_REUSEADDR,
+                       (void *)&val1, sizeof(val1));
+               if (bind(connGen.fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
+                       die("bind");
+               if (listen(connGen.fd, 1) < 0)
+                       die("listen");
+
+               AddEventGen(&connGen);
+       } //setup listening sock
+
+       StartServer(); //server loop
+
        return 0;
 }
 
-/*
- * vi: ts=4 ai
- * vim: noai si
- */