unofficial version 0.6: first major updates v0.6
authorMischa POSLAWSKY <netris@shiar.org>
Fri, 9 Aug 2002 19:27:00 +0000 (21:27 +0200)
committerMischa POSLAWSKY <netris@shiar.org>
Tue, 27 Feb 2007 04:48:52 +0000 (05:48 +0100)
- changelog
- levels (speed up every 10 lines)
- statistics (apm, ppm, score using gameboy system)
- dedicated server
- user config file
- support more than 2 players
- linedraw characters if available
- more

14 files changed:
.gitignore
CHANGES [new file with mode: 0644]
Configure
VERSION
board.c
curses.c
game.c
inet.c
netris.conf [new file with mode: 0644]
netris.h
server.c [new file with mode: 0644]
shapes.c
sr.c
util.c

index 62ad90c7bad67d646931c86a8ad1184b89caddba..a7463487ef18d09a93b738c1febb9f468666f44f 100644 (file)
@@ -6,4 +6,5 @@ proto.chg
 version.c
 *.o
 netris
+server
 sr
diff --git a/CHANGES b/CHANGES
new file mode 100644 (file)
index 0000000..0aa3d64
--- /dev/null
+++ b/CHANGES
@@ -0,0 +1,91 @@
+---- done: -------------------------------------------------------------------
+
+ -- v0.6 ---------- 31.VII.02
+
+ * -D enables instant-drop (classic drop), original -D is now -d
+ * addition key for rotating pieces counterclockwise
+ * displays number of pieces dropped and lines made
+
+ -- v0.6.81 -------
+
+ * shows next piece
+ * displays elapsed time instead of clock
+ * shows ppm, apm and yield
+ * uses arrow keys by default (linux workaround, doesn't work on all systems)
+
+ -- v0.6.82 -------
+
+ * counts score (as in gameboy version)
+ * reads default parameters from netris.conf (or other file specified with -f)
+ -- v0.6.83 -------
+
+ * level up every 10 lines (speed x1.2)
+ -- v0.6.84 -------
+
+ * same handler for configfile/parameters
+ * linedraw characters unless --ascii specified
+ * levelup handeled correctly
+ * handicap with --level (affects yourself only, unlike --speed)
+ * some variables moved and altered
+ * displays name+host above opponent field
+
+ -- v0.6.85 -------
+
+ * titlebar somewhat improved
+ * pause stops time
+ * junklines doesn't move pieces up (unless inside ground)
+ * improved (insta)drop handling
+ * drop/down at bottom forces drop
+ * updated help messages (-[hH])
+
+ -- v0.6.86 -------
+
+ * server doesn't play
+
+ -- v0.6.87 -------
+
+ * multiplay can handle over 2 players!
+
+ -- v0.6.88 -------
+
+ * player 1 always leftmost
+ * server seperate program
+
+ -- v0.6.89 -------
+
+ * server receive fixed (checked nonexisting sock)
+ * host generates and distributes player's host string
+ * handle sigint correctly (not just exit(0))
+ * fix multiplay g/o (close connection at g/o or server signal)
+ * spy=n0 in configfile disables spying for player n
+
+
+---- near-future: ------------------------------------------------------------
+
+ * teams
+ * send game options
+ * fix multplay pause
+ * server continuously acceping new connections, and immediate handshake
+ * fix g/o (server should count, maybe reorder players)
+ * everybody start paused?
+ * show n fields (improved spy parameter)
+
+
+---- asap: -------------------------------------------------------------------
+
+ * fix -f
+ * midgame join option
+ * optional enemy field resize
+ * multiplayer stats
+ * pause message over player's field
+
+
+---- distant future: ---------------------------------------------------------
+
+ * special blocks
+ * tetrinet compatible?
+
+
+------------------------------------------------------------------------------
index fbc57a816bdf864ac39d4cd7c2c394dfae2859b0..97479dbb54247710f890d26b793ad4220fcd4846 100755 (executable)
--- a/Configure
+++ b/Configure
@@ -21,7 +21,7 @@
 #
 
 CC="gcc"
-COPT="-g -O"
+COPT="-O -m486"
 CEXTRA=""
 LEXTRA=""
 CURSES_HACK=false
@@ -31,8 +31,8 @@ while [ $# -ge 1 ]; do
        shift
        case "$opt" in
                -g)
-                       COPT="-g"
-                       CEXTRA="-Wall -Wstrict-prototypes"
+                       COPT="-g -O0"
+#                      CEXTRA="-Wall -Wstrict-prototypes"
                        ;;
                -O*)
                        COPT="$opt"
@@ -144,7 +144,7 @@ SOURCES="$ORIG_SOURCES $GEN_SOURCES"
 SRCS="`echo $SOURCES | sed -e s/-/.c/g`"
 OBJS="`echo $SOURCES | sed -e s/-/.o/g`"
 
-DISTFILES="README FAQ COPYING VERSION Configure netris.h sr.c robot_desc"
+DISTFILES="README FAQ COPYING VERSION Configure netris.h sr.c server.c robot_desc"
 DISTFILES="$DISTFILES `echo $ORIG_SOURCES | sed -e s/-/.c/g`"
 
 echo > .depend
@@ -173,13 +173,16 @@ SRCS = -SRCS-
 OBJS = -OBJS-
 DISTFILES = -DISTFILES-
 
-all: Makefile config.h proto.h $(PROG) sr
+all: Makefile config.h proto.h $(PROG) sr server
 
 $(PROG): $(OBJS)
-       $(CC) -o $(PROG) $(OBJS) $(LFLAGS)
+       $(CC) -o $(PROG) $(OBJS) $(LFLAGS) $(CFLAGS)
 
 sr: sr.o
-       $(CC) -o sr sr.o $(LFLAGS)
+       $(CC) -o sr sr.o $(LFLAGS) $(CFLAGS)
+
+server: server.o
+               $(CC) -o server server.o $(LFLAGS) $(CFLAGS)
 
 .c.o:
        $(CC) $(CFLAGS) -c $<
@@ -223,7 +226,7 @@ dist: $(DISTFILES)
        tar -cvzof $$dir.tar.gz $$dir
 
 clean:
-       rm -f proto.h proto.chg $(PROG) $(OBJS) version.c test.c a.out sr sr.o
+       rm -f proto.h proto.chg $(PROG) $(OBJS) version.c test.c a.out sr sr.o server server.o
 
 cleandir: clean
        rm -f .depend Makefile config.h
diff --git a/VERSION b/VERSION
index 5a2a5806df6e909afe3609b5706cb1012913ca0e..83fb97f653d1dcd82918801ac0a886fc8af5ec68 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.6
+0.5.88
diff --git a/board.c b/board.c
index 245cea1d43eb6194f697f82c8488a840f0d59864..8524cc5b7adeceff018baa0c2b8df445fca8880a 100644 (file)
--- a/board.c
+++ b/board.c
@@ -34,14 +34,6 @@ static unsigned int changed[MAX_SCREENS][MAX_BOARD_HEIGHT];
 static int falling[MAX_SCREENS][MAX_BOARD_WIDTH];
 static int oldFalling[MAX_SCREENS][MAX_BOARD_WIDTH];
 
-ExtFunc void InitBoard(int scr)
-{
-       boardHeight[scr] = MAX_BOARD_HEIGHT;
-       boardVisible[scr] = 20;
-       boardWidth[scr] = 10;
-       InitScreen(scr);
-}
-
 ExtFunc void CleanupBoard(int scr)
 {
        CleanupScreen(scr);
@@ -49,9 +41,9 @@ ExtFunc void CleanupBoard(int scr)
 
 ExtFunc BlockType GetBlock(int scr, int y, int x)
 {
-       if (y < 0 || x < 0 || x >= boardWidth[scr])
+       if (y < 0 || x < 0 || x >= Players[scr].boardWidth)
                return BT_wall;
-       else if (y >= boardHeight[scr])
+       else if (y >= Players[scr].boardHeight)
                return BT_none;
        else
                return abs(board[scr][y][x]);
@@ -59,8 +51,9 @@ ExtFunc BlockType GetBlock(int scr, int y, int x)
 
 ExtFunc void SetBlock(int scr, int y, int x, BlockType type)
 {
-       if (y >= 0 && y < boardHeight[scr] && x >= 0 && x < boardWidth[scr]) {
-               if (y < boardVisible[scr])
+       if (y >= 0 && y < Players[scr].boardHeight &&
+               x >= 0 && x < Players[scr].boardWidth) {
+               if (y < Players[scr].boardVisible)
                        falling[scr][x] += (type < 0) - (board[scr][y][x] < 0);
                board[scr][y][x] = type;
                changed[scr][y] |= 1 << x;
@@ -73,11 +66,11 @@ ExtFunc int RefreshBoard(int scr)
        unsigned int c;
        BlockType b;
 
-       for (y = boardVisible[scr] - 1; y >= 0; --y)
+       for (y = Players[scr].boardVisible - 1; y >= 0; --y)
                if ((c = changed[scr][y])) {
                        if (robotEnable) {
                                RobotCmd(0, "RowUpdate %d %d", scr, y);
-                               for (x = 0; x < boardWidth[scr]; ++x) {
+                               for (x = 0; x < Players[scr].boardWidth; ++x) {
                                        b = board[scr][y][x];
                                        if (fairRobot)
                                                b = abs(b);
@@ -95,7 +88,7 @@ ExtFunc int RefreshBoard(int scr)
                }
        if (robotEnable)
                RobotTimeStamp();
-       for (x = 0; x < boardWidth[scr]; ++x)
+       for (x = 0; x < Players[scr].boardWidth; ++x)
                if (oldFalling[scr][x] != !!falling[scr][x]) {
                        oldFalling[scr][x] = !!falling[scr][x];
                        PlotUnderline(scr, x, oldFalling[scr][x]);
@@ -106,7 +99,7 @@ ExtFunc int RefreshBoard(int scr)
 
 ExtFunc int GlanceFunc(int scr, int y, int x, BlockType type, void *data)
 {
-       PlotBlock1(scr, y, x, type);
+       PlotBlock1(scr, 20 - y, x * 2, type);
        return 0;
 }
 
@@ -129,7 +122,8 @@ ExtFunc int CollisionFunc(int scr, int y, int x, BlockType type, void *data)
 
 ExtFunc int VisibleFunc(int scr, int y, int x, BlockType type, void *data)
 {
-       return (y >= 0 && y < boardVisible[scr] && x >= 0 && x < boardWidth[scr]);
+       return (y >= 0 && y < Players[scr].boardVisible &&
+                       x >= 0 && x < Players[scr].boardWidth);
 }
 
 ExtFunc void PlotShape(Shape *shape, int scr, int y, int x, int falling)
@@ -156,14 +150,15 @@ ExtFunc int MovePiece(int scr, int deltaY, int deltaX)
 {
        int result;
 
-       EraseShape(curShape[scr], scr, curY[scr], curX[scr]);
-       result = ShapeFits(curShape[scr], scr, curY[scr] + deltaY,
-                               curX[scr] + deltaX);
+       EraseShape(Players[scr].curShape, scr,
+               Players[scr].curY, Players[scr].curX);
+       result = ShapeFits(Players[scr].curShape, scr, Players[scr].curY + deltaY,
+                               Players[scr].curX + deltaX);
        if (result) {
-               curY[scr] += deltaY;
-               curX[scr] += deltaX;
+               Players[scr].curY += deltaY;
+               Players[scr].curX += deltaX;
        }
-       PlotShape(curShape[scr], scr, curY[scr], curX[scr], 1);
+       PlotShape(Players[scr].curShape, scr, Players[scr].curY, Players[scr].curX, 1);
        return result;
 }
 
@@ -171,12 +166,15 @@ ExtFunc int RotatePiece(int scr, int dir)
 {
        int result;
 
-       EraseShape(curShape[scr], scr, curY[scr], curX[scr]);
-       result = ShapeFits(dir ? curShape[scr]->rotateTo : curShape[scr]->rotateFrom,
-               scr, curY[scr], curX[scr]);
+       EraseShape(Players[scr].curShape, scr, Players[scr].curY, Players[scr].curX);
+       result = ShapeFits(dir ? Players[scr].curShape->rotateTo
+                                                  : Players[scr].curShape->rotateFrom,
+               scr, Players[scr].curY, Players[scr].curX);
        if (result)
-               curShape[scr] = dir ? curShape[scr]->rotateTo : curShape[scr]->rotateFrom;
-       PlotShape(curShape[scr], scr, curY[scr], curX[scr], 1);
+               Players[scr].curShape = dir ? Players[scr].curShape->rotateTo
+                                                                       : Players[scr].curShape->rotateFrom;
+       PlotShape(Players[scr].curShape, scr,
+                       Players[scr].curY, Players[scr].curX, 1);
        return result;
 }
 
@@ -184,12 +182,15 @@ ExtFunc int DropPiece(int scr)
 {
        int count = 0;
 
-       EraseShape(curShape[scr], scr, curY[scr], curX[scr]);
-       while (ShapeFits(curShape[scr], scr, curY[scr] - 1, curX[scr])) {
-               --curY[scr];
+       EraseShape(Players[scr].curShape, scr,
+               Players[scr].curY, Players[scr].curX);
+       while (ShapeFits(Players[scr].curShape, scr,
+                       Players[scr].curY - 1, Players[scr].curX)) {
+               --Players[scr].curY;
                ++count;
        }
-       PlotShape(curShape[scr], scr, curY[scr], curX[scr], 1);
+       PlotShape(Players[scr].curShape, scr,
+               Players[scr].curY, Players[scr].curX, 1);
        return count;
 }
 
@@ -197,7 +198,7 @@ ExtFunc int LineIsFull(int scr, int y)
 {
        int x;
 
-       for (x = 0; x < boardWidth[scr]; ++x)
+       for (x = 0; x < Players[scr].boardWidth; ++x)
                if (GetBlock(scr, y, x) == BT_none)
                        return 0;
        return 1;
@@ -208,7 +209,7 @@ ExtFunc void CopyLine(int scr, int from, int to)
        int x;
 
        if (from != to)
-               for (x = 0; x < boardWidth[scr]; ++x)
+               for (x = 0; x < Players[scr].boardWidth; ++x)
                        SetBlock(scr, to, x, GetBlock(scr, from, x));
 }
 
@@ -217,7 +218,7 @@ ExtFunc int ClearFullLines(int scr)
        int from, to;
 
        from = to = 0;
-       while (to < boardHeight[scr]) {
+       while (to < Players[scr].boardHeight) {
                while (LineIsFull(scr, from))
                        ++from;
                CopyLine(scr, from++, to++);
@@ -230,8 +231,8 @@ ExtFunc void FreezePiece(int scr)
        int y, x;
        BlockType type;
 
-       for (y = 0; y < boardHeight[scr]; ++y)
-               for (x = 0; x < boardWidth[scr]; ++x)
+       for (y = 0; y < Players[scr].boardHeight; ++y)
+               for (x = 0; x < Players[scr].boardWidth; ++x)
                        if ((type = board[scr][y][x]) < 0)
                                SetBlock(scr, y, x, -type);
 }
@@ -240,12 +241,19 @@ ExtFunc void InsertJunk(int scr, int count, int column)
 {
        int y, x;
 
-       for (y = boardHeight[scr] - count - 1; y >= 0; --y)
+       EraseShape(Players[scr].curShape, scr,
+               Players[scr].curY, Players[scr].curX);
+       for (y = Players[scr].boardHeight - count - 1; y >= 0; --y)
                CopyLine(scr, y, y + count);
        for (y = 0; y < count; ++y)
-               for (x = 0; x < boardWidth[scr]; ++x)
+               for (x = 0; x < Players[scr].boardWidth; ++x)
                        SetBlock(scr, y, x, (x == column) ? BT_none : BT_white);
-       curY[scr] += count;
+       Players[scr].curY += count; //move piece up..
+       for (y = 0; y < count; ++y)
+               if (ShapeFits(Players[scr].curShape, scr, Players[scr].curY - 1, Players[scr].curX))
+                       Players[scr].curY--; //..and down as far as possible
+               else break;
+       PlotShape(Players[scr].curShape, scr, Players[scr].curY, Players[scr].curX, 1);
 }
 
 /*
index 9649d853a9b9062dc7863328f20f336478c5d2c5..4f8ddbdf7e3536342f350c29356172a59c8a3278 100644 (file)
--- a/curses.c
+++ b/curses.c
@@ -83,9 +83,8 @@ ExtFunc void InitScreens(void)
 #endif
 
 #ifdef HAVE_NCURSES
-       haveColor = colorEnable && has_colors();
-       if (haveColor)
-       {
+       haveColor = Game.color && has_colors();
+       if (haveColor) {
                int i = 0;
 
                start_color();
@@ -105,11 +104,17 @@ ExtFunc void InitScreens(void)
        OutputTermStr(term_vi, 0);
        AddEventGen(&keyGen);
 
-       move(0, 0);
-       addstr("Netris ");
+       move(0, 1);
+       addstr("NETRIS ");
        addstr(version_string);
-       addstr(" (C) 1994-1996,1999  Mark H. Weaver     "
-                       "\"netris -h\" for more info");
+       {
+               int rows, cols;
+
+               getmaxyx(stdscr, rows, cols);
+               move(0, cols - 48);
+               addstr("(C)1994-1996,1999 Mark H. Weaver, (C)2002 Shiar");
+//             addstr("    \"netris -h\" for more info");
+       }
        statusYPos = 22;
        statusXPos = 0;
 }
@@ -186,35 +191,62 @@ ExtFunc void OutputTermStr(char *str, int flush)
        }
 }
 
-ExtFunc void InitScreen(int scr)
+ExtFunc void DrawField(int scr)
 {
-       int y, x;
+       {
+               int y, x;
+
+               for (y = Players[scr].boardVisible - 1; y >= 0; --y) {
+                       mvaddch(boardYPos[scr] - y, boardXPos[scr] - 1,
+                               Game.ascii ? '|' : ACS_VLINE); //left
+                       mvaddch(boardYPos[scr] - y,
+                               boardXPos[scr] + 2 * Players[scr].boardWidth,
+                               Game.ascii ? '|' : ACS_VLINE); //right
+               }
+               move(2, boardXPos[scr] - 1); //top
+               addch(Game.ascii ? '+' : ACS_ULCORNER);
+               for (x = Players[scr].boardWidth * 2 - 1; x >= 0; --x)
+                       addch(Game.ascii ? '-' : ACS_HLINE);
+               addch(Game.ascii ? '+' : ACS_URCORNER);
+               move(boardYPos[scr] + 1, boardXPos[scr] - 1); //bottom
+               addch(Game.ascii ? '+' : ACS_LLCORNER);
+               for (x = Players[scr].boardWidth * 2 - 1; x >= 0; --x)
+                       addch(Game.ascii ? '-' : ACS_HLINE);
+               addch(Game.ascii ? '+' : ACS_LRCORNER);
+       } //draw field grid
 
-       if (scr == 0)
-               boardXPos[scr] = 1;
-       else
-               boardXPos[scr] = boardXPos[scr - 1] +
-                                       2 * boardWidth[scr - 1] + 3;
-       if (scr == 1)
-               boardXPos[scr] += 24;
-       boardYPos[scr] = 22;
-       statusXPos = 2 * boardWidth[0] + 3;
-//     if (statusXPos < boardXPos[scr] + 2 * boardWidth[scr] + 3)
-//             statusXPos = boardXPos[scr] + 2 * boardWidth[scr] + 3;
-       for (y = boardVisible[scr] - 1; y >= 0; --y) {
-               move(boardYPos[scr] - y, boardXPos[scr] - 1);
-               addch('|');
-               move(boardYPos[scr] - y, boardXPos[scr] + 2 * boardWidth[scr]);
-               addch('|');
-       }
-       for (y = boardVisible[scr]; y >= -1; y -= boardVisible[scr] + 1) {
-               move(boardYPos[scr] - y, boardXPos[scr] - 1);
-               addch('+');
-               for (x = boardWidth[scr] - 1; x >= 0; --x)
-                       addstr("--");
-               addch('+');
+       {
+               char userstr[300];
+
+               sprintf(userstr, "%s <%s>", Players[scr].name, Players[scr].host);
+               userstr[20 - 7*((Players[scr].flags & SCF_usingRobot) != 0)
+                       - 5*((Players[scr].flags & SCF_fairRobot) != 0)] = 0;
+               mvaddstr(1, boardXPos[scr], userstr);
+
+               if (Players[scr].flags & SCF_usingRobot) {
+                       addstr((Players[scr].flags & SCF_fairRobot)
+                               ? "(fair robot)" : "(robot)");
+               }
+       } //display playername/host
+} //DrawScreen
+
+ExtFunc void InitFields()
+{
+       int scr, prevscr;
+
+       boardXPos[me] = 1;
+       boardYPos[me] = 22;
+       prevscr = me;
+       for (scr = 1; scr <= totalPlayers + 1; scr++) if (scr != me) {
+               boardXPos[scr] =
+                       boardXPos[prevscr] + 2 * Players[prevscr].boardWidth + 3;
+               if (prevscr == me)
+                       boardXPos[scr] += 24; //scorebar
+               boardYPos[scr] = 22;
+               prevscr = scr;
        }
-}
+       statusXPos = 2 * Players[me].boardWidth + 3;
+} //InitScreen
 
 ExtFunc void CleanupScreen(int scr)
 {
@@ -224,13 +256,13 @@ ExtFunc void PlotBlock1(int scr, int y, int x, BlockType type)
 {
        int colorIndex = abs(type);
 
-       move(boardYPos[scr] - y, boardXPos[scr] + 2 * x);
+       move(y, x);
 
        if (type == BT_none)
                addstr("  ");
        else
        {
-               if (standoutEnable)
+               if (Game.standout)
                {
 #ifdef HAVE_NCURSES
                        if (haveColor)
@@ -247,64 +279,68 @@ ExtFunc void PlotBlock1(int scr, int y, int x, BlockType type)
 
 ExtFunc void PlotBlock(int scr, int y, int x, BlockType type)
 {
-       if (y >= 0 && y < boardVisible[scr] && x >= 0 && x < boardWidth[scr])
-               PlotBlock1(scr, y, x, type);
+       if (y >= 0 && y < Players[scr].boardVisible &&
+               x >= 0 && x < Players[scr].boardWidth)
+         PlotBlock1(scr, boardYPos[scr] - y, boardXPos[scr] + 2 * x, type);
 }
 
 ExtFunc void PlotUnderline(int scr, int x, int flag)
 {
-       move(boardYPos[scr] + 1, boardXPos[scr] + 2 * x);
+  move(boardYPos[scr] + 1, boardXPos[scr] + 2 * x);
+  if (Game.ascii)
        addstr(flag ? "==" : "--");
+  else {
+       addch(flag ? ACS_BTEE : ACS_HLINE);
+       addch(flag ? ACS_BTEE : ACS_HLINE);
+  }
 }
 
 ExtFunc void ShowDisplayInfo(void)
 {
-       move(statusYPos - 9, statusXPos);
-       printw("Seed: %010d", initSeed);
-       move(statusYPos - 8, statusXPos);
-       printw("Speed: %dms ", speed / 1000);
-       if (robotEnable) {
-               move(statusYPos - 6, statusXPos);
-               if (fairRobot)
-                       addstr("Controlled by a fair robot");
-               else
-                       addstr("Controlled by a robot");
-//             clrtoeol();
-       }
-       if (opponentFlags & SCF_usingRobot) {
-               move(statusYPos - 5, statusXPos);
-               if (opponentFlags & SCF_fairRobot)
-                       addstr("The opponent is a fair robot");
-               else
-                       addstr("The opponent is a robot");
-//             clrtoeol();
-       }
+  move(statusYPos - 9, statusXPos);
+  printw("Seed: %010d", Game.seed);
+  //   move(statusYPos - 8, statusXPos);
+  //   printw("Speed: %dms ", speed / 1000);
+  if (robotEnable) {
+       move(statusYPos - 6, statusXPos);
+       if (fairRobot)
+         addstr("Controlled by a fair robot");
+       else
+         addstr("Controlled by a robot");
+       //              clrtoeol();
+  }
+  if (Players[1].flags & SCF_usingRobot) {
+       move(statusYPos - 5, statusXPos);
+       if (Players[1].flags & SCF_fairRobot)
+         addstr("The opponent is a fair robot");
+       else
+         addstr("The opponent is a robot");
+       //              clrtoeol();
+  }
 }
 
-ExtFunc void ShowScore(int scr, int totalDrops, int totalLines, int totalAdds)
+ExtFunc void ShowScore(int scr, struct _Score score)
 {
        float timer;
-       move(6, statusXPos); addstr("Next:         ");
-       move(7, statusXPos + 6);    addstr("        ");
-       ShapeIterate(nextShape[scr], scr,
-               ShapeToNetNum(nextShape[scr]) == 15 ? 15 : 16, statusXPos/2 + 4,
-               1, GlanceFunc, NULL);
+
+       move(6, statusXPos); addstr("Next:          ");
+       move(7, statusXPos + 7);    addstr("        ");
+       ShapeIterate(Players[scr].nextShape, scr,
+         ShapeToNetNum(Players[scr].nextShape) == 15 ? 13 : 14,
+         statusXPos / 2 + 5, 1, GlanceFunc, NULL);
+       move(statusYPos - 21 + 1, statusXPos);
+       printw("Score:%6d  level: %2d", score.score, score.level);
        move(statusYPos - 20 + 1, statusXPos);
        timer = CurTimeval() / 1e6;
-       printw("Lines: %05d", totalLines);
-       if (timer > 4)
-               printw(" (%.1f ppm)", totalDrops * 60 / timer);
-       move(statusYPos - 18, statusXPos);
-       printw("apm: %.1f", totalAdds * 60 / timer);
-       if (totalLines > 0)
-               printw(" (%d%% yield)  ", 100 * totalAdds / totalLines);
-}
-
-ExtFunc void UpdateOpponentDisplay(void)
-{
-       move(1, 0);
-       printw("Playing %s@%s", opponentName, opponentHost);
-       clrtoeol();
+       printw("Lines:%6d", score.lines);
+       if (timer > 4) {
+               printw("  ppm:%5.1f", score.drops * 60 / timer);
+               move(statusYPos - 18, statusXPos);
+               if (score.lines > 0)
+                       printw("yield:  %3d%%", 100 * score.adds / score.lines);
+               else addstr("            ");
+               printw("  apm:%5.1f", score.adds * 60 / timer);
+       }
 }
 
 ExtFunc void ShowPause(int pausedByMe, int pausedByThem)
@@ -335,28 +371,12 @@ ExtFunc void Message(char *s)
        clrtoeol();
 }
 
-ExtFunc void RefreshScreen(void)
+ExtFunc void ShowTime(void)
 {
-/*
-       static char timeStr[2][32];
-       time_t theTime;
-
-       time(&theTime);
-       strftime(timeStr[0], 30, "%I:%M %p", localtime(&theTime));
-       // Just in case the local curses library sucks
-       if (strcmp(timeStr[0], timeStr[1]))
-       {
-               move(statusYPos, statusXPos);
-               addstr(timeStr[0]);
-               strcpy(timeStr[1], timeStr[0]);
-       }
-       move(boardYPos[0] + 1, boardXPos[0] + 2 * boardWidth[0] + 1);
-       refresh();
-*/
        move(statusYPos, statusXPos);
        printw("Timer: %.0f ", CurTimeval() / 1e6);
-       move(boardYPos[0] + 1, boardXPos[0] + 2 * boardWidth[0] + 1);
-       refresh();
+       move(boardYPos[0] + 1, boardXPos[0] + 2 * Players[0].boardWidth + 1);
+//     refresh();
 }
 
 ExtFunc void ScheduleFullRedraw(void)
diff --git a/game.c b/game.c
index 423befb475c35d7686916d6f59cdd4118998fbd6..87208fbf3ee10f2863f5afecbfdd0e36d0950f4f 100644 (file)
--- a/game.c
+++ b/game.c
 #include <sys/types.h>
 #include <netinet/in.h>
 
+static struct option options[] = {
+       { "ascii",              2, 0, 'a' },
+       { "connect",    1, 0, 'c' },
+       { "port",               1, 0, 'p' },
+       { "speed",              1, 0, 'i' },
+       { "level",              1, 0, 'l' },
+       { "spy",                1, 0,  1  },
+       { "seed",               1, 0, 's' },
+       { "robot",              1, 0, 'r' },
+       { "fair-robot", 0, 0, 'F' },
+       { "dropmode",   2, 0, 'd' },
+       { "instadrop",  2, 0, 'D' },
+       { "color",              2, 0, 'C' },
+       { "slowterm",   2, 0, 'S' },
+       { "keys",               1, 0, 'k' },
+       { "rules",              0, 0, 'R' },
+       { "info",               0, 0, 'H' },
+       { "help",               0, 0, 'h' },
+       { 0,                    0, 0,  0 }
+};
+
 enum { KT_left, KT_right, KT_rotright, KT_rotleft, KT_drop, KT_down,
-       KT_toggleSpy, KT_pause, KT_faster, KT_redraw, KT_numKeys };
+               KT_toggleSpy, KT_pause, KT_faster, KT_redraw, KT_numKeys };
 
 static char *keyNames[KT_numKeys+1] = {
        "Left", "Right", "RotRight", "RotLeft", "Drop", "Down",
@@ -37,8 +58,7 @@ static char *keyNames[KT_numKeys+1] = {
 static char *gameNames[GT_len] = { "OnePlayer", "ClassicTwo" };
 
 static char keyTable[KT_numKeys+1];
-static int dropModeEnable = 0;
-static char *robotProg;
+
 
 ExtFunc void MapKeys(char *newKeys)
 {
@@ -73,85 +93,212 @@ ExtFunc void MapKeys(char *newKeys)
        }
        if (errs)
                exit(1);
-}
+} //MapKeys
+
+ExtFunc void WriteConf(void)
+{
+       FILE *file_out;
+
+       file_out = fopen(CONFIG_FILE, "w");
+       if (file_out == NULL)
+               die("Error writing config file");
+
+       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
+
+ExtFunc void HandleOption(char tag, char *value)
+{
+       switch (tag) {
+               case 'a':       //ascii
+                       if (value && !strcasecmp(value, "0")) Game.ascii = 0;
+                       else Game.ascii = 1;
+                       break;
+               case 'c':       //connect
+                       initConn = 1;
+                       hostStr = value;
+                       break;
+               case 'p':       //port
+                       portStr = value; break;
+               case 'i':       //speed (of level 1)
+                       Game.initspeed = atof(value) * 1e6;
+                       break;
+               case 'l':       //level
+                       if ((Players[0].score.level = atof(value)) < 1)
+                               Players[0].score.level = 1;
+                       if (Players[0].score.level > 15)
+                               Players[0].score.level = 15;
+                       break;
+               case 1:         //spy
+                       {
+                               int i;
+                               i = atof(value);
+                               Players[i / 10].spy = i % 10;
+                       }
+                       break;
+               case 's':       //seed
+                       Game.seed = atoi(value);
+                       break;
+               case 'r':       //robot
+                       robotEnable = 1;
+                       Players[0].flags |= SCF_usingRobot;
+                       InitRobot(value);
+                       break;
+               case 'F':       //fair robot
+                       fairRobot = 1;
+                       Players[0].flags |= SCF_fairRobot;
+                       break;
+               case 'd':       //drop mode
+                       if (value && !strcasecmp(value, "0")) Players[0].dropmode &= 254;
+                       else Players[0].dropmode |= 1;
+                       break;
+               case 'D':       //instadrop
+                       if (value && !strcasecmp(value, "0")) Players[0].dropmode &= 253;
+                       else Players[0].dropmode |= 2;
+                       break;
+               case 'C':       //color
+                       if (value && !strcasecmp(value, "1")) Game.color = 1;
+                       else Game.color = 0;
+                       break;
+               case 'S':       //slowterm
+                       if (value && !strcasecmp(value, "1")) Game.standout = 1;
+                       else Game.standout = 0;
+                       break;
+               case 'k':       //keys
+                       MapKeys(value); break;
+               case 'H':       //info
+                       DistInfo(); exit(0);
+               case 'R':       //rules
+                       Rules(); exit(0);
+               case 'h':       //help
+                       Usage(); exit(0);
+               default:
+                       Usage(); exit(1);
+       }
+} //HandleParam
+
+ExtFunc void ReadConf(char *filename)
+{
+       FILE *file_in;
+       char buf[513];
+       int i;
+       char *ch;
+       char tag[81], value[81];
+
+       file_in = fopen(filename, "r");
+       if (file_in) {
+               while (fgets(buf, 512, file_in) != NULL) {
+                       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)
+                                       buf[i] = '\0';
+                               else break;
+
+                       sscanf(buf, "%80[^= \t] = %80[^\n]", tag, value);
+                       for (i = 0; options[i].name; i++){
+                               if (!strcasecmp(options[i].name, tag)) {
+                                       HandleOption(options[i].val, value);
+                                       break;
+                               }
+                       }
+               }
+               fclose(file_in);
+       } //read file
+       else {
+               fprintf(stderr, "Unable to open config file %s.\n", filename);
+       } //defaults
+
+} //ReadConf
 
 ExtFunc int StartNewPiece(int scr, Shape *shape)
 {
-       if (nextShape[scr]) {
-               curShape[scr] = nextShape[scr];
-               nextShape[scr] = shape;
+       if (Players[scr].nextShape) {
+               Players[scr].curShape = Players[scr].nextShape;
+               Players[scr].nextShape = shape;
        }
        else
-               curShape[scr] = shape;
-       curY[scr] = boardVisible[scr] + 4;
-       curX[scr] = boardWidth[scr] / 2;
-       while (!ShapeVisible(curShape[scr], scr, curY[scr], curX[scr]))
-               --curY[scr];
-       if (!ShapeFits(curShape[scr], scr, curY[scr], curX[scr]))
+               Players[scr].curShape = shape;
+       Players[scr].curY = Players[scr].boardVisible + 4;
+       Players[scr].curX = Players[scr].boardWidth / 2;
+       while (!ShapeVisible(Players[scr].curShape, scr,
+                       Players[scr].curY, Players[scr].curX))
+               Players[scr].curY--;
+       if (!ShapeFits(Players[scr].curShape, scr,
+                       Players[scr].curY, Players[scr].curX))
                return 0;
-       PlotShape(curShape[scr], scr, curY[scr], curX[scr], 1);
+       PlotShape(Players[scr].curShape, scr,
+               Players[scr].curY, Players[scr].curX, 1);
        return 1;
 }
 
-ExtFunc void OneGame(int scr, int scr2)
+ExtFunc void OneGame(int scr)
 {
        MyEvent event;
        int linesCleared, changed = 0;
-       int totalDrops = 0, totalLines = 0, totalAdds = 0;
-       int spied = 0, spying = 0, dropMode = 0;
+       int spied = 0, dropMode = 0;
        int oldPaused = 0, paused = 0, pausedByMe = 0, pausedByThem = 0;
        long pauseTimeLeft;
        int pieceCount = 0;
        int key;
        char *p, *cmd;
+       int linevalues[4] = { 40, 100, 400, 1200 }; //= 50*lines! - 10*(lines==1)
+       int i;
 
-       speed = stepDownInterval;
+       Game.speed = Game.initspeed;
+       for (i = 1; i < Players[scr].score.level; i++)
+               Game.speed /= SPEEDINC;
+       if (Game.speed < SPEEDMINIMUM)
+               Game.speed = SPEEDMINIMUM;
        ResetBaseTime();
-       InitBoard(scr);
-       if (scr2 >= 0) {
+       InitFields();
+       for (i = 1; i <= totalPlayers + 1; i++)
+               if (Players[i].spy) DrawField(i);
+       if (totalPlayers > 0) {
                spied = 1;
-               spying = 1;
-               InitBoard(scr2);
-               UpdateOpponentDisplay();
        }
        ShowDisplayInfo();
-       SetITimer(speed, speed);
+       SetITimer(Game.speed, Game.speed);
        if (robotEnable) {
                RobotCmd(0, "GameType %s\n", gameNames[game]);
                RobotCmd(0, "BoardSize 0 %d %d\n",
-                               boardVisible[scr], boardWidth[scr]);
-               if (scr2 >= 0) {
-                       RobotCmd(0, "BoardSize 1 %d %d\n",
-                                       boardVisible[scr2], boardWidth[scr2]);
-                       RobotCmd(0, "Opponent 1 %s %s\n", opponentName, opponentHost);
-                       if (opponentFlags & SCF_usingRobot)
-                               RobotCmd(0, "OpponentFlag 1 robot\n");
-                       if (opponentFlags & SCF_fairRobot)
-                               RobotCmd(0, "OpponentFlag 1 fairRobot\n");
+                       Players[scr].boardVisible, Players[scr].boardWidth);
+               for (i = 1; i <= totalPlayers + 1; i++) {
+                       RobotCmd(0, "BoardSize %d %d %d\n",
+                               i, Players[i].boardVisible, Players[i].boardWidth);
+                       RobotCmd(0, "Opponent %d %s %s\n",
+                               i, Players[i].name, Players[i].host);
+                       if (Players[i].flags & SCF_usingRobot)
+                               RobotCmd(0, "OpponentFlag %d robot\n", i);
+                       if (Players[i].flags & SCF_fairRobot)
+                               RobotCmd(0, "OpponentFlag %d fairRobot\n", i);
                }
-               RobotCmd(0, "TickLength %.3f\n", speed / 1.0e6);
+               RobotCmd(0, "TickLength %.3f\n", Game.speed / 1.0e6);
                RobotCmd(0, "BeginGame\n");
                RobotTimeStamp();
        }
-       nextShape[scr] = ChooseOption(stdOptions);
+       Players[scr].nextShape = ChooseOption(stdOptions);
        while (StartNewPiece(scr, ChooseOption(stdOptions))) {
-               ShowScore(scr, totalDrops, totalLines, totalAdds);
+               ShowScore(scr, Players[scr].score);
                if (robotEnable && !fairRobot)
                        RobotCmd(1, "NewPiece %d\n", ++pieceCount);
                if (spied) {
                        short shapeNum;
                        netint2 data[1];
 
-                       shapeNum = ShapeToNetNum(curShape[scr]);
+                       shapeNum = ShapeToNetNum(Players[scr].curShape);
                        data[0] = hton2(shapeNum);
                        SendPacket(scr, NP_newPiece, sizeof(data), data);
                }
                for (;;) {
                        changed = RefreshBoard(scr) || changed;
-                       if (spying)
-                               changed = RefreshBoard(scr2) || changed;
+                       for (i = 1; i <= totalPlayers+1; i++) if (Players[i].spy && i != me)
+                               changed = RefreshBoard(i) || changed;
                        if (changed) {
-                               RefreshScreen();
+                               if (!paused) ShowTime();
+                               refresh();
                                changed = 0;
                        }
                        CheckNetConn();
@@ -194,23 +341,20 @@ ExtFunc void OneGame(int scr, int scr2)
                                                                SendPacket(scr, NP_rotright, 0, NULL);
                                                        break;
                                                case KT_down:
-                                                       if (MovePiece(scr, -1, 0) && spied)
-                                                               SendPacket(scr, NP_down, 0, NULL);
-                                                       SetITimer(speed, speed);
-                                                       break;
-                                               case KT_toggleSpy:
-                                                       spying = (!spying) && (scr2 >= 0);
+                                                       SetITimer(Game.speed, Game.speed);
+                                                       if (MovePiece(scr, -1, 0)) {
+                                                               if (spied) SendPacket(scr, NP_down, 0, NULL);
+                                                       }
+                                                       else goto nextPiece;
                                                        break;
                                                case KT_drop:
-                                                       if (DropPiece(scr) > 0) {
-                                                               if (spied)
-                                                                       SendPacket(scr, NP_drop, 0, NULL);
-                                                               if (dropModeEnable == 2)
-                                                                       SetITimer(speed, 1); //instantdrop
-                                                               else
-                                                                       SetITimer(speed, speed);
+                                                       SetITimer(Game.speed, Game.speed);
+                                                       if (DropPiece(scr)) {
+                                                               if (spied) SendPacket(scr, NP_drop, 0, NULL);
+                                                               if (Players[scr].dropmode == 2) goto nextPiece;
                                                        }
-                                                       dropMode = dropModeEnable;
+                                                       else goto nextPiece;
+                                                       dropMode = Players[scr].dropmode;
                                                        break;
                                                case KT_pause:
                                                        pausedByMe = !pausedByMe;
@@ -230,21 +374,23 @@ ExtFunc void OneGame(int scr, int scr2)
                                                case KT_faster:
                                                        if (game != GT_onePlayer)
                                                                break;
-                                                       speed = speed * 0.8;
-                                                       SetITimer(speed, SetITimer(0, 0));
-                                                       ShowDisplayInfo();
+                                                       if ((Game.speed /= SPEEDINC) < SPEEDMINIMUM)
+                                                               Game.speed = SPEEDMINIMUM;
+                                                       SetITimer(Game.speed, SetITimer(0, 0));
+                                                       Players[scr].score.level++;
+                                                       ShowScore(scr, Players[scr].score);
                                                        changed = 1;
                                                        break;
                                                case KT_redraw:
                                                        ScheduleFullRedraw();
-                                                       if (paused)
-                                                               RefreshScreen();
+//                                                     if (paused)
+//                                                             RefreshScreen();
                                                        break;
                                        }
                                        if (dropMode && DropPiece(scr) > 0) {
+                                               SetITimer(Game.speed, Game.speed);
                                                if (spied)
                                                        SendPacket(scr, NP_drop, 0, NULL);
-                                               SetITimer(speed, speed);
                                        }
                                        break;
                                case E_robot:
@@ -275,7 +421,7 @@ ExtFunc void OneGame(int scr, int scr2)
                                                        short column;
 
                                                        memcpy(data, event.u.net.data, sizeof(data[0]));
-                                                       column = Random(0, boardWidth[scr]);
+                                                       column = Random(0, Players[scr].boardWidth);
                                                        data[1] = hton2(column);
                                                        InsertJunk(scr, ntoh2(data[0]), column);
                                                        if (spied)
@@ -288,39 +434,39 @@ ExtFunc void OneGame(int scr, int scr2)
                                                        short shapeNum;
                                                        netint2 data[1];
 
-                                                       FreezePiece(scr2);
+                                                       FreezePiece(event.u.net.uid);
                                                        memcpy(data, event.u.net.data, sizeof(data));
                                                        shapeNum = ntoh2(data[0]);
-                                                       StartNewPiece(scr2, NetNumToShape(shapeNum));
+                                                       StartNewPiece(event.u.net.uid, NetNumToShape(shapeNum));
                                                        break;
                                                }
                                                case NP_down:
-                                                       MovePiece(scr2, -1, 0);
+                                                       MovePiece(event.u.net.uid, -1, 0);
                                                        break;
                                                case NP_left:
-                                                       MovePiece(scr2, 0, -1);
+                                                       MovePiece(event.u.net.uid, 0, -1);
                                                        break;
                                                case NP_right:
-                                                       MovePiece(scr2, 0, 1);
+                                                       MovePiece(event.u.net.uid, 0, 1);
                                                        break;
                                                case NP_rotleft:
-                                                       RotatePiece(scr2, 0);
+                                                       RotatePiece(event.u.net.uid, 0);
                                                        break;
                                                case NP_rotright:
-                                                       RotatePiece(scr2, 1);
+                                                       RotatePiece(event.u.net.uid, 1);
                                                        break;
                                                case NP_drop:
-                                                       DropPiece(scr2);
+                                                       DropPiece(event.u.net.uid);
                                                        break;
                                                case NP_clear:
-                                                       ClearFullLines(scr2);
+                                                       ClearFullLines(event.u.net.uid);
                                                        break;
                                                case NP_insertJunk:
                                                {
                                                        netint2 data[2];
 
                                                        memcpy(data, event.u.net.data, sizeof(data));
-                                                       InsertJunk(scr2, ntoh2(data[0]), ntoh2(data[1]));
+                                                       InsertJunk(event.u.net.uid, ntoh2(data[0]), ntoh2(data[1]));
                                                        break;
                                                }
                                                case NP_pause:
@@ -348,23 +494,37 @@ ExtFunc void OneGame(int scr, int scr2)
                                        break;
                        }
                        if (paused != oldPaused) {
-                               if (paused)
+                               if (paused) {
+                                       PauseTime();
                                        pauseTimeLeft = SetITimer(0, 0);
-                               else
-                                       SetITimer(speed, pauseTimeLeft);
+                               }
+                               else {
+                                       SetITimer(Game.speed, pauseTimeLeft);
+                                       ResumeTime();
+                               }
                                oldPaused = paused;
                        }
                }
        nextPiece:
                dropMode = 0;
                FreezePiece(scr);
-               totalDrops++;
+               Players[scr].score.drops++;
+               Players[scr].score.score++;
                if ((linesCleared = ClearFullLines(scr)) > 0) {
-                       totalLines += linesCleared;
-                       totalAdds += linesCleared - (linesCleared < 4);
+                       if (game == GT_onePlayer)
+                               if ((Players[scr].score.lines / 10) <
+                                               ((Players[scr].score.lines + linesCleared) / 10)) {
+                                       if ((Game.speed /= SPEEDINC) < SPEEDMINIMUM)
+                                               Game.speed = SPEEDMINIMUM;
+                                       SetITimer(Game.speed, SetITimer(0, 0));
+                                       Players[scr].score.level++;
+                               } //level up
+                       Players[scr].score.score += linevalues[linesCleared];
+                       Players[scr].score.lines += linesCleared;
+                       Players[scr].score.adds += linesCleared - (linesCleared < 4);
+                       if (spied)
+                               SendPacket(scr, NP_clear, 0, NULL);
                }
-               if (linesCleared > 0 && spied)
-                       SendPacket(scr, NP_clear, 0, NULL);
                if (game == GT_classicTwo && linesCleared > 1) {
                        short junkLines;
                        netint2 data[1];
@@ -380,181 +540,59 @@ gameOver:
 
 ExtFunc int main(int argc, char **argv)
 {
-       int initConn = 0, waitConn = 0, ch;
-       char *hostStr = NULL, *portStr = NULL;
+       char ch;
 
-       standoutEnable = colorEnable = 1;
-       stepDownInterval = DEFAULT_INTERVAL;
+       Game.standout = Game.color = 1;
+       Game.initspeed = DEFAULT_INTERVAL;
        MapKeys(DEFAULT_KEYS);
-       while ((ch = getopt(argc, argv, "hHRs:r:Fk:c:wodDSCp:i:")) != -1)
-               switch (ch) {
-                       case 'c':
-                               initConn = 1;
-                               hostStr = optarg;
-                               break;
-                       case 'w':
-                               waitConn = 1;
-                               break;
-                       case 'p':
-                               portStr = optarg;
-                               break;
-                       case 'i':
-                               stepDownInterval = atof(optarg) * 1e6;
-                               break;
-                       case 's':
-                               initSeed = atoi(optarg);
-                               myFlags |= SCF_setSeed;
-                               break;
-                       case 'r':
-                               robotEnable = 1;
-                               robotProg = optarg;
-                               myFlags |= SCF_usingRobot;
-                               break;
-                       case 'F':
-                               fairRobot = 1;
-                               myFlags |= SCF_fairRobot;
-                               break;
-                       case 'd':
-                               dropModeEnable = 1;
-                               break;
-                       case 'D':
-                               dropModeEnable = 2;
-                               break;
-                       case 'C':
-                               colorEnable = 0;
-                               break;
-                       case 'S':
-                               standoutEnable = 0;
-                               break;
-                       case 'k':
-                               MapKeys(optarg);
-                               break;
-                       case 'H':
-                               DistInfo();
-                               exit(0);
-                       case 'R':
-                               Rules();
-                               exit(0);
-                       case 'h':
-                               Usage();
-                               exit(0);
-                       default:
-                               Usage();
-                               exit(1);
-               }
-       if (optind < argc || (initConn && waitConn)) {
+       {
+               int i;
+               char *userName;
+
+               for (i = 0; i <= MAX_SCREENS; i++)
+                       Players[i].score.level = Players[i].spy = 1;
+               if (!(userName = getenv("LOGNAME")) || !userName[0])
+                       if (!(userName = getenv("USER")) || !userName[0])
+                               userName = "Anonymous";
+               strncpy(Players[0].name, userName, 16); //sizeof(Player.name)
+               Players[0].name[16] = 0;
+               Players[0].boardWidth = 10;
+               Players[0].boardHeight = MAX_BOARD_HEIGHT;
+               Players[0].boardVisible = 20;
+               Players[0].spy = 1;
+               strcpy(Players[0].host, "localhost");
+       }
+
+//     if (getopt(argc, argv, "f:") == 'f')
+//             ReadConf(optarg);
+//     else
+       ReadConf(CONFIG_FILE);
+       while ((ch = getopt_long(argc, argv,
+                       "hHRs:r:Fk:c:odDSCap:i:l:", options, NULL)) != -1)
+               HandleOption(ch, optarg);
+       if (optind < argc) {
                Usage();
                exit(1);
        }
        if (fairRobot && !robotEnable)
                fatal("You can't use the -F option without the -r option");
+//     WriteConf();
+
        InitUtil();
-       if (robotEnable)
-               InitRobot(robotProg);
-       InitNet();
        InitScreens();
-       if (initConn || waitConn) {
-               MyEvent event;
-
+       if (initConn) {
                game = GT_classicTwo;
-               if (initConn)
-                       InitiateConnection(hostStr, portStr);
-               else if (waitConn)
-                       WaitForConnection(portStr);
-               {
-                       netint4 data[2];
-                       int major;
-
-                       data[0] = hton4(MAJOR_VERSION);
-                       data[1] = hton4(PROTOCOL_VERSION);
-                       SendPacket(0, NP_version, sizeof(data), data);
-                       if (WaitMyEvent(&event, EM_net) != E_net)
-                               fatal("Network negotiation failed");
-                       memcpy(data, event.u.net.data, sizeof(data));
-                       major = ntoh4(data[0]);
-                       protocolVersion = ntoh4(data[1]);
-                       if (event.u.net.type != NP_version || major < MAJOR_VERSION)
-                               fatal("Your opponent is using an old, incompatible version\n"
-                                       "of Netris.  They should get the latest version.");
-                       if (major > MAJOR_VERSION)
-                               fatal("Your opponent is using an newer, incompatible version\n"
-                                       "of Netris.  Get the latest version.");
-                       if (protocolVersion > PROTOCOL_VERSION)
-                               protocolVersion = PROTOCOL_VERSION;
-               }
-               if (protocolVersion < 3 && stepDownInterval != DEFAULT_INTERVAL)
-                       fatal("Your opponent's version of Netris predates the -i option.\n"
-                                       "For fairness, you shouldn't use the -i option either.");
-               {
-                       netint4 data[3];
-                       int len;
-                       int seed;
-
-                       if (protocolVersion >= 3)
-                               len = sizeof(data);
-                       else
-                               len = sizeof(netint4[2]);
-                       if ((myFlags & SCF_setSeed))
-                               seed = initSeed;
-                       else
-                               seed = time(0);
-                       if (waitConn)
-                               SRandom(seed);
-                       data[0] = hton4(myFlags);
-                       data[1] = hton4(seed);
-                       data[2] = hton4(stepDownInterval);
-                       SendPacket(0, NP_startConn, len, data);
-                       if (WaitMyEvent(&event, EM_net) != E_net ||
-                                       event.u.net.type != NP_startConn)
-                               fatal("Network negotiation failed");
-                       memcpy(data, event.u.net.data, len);
-                       opponentFlags = ntoh4(data[0]);
-                       seed = ntoh4(data[1]);
-                       if (initConn) {
-                               if ((opponentFlags & SCF_setSeed) != (myFlags & SCF_setSeed))
-                                       fatal("If one player sets the random number seed, "
-                                                       "both must.");
-                               if ((myFlags & SCF_setSeed) && seed != initSeed)
-                                       fatal("Both players have set the random number seed, "
-                                                       "and they are unequal.");
-                               if (protocolVersion >= 3 && stepDownInterval != ntoh4(data[2]))
-                                       fatal("Your opponent is using a different step-down "
-                                               "interval (-i).\nYou must both use the same one.");
-                               SRandom(seed);
-                       }
-               }
-               {
-                       char *userName;
-                       int len, i;
-
-                       userName = getenv("LOGNAME");
-                       if (!userName || !userName[0])
-                               userName = getenv("USER");
-                       if (!userName || !userName[0])
-                               strcpy(userName, "???");
-                       len = strlen(userName)+1;
-                       if (len > sizeof(opponentName))
-                               len = sizeof(opponentName);
-                       SendPacket(0, NP_userName, len, userName);
-                       if (WaitMyEvent(&event, EM_net) != E_net ||
-                                       event.u.net.type != NP_userName)
-                               fatal("Network negotiation failed");
-                       strncpy(opponentName, event.u.net.data,
-                               sizeof(opponentName)-1);
-                       opponentName[sizeof(opponentName)-1] = 0;
-                       for (i = 0; opponentName[i]; ++i)
-                               if (!isprint(opponentName[i]))
-                                       opponentName[i] = '?';
-                       for (i = 0; opponentHost[i]; ++i)
-                               if (!isprint(opponentHost[i]))
-                                       opponentHost[i] = '?';
-               }
-               OneGame(0, 1);
-       }
+               InitiateConnection(hostStr, portStr);
+               HandShake();
+               OneGame(me);
+       } //client
        else {
                game = GT_onePlayer;
-               OneGame(0, -1);
-       }
+               totalPlayers = 0;
+               me = 1;
+               memcpy(&Players[me], &Players[0], sizeof(Player));
+               OneGame(me);
+       } //singleplay
        return 0;
 }
 
diff --git a/inet.c b/inet.c
index 6aadecc07165db5e1c6d422f26c15283b20853d0..85b1b70a1fc384f6911e306cd7cc6ce6d3e504e5 100644 (file)
--- a/inet.c
+++ b/inet.c
 #define HEADER_SIZE sizeof(netint2[2])
 #define HEADER_SIZE3 sizeof(netint4[3])
 
-static MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event);
+ExtFunc MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event);
 
-static int sock = -1;
-static EventGenRec netGen = { NULL, 0, FT_read, -1, NetGenFunc, EM_net };
+EventGenRec netGen[MAX_SCREENS] = {
+       { NULL, 0, FT_read, -1, NetGenFunc, EM_net, 0, "\0", 0, HEADER_SIZE3 } };
 
-static char netBuf[64];
-static int netBufSize, netBufGoal = HEADER_SIZE3;
-static int isServer, lostConn, gotEndConn;
+//static char netBuf[64];
+//static int netBufSize, netBufGoal = HEADER_SIZE3;
 
-ExtFunc void InitNet(void)
+ExtFunc void make_netGen(void)
 {
-       AtExit(CloseNet);
-}
+       int i;
 
-ExtFunc int WaitForConnection(char *portStr)
-{
-       struct sockaddr_in addr;
-       struct hostent *host;
-       int sockListen;
-       int addrLen;
-       short port;
-       int val1;
-       struct linger val2;
+       for (i = 1; i <= MAX_SCREENS; i++)
+               memcpy(netGen+i, &netGen[0], sizeof(EventGenRec));
+} //Make_netGen
 
-       if (portStr)
-               port = atoi(portStr);   /* XXX Error checking */
-       else
-               port = DEFAULT_PORT;
-       memset(&addr, 0, sizeof(addr));
-       addr.sin_family = AF_INET;
-       addr.sin_addr.s_addr = htonl(INADDR_ANY);
-       addr.sin_port = htons(port);
-       sockListen = socket(AF_INET, SOCK_STREAM, 0);
-       if (sockListen < 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");
-
- //    while(1) {
-       addrLen = sizeof(addr);
-       if ((sock = accept(sockListen, (struct sockaddr *)&addr, &addrLen)) < 0)
-               die("accept");
-       fprintf(stderr, "Connection: %s\n", inet_ntoa(addr.sin_addr));
- //    if (!fork()) {
-               close(sockListen);
-               val2.l_onoff = 1;
-               val2.l_linger = 0;
-               setsockopt(sock, SOL_SOCKET, SO_LINGER,
-                               (void *)&val2, sizeof(val2));
-               netGen.fd = sock;
-               strcpy(opponentHost, "???");
-               if (addr.sin_family == AF_INET) {
-                       host = gethostbyaddr((void *)&addr.sin_addr,
-                                       sizeof(struct in_addr), AF_INET);
-                       if (host) {
-                               strncpy(opponentHost, host->h_name, sizeof(opponentHost)-1);
-                               opponentHost[sizeof(opponentHost)-1] = 0;
-                       }
-               }
-               AddEventGen(&netGen);
-//             close(sock);
- //            exit(0);
- //    }
-//     close(sock);
- //    }
-       isServer = 1;
-       return 0;
-}
 
 ExtFunc int InitiateConnection(char *hostStr, char *portStr)
-{
+{ //connect to host
        struct sockaddr_in addr;
        struct hostent *host;
        short port;
-       int mySock;
 
+//     make_netGen();
+       AtExit(CloseNet);
        if (portStr)
                port = atoi(portStr);   /* XXX Error checking */
        else
@@ -121,105 +65,207 @@ ExtFunc int InitiateConnection(char *hostStr, char *portStr)
        if (!host)
                die("gethostbyname");
        assert(host->h_addrtype == AF_INET);
-       strncpy(opponentHost, host->h_name, sizeof(opponentHost)-1);
-       opponentHost[sizeof(opponentHost)-1] = 0;
  again:
        memset(&addr, 0, sizeof(addr));
        addr.sin_family = host->h_addrtype;
        memcpy(&addr.sin_addr, host->h_addr, host->h_length);
        addr.sin_port = htons(port);
-       mySock = socket(AF_INET, SOCK_STREAM, 0);
-       if (mySock < 0)
+       if ((netGen[0].fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
                die("socket");
-       if (connect(mySock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+       if (connect(netGen[0].fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
                if (errno != ECONNREFUSED)
                        die("connect");
-               close(mySock);
+               close(netGen[0].fd);
                sleep(1);
                goto again;
        }
-       netGen.fd = sock = mySock;
-       AddEventGen(&netGen);
+       AddEventGen(&netGen[0]);
+       totalPlayers = 0;
        return 0;
+} //InitiateConnection
+
+ExtFunc void HandShake(void)
+{ //talk to your host
+       MyEvent event;
+
+       {
+               netint4 versiondata[2];
+               versiondata[0] = hton4(MAJOR_VERSION);
+               versiondata[1] = hton4(PROTOCOL_VERSION);
+               SendPacket(0, NP_hello, sizeof(versiondata), versiondata);
+       }
+
+       do {
+               if (WaitMyEvent(&event, EM_net) == E_net)
+               switch (event.u.net.type) {
+                       case NP_hello:
+                       {
+                               me = event.u.net.uid;
+                               memcpy(&Players[me], &Players[0], sizeof(Player));
+                               fprintf(stderr, "Accepted (%s) as #%d (%s)\n",
+                                       event.u.net.data, me, Players[me].name);
+                               SendPacket(0, NP_newPlayer,
+                                       sizeof(Player) - sizeof(Players[me].host) - sizeof(Players[me].spy),
+                                       &Players[me]);
+                               break;
+                       }
+                       case NP_gamedata:
+                       {
+                               fprintf(stderr, ": %d\n", event.type);
+                               break;
+                       }
+                       case NP_newPlayer:
+                       {
+                               totalPlayers++;
+                               memcpy(&Players[event.u.net.uid],
+                                       event.u.net.data, event.u.net.size);
+                               fprintf(stderr, "Receiving player #%d (%s)\n",
+                                       event.u.net.uid, Players[event.u.net.uid].name);
+                               break;
+                       }
+                       case NP_error:
+                       {
+                               fprintf(stderr, "Rejected by server: %s\n", event.u.net.data);
+                               exit(1);
+                       }
+                       default:
+                               break;
+               }
+               else
+                       fatal("Hm, the party apparantly ended prematurely.");
+       }
+       while (event.u.net.type != NP_goAhead);
+
+       // send Players[0]
+       // receive seed, initspeed
+               // receive #players
+               // receive Players[*]
+
+       /*
+                       {
+                       netint4 data[3];
+                       int len;
+                       int seed;
+
+                       if (protocolVersion >= 3)
+                               len = sizeof(data);
+                       else
+                               len = sizeof(netint4[2]);
+                       if ((Players[0].flags & SCF_setSeed))
+                               seed = Game.seed;
+                       else
+                               seed = time(0);
+                       if (waitConn)
+                               SRandom(seed);
+                       data[0] = hton4(Players[0].flags);
+                       data[1] = hton4(seed);
+                       data[2] = hton4(Game.initspeed);
+                       SendPackets(0, NP_startConn, len, data);
+                       if (WaitMyEvent(&event, EM_net) != E_net ||
+                                       event.u.net.type != NP_startConn)
+                               fatal("Network negotiation failed");
+                       memcpy(data, event.u.net.data, len);
+                       Players[1].flags = ntoh4(data[0]);
+                       seed = ntoh4(data[1]);
+                       if (initConn) {
+                               if ((Players[0].flags & SCF_setSeed) != (Players[1].flags & SCF_setSeed))
+                                       fatal("If one player sets the random number seed, "
+                                                       "both must.");
+                               if ((Players[0].flags & SCF_setSeed) && seed != Game.seed)
+                                       fatal("Both players have set the random number seed, "
+                                                       "and they are unequal.");
+                               if (protocolVersion >= 3 && Game.initspeed != ntoh4(data[2]))
+                                       fatal("Your opponent is using a different step-down "
+                                               "interval (-i).\nYou must both use the same one.");
+                               SRandom(seed);
+                       }
+               }
+               */
+//             SendPackets(0, NP_initData, strlen(Players[0].name) + 1, Players[0].name);
+
+/*
+                       if (WaitMyEvent(&event, EM_net) != E_net ||
+                                       event.u.net.type != NP_userName)
+                               fatal("Network negotiation failed");
+                       strncpy(Players[1].name, event.u.net.data,
+                               sizeof(Players[1].name) - 1);
+                       Players[1].name[sizeof(Players[1].name)-1] = 0;
+                       for (i = 0; Players[1].name[i]; ++i)
+                               if (!isprint(Players[1].name[i]))
+                                       Players[1].name[i] = '?';
+                       for (i = 0; Players[1].host[i]; ++i)
+                               if (!isprint(Players[1].host[i]))
+                                       Players[1].host[i] = '?';
+*/
+} //HandShake
+
+ExtFunc void CheckNetConn(void)
+{ //am I necessary?
 }
 
-static MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event)
-{
+
+ExtFunc MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event)
+{ //receive
        int result;
        short uid, type, size;
-       netint4 data[3];
+       netint4 *data = (netint4*)&(gen->buf);
 
-       result = MyRead(sock, netBuf + netBufSize, netBufGoal - netBufSize);
+       result = MyRead(gen->fd, gen->buf + gen->bufSize,
+               gen->bufGoal - gen->bufSize);
        if (result < 0) {
-               lostConn = 1;
+               close(gen->fd);
+               gen->fd = -1;
                return E_lostConn;
        }
-       netBufSize += result;
-       if (netBufSize < netBufGoal)
+       gen->bufSize += result;
+       if (gen->bufSize < gen->bufGoal)
                return E_none;
-       memcpy(data, netBuf, sizeof(data));
+       // *ugly* memcpy(data, gen->buf, sizeof(data));
        uid = ntoh4(data[0]);
        type = ntoh4(data[1]);
        size = ntoh4(data[2]);
-       netBufGoal = size;
-       if (netBufSize < netBufGoal)
+       gen->bufGoal = size;
+       if (gen->bufSize < gen->bufGoal)
                return E_none;
-       netBufSize = 0;
-       netBufGoal = HEADER_SIZE3;
+       gen->bufSize = 0;
+       gen->bufGoal = HEADER_SIZE3;
+       event->u.net.sender = gen->player;
+       event->u.net.uid = uid;
        event->u.net.type = type;
        event->u.net.size = size - HEADER_SIZE3;
-       event->u.net.data = netBuf + HEADER_SIZE3;
+       event->u.net.data = gen->buf + HEADER_SIZE3;
        if (type == NP_endConn) {
-               gotEndConn = 1;
-               return E_lostConn;
-       }
-       else if (type == NP_byeBye) {
-               lostConn = 1;
+               fprintf(stderr, "Close connection\n");
                return E_lostConn;
        }
        return E_net;
-}
-
-ExtFunc void CheckNetConn(void)
-{
-}
+} //NetGenFunc
 
 ExtFunc void SendPacket(short uid, NetPacketType type, int size, void *data)
-{
+{ //send shit to server
        netint4 header[3];
 
        header[0] = hton4(uid);
        header[1] = hton4(type);
        header[2] = hton4(size + HEADER_SIZE3);
-       if (MyWrite(sock, header, HEADER_SIZE3) != HEADER_SIZE3)
+       if (MyWrite(netGen[0].fd, header, HEADER_SIZE3) != HEADER_SIZE3)
+               die("write (header)");
+       if (size > 0 && data && MyWrite(netGen[0].fd, data, size) != size)
                die("write");
-       if (size > 0 && data && MyWrite(sock, data, size) != size)
-               die("write");
-}
+} //SendPacket
 
 ExtFunc void CloseNet(void)
-{
+{ //kick some connection's ass!
        MyEvent event;
 
-       if (sock >= 0) {
-               if (!lostConn) {
-                       SendPacket(0, NP_endConn, 0, NULL);
-                       if (isServer) {
-                               while (!lostConn)
-                                       WaitMyEvent(&event, EM_net);
-                       }
-                       else {
-                               while (!gotEndConn)
-                                       WaitMyEvent(&event, EM_net);
-                               SendPacket(0, NP_byeBye, 0, NULL);
-                       }
-               }
-               close(sock);
-               sock = -1;
+       if (netGen[0].fd >= 0) {
+               SendPacket(0, NP_endConn, 0, NULL);
+               close(netGen[0].fd);
+               netGen[0].fd = -1;
        }
-       if (netGen.next)
-               RemoveEventGen(&netGen);
-}
+       if (netGen[0].next)
+               RemoveEventGen(&netGen[0]);
+} //CloseNet
 
 /*
  * vi: ts=4 ai
diff --git a/netris.conf b/netris.conf
new file mode 100644 (file)
index 0000000..b8a153c
--- /dev/null
@@ -0,0 +1,9 @@
+### NETRIS 0.5.82 Config file ###
+
+Keys   = 4685 2
+Color  = 1
+InstaDrop
+Handicap= 1
+ascii  = 0
+#spy = 10
+#spy = 20
index 874ec0b70063b2cd201c990a6e6f7be7469fd96a..b04e04036b2d04274fe18871b4d2c97d6e72ea7a 100644 (file)
--- a/netris.h
+++ b/netris.h
@@ -38,9 +38,9 @@
 # define IN(a)
 #endif
 
-#ifndef NULL
-# define NULL ((void *)0)
-#endif
+/*#ifndef NULL
+  # define NULL ((void *)0)
+  #endif*/
 
 #ifdef HAS_SIGPROCMASK
 typedef sigset_t MySigSet;
@@ -59,31 +59,25 @@ typedef long netint4;
 
 #define hton2(x) htons(x)
 #define hton4(x) htonl(x)
-
 #define ntoh2(x) ntohs(x)
 #define ntoh4(x) ntohl(x)
 
+/* Protocol versions */
+#define MAJOR_VERSION          1       
+#define PROTOCOL_VERSION       4
+#define ROBOT_VERSION          1
+
 #define DEFAULT_PORT 9284      /* Very arbitrary */
 
+#define CONFIG_FILE "netris.conf"
+
 //#define DEFAULT_KEYS "hlkj mspf^l"
 //#define DEFAULT_KEYS "4685 2spf^l"
 #define DEFAULT_KEYS "dcaf b^sp^f^l"
 
-/* Protocol versions */
-#define MAJOR_VERSION          1       
-#define PROTOCOL_VERSION       4
-#define ROBOT_VERSION          1
-
 #define MAX_BOARD_WIDTH                32
 #define MAX_BOARD_HEIGHT       64
-#define MAX_SCREENS                    2
-
-#define DEFAULT_INTERVAL       300000  /* Step-down interval in microseconds */
-
-/* NP_startConn flags */
-#define SCF_usingRobot         000001
-#define SCF_fairRobot          000002
-#define SCF_setSeed                    000004
+#define MAX_SCREENS                    5
 
 /* Event masks */
 #define EM_alarm                       000001
@@ -93,20 +87,22 @@ typedef long netint4;
 #define EM_any                         000777
 
 typedef enum _GameType { GT_onePlayer, GT_classicTwo, GT_len } GameType;
-typedef enum _BlockTypeA { BT_none, BT_white, BT_blue, BT_magenta,
-                                                       BT_cyan, BT_yellow, BT_green, BT_red,
-                                                       BT_wall, BT_len } BlockTypeA;
+typedef enum _BlockTypeA {
+       BT_none, BT_white, BT_blue, BT_magenta, BT_cyan, BT_yellow, BT_green,
+       BT_red, BT_wall, BT_len
+} BlockTypeA;
 typedef enum _Dir { D_down, D_right, D_up, D_left } Dir;
 typedef enum _Cmd { C_end, C_forw, C_back, C_left, C_right, C_plot } Cmd;
 typedef enum _FDType { FT_read, FT_write, FT_except, FT_len } FDType;
-typedef enum _MyEventType { E_none, E_alarm, E_key, E_net,
-                                                       E_lostConn, E_robot, E_lostRobot } MyEventType;
-typedef enum _NetPacketType { NP_endConn, NP_giveJunk, NP_newPiece,
-                                                       NP_down, NP_left, NP_right,
-                                                       NP_rotright, NP_rotleft, NP_drop, NP_clear,
-                                                       NP_insertJunk, NP_startConn,
-                                                       NP_userName, NP_pause, NP_version,
-                                                       NP_byeBye } NetPacketType;
+typedef enum _MyEventType {
+       E_none, E_alarm, E_key, E_net, E_lostConn, E_robot, E_lostRobot
+} MyEventType;
+typedef enum _NetPacketType {
+       NP_endConn, NP_byeBye,
+       NP_error, NP_hello, NP_gamedata, NP_newPlayer, NP_goAhead,
+       NP_pause, NP_giveJunk, NP_newPiece, NP_down, NP_left, NP_right,
+       NP_rotright, NP_rotleft, NP_drop, NP_clear, NP_insertJunk
+} NetPacketType;
 
 typedef signed char BlockType;
 
@@ -115,6 +111,7 @@ typedef struct _MyEvent {
        union {
                char key;
                struct {
+                       short sender, uid;
                        NetPacketType type;
                        int size;
                        void *data;
@@ -136,7 +133,13 @@ typedef struct _EventGenRec {
        int fd;
        EventGenFunc func;
        int mask;
+       short player;
+       char buf[512];
+       int bufSize, bufGoal;
 } EventGenRec;
+extern EventGenRec netGen[MAX_SCREENS];
+
+MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event);
 
 typedef struct _Shape {
        struct _Shape *rotateTo, *rotateFrom;
@@ -154,20 +157,48 @@ typedef struct _ShapeOption {
 typedef int (*ShapeDrawFunc)(int scr, int y, int x,
                                        BlockType type, void *data);
 
+/* NP_startConn flags */
+#define SCF_usingRobot         000001
+#define SCF_fairRobot          000002
+#define SCF_setSeed                    000004
+
+EXT int totalPlayers;
+
+typedef struct _Player {
+       char name[16];
+       int flags;
+       int dropmode;
+       int boardHeight, boardWidth, boardVisible;
+       int curX, curY;
+       Shape *curShape, *nextShape;
+       struct _Score {
+               short level;
+               long score;
+               int drops, lines, adds;
+       } score;
+       char host[256];
+       int spy;
+} Player;
+EXT Player Players[MAX_SCREENS];
+EXT short me;
+
+#define DEFAULT_INTERVAL       1000000 /* Step-down interval in microseconds */
+#define SPEEDINC                       1.2
+#define SPEEDMINIMUM           40000
+
+typedef struct __Game {
+       long seed;
+       long initspeed, speed;
+       int standout, color, ascii;
+} _Game;
+EXT _Game Game;
+
 EXT GameType game;
-EXT int boardHeight[MAX_SCREENS];
-EXT int boardVisible[MAX_SCREENS], boardWidth[MAX_SCREENS];
-EXT Shape *curShape[MAX_SCREENS], *nextShape[MAX_SCREENS];
-EXT int curY[MAX_SCREENS], curX[MAX_SCREENS];
-EXT char opponentName[16], opponentHost[256];
-EXT int standoutEnable, colorEnable;
 EXT int robotEnable, robotVersion, fairRobot;
 EXT int protocolVersion;
 
-EXT long initSeed;
-EXT long stepDownInterval, speed;
-
-EXT int myFlags, opponentFlags;
+EXT int initConn, waitConn;
+EXT char *hostStr, *portStr;
 
 EXT char scratch[1024];
 
diff --git a/server.c b/server.c
new file mode 100644 (file)
index 0000000..7e0aca1
--- /dev/null
+++ b/server.c
@@ -0,0 +1,567 @@
+/*
+ * Netris -- A free networked version of T*tris
+ * Copyright (C) 1994,1995,1996  Mark H. Weaver <mhw@netris.org>
+ * 
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * 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 <ctype.h>
+#include <string.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <errno.h>
+#include <setjmp.h>
+
+#define HEADER_SIZE sizeof(netint4[3])
+#define MAX_CONNECTIONS 3
+
+char *version_string = "0.5.89";
+
+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 }
+};
+
+static char *gameNames[GT_len] = { "OnePlayer", "ClassicTwo" };
+
+ExtFunc MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event);
+
+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 *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
+
+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;
+       }
+       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
+
+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;
+       }
+} //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
+
+ExtFunc void ByeClient(int playa)
+{ //client went away
+       fprintf(stderr, "Close connection #%d\n", playa);
+       close(netGen[playa].fd);
+       netGen[playa].fd = -1;
+} //ByeClient
+
+ExtFunc MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event)
+{ //receive
+       int result;
+       short uid, type, size;
+       netint4 *data = (netint4*)&(gen->buf);
+
+       result = MyRead(gen->fd, gen->buf + gen->bufSize,
+               gen->bufGoal - gen->bufSize);
+       if (result < 0) {
+               ByeClient(gen->player);
+               type = NP_endConn;
+               return E_net;
+       }
+       gen->bufSize += result;
+       if (gen->bufSize < gen->bufGoal)
+               return E_none;
+       // *ugly* memcpy(data, gen->buf, sizeof(data));
+       uid = ntoh4(data[0]);
+       type = ntoh4(data[1]);
+       size = ntoh4(data[2]);
+       gen->bufGoal = size;
+       if (gen->bufSize < gen->bufGoal)
+               return E_none;
+       gen->bufSize = 0;
+       gen->bufGoal = HEADER_SIZE;
+       event->u.net.sender = gen->player;
+       event->u.net.uid = uid;
+       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);
+       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)
+{
+       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");
+
+       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));
+               if (addr.sin_family == 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;
+                       }
+               }
+               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
+               }
+       }
+       close(sockListen);
+       return 0;
+} //WaitForConnection
+
+ExtFunc int StartServer(char *portStr)
+{
+       MyEvent event;
+       char serverdata[255];
+       int playercount;
+       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
+                               case NP_hello:
+                                       break;
+                               case NP_newPlayer:
+                               { //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++;
+                                       } //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++)
+                                               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;
+                               }
+                       } //E_net
+               }
+       } //loop
+} //StartServer
+
+
+ExtFunc void Header(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);
+}
+
+ExtFunc void DistInfo(void)
+{
+       Header();
+       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
+
+ExtFunc 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;
+       }
+} //HandleParam
+
+ExtFunc void ReadConf(char *filename)
+{
+       FILE *file_in;
+       char buf[513];
+       int i;
+       char *ch;
+       char tag[81], value[81];
+
+       file_in = fopen(filename, "r");
+       if (file_in) {
+               while (fgets(buf, 512, file_in) != NULL) {
+                       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)
+                                       buf[i] = '\0';
+                               else break;
+
+                       sscanf(buf, "%80[^= \t] = %80[^\n]", tag, value);
+                       for (i = 0; options[i].name; i++){
+                               if (!strcasecmp(options[i].name, tag)) {
+                                       HandleOption(options[i].val, value);
+                                       break;
+                               }
+                       }
+               }
+               fclose(file_in);
+       } //read file
+       else {
+               fprintf(stderr, "Unable to open config file %s.\n", filename);
+       } //defaults
+
+} //ReadConf
+
+ExtFunc void CatchInt(int sig)
+{
+       siglongjmp(close_env, 1);
+}
+
+ExtFunc int main(int argc, char **argv)
+{
+       char ch;
+
+       if (sigsetjmp(close_env, 1)) exit(0);
+       signal(SIGINT, CatchInt);
+       Game.standout = Game.color = 1;
+       Game.initspeed = DEFAULT_INTERVAL;
+
+//     if (getopt(argc, argv, "f:") == 'f')
+//             ReadConf(optarg);
+//     else
+       ReadConf(CONFIG_FILE);
+       while ((ch = getopt_long(argc, argv,
+                       "hHp:i:s:", options, NULL)) != -1)
+               HandleOption(ch, optarg);
+       if (optind < argc) {
+               Usage();
+               exit(1);
+       }
+//     WriteConf();
+
+       Header();
+       StartServer(portStr);
+       return 0;
+}
+
+/*
+ * vi: ts=4 ai
+ * vim: noai si
+ */
index 2309bcc7ee291d0c9badea3734fdc09aa4b0de21..42fdf514e26a3740cb87ccb6d045d942dd41a26c 100644 (file)
--- a/shapes.c
+++ b/shapes.c
@@ -121,7 +121,7 @@ ExtFunc Dir RotateDir(Dir dir, int delta)
 }
 
 ExtFunc int ShapeIterate(Shape *s, int scr, int y, int x, int falling,
-ExtFunc                                ShapeDrawFunc func, void *data)
+ExtFunc                                                        ShapeDrawFunc func, void *data)
 {
        int i, mirror, result;
        Dir dir;
diff --git a/sr.c b/sr.c
index a5974fdc2375d54c5c54a06b4404878750bf8c06..2cc3c93e06d294e4ed7f557419cf7764157ee151 100644 (file)
--- a/sr.c
+++ b/sr.c
@@ -446,7 +446,7 @@ int main(int argc, char **argv)
                        }
                        if (pieceState == 1) {          /* Decided */
                                if (memcmp(piece, pieceDest, sizeof(piece))) {
-                                       WriteLine("Rotate %d\n", pieceCount);
+                                       WriteLine("RotRight %d\n", pieceCount);
                                        pieceState = 2;
                                }
                                else if (pieceLeft != leftDest) {
diff --git a/util.c b/util.c
index 3bbd704c43b3c47d135ce764b43b394168b5ce20..f93b8c857881b39b15b206c11fda7391cf12b1c2 100644 (file)
--- a/util.c
+++ b/util.c
@@ -28,6 +28,7 @@
 #include <sys/time.h>
 #include <netdb.h>
 #include <errno.h>
+#include <setjmp.h>
 
 static MyEventType AlarmGenFunc(EventGenRec *gen, MyEvent *event);
 
@@ -35,24 +36,11 @@ static EventGenRec alarmGen =
                { &alarmGen, 0, FT_read, -1, AlarmGenFunc, EM_alarm };
 static EventGenRec *nextGen = &alarmGen;
 
-static myRandSeed = 1;
+static sigjmp_buf close_env;
 
-static struct timeval baseTimeval;
+static int myRandSeed = 1;
 
-ExtFunc void InitUtil(void)
-{
-       if (initSeed)
-               SRandom(initSeed);
-       else
-               SRandom(time(0));
-       signal(SIGINT, CatchInt);
-       ResetBaseTime();
-}
-
-ExtFunc void ResetBaseTime(void)
-{
-       gettimeofday(&baseTimeval, NULL);
-}
+static long baseTimeval;
 
 ExtFunc void AtExit(void (*handler)(void))
 {
@@ -63,40 +51,50 @@ ExtFunc void AtExit(void (*handler)(void))
 #endif
 }
 
+///////////// HELP MESSAGES /////////////
+
+ExtFunc void Header(void)
+{
+       fprintf(stderr,
+         "NETRIS %s\t(c) 1994-1996,1999 Mark H. Weaver <mhw@netris.org>\n"
+         "          \t(c) 2002 Shiar <shiar@shiar.org>\n\n",
+         version_string);
+} //Header
+
 ExtFunc void Usage(void)
 {
+       Header();
        fprintf(stderr,
-         "Netris version %s (C) 1994-1996,1999  Mark H. Weaver <mhw@netris.org>\n"
          "Usage: netris <options>\n"
-         "  -h         Print usage information\n"
-         "  -w         Wait for connection\n"
-         "  -c <host>  Initiate connection\n"
-         "  -p <port>  Set port number (default is %d)\n"
-         "  -k <keys>  Remap keys.  The argument is a prefix of the string\n"
-         "               containing the keys in order: left, rotate, right, drop,\n"
-         "               down-faster, toggle-spying, pause, faster, redraw.\n"
-         "               \"^\" prefixes controls.  (default is \"%s\")\n"
-         "  -i <sec>   Set the step-down interval, in seconds\n"
-         "  -r <robot> Execute <robot> (a command) as a robot controlling\n"
-         "               the game instead of the keyboard\n"
-         "  -F         Use fair robot interface\n"
-         "  -s <seed>  Start with given random seed\n"
-         "  -d         Drops go into drop mode\n"
-         "               This means that sliding off a cliff after a drop causes\n"
-         "               another drop automatically\n"
-         "  -D         Instant drop\n"
-         "  -S         Disable inverse/bold/color for slow terminals\n"
-         "  -C         Disable color\n"
-         "  -H         Show distribution and warranty information\n"
-         "  -R         Show rules\n",
-         version_string, DEFAULT_PORT, DEFAULT_KEYS);
+         "\n"
+         "  -h, --help\t\tPrint this usage information\n"
+         "  -H, --info\t\tShow distribution and warranty information\n"
+         "  -R, --rules\t\tShow game rules\n"
+         "\n"
+         "  -S, --slowterm\tDisable inverse/bold/color for slow terminals\n"
+         "  -a, --ascii\t\tUse ascii characters\n"
+         "  -C, --color=0\t\tDisable color\n"
+         "\n"
+         "  -w, --wait\t\tWait for connection\n"
+         "  -c, --connect <host>\tInitiate connection\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"
+         "  -l, --level <lvl>\tBegin at a higher level (can be used as handicap)\n"
+         "  -k, --keys <keys>\tRemap keys (default is \"%s\" for cursors)\n"
+         "  -d, --dropmode\tDrops go into drop mode\n"
+         "  -D, --instadrop\tInstant drop\n"
+         "\n"
+         "  -r, --robot <cmd>\tExecute program to control the game instead of keyboard\n"
+         "  -F, --fair-robot\tUse fair robot interface\n"
+         "\n", DEFAULT_PORT, DEFAULT_KEYS);
 }
 
 ExtFunc void DistInfo(void)
 {
+       Header();
        fprintf(stderr,
-         "Netris version %s (C) 1994-1996,1999  Mark H. Weaver <mhw@netris.org>\n"
-         "\n"
          "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"
@@ -109,37 +107,46 @@ ExtFunc void DistInfo(void)
          "\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",
-         version_string);
-}
+         "Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n"
+         "\n");
+} //DistInfo
 
 ExtFunc void Rules(void)
 {
+       Header();
        fprintf(stderr,
-         "Netris version %s rules\n"
+         "One player mode\n"
+         "---------------\n"
+         "Good old Tetris. Scoring is like on the GameBoy version (so try to\n"
+         "remove as many lines at once as you can). After removing ten lines\n"
+         "you go to the next level, which will be faster thus making the game\n"
+         "harder to play.\n"
          "\n"
          "Two player mode\n"
          "---------------\n"
          "It's just like normal T*tris except that when you clear more than\n"
-         "one row with a single piece, the other player's board is moved up\n"
-         "and junk rows are added to the bottom.  If you clear 2, 3 or 4\n"
-         "rows, 1, 2 or 4 junk rows are added to your opponent's board,\n"
-         "respectively.  The junk rows have exactly one empty column.\n"
-         "For each group of junk rows given, the empty columns will line\n"
-         "up.  This is intentional.\n"
+         "one row with a single piece, the other player receives penalty lines\n"
+         "For clearing 2, 3 or 4 rows, respectively 1, 2 or 4 junk rows will\n"
+         "be added to the bottom of your opponent's board respectively.\n"
+         "The junk rows have exactly one empty column, which will line up for\n"
+         "multiple rows.\n"
          "\n"
          "The longest surviving player wins the game.\n"
-         "\n"
-         "One player mode\n"
-         "---------------\n"
-         "This mode is currently very boring, because there's no scoring\n"
-         "and it never gets any faster.  This will be rectified at some point.\n"
-         "I'm not very motivated to do it right now because I'm sick of one\n"
-         "player T*tris.  For now, use the \"f\" key (by default) to make the\n"
-         "game go faster.  Speedups cannot be reversed for the remainder of\n"
-         "the game.\n",
-         version_string);
-}
+         "\n");
+} //Rules
+
+///////////// RANDOM /////////////
+
+ExtFunc void InitUtil(void)
+{
+       if (Game.seed)
+               SRandom(Game.seed);
+       else
+               SRandom(time(0));
+       if (sigsetjmp(close_env, 1)) exit(0);
+       signal(SIGINT, CatchInt);
+       ResetBaseTime();
+} //InitUtil
 
 /*
  * My really crappy random number generator follows
@@ -147,15 +154,17 @@ ExtFunc void Rules(void)
  */
 ExtFunc void SRandom(int seed)
 {
-       initSeed = seed;
+       Game.seed = seed;
        myRandSeed = seed % 31751 + 1;
-}
+} //SRandom
 
 ExtFunc int Random(int min, int max1)
 {
        myRandSeed = (myRandSeed * 31751 + 15437) % 32767;
        return myRandSeed % (max1 - min) + min;
-}
+} //Random
+
+///////////// I/O /////////////
 
 ExtFunc int MyRead(int fd, void *data, int len)
 {
@@ -172,7 +181,7 @@ ExtFunc int MyRead(int fd, void *data, int len)
                        return result;
        }
        return len;
-}
+} //MyRead
 
 ExtFunc int MyWrite(int fd, void *data, int len)
 {
@@ -189,7 +198,9 @@ ExtFunc int MyWrite(int fd, void *data, int len)
                        return result;
        }
        return len;
-}
+} //MyWrite
+
+///////////// TIME /////////////
 
 ExtFunc void NormalizeTime(struct timeval *tv)
 {
@@ -203,7 +214,7 @@ ExtFunc void NormalizeTime(struct timeval *tv)
 
 ExtFunc void CatchInt(int sig)
 {
-       exit(0);
+       siglongjmp(close_env, 1);
 }
 
 ExtFunc void CatchAlarm(int sig)
@@ -217,26 +228,47 @@ static MyEventType AlarmGenFunc(EventGenRec *gen, MyEvent *event)
        return E_alarm;
 }
 
-ExtFunc long CurTimeval(void)
+ExtFunc void SetTimeval(struct timeval *tv, long usec)
+{
+       tv->tv_sec = usec / 1000000;
+       tv->tv_usec = usec % 1000000;
+} //SetTimeval
+
+ExtFunc long GetTimeval(struct timeval *tv)
+{
+       return tv->tv_sec * 1000000 + tv->tv_usec;
+} //GetTimeval
+
+ExtFunc long AbsTimeval(void)
 {
        struct timeval tv;
 
        gettimeofday(&tv, NULL);
-       tv.tv_sec -= baseTimeval.tv_sec;
-       tv.tv_usec -= baseTimeval.tv_usec;
        return GetTimeval(&tv);
-}
+} //CurTimeval
 
-ExtFunc void SetTimeval(struct timeval *tv, long usec)
+ExtFunc void ResetBaseTime(void)
 {
-       tv->tv_sec = usec / 1000000;
-       tv->tv_usec = usec % 1000000;
-}
+       baseTimeval = AbsTimeval();
+} //ResetBaseTime
 
-ExtFunc long GetTimeval(struct timeval *tv)
+ExtFunc void PauseTime(void)
 {
-       return tv->tv_sec * 1000000 + tv->tv_usec;
-}
+       baseTimeval -= AbsTimeval();
+} //PauseTime
+
+ExtFunc void ResumeTime(void)
+{
+       baseTimeval += AbsTimeval();
+} //ResumeTime
+
+ExtFunc long CurTimeval(void)
+{
+       struct timeval tv;
+
+       gettimeofday(&tv, NULL);
+       return GetTimeval(&tv) - baseTimeval;
+} //CurTimeval
 
 static long SetITimer1(long interval, long value)
 {
@@ -260,6 +292,8 @@ ExtFunc long SetITimer(long interval, long value)
        return old;
 }
 
+///////////// ... /////////////
+
 ExtFunc volatile void die(char *msg)
 {
        perror(msg);
@@ -311,33 +345,34 @@ ExtFunc void RestoreSignals(MySigSet *saved, MySigSet *set)
 #endif
 }
 
+///////////// EVENTS /////////////
+
 ExtFunc void AddEventGen(EventGenRec *gen)
 {
        assert(gen->next == NULL);
        gen->next = nextGen->next;
        nextGen->next = gen;
-}
+} //AddEventGen
 
 ExtFunc void RemoveEventGen(EventGenRec *gen)
 {
-       /* assert(gen->next != NULL);   /* Be more forgiving, for SIGINTs */
+       // 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;
        }
-}
+} //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;
 
-       /* XXX In certain circumstances, this routine does polling */
        for (;;) {
                for (i = 0; i < FT_len; ++i)
                        FD_ZERO(&fds[i]);
@@ -381,7 +416,7 @@ ExtFunc MyEventType WaitMyEvent(MyEvent *event, int mask)
                } while (gen != nextGen);
                retry = 1;
        }
-}
+} //WaitMyEvent
 
 /*
  * vi: ts=4 ai