From: Mischa POSLAWSKY Date: Tue, 20 Aug 2002 01:23:00 +0000 (+0200) Subject: unofficial version 0.7.1: ui improvements X-Git-Tag: v0.7.1 X-Git-Url: http://git.shiar.nl/netris.git/commitdiff_plain/4f561019fc85c2817e3a72341397d1df32bc0868 unofficial version 0.7.1: ui improvements Server fixes, many visual improvements (event messages, piece shadow). --- diff --git a/CHANGES b/CHANGES index ff4175c..be0fa70 100644 --- a/CHANGES +++ b/CHANGES @@ -91,26 +91,52 @@ * doesn't display fields which don't fit on screen automatically * quit key (q by default) + -- v0.7.811 ------ ----- near-future: ------------------------------------------------------------ + * title bar inverted + * scorebar less wide (now 3 fields take 82 chars) + -- v0.7.813 ------ ----- asap: ------------------------------------------------------------------- + * blocks are normal text in !curses (not bold or anything) + * server handles incoming connections as events too (continuously) - * fix -f - * fix bot - * server continuously acceping new connections, and immediate handshake - * midgame join option - * optional smaller enemy fields - * multiplayer stats + -- v0.7.814 ------ + * show (paused) game right after receiving game data, before receiving players + * server starts game after receiving minimum number of players (--min-players) + * client altered to receive players during game loop + * server has --max-players option for player limit (default is 8) + * (empty) fields are drawn for maximum number of players + * server quits correctly, client quits only when no players are left + * players join paused when game hasn't started yet + * player's pause state is transmitted by server + * server does not keep repeating goahead on every join + * ^l (by default) redraws screen (doesn't work correctly on all terms yet) + * before game has started, '(not) ready' is displayed instead of 'pause' + * 'empty' message over empty fields, 'game over' over dead players + * player rejoin fixed ----- distant future: --------------------------------------------------------- + -- v0.7.815 ------ - * special blocks - * tetrinet compatible? - * remove bot delay (make it humanplayer-like) - * new+better bot? + * server started w/o --continuous exits when just 1 player or team left + * players can't join a started non-continuous game + * game starts if non-ready player leaves and everybody else is ready + * message when you or enemies add junklines + -- v0.7.817 ------ + + * went g/o when one player of a team went g/o instead of all of 'em + * improved handling of closing connections in server + * naw player's name refreshed on join + * clients don't receive death signal from rejected player anymore + + -- v0.7.819 ------ + + * shadow piece (preview current piece dropped down) + * players are now 'fragged' by the player last adding them lines + * ^ when that last player was himself(m/f) (s)he just 'died' + * 'Close connection' message no longer displayed + * players in teams are displayed in their teamcolor ------------------------------------------------------------------------------ diff --git a/Configure b/Configure index a17707e..537142f 100755 --- a/Configure +++ b/Configure @@ -21,8 +21,8 @@ # CC="gcc" -COPT="-O -m486" -CEXTRA="" +COPT="-O" +CEXTRA="-m486" LEXTRA="" CURSES_HACK=false @@ -32,7 +32,7 @@ while [ $# -ge 1 ]; do case "$opt" in -g) COPT="-g -O0" -# CEXTRA="-Wall -Wstrict-prototypes" +# CEXTRA="-Wall -Wstrict-prototypes -m486" ;; -O*) COPT="$opt" diff --git a/TODO b/TODO new file mode 100644 index 0000000..3696e06 --- /dev/null +++ b/TODO @@ -0,0 +1,37 @@ +---- near-future: ------------------------------------------------------------ + + * everybody in a team + * teams are colored + * server never quits + * 'you won' message, remove 'close connection' echo + * (check if commandprompt is below game) + * transmit player fields of game in progress to new player + * server has to maintain copy of player fields + * empty new player's field (in case of recycle) + * completely fix redraw + * redraw on window resize + * message position/size + * allow custom nicks + * move piece when unable to rotate at screenedge + + +---- asap: ------------------------------------------------------------------- + + * observers (join as g/o player) + * fix -f + * half width enemy fields if out of screen space + * fix bot + * multiplayer stats + * time-based singleplayer leveling? + * shapes stored as bitmap, rotate by modifying for + + +---- distant future: --------------------------------------------------------- + + * horizontally _and_ vertically resize enemy fields + * special blocks + * tetrinet compatible? + * remove bot delay (make it humanplayer-like) + * new+better bot? + * replay ability? (tspec replay compatible?) + diff --git a/VERSION b/VERSION deleted file mode 100644 index 83fb97f..0000000 --- a/VERSION +++ /dev/null @@ -1 +0,0 @@ -0.5.88 diff --git a/board.c b/board.c index 8524cc5..55983b4 100644 --- a/board.c +++ b/board.c @@ -33,11 +33,18 @@ static BlockType oldBoard[MAX_SCREENS][MAX_BOARD_HEIGHT][MAX_BOARD_WIDTH]; 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]; +static int shadowy; -ExtFunc void CleanupBoard(int scr) + +ExtFunc void ClearField(int scr) { - CleanupScreen(scr); -} + int y, x; + + for (y = Players[scr].boardHeight - 1; y >= 0; --y) + for (x = 0; x < Players[scr].boardWidth; ++x) { + oldBoard[scr][y][x] = board[scr][y][x] = BT_none; + } +} //ClearField ExtFunc BlockType GetBlock(int scr, int y, int x) { @@ -47,7 +54,7 @@ ExtFunc BlockType GetBlock(int scr, int y, int x) return BT_none; else return abs(board[scr][y][x]); -} +} //GetBlock ExtFunc void SetBlock(int scr, int y, int x, BlockType type) { @@ -58,10 +65,10 @@ ExtFunc void SetBlock(int scr, int y, int x, BlockType type) board[scr][y][x] = type; changed[scr][y] |= 1 << x; } -} +} //SetBlock ExtFunc int RefreshBoard(int scr) -{ +{ //draw changes to screen int y, x, any = 0; unsigned int c; BlockType b; @@ -77,7 +84,7 @@ ExtFunc int RefreshBoard(int scr) RobotCmd(0, " %d", b); } RobotCmd(0, "\n"); - } + } //robot changed[scr][y] = 0; any = 1; for (x = 0; c; (c >>= 1), (++x)) @@ -85,9 +92,8 @@ ExtFunc int RefreshBoard(int scr) PlotBlock(scr, y, x, B_OLD(board[scr][y][x])); oldBoard[scr][y][x] = B_OLD(board[scr][y][x]); } - } - if (robotEnable) - RobotTimeStamp(); + } //changed row + if (robotEnable) RobotTimeStamp(); for (x = 0; x < Players[scr].boardWidth; ++x) if (oldFalling[scr][x] != !!falling[scr][x]) { oldFalling[scr][x] = !!falling[scr][x]; @@ -95,78 +101,90 @@ ExtFunc int RefreshBoard(int scr) any = 1; } return any; -} +} //RefreshBoard ExtFunc int GlanceFunc(int scr, int y, int x, BlockType type, void *data) { PlotBlock1(scr, 20 - y, x * 2, type); return 0; -} +} //GlanceFunc + +ExtFunc int ShadowFunc(int scr, int y, int x, BlockType type, void *data) +{ //draw shadow + SetBlock(scr, y, x, BT_shadow); + return 0; +} //ShadowFunc ExtFunc int PlotFunc(int scr, int y, int x, BlockType type, void *data) { SetBlock(scr, y, x, type); return 0; } +ExtFunc void PlotShape(Shape *shape, int scr, int y, int x, int falling, int shadow) +{ //put shape on field + if (shadow && scr == me) { + for (shadowy = y - 1; shadowy >= 0; shadowy--) + if (!ShapeFits(shape, scr, shadowy, x)) + break; + ShapeIterate(shape, scr, shadowy + 1, x, falling, ShadowFunc, NULL); + } //draw shadow + ShapeIterate(shape, scr, y, x, falling, PlotFunc, NULL); +} //PlotShape ExtFunc int EraseFunc(int scr, int y, int x, BlockType type, void *data) { SetBlock(scr, y, x, BT_none); return 0; } +ExtFunc void EraseShape(Shape *shape, int scr, int y, int x, int shadow) +{ //remove block from field + ShapeIterate(shape, scr, y, x, 0, EraseFunc, NULL); + if (shadow && scr == me) //draw shadow + ShapeIterate(shape, scr, shadowy + 1, x, 0, EraseFunc, NULL); +} //EraseShape ExtFunc int CollisionFunc(int scr, int y, int x, BlockType type, void *data) { - return GetBlock(scr, y, x) != BT_none; + return GetBlock(scr, y, x) > BT_none; } +ExtFunc int ShapeFits(Shape *shape, int scr, int y, int x) +{ //check if there's nothing in the way + return !ShapeIterate(shape, scr, y, x, 0, CollisionFunc, NULL); +} //ShapeFits ExtFunc int VisibleFunc(int scr, int y, int x, BlockType type, void *data) { 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) -{ - ShapeIterate(shape, scr, y, x, falling, PlotFunc, NULL); -} - -ExtFunc void EraseShape(Shape *shape, int scr, int y, int x) -{ - ShapeIterate(shape, scr, y, x, 0, EraseFunc, NULL); -} - -ExtFunc int ShapeFits(Shape *shape, int scr, int y, int x) -{ - return !ShapeIterate(shape, scr, y, x, 0, CollisionFunc, NULL); -} - ExtFunc int ShapeVisible(Shape *shape, int scr, int y, int x) { return ShapeIterate(shape, scr, y, x, 0, VisibleFunc, NULL); -} +} //ShapeVisible ExtFunc int MovePiece(int scr, int deltaY, int deltaX) { int result; EraseShape(Players[scr].curShape, scr, - Players[scr].curY, Players[scr].curX); + Players[scr].curY, Players[scr].curX, 1); result = ShapeFits(Players[scr].curShape, scr, Players[scr].curY + deltaY, Players[scr].curX + deltaX); if (result) { Players[scr].curY += deltaY; Players[scr].curX += deltaX; } - PlotShape(Players[scr].curShape, scr, Players[scr].curY, Players[scr].curX, 1); + PlotShape(Players[scr].curShape, scr, Players[scr].curY, Players[scr].curX, + 1, 1); return result; -} +} //MovePiece ExtFunc int RotatePiece(int scr, int dir) { int result; - EraseShape(Players[scr].curShape, scr, Players[scr].curY, Players[scr].curX); + EraseShape(Players[scr].curShape, scr, Players[scr].curY, + Players[scr].curX, 1); result = ShapeFits(dir ? Players[scr].curShape->rotateTo : Players[scr].curShape->rotateFrom, scr, Players[scr].curY, Players[scr].curX); @@ -174,32 +192,32 @@ ExtFunc int RotatePiece(int scr, int dir) Players[scr].curShape = dir ? Players[scr].curShape->rotateTo : Players[scr].curShape->rotateFrom; PlotShape(Players[scr].curShape, scr, - Players[scr].curY, Players[scr].curX, 1); + Players[scr].curY, Players[scr].curX, 1, 1); return result; -} +} //RotatePiece ExtFunc int DropPiece(int scr) { int count = 0; EraseShape(Players[scr].curShape, scr, - Players[scr].curY, Players[scr].curX); + Players[scr].curY, Players[scr].curX, 1); while (ShapeFits(Players[scr].curShape, scr, Players[scr].curY - 1, Players[scr].curX)) { --Players[scr].curY; ++count; } PlotShape(Players[scr].curShape, scr, - Players[scr].curY, Players[scr].curX, 1); + Players[scr].curY, Players[scr].curX, 1, 0); return count; -} +} //DropPiece ExtFunc int LineIsFull(int scr, int y) { int x; for (x = 0; x < Players[scr].boardWidth; ++x) - if (GetBlock(scr, y, x) == BT_none) + if (GetBlock(scr, y, x) <= BT_none) return 0; return 1; } @@ -242,7 +260,7 @@ ExtFunc void InsertJunk(int scr, int count, int column) int y, x; EraseShape(Players[scr].curShape, scr, - Players[scr].curY, Players[scr].curX); + Players[scr].curY, Players[scr].curX, 1); for (y = Players[scr].boardHeight - count - 1; y >= 0; --y) CopyLine(scr, y, y + count); for (y = 0; y < count; ++y) @@ -251,10 +269,11 @@ ExtFunc void InsertJunk(int scr, int count, int column) 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 + Players[scr].curY--; //..and down again as far as possible else break; - PlotShape(Players[scr].curShape, scr, Players[scr].curY, Players[scr].curX, 1); -} + PlotShape(Players[scr].curShape, scr, Players[scr].curY, Players[scr].curX, + 1, 1); +} //InoertJunk /* * vi: ts=4 ai diff --git a/curses.c b/curses.c index f28a6a3..030ed25 100644 --- a/curses.c +++ b/curses.c @@ -30,30 +30,15 @@ # define HAVE_NCURSES #endif -#ifdef HAVE_NCURSES -static struct { - BlockType type; - short color; -} myColorTable[] = { - { BT_white, COLOR_WHITE }, - { BT_blue, COLOR_BLUE }, - { BT_magenta, COLOR_MAGENTA }, - { BT_cyan, COLOR_CYAN }, - { BT_yellow, COLOR_YELLOW }, - { BT_green, COLOR_GREEN }, - { BT_red, COLOR_RED }, - { BT_none, 0 } -}; -#endif - ExtFunc void PlotBlock1(int scr, int y, int x, BlockType type); -static MyEventType KeyGenFunc(EventGenRec *gen, MyEvent *event); +static MyEventType KeyGenFunc(EventGenRec *gen, MyEvent *event); static EventGenRec keyGen = { NULL, 0, FT_read, STDIN_FILENO, KeyGenFunc, EM_key }; static int boardYPos[MAX_SCREENS], boardXPos[MAX_SCREENS]; static int statusYPos, statusXPos; +static int messageYPos, messageXPos, messageHeight, messageWidth; static int haveColor; static char *term_vi; /* String to make cursor invisible */ @@ -70,7 +55,7 @@ ExtFunc void InitScreens(void) * Ctrl-C during initialization might leave the terminal in a bad state. */ BlockSignals(&oldMask, SIGINT, 0); - initscr(); + initscr(); //start curses #ifdef CURSES_HACK { @@ -83,44 +68,48 @@ ExtFunc void InitScreens(void) #ifdef HAVE_NCURSES haveColor = Game.color && has_colors(); if (haveColor) { + static struct { + BlockType type; + short color; + } myColorTable[] = { + { BT_white, COLOR_WHITE }, + { BT_blue, COLOR_BLUE }, + { BT_magenta, COLOR_MAGENTA }, + { BT_cyan, COLOR_CYAN }, + { BT_yellow, COLOR_YELLOW }, + { BT_green, COLOR_GREEN }, + { BT_red, COLOR_RED }, + { BT_none, 0 } + }; //myColorTable int i = 0; start_color(); + if (can_change_color()) { + init_color (COLOR_YELLOW, 1000, 1000, 0); + } //I've never worked on a color-changable terminal, so no idea.. for (i = 0; myColorTable[i].type != BT_none; ++i) init_pair(myColorTable[i].type, COLOR_BLACK, myColorTable[i].color); - } + } //haveColor #else haveColor = 0; #endif - AtExit(CleanupScreens); + AtExit(CleanupScreens); //restore everything when done RestoreSignals(NULL, &oldMask); - cbreak(); + cbreak(); //no line buffering noecho(); +// keypad(stdscr, TRUE); //get arrow/functionkeys 'n stuff OutputTermStr(term_vi, 0); - AddEventGen(&keyGen); - - move(0, 1); - addstr("NETRIS "); - addstr(version_string); - { - 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; + AddEventGen(&keyGen); //key handler + standend(); //normal text } //InitScreens ExtFunc void CleanupScreens(void) { RemoveEventGen(&keyGen); - endwin(); + endwin(); //end curses OutputTermStr(term_ve, 1); } //CleanupScreens @@ -188,102 +177,164 @@ ExtFunc void OutputTermStr(char *str, int flush) } } //OutputTermStr -ExtFunc void DrawField(int scr) -{ //drow field for player scr - { - int y, x; - - getmaxyx(stdscr, y, x); - if (x < boardXPos[scr] + 2 * Players[scr].boardWidth + 1) { - Players[scr].spy = 0; - return; - } +ExtFunc void DrawTitle(void) +{ + int rows, cols; + char s[255]; - 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 +#ifdef HAVE_NCURSES + attrset(A_REVERSE); +#else + standout(); +#endif + getmaxyx(stdscr, rows, cols); + sprintf(s, " NETRIS %s", version_string); + memset(&s[strlen(s)], ' ', 254 - strlen(s)); + if (cols > 56 + strlen(version_string)) + memcpy(&s[cols - 48], + "(C)1994-1996,1999 Mark H. Weaver, (C)2002 Shiar \0", 49); + else memcpy(&s[cols], "\0", 1); + mvaddstr(0, 0, s); + standend(); //normal text +} //DrawTitle + +ExtFunc void DrawBox(int x1, int y1, int x2, int y2) +{ //draw grid + int y, x; + + for (y = y1 + 1; y < y2; y++) { + mvaddch(y, x1, Game.ascii ? '|' : ACS_VLINE); //left + mvaddch(y, x2, Game.ascii ? '|' : ACS_VLINE); //right + } + move(y1, x1); //top + addch(Game.ascii ? '+' : ACS_ULCORNER); + for (x = x1 + 1; x < x2; x++) + addch(Game.ascii ? '-' : ACS_HLINE); + addch(Game.ascii ? '+' : ACS_URCORNER); + move(y2, x1); //bottom + addch(Game.ascii ? '+' : ACS_LLCORNER); + for (x = x1 + 1; x < x2; x++) + addch(Game.ascii ? '-' : ACS_HLINE); + addch(Game.ascii ? '+' : ACS_LRCORNER); +} //DrawBox +ExtFunc void DrawField(int scr) +{ //draw field for player scr + if (!Players[scr].spy) return; + DrawBox(boardXPos[scr] - 1, boardYPos[scr] - Players[scr].boardVisible, + boardXPos[scr] + 2 * Players[scr].boardWidth, boardYPos[scr] + 1); { - char userstr[300]; + char s[2*Players[scr].boardWidth]; + memset(s, ' ', sizeof(s)); if (Players[scr].host && Players[scr].host[0]) - sprintf(userstr, "%s <%s>", Players[scr].name, Players[scr].host); - else sprintf(userstr, "%s", Players[scr].name); - userstr[20 - 7*((Players[scr].flags & SCF_usingRobot) != 0) + snprintf(s, sizeof(s), "%s <%s>", + Players[scr].name, Players[scr].host); + else snprintf(s, sizeof(s), "%s", Players[scr].name); + s[strlen(s)] = ' '; + s[sizeof(s) - 7*((Players[scr].flags & SCF_usingRobot) != 0) - 5*((Players[scr].flags & SCF_fairRobot) != 0)] = 0; - mvaddstr(1, boardXPos[scr], userstr); + mvaddstr(1, boardXPos[scr], s); if (Players[scr].flags & SCF_usingRobot) { addstr((Players[scr].flags & SCF_fairRobot) ? "(fair robot)" : "(robot)"); - } + } //add robot indicator } //display playername/host + // draw blocks (which is usually just clear field) + ShowPause(scr); -} //DrawScreen +} //DrawField -ExtFunc void InitFields() -{ //place fields for all players +ExtFunc void InitFields(void) +{ //calculate positions of all fields int scr, prevscr; + int y, x; - statusXPos = 2 * Players[me].boardWidth + 3; + getmaxyx(stdscr, y, x); boardXPos[me] = 1; boardYPos[me] = 22; + statusXPos = 2 * Players[me].boardWidth + 3; + statusYPos = 22; + messageXPos = 2; + messageYPos = 25; + messageWidth = x - messageXPos - 2; + if ((messageHeight = y - messageYPos - 1) < 0) messageHeight = 0; + else DrawBox(messageXPos - 2, messageYPos - 1, + messageXPos + messageWidth + 1, messageYPos + messageHeight); prevscr = me; - for (scr = 1; scr <= totalPlayers + 1; scr++) if (scr != me) { + for (scr = 1; scr < MAX_SCREENS; scr++) if (scr != me) { boardXPos[scr] = boardXPos[prevscr] + 2 * Players[prevscr].boardWidth + 3; if (prevscr == me) - boardXPos[scr] += 24; //scorebar + boardXPos[scr] += 14; //scorebar boardYPos[scr] = 22; + if (x < boardXPos[scr] + 2 * Players[scr].boardWidth + 1) + Players[scr].spy = 0; //field doesn't fit on screen prevscr = scr; } -} //InitScreen + for (scr = 1; scr <= Game.maxplayers; scr++) + DrawField(scr); +} //InitFields ExtFunc void CleanupScreen(int scr) { } +ExtFunc void Messagef(char *fmt, ...) +{ //print game/bot message + static int line = 0; + va_list args; + char s[255]; + char *p, *psearch; + int i; + + if (!messageHeight) return; + va_start(args, fmt); + move(messageYPos + line, messageXPos); +// vwprintw(stdscr, fmt, args); //doesn't seem to be a vprintw + vsprintf(s, fmt, args); + p = s; + while (psearch = strchr(s, '\\')) { + *psearch = '\0'; + addstr(p); + if (haveColor) + attrset(A_REVERSE | COLOR_PAIR(atoi(psearch + 1) + 1)); + p = psearch + 2; + } //search for color escapes (\) + addstr(p); + if (messageHeight > 1) { + char s[messageWidth + 1]; + line = (line + 1) % messageHeight; + memset(s, ' ', messageWidth); + s[messageWidth] = 0; + mvaddstr(messageYPos + line, messageXPos, s); + } //multiple lines + if (haveColor) standend(); + va_end(args); +} //Message + ExtFunc void PlotBlock1(int scr, int y, int x, BlockType type) { int colorIndex = abs(type); move(y, x); - - if (type == BT_none) - addstr(" "); - else - { - if (Game.standout) - { + if (type == BT_none) addstr(" "); + else if (type == BT_shadow) addstr("::"); + else { + if (Game.standout) { #ifdef HAVE_NCURSES - if (haveColor) - attrset(COLOR_PAIR(colorIndex)); - else + if (haveColor) attrset(COLOR_PAIR(colorIndex)); + else attrset(A_REVERSE); #endif - standout(); } - addstr(type ? "[]" : "$$"); - standend(); - } +#ifdef HAVE_NCURSES + if (Game.standout) standend(); +#endif + } //display one brick } //PlotBlock1 - ExtFunc void PlotBlock(int scr, int y, int x, BlockType type) { if (y >= 0 && y < Players[scr].boardVisible && @@ -291,38 +342,66 @@ ExtFunc void PlotBlock(int scr, int y, int x, BlockType type) PlotBlock1(scr, boardYPos[scr] - y, boardXPos[scr] + 2 * x, type); } //PlotBlock -ExtFunc void PlotUnderline(int scr, int x, int flag) +ExtFunc void PlotShadowBlock1(int scr, int y, int x, BlockType type) +{ + move(y, x); + if (type == BT_none) addstr(" "); + else addstr("::"); +} //PlotShadowBlock1 +ExtFunc void PlotShadowBlock(int scr, int y, int x, BlockType type) +{ + if (y >= 0 && y < Players[scr].boardVisible && + x >= 0 && x < Players[scr].boardWidth) + PlotShadowBlock1(scr, boardYPos[scr] - y, boardXPos[scr] + 2 * x, type); +} //PlotShadowBlock + +ExtFunc void PlotBlockS1(int scr, int y, int x, BlockType type) +{ //DOESN"T WORK YET... + move(y, x); + if (type == BT_none) addstr(" "); + else { + addstr(type ? "O" : "$"); + standend(); + } //display one brick +} //PlotBlock1 +ExtFunc void PlotBlockS(int scr, int y, int x, BlockType type) { + if (y >= 0 && y < Players[scr].boardVisible && + x >= 0 && x < Players[scr].boardWidth) + PlotBlockS1(scr, boardYPos[scr] - y, boardXPos[scr] + x, type); +} //PlotBlock + +ExtFunc void PlotUnderline(int scr, int x, int flag) +{ //display piece of bottom fieldgrid 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); - } + } //ncurses graphics } //PlotUnderline ExtFunc void ShowScore(int scr, struct _Score score) { //show score stuff float timer; - move(6, statusXPos); addstr("Next: "); - move(7, statusXPos + 7); addstr(" "); + mvaddstr(13, statusXPos, "next "); + mvaddstr(14, statusXPos + 5, " "); 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); + ShapeToNetNum(Players[scr].nextShape) == 15 ? 6 : 7, + statusXPos / 2 + 4, 1, GlanceFunc, NULL); + mvprintw(3, statusXPos, "level %5d", score.level); + mvprintw(5, statusXPos, "score%8d", score.score); + mvprintw(6, statusXPos, "lines%8d", score.lines); timer = CurTimeval() / 1e6; - 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); + mvprintw(9, statusXPos, "ppm %9.1f", score.drops * 60 / timer); + if (score.lines > 0) { + mvprintw(7, statusXPos, + "yield %3d%%", 100 * score.adds / score.lines); + mvprintw(10, statusXPos, "apm %9.1f", score.adds * 60 / timer); + } } } //ShowScore @@ -335,7 +414,11 @@ ExtFunc void FieldMessage(int playa, char *message) memcpy(&s[Players[playa].boardWidth - strlen(message) / 2], message, strlen(message)); s[Players[playa].boardWidth * 2] = 0; - if (Game.standout) standout(); +#ifdef HAVE_NCURSES + attrset(A_REVERSE); +#else + standout(); +#endif mvprintw(boardYPos[playa] - Players[playa].boardVisible / 2, boardXPos[playa], "%s", s); standend(); @@ -350,31 +433,20 @@ ExtFunc void FieldMessage(int playa, char *message) ExtFunc void ShowPause(int playa) { //put paused over player's field - if (Players[playa].flags & SCF_paused) - FieldMessage(playa, "P A U S E D"); - else FieldMessage(playa, NULL); + if (Players[playa].alive) + if (Players[playa].flags & SCF_paused) + FieldMessage(playa, Game.started > 1 + ? "P A U S E D" : "N O T R E A D Y"); + else FieldMessage(playa, Game.started > 1 ? NULL : "R E A D Y"); + else FieldMessage(playa, playa > maxPlayer + ? "E M P T Y" : "G A M E O V E R"); } //ShowPause -ExtFunc void Message(char *s) -{ - static int line = 0; - -// move(statusYPos - 20 + line, statusXPos); - move(statusYPos + 2 + line, 1); - addstr(s); /* XXX Should truncate long lines */ - clrtoeol(); - line = (line + 1) % 10; -// move(statusYPos - 20 + line, statusXPos); - move(statusYPos + 2 + line, 1); - clrtoeol(); -} //Message - ExtFunc void ShowTime(void) { //display timer - move(statusYPos, statusXPos); - printw("Timer: %.0f ", CurTimeval() / 1e6); - move(boardYPos[0] + 1, boardXPos[0] + 2 * Players[0].boardWidth + 1); + mvprintw(statusYPos, statusXPos, "timer %7.0f ", CurTimeval() / 1e6); +// move(boardYPos[0] + 1, boardXPos[0] + 2 * Players[0].boardWidth + 1); // refresh(); } //ShowTime diff --git a/game.c b/game.c index 07dda94..b88e05d 100644 --- a/game.c +++ b/game.c @@ -26,6 +26,7 @@ #include #include #include +#include static struct option options[] = { { "ascii", 2, 0, 'a' }, @@ -61,6 +62,8 @@ static char keyTable[KT_numKeys+1]; static char *hostStr; static int paused = 0; +static sigjmp_buf close_env; + ExtFunc void MapKeys(char *newKeys) { @@ -233,7 +236,7 @@ ExtFunc int StartNewPiece(int scr, Shape *shape) Players[scr].curY, Players[scr].curX)) return 0; PlotShape(Players[scr].curShape, scr, - Players[scr].curY, Players[scr].curX, 1); + Players[scr].curY, Players[scr].curX, 1, 1); return 1; } @@ -241,103 +244,113 @@ ExtFunc void checkPaused(void) { //check whether anyone paused the game int i; - paused = 0; - for (i = 1; i <= totalPlayers; i++) - if (Players[i].alive) - paused |= Players[i].flags & SCF_paused; + paused = Game.started < 1; + for (i = 1; i < MAX_SCREENS; i++) if (Players[i].alive) + paused |= Players[i].flags & SCF_paused; + if (paused) paused = 1; + else if (Game.started == 1) { + Game.started = 2; + Messagef("The game has started"); + for (i = 1; i < MAX_SCREENS; i++) if (Players[i].alive) + ShowPause(i); + } //everybody ready } //checkPaused -ExtFunc void OneGame(int scr) +ExtFunc void OneGame(void) { MyEvent event; int linesCleared, changed = 0; - int spied = 0, dropMode = 0; + int dropMode = 0; int oldPaused = 0; long pauseTimeLeft; int pieceCount = 0; int key; char *p, *cmd; int playercount; + char lastadd; int linevalues[4] = { 40, 100, 400, 1200 }; //= 50*lines! - 10*(lines==1) int i; + maxPlayer = lastadd = me; Game.speed = Game.initspeed; - for (i = 1; i < Players[scr].score.level; i++) + for (i = 1; i < Players[me].score.level; i++) Game.speed /= SPEEDINC; if (Game.speed < SPEEDMINIMUM) Game.speed = SPEEDMINIMUM; - ResetBaseTime(); + ResetBaseTime(); //reset timer + ClearField(me); InitFields(); - for (i = 1; i <= totalPlayers; i++) - if (Players[i].spy) DrawField(i); - if (totalPlayers > 1) { - spied = 1; - } SetITimer(Game.speed, Game.speed); if (robotEnable) { + int counter; RobotCmd(0, "GameType %s\n", gameNames[game]); RobotCmd(0, "BoardSize 0 %d %d\n", - Players[scr].boardVisible, Players[scr].boardWidth); - for (i = 1; i <= totalPlayers; i++) { + Players[me].boardVisible, Players[me].boardWidth); + for (i = 1; i < MAX_SCREENS; i++) if (Players[i].alive && i != me) { RobotCmd(0, "BoardSize %d %d %d\n", - i, Players[i].boardVisible, Players[i].boardWidth); + counter, Players[i].boardVisible, Players[i].boardWidth); RobotCmd(0, "Opponent %d %s %s\n", - i, Players[i].name, Players[i].host); + counter, 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); + counter++; } RobotCmd(0, "TickLength %.3f\n", Game.speed / 1.0e6); RobotCmd(0, "BeginGame\n"); RobotTimeStamp(); } - Players[scr].nextShape = ChooseOption(stdOptions); + Players[me].nextShape = ChooseOption(stdOptions); while (1) { - if (Players[scr].alive) { - if (!StartNewPiece(scr, ChooseOption(stdOptions))) { + if (Players[me].alive) { + if (!StartNewPiece(me, ChooseOption(stdOptions))) { netint4 data[4]; - Players[scr].alive = 0; - FieldMessage(scr, "G A M E O V E R"); - changed = 1; + Players[me].alive = 0; + if (lastadd == me) Messagef("You died"); + else Messagef("\\%d%s fragged you", + Players[lastadd].team > 7 ? 7 : Players[lastadd].team, + Players[lastadd].name); if (game == GT_classicTwo) - SendPacket(scr, NP_argghhh, - sizeof(Players[0].alive), &Players[scr].alive); + SendPacket(me, NP_argghhh, sizeof(lastadd), &lastadd); + ShowPause(me); + changed = 1; } //die else { - ShowScore(scr, Players[scr].score); + ShowScore(me, Players[me].score); if (robotEnable && !fairRobot) RobotCmd(1, "NewPiece %d\n", ++pieceCount); if (spied) { short shapeNum; netint2 data[1]; - shapeNum = ShapeToNetNum(Players[scr].curShape); + shapeNum = ShapeToNetNum(Players[me].curShape); data[0] = hton2(shapeNum); - SendPacket(scr, NP_newPiece, sizeof(data), data); + SendPacket(me, NP_newPiece, sizeof(data), data); } //send new piece } - } - for (;;) { - for (i = 1; i <= totalPlayers; i++) if (Players[i].spy) - changed |= RefreshBoard(i); + } //new piece + while (1) { + for (i = 1; i < MAX_SCREENS; i++) + if (Players[i].alive && Players[i].spy) + changed |= RefreshBoard(i); if (changed) { if (!paused) ShowTime(); refresh(); changed = 0; - } + } //screen update playercount = 0; - for (i = 1; i <= totalPlayers; i++) + for (i = 1; i < MAX_SCREENS; i++) if (Players[i].alive) playercount++; - if (playercount < game + 1) goto gameOver; + if (playercount < 1) goto gameOver; CheckNetConn(); switch (WaitMyEvent(&event, EM_any)) { case E_alarm: - if (Players[scr].alive) - if (!MovePiece(scr, -1, 0)) + if (!paused && Players[me].alive) + if (!MovePiece(me, -1, 0)) goto nextPiece; else if (spied) - SendPacket(scr, NP_down, 0, NULL); + SendPacket(me, NP_down, 0, NULL); break; case E_key: p = strchr(keyTable, tolower(event.u.key)); @@ -350,49 +363,40 @@ ExtFunc void OneGame(int scr) } //let robot handle keypress if (!p) break; keyEvent: - if (!Players[scr].alive && key != KT_quit) break; + if (!Players[me].alive && key != KT_quit) break; if (paused && key < KT_pause) break; switch(key) { case KT_left: - if (MovePiece(scr, 0, -1) && spied) - SendPacket(scr, NP_left, 0, NULL); + if (MovePiece(me, 0, -1) && spied) + SendPacket(me, NP_left, 0, NULL); break; case KT_right: - if (MovePiece(scr, 0, 1) && spied) - SendPacket(scr, NP_right, 0, NULL); + if (MovePiece(me, 0, 1) && spied) + SendPacket(me, NP_right, 0, NULL); break; case KT_rotleft: - if (RotatePiece(scr, 0) && spied) - SendPacket(scr, NP_rotleft, 0, NULL); + if (RotatePiece(me, 0) && spied) + SendPacket(me, NP_rotleft, 0, NULL); break; case KT_rotright: - if (RotatePiece(scr, 1) && spied) - SendPacket(scr, NP_rotright, 0, NULL); + if (RotatePiece(me, 1) && spied) + SendPacket(me, NP_rotright, 0, NULL); break; case KT_down: SetITimer(Game.speed, Game.speed); - if (MovePiece(scr, -1, 0)) { - if (spied) SendPacket(scr, NP_down, 0, NULL); + if (MovePiece(me, -1, 0)) { + if (spied) SendPacket(me, NP_down, 0, NULL); } else goto nextPiece; break; case KT_drop: SetITimer(Game.speed, Game.speed); - if (DropPiece(scr)) { - if (spied) SendPacket(scr, NP_drop, 0, NULL); - if (Players[scr].dropmode == 2) goto nextPiece; + if (DropPiece(me)) { + if (spied) SendPacket(me, NP_drop, 0, NULL); + if (Players[me].dropmode == 2) goto nextPiece; } else goto nextPiece; - dropMode = Players[scr].dropmode; - break; - case KT_pause: - Players[scr].flags ^= SCF_paused; - checkPaused(); - if (game == GT_classicTwo) - SendPacket(scr, NP_pause, 0, NULL); - if (robotEnable) RobotCmd(1, "Pause %d\n", paused); - ShowPause(scr); - changed = 1; + dropMode = Players[me].dropmode; break; case KT_faster: if (game != GT_onePlayer) @@ -400,24 +404,44 @@ ExtFunc void OneGame(int scr) if ((Game.speed /= SPEEDINC) < SPEEDMINIMUM) Game.speed = SPEEDMINIMUM; SetITimer(Game.speed, SetITimer(0, 0)); - Players[scr].score.level++; - ShowScore(scr, Players[scr].score); + Players[me].score.level++; + ShowScore(me, Players[me].score); + changed = 1; + break; + case KT_pause: + Players[me].flags ^= SCF_paused; + if (Game.started > 1) + Messagef(Players[me].flags & SCF_paused + ? "You paused the game" + : "You unpaused the game"); + else + Messagef(Players[me].flags & SCF_paused + ? "You are not ready" : "You are ready"); + checkPaused(); + if (game == GT_classicTwo) + SendPacket(me, NP_pause, 0, NULL); + if (robotEnable) RobotCmd(1, "Pause %d\n", paused); + ShowPause(me); changed = 1; break; case KT_redraw: - ScheduleFullRedraw(); -// if (paused) -// RefreshScreen(); + DrawTitle(); + InitFields(); +// ScheduleFullRedraw(); + for (i = 1; i < MAX_SCREENS; i++) + if (Players[i].alive && Players[i].spy) + RefreshBoard(i); + refresh(); break; case KT_quit: - FieldMessage(me, "G A M E O V E R"); + ShowPause(me); refresh(); goto gameOver; } //E_key - if (dropMode && DropPiece(scr) > 0) { + if (dropMode && DropPiece(me) > 0) { SetITimer(Game.speed, Game.speed); if (spied) - SendPacket(scr, NP_drop, 0, NULL); + SendPacket(me, NP_drop, 0, NULL); } break; case E_robot: @@ -435,28 +459,13 @@ ExtFunc void OneGame(int scr) num == pieceCount))) goto keyEvent; if (!strcmp(cmd, "Message")) { - Message(p); + Messagef(p); changed = 1; } break; } //E_robot case E_net: switch(event.u.net.type) { - case NP_giveJunk: - { - netint2 data[2]; - short column; - - if (!Players[scr].alive) break; - memcpy(data, event.u.net.data, sizeof(data[0])); - column = Random(0, Players[scr].boardWidth); - data[1] = hton2(column); - InsertJunk(scr, ntoh2(data[0]), column); - if (spied) - SendPacket(scr, NP_insertJunk, sizeof(data), data); -// Message("Opponent added %d lines"); - break; - } //receive junklines case NP_newPiece: { short shapeNum; @@ -465,7 +474,8 @@ ExtFunc void OneGame(int scr) FreezePiece(event.u.net.uid); memcpy(data, event.u.net.data, sizeof(data)); shapeNum = ntoh2(data[0]); - StartNewPiece(event.u.net.uid, NetNumToShape(shapeNum)); + StartNewPiece(event.u.net.uid, + NetNumToShape(shapeNum)); break; } //new piece case NP_down: @@ -494,12 +504,74 @@ ExtFunc void OneGame(int scr) netint2 data[2]; memcpy(data, event.u.net.data, sizeof(data)); - InsertJunk(event.u.net.uid, ntoh2(data[0]), ntoh2(data[1])); + InsertJunk(event.u.net.uid, ntoh2(data[0]), + ntoh2(data[1])); break; } //player added junklines + case NP_giveJunk: + { + netint2 data[2]; + short column; + + if (!Players[me].alive) break; + memcpy(data, event.u.net.data, sizeof(data[0])); + column = Random(0, Players[me].boardWidth); + data[1] = hton2(column); + Messagef("\\%d%s sends %d lines", + Players[event.u.net.uid].team > 7 ? 7 + : Players[event.u.net.uid].team, + Players[event.u.net.uid].name, ntoh2(data[0])); + lastadd = event.u.net.uid; + InsertJunk(me, ntoh2(data[0]), column); + if (spied) + SendPacket(me, NP_insertJunk, sizeof(data), + data); + break; + } //receive junklines + case NP_start: + { + Game.started = 1; + checkPaused(); + break; + } //start game + case NP_newPlayer: + { + if (event.u.net.uid > maxPlayer) + maxPlayer = event.u.net.uid; + memcpy(&Players[event.u.net.uid], + event.u.net.data, event.u.net.size); + ClearField(event.u.net.uid); + Messagef("\\%d%s joins the game", + Players[event.u.net.uid].team > 7 ? 7 + : Players[event.u.net.uid].team, + Players[event.u.net.uid].name); + if (Players[event.u.net.uid].flags & SCF_paused) { + checkPaused(); + if (robotEnable) + RobotCmd(1, "Pause %d\n", paused); + } //player has paused + DrawField(event.u.net.uid); +// ShowPause(event.u.net.uid); + changed = 1; + break; + } //player joined case NP_pause: { + char s[20]; + Players[event.u.net.uid].flags ^= SCF_paused; + if (Game.started > 1) + strcpy(s, + Players[event.u.net.uid].flags & SCF_paused + ? "paused the game" : "unpaused the game"); + else + strcpy(s, + Players[event.u.net.uid].flags & SCF_paused + ? "is not ready" : "is good to go"); + Messagef("\\%d%s %s", + Players[event.u.net.uid].team > 7 ? 7 + : Players[event.u.net.uid].team, + Players[event.u.net.uid].name, s); checkPaused(); if (robotEnable) RobotCmd(1, "Pause %d\n", paused); ShowPause(event.u.net.uid); @@ -508,10 +580,21 @@ ExtFunc void OneGame(int scr) } //(un)pause case NP_argghhh: { - memcpy(&Players[event.u.net.uid].alive, - event.u.net.data, sizeof(Players[0].alive)); -// checkPaused(); - FieldMessage(event.u.net.uid, "G A M E O V E R"); + char i; + memcpy(&i, event.u.net.data, sizeof(i)); + Players[event.u.net.uid].alive = 0; + if (i == me) Messagef("You fragged %s", + Players[event.u.net.uid].name); + else if (i == event.u.net.uid) + Messagef("\\%d%s died", + Players[i].team > 7 ? 7 : Players[i].team, + Players[i].name); + else Messagef("\\%d%s fragged %s", + Players[i].team > 7 ? 7 : Players[i].team, + Players[i].name, + Players[event.u.net.uid].name); + checkPaused(); + ShowPause(event.u.net.uid); changed = 1; break; } //G/O @@ -539,23 +622,23 @@ ExtFunc void OneGame(int scr) } //game loop nextPiece: dropMode = 0; - FreezePiece(scr); - Players[scr].score.drops++; - Players[scr].score.score++; - if ((linesCleared = ClearFullLines(scr)) > 0) { + FreezePiece(me); + Players[me].score.drops++; + Players[me].score.score++; + if ((linesCleared = ClearFullLines(me)) > 0) { if (game == GT_onePlayer) - if ((Players[scr].score.lines / 10) < - ((Players[scr].score.lines + linesCleared) / 10)) { + if ((Players[me].score.lines / 10) < + ((Players[me].score.lines + linesCleared) / 10)) { if ((Game.speed /= SPEEDINC) < SPEEDMINIMUM) Game.speed = SPEEDMINIMUM; SetITimer(Game.speed, SetITimer(0, 0)); - Players[scr].score.level++; + Players[me].score.level++; } //level up - Players[scr].score.score += linevalues[linesCleared]; - Players[scr].score.lines += linesCleared; - Players[scr].score.adds += linesCleared - (linesCleared < 4); + Players[me].score.score += linevalues[linesCleared - 1]; + Players[me].score.lines += linesCleared; + Players[me].score.adds += linesCleared - (linesCleared < 4); if (spied) - SendPacket(scr, NP_clear, 0, NULL); + SendPacket(me, NP_clear, 0, NULL); } if (game == GT_classicTwo && linesCleared > 1) { short junkLines; @@ -563,13 +646,19 @@ ExtFunc void OneGame(int scr) junkLines = linesCleared - (linesCleared < 4); data[0] = hton2(junkLines); - SendPacket(scr, NP_giveJunk, sizeof(data), data); - } + SendPacket(me, NP_giveJunk, sizeof(data), data); + Messagef("You sent %d lines", junkLines); + } //send junk to others } //new piece loop gameOver: SetITimer(0, 0); } +ExtFunc void CatchInt(int sig) +{ + siglongjmp(close_env, 1); +} + ExtFunc int main(int argc, char **argv) { char ch; @@ -582,19 +671,20 @@ ExtFunc int main(int argc, char **argv) int i; char *userName; - for (i = 0; i <= MAX_SCREENS; i++) + for (i = 0; i < MAX_SCREENS; i++) { Players[i].score.level = Players[i].spy = 1; + Players[i].boardWidth = 10; + Players[i].boardHeight = MAX_BOARD_HEIGHT; + Players[i].boardVisible = 20; + strcpy(Players[i].name, "???"); + } 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; Players[0].alive = 1; - } + } //set defaults // if (getopt(argc, argv, "f:") == 'f') // ReadConf(optarg); @@ -611,22 +701,27 @@ ExtFunc int main(int argc, char **argv) fatal("You can't use the -F option without the -r option"); // WriteConf(); - InitUtil(); - InitScreens(); + SRandom(time(0)); //randomize + if (sigsetjmp(close_env, 1)) + exit(0); + signal(SIGINT, CatchInt); //handle exit (^C) + InitScreens(); //setup screen + DrawTitle(); if (initConn) { game = GT_classicTwo; - Players[0].flags |= SCF_paused; - paused = SCF_paused; + spied = 1; InitiateConnection(hostStr, port); HandShake(); - OneGame(me); + checkPaused(); + OneGame(); } //client else { game = GT_onePlayer; - totalPlayers = 1; + Game.started = 2; + Game.maxplayers = 1; me = 1; memcpy(&Players[me], &Players[0], sizeof(Player)); - OneGame(me); + OneGame(); } //singleplay return 0; } diff --git a/inet.c b/inet.c index b21b137..2caca52 100644 --- a/inet.c +++ b/inet.c @@ -33,9 +33,8 @@ #define HEADER_SIZE3 sizeof(netint4[3]) ExtFunc MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event); - -EventGenRec netGen[MAX_SCREENS] = { - { NULL, 0, FT_read, -1, NetGenFunc, EM_net, 0, "\0", 0, HEADER_SIZE3 } }; +EventGenRec netGen = + { NULL, 0, FT_read, -1, NetGenFunc, EM_net, 0, "\0", 0, HEADER_SIZE3 }; ExtFunc int InitiateConnection(char *hostStr, short port) @@ -53,17 +52,16 @@ ExtFunc int InitiateConnection(char *hostStr, short port) addr.sin_family = host->h_addrtype; memcpy(&addr.sin_addr, host->h_addr, host->h_length); addr.sin_port = htons(port); - if ((netGen[0].fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) + if ((netGen.fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) die("socket"); - if (connect(netGen[0].fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + if (connect(netGen.fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { if (errno != ECONNREFUSED) die("connect"); - close(netGen[0].fd); + close(netGen.fd); sleep(1); goto again; } - AddEventGen(&netGen[0]); - totalPlayers = 1; + AddEventGen(&netGen); return 0; } //InitiateConnection @@ -88,25 +86,29 @@ ExtFunc void HandShake(void) 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), + sizeof(Player) - sizeof(Players[me].host) + - sizeof(Players[me].spy) - sizeof(Players[me].small), &Players[me]); break; } case NP_gamedata: { - memcpy(&Game, event.u.net.data, event.u.net.size); + static struct { + int playerflags; + int maxplayers; //1 + int started; //2 + int continuous; //3 + long seed; //4 + int initspeed; //5 + } data; + + memcpy(&data, event.u.net.data, event.u.net.size); + memcpy(&Players[me].flags, &data, sizeof(data.playerflags)); + memcpy(&Game, &data.maxplayers, + sizeof(data) - sizeof(data.playerflags)); SRandom(Game.seed); 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); @@ -118,69 +120,7 @@ ExtFunc void HandShake(void) 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] = '?'; -*/ + while (event.u.net.type != NP_gamedata); } //HandShake ExtFunc void CheckNetConn(void) @@ -218,10 +158,7 @@ ExtFunc MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event) event->u.net.type = type; event->u.net.size = size - HEADER_SIZE3; event->u.net.data = gen->buf + HEADER_SIZE3; - if (type == NP_endConn) { - fprintf(stderr, "Close connection\n"); - return E_lostConn; - } + if (type == NP_endConn) return E_lostConn; return E_net; } //NetGenFunc @@ -232,9 +169,9 @@ ExtFunc void SendPacket(short uid, NetPacketType type, int size, void *data) header[0] = hton4(uid); header[1] = hton4(type); header[2] = hton4(size + HEADER_SIZE3); - if (MyWrite(netGen[0].fd, header, HEADER_SIZE3) != HEADER_SIZE3) + if (MyWrite(netGen.fd, header, HEADER_SIZE3) != HEADER_SIZE3) die("write (header)"); - if (size > 0 && data && MyWrite(netGen[0].fd, data, size) != size) + if (size > 0 && data && MyWrite(netGen.fd, data, size) != size) die("write"); } //SendPacket @@ -242,13 +179,13 @@ ExtFunc void CloseNet(void) { //kick some connection's ass! MyEvent event; - if (netGen[0].fd >= 0) { + if (netGen.fd >= 0) { SendPacket(0, NP_endConn, 0, NULL); - close(netGen[0].fd); - netGen[0].fd = -1; + close(netGen.fd); + netGen.fd = -1; } - if (netGen[0].next) - RemoveEventGen(&netGen[0]); + if (netGen.next) + RemoveEventGen(&netGen); } //CloseNet /* diff --git a/netris.h b/netris.h index 30ce772..7bbfc3c 100644 --- a/netris.h +++ b/netris.h @@ -28,7 +28,7 @@ #include #include -#define version_string "0.5.810" +#define version_string "0.7.819" #define ExtFunc /* Marks functions that need prototypes */ @@ -80,24 +80,45 @@ typedef long netint4; #define EM_key 000002 #define EM_net 000004 #define EM_robot 000010 +#define EM_connect 000020 #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 + BT_shadow, BT_none, + BT_green, BT_cyan, BT_blue, BT_magenta, BT_red, BT_yellow, BT_white, + 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 + E_none, E_alarm, E_key, E_connect, 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_argghhh, NP_giveJunk, NP_newPiece, NP_down, NP_left, NP_right, - NP_rotright, NP_rotleft, NP_drop, NP_clear, NP_insertJunk + NP_endConn, //client/server quits + NP_byeBye, //unused atm + NP_error, //handshake error + NP_hello, //check versions + NP_gamedata, //game options + + NP_start, //game ok to start + NP_pause, //player (un)pauses + NP_stop, //game ended + NP_newPlayer, //add new player + NP_argghhh, //player died + + NP_newPiece, //new piece info + NP_rotright, //rotate piece clockwise + NP_rotleft, //rotate piece counterclockwise + NP_left, //move piece left + NP_right, //move piece right + NP_down, //move piece one down + NP_drop, //drop piece to bottom + NP_clear, //line cleared + NP_insertJunk, //player added junk + + NP_giveJunk //player has to add junk } NetPacketType; typedef signed char BlockType; @@ -133,7 +154,6 @@ typedef struct _EventGenRec { char buf[512]; int bufSize, bufGoal; } EventGenRec; -EXT EventGenRec netGen[MAX_SCREENS]; MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event); @@ -158,8 +178,6 @@ typedef int (*ShapeDrawFunc)(int scr, int y, int x, #define SCF_fairRobot 000002 #define SCF_paused 000004 -EXT int totalPlayers; - typedef struct _Player { int alive; char name[16]; @@ -175,18 +193,23 @@ typedef struct _Player { int drops, lines, adds; } score; char host[256]; //last-1 - int spy; //last + int spy,small; //last } Player; EXT Player Players[MAX_SCREENS]; EXT short me; +EXT short maxPlayer; +EXT int spied; //in player.flags #define DEFAULT_INTERVAL 1000000 /* Step-down interval in microseconds */ #define SPEEDINC 1.2 #define SPEEDMINIMUM 40000 typedef struct __Game { - long seed; //1st - int initspeed; //2nd + int maxplayers; //1 + int started; //2 + int continuous; //3 + long seed; //4 + int initspeed; //5 int speed; int standout, color, ascii; } _Game; diff --git a/robot.c b/robot.c index 23875a3..31bb5d8 100644 --- a/robot.c +++ b/robot.c @@ -30,7 +30,6 @@ #include static MyEventType RobotGenFunc(EventGenRec *gen, MyEvent *event); - static EventGenRec robotGen = { NULL, 0, FT_read, -1, RobotGenFunc, EM_robot }; diff --git a/server.c b/server.c index 9297b13..f67721f 100644 --- a/server.c +++ b/server.c @@ -37,30 +37,42 @@ static struct option options[] = { { "wait", 0, 0, 'w' }, { "port", 1, 0, 'p' }, - { "connections",1, 0, 'c' }, + { "min-players",1, 0, 'm' }, + { "max-players",1, 0, 'x' }, + { "continuous", 1, 0, 'c' }, { "speed", 1, 0, 'i' }, { "seed", 1, 0, 's' }, + { "verbose", 0, 0, 'v' }, { "info", 0, 0, 'H' }, { "help", 0, 0, 'h' }, { 0, 0, 0, 0 } }; -static char Connections = 2; +static char minplayers = 2; +static char countPlayers = 0; +static char verbose = 0; + +struct sockaddr_in addr; static char *gameNames[GT_len] = { "OnePlayer", "ClassicTwo" }; ExtFunc MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event); - -EventGenRec netGen[MAX_SCREENS] = { +static EventGenRec netGen[MAX_SCREENS] = { { NULL, 0, FT_read, -1, NetGenFunc, EM_net, 0, "\0", 0, HEADER_SIZE } }; static MyEventType AlarmGenFunc(EventGenRec *gen, MyEvent *event); static EventGenRec alarmGen = - { &alarmGen, 0, FT_read, -1, AlarmGenFunc, EM_alarm }; + { &alarmGen, 0, FT_read, -1, AlarmGenFunc, EM_alarm }; + +static MyEventType ConnGenFunc(EventGenRec *gen, MyEvent *event); +static EventGenRec connGen = + { NULL, 0, FT_read, -1, ConnGenFunc, EM_connect }; + static EventGenRec *nextGen = &alarmGen; static sigjmp_buf close_env; + ExtFunc volatile void die(char *msg) { perror(msg); @@ -101,10 +113,25 @@ ExtFunc int MyWrite(int fd, void *data, int len) return len; } //MyWrite +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 + static MyEventType AlarmGenFunc(EventGenRec *gen, MyEvent *event) { return E_alarm; -} +} //AlarmGenFunc ExtFunc void AddEventGen(EventGenRec *gen) { @@ -125,6 +152,39 @@ ExtFunc void RemoveEventGen(EventGenRec *gen) } } //RemoveEventGen +ExtFunc void AtExit(void (*handler)(void)) +{ //setup something to do at exit (^C) +#ifdef HAS_ON_EXIT + on_exit((void *)handler, NULL); +#else + atexit(handler); +#endif +} //AtExit + +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; + + fprintf(stderr, "- Closing connections...\n"); + for (i = 1; i < MAX_SCREENS; i++) + SCloseNet(i); //bye everybuddy + fprintf(stderr, "* All Done\n\n"); +} //CloseNets + ExtFunc MyEventType WaitMyEvent(MyEvent *event, int mask) { //poll int i, retry = 0; @@ -178,13 +238,6 @@ ExtFunc MyEventType WaitMyEvent(MyEvent *event, int mask) } } //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; @@ -194,9 +247,8 @@ ExtFunc MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event) result = MyRead(gen->fd, gen->buf + gen->bufSize, gen->bufGoal - gen->bufSize); if (result < 0) { - ByeClient(gen->player); - type = NP_endConn; - return E_net; + fprintf(stderr, "- Closed connection to player #%d\n", gen->player); + return E_lostConn; } gen->bufSize += result; if (gen->bufSize < gen->bufGoal) @@ -215,206 +267,228 @@ ExtFunc MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event) event->u.net.type = type; event->u.net.size = size - HEADER_SIZE; event->u.net.data = gen->buf + HEADER_SIZE; - if (type == NP_endConn) - ByeClient(gen->player); + if (type == NP_endConn) { + fprintf(stderr, "- Quit player #%d\n", gen->player); + return E_lostConn; + } //client sent quit signal return E_net; } //NetGenFunc -ExtFunc void SendPacketTo(short playa, short uid, NetPacketType type, int size, void *data) -{ //send to someone - netint4 header[3]; - - if (netGen[playa].fd >= 0) { - header[0] = hton4(uid); - header[1] = hton4(type); - header[2] = hton4(size + HEADER_SIZE); - if (MyWrite(netGen[playa].fd, header, HEADER_SIZE) != HEADER_SIZE) - die("write (header)"); - if (size > 0 && data && MyWrite(netGen[playa].fd, data, size) != size) - die("write"); - } -} //SendPacketTo - -ExtFunc void AtExit(void (*handler)(void)) -{ -#ifdef HAS_ON_EXIT - on_exit((void *)handler, NULL); -#else - atexit(handler); -#endif -} - -ExtFunc void SCloseNet(short playa) -{ //kick some connection's ass! - MyEvent event; - - if (netGen[playa].fd >= 0) { - SendPacketTo(playa, 0, NP_endConn, 0, NULL); - do{} while (WaitMyEvent(&event, EM_net) != E_lostConn); - close(netGen[playa].fd); - netGen[playa].fd = -1; - } - if (netGen[playa].next) - RemoveEventGen(&netGen[playa]); -} //SCloseNet - -ExtFunc void CloseNets(void) -{ //nou oogjes dicht en snaveltjes toe - int i; - - for (i = 1; i <= totalPlayers; i++) - SCloseNet(i); -} //CloseNets - -ExtFunc int WaitForConnection(short port) +static MyEventType ConnGenFunc(EventGenRec *gen, MyEvent *event) { - struct sockaddr_in addr; - struct hostent *host; - int sockListen; int addrLen; - int val1; struct linger val2; - int i; - - AtExit(CloseNets); - for (i = 1; i < MAX_SCREENS; i++) - memcpy(&netGen[i], &netGen[0], sizeof(EventGenRec)); - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr.s_addr = htonl(INADDR_ANY); - if ((sockListen = socket(AF_INET, SOCK_STREAM, 0)) < 0) - die("socket"); - val1 = 1; - setsockopt(sockListen, SOL_SOCKET, SO_REUSEADDR, - (void *)&val1, sizeof(val1)); - if (bind(sockListen, (struct sockaddr *)&addr, sizeof(addr)) < 0) - die("bind"); - if (listen(sockListen, 1) < 0) - die("listen"); + int new; addrLen = sizeof(addr); - for (i = 1; i <= Connections; i++) { - if ((netGen[i].fd = accept(sockListen, (struct sockaddr *)&addr, &addrLen)) < 0) + for (new = 1; new <= MAX_SCREENS; new++) + if (netGen[new].fd < 0) break; + if (new > Game.maxplayers) return; + + if ((netGen[new].fd = + accept(gen->fd, (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 + fprintf(stderr, "+ Connection: %s\n", inet_ntoa(addr.sin_addr)); + val2.l_onoff = 1; + val2.l_linger = 0; + setsockopt(netGen[new].fd, SOL_SOCKET, SO_LINGER,(void *)&val2, + sizeof(val2)); + AddEventGen(&netGen[new]); + netGen[new].player = event->u.net.uid = new; + { //new connection; netGen already initialized in GenFunc + struct hostent *host; + + sprintf(Players[new].host, "%s", inet_ntoa(addr.sin_addr)); + if (addr.sin_family == AF_INET) { + host = gethostbyaddr((void *)&addr.sin_addr, + sizeof(struct in_addr), AF_INET); + if (host) { + strncpy(Players[new].host, host->h_name, + sizeof(Players[new].host) - 1); + Players[new].host[sizeof(Players[new].host) - 1] = 0; + } //set player's hostname + } + } //E_connect + return E_connect; +} //ConnGenFunc ExtFunc int StartServer(void) { MyEvent event; + netint2 currentpiece[MAX_SCREENS]; int playercount; int playersReady = 0; int i; - { - char serverdata[255]; - for (i = 1; i <= totalPlayers; i++) { - sprintf(serverdata, "Netris server %s", version_string); - SendPacketTo(i, i, NP_hello, strlen(serverdata)+1, serverdata); - } - } - do { - 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 :( - Players[event.u.net.sender].alive = 0; - for (i = 1; i <= totalPlayers; i++) + switch (WaitMyEvent(&event, EM_any)) { + case E_lostConn: //client went away :( + Players[event.u.net.sender].alive = 0; + for (i = 1; i < MAX_SCREENS; i++) + if (Players[i].alive) SendPacketTo(i, event.u.net.sender, NP_argghhh, sizeof(Players[0].alive), &Players[event.u.net.sender].alive); - 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); - if (!Players[event.u.net.sender].team) - Players[event.u.net.sender].team = 256 - event.u.net.sender; - 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); - SendPacketTo(event.u.net.sender, 0, NP_gamedata, - sizeof(Game.seed)+sizeof(Game.initspeed), &Game); - 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 (++playersReady >= totalPlayers) { - fprintf(stderr, "Starting game (%010d)\n", Game.seed); - for (i = 1; i <= totalPlayers; i++) - SendPacketTo(i, 0, NP_goAhead, 0, NULL); - } //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) - if (event.u.net.type != NP_giveJunk - || Players[i].team != Players[event.u.net.sender].team) - SendPacketTo(i, event.u.net.sender, event.u.net.type, - event.u.net.size, event.u.net.data); - break; + SCloseNet(event.u.net.sender); + break; //NP_endConn + case E_net: + if (verbose) fprintf(stderr, ": %d sent %d\n", + netGen[event.u.net.sender].fd, event.u.net.type); + switch(event.u.net.type) { + case NP_hello: +// if (event.u.net.type != NP_hello) ByeClient(new); + { + netint4 versiondata[2]; + char data[255]; + int major; + + 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, "= Wrong version player #%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 player #%d\n", + event.u.net.sender); + break; + } //NP_hello + case NP_newPlayer: + //receive player details and return other players + memcpy(&Players[event.u.net.sender], + event.u.net.data, event.u.net.size); + if (!Players[event.u.net.sender].team) + Players[event.u.net.sender].team = + 256 - event.u.net.sender; + if (Game.started < 2) + Players[event.u.net.sender].flags |= SCF_paused; + if (!Game.continuous && Game.started >= 2) { + char data[40]; + strcpy(data,"Can't join: Game has already started"); + fprintf(stderr, "- Can't join player #%d in " + "non-continuous game\n", event.u.net.sender); + SendPacketTo(event.u.net.sender, 0, NP_error, + strlen(data)+1, data); +// SCloseNet(event.u.net.sender, 0); + break; + } //can't join started game + { + static struct { + int playerflags; + int maxplayers; //1 + int started; //2 + int continuous; //3 + long seed; //4 + int initspeed; //5 + } data; + + memcpy(&data, &Players[event.u.net.sender].flags, + sizeof(data.playerflags)); + memcpy(&data.maxplayers, &Game, + sizeof(data) - sizeof(data.playerflags)); + SendPacketTo(event.u.net.sender, 0, NP_gamedata, + sizeof(data), &data); + } //send game options + for (i = 1; i < MAX_SCREENS; i++) + if (netGen[i].fd >= 0 && i != event.u.net.sender) { + SendPacketTo(event.u.net.sender, i, + NP_newPlayer, sizeof(Player) + - sizeof(Players[0].spy) + - sizeof(Players[0].small), + &Players[i]); + SendPacketTo(event.u.net.sender, i, + NP_newPiece, sizeof(currentpiece[i]), + ¤tpiece[i]); + SendPacketTo(i, event.u.net.sender, + NP_newPlayer, sizeof(Player) + - sizeof(Players[0].spy) + - sizeof(Players[0].small), + &Players[event.u.net.sender]); + } //send (to) players + fprintf(stderr, "> Joined player #%d: %s <%s>\n", + event.u.net.sender, + Players[event.u.net.sender].name, + Players[event.u.net.sender].host); + if (++playersReady >= minplayers) { + if (Game.started) + SendPacketTo(event.u.net.sender, 0, + NP_start, 0, NULL); + else { + fprintf(stderr, "* Starting game (%010d)\n", + Game.seed); + for (i = 1; i < MAX_SCREENS; i++) + SendPacketTo(i, 0, NP_start, 0, NULL); + Game.started++; + } //first goahead (to all) + } //give go ahead + break; //NP_playerdata + case NP_newPiece: + memcpy(¤tpiece[event.u.net.sender], + event.u.net.data, sizeof(currentpiece[0])); + goto sendtoall; + case NP_argghhh: + Players[event.u.net.sender].alive = 0; + fprintf(stderr, "< Player #%d died\n", + event.u.net.sender); + //check for unpaused game + case NP_pause: + { + int paused; + Players[event.u.net.sender].flags ^= SCF_paused; + paused = Game.started < 1; + for (i = 1; i < MAX_SCREENS; i++) if (Players[i].alive) + paused |= Players[i].flags & SCF_paused; + if (paused) paused = 1; + else if (Game.started == 1) Game.started++; + goto sendtoall; + } //NP_pause + default: //relay data to all players + sendtoall: +// if (event.u.net.type >= NP_pause) + for (i = 1; i < MAX_SCREENS; i++) + if (i != event.u.net.sender) + if (event.u.net.type != NP_giveJunk || + Players[i].team != Players[event.u.net.sender].team) + SendPacketTo(i, event.u.net.sender, + event.u.net.type, event.u.net.size, + event.u.net.data); + break; //>=NP_paused } - } - } //E_net - playercount = 0; - for (i = 1; i <= totalPlayers; i++) - if (netGen[i].fd >= 0) playercount++; - } while (playercount > 1); + break; //E_net + case E_connect: + { //new connection; netGen already initialized in GenFunc + char serverdata[255]; + + sprintf(serverdata, "Netris server %s", version_string); + SendPacketTo(event.u.net.uid, event.u.net.uid, NP_hello, + strlen(serverdata)+1, serverdata); + break; + } //E_connect + } //event + { + int j; + playercount = 0; + for (i = 1; i < MAX_SCREENS; i++) if (Players[i].alive) { + if (Players[i].team < 128) for (j = 1; j < i; j++) + if (Players[j].alive + && (Players[j].team == Players[i].team)) { + playercount--; + break; + } //player of same team counted before + playercount++; + } //player alive + } //count players + } while (Game.started < 2 || playercount > 1 || Game.continuous); + fprintf(stderr, "* Exiting server\n"); } //StartServer @@ -438,6 +512,9 @@ ExtFunc void Usage(void) "\n" " -s, --seed \tStart with given random seed\n" " -i, --speed \tSet the initial step-down interval, in seconds\n" + " -m, --min-players <2>\tNumber of players required before starting the game\n" + " -x, --max-players <8>\tMaximum number of players allowed in the game\n" + " -c, --continuous\tDon'n quit the game\n" "\n", DEFAULT_PORT); } @@ -480,11 +557,22 @@ ExtFunc void WriteConf(void) ExtFunc void HandleOption(char tag, char *value) { switch (tag) { + case 'v': //verbose + verbose = 1; + break; case 'p': //port port = atoi(value); break; - case 'c': //connections - Connections = atoi(value); + case 'c': //min-players + Game.continuous = atoi(value); + break; + case 'm': //min-players + minplayers = atoi(value); + break; + case 'x': //max-players + Game.maxplayers = atoi(value); + if (Game.maxplayers >= MAX_SCREENS) + Game.maxplayers = MAX_SCREENS; break; case 'i': //speed (of level 1) Game.initspeed = atof(value) * 1e6; @@ -515,7 +603,8 @@ ExtFunc void ReadConf(char *filename) if ((ch = strchr(buf, '#'))) *ch = '\0'; // truncate string from # char for (i = strlen(buf)-1; i >= 0; i--) - if (buf[i] == ' ' || buf[i] == '\n' || buf[i] == '\t' || buf[i] == 13) + if (buf[i] == ' ' || buf[i] == '\t' + || buf[i] == '\n' || buf[i] == 13) buf[i] = '\0'; else break; @@ -547,6 +636,7 @@ ExtFunc int main(int argc, char **argv) if (sigsetjmp(close_env, 1)) exit(0); signal(SIGINT, CatchInt); port = DEFAULT_PORT; + Game.maxplayers = 8; Game.initspeed = DEFAULT_INTERVAL; Game.seed = time(0); @@ -555,7 +645,7 @@ ExtFunc int main(int argc, char **argv) // else ReadConf(CONFIG_FILE); while ((ch = getopt_long(argc, argv, - "hHp:i:s:c:", options, NULL)) != -1) + "hHvp:i:s:c:m:x:", options, NULL)) != -1) HandleOption(ch, optarg); if (optind < argc) { Usage(); @@ -564,8 +654,38 @@ ExtFunc int main(int argc, char **argv) // WriteConf(); Header(); - WaitForConnection(port); - StartServer(); + + { + int i; + + for (i = 1; i < MAX_SCREENS; i++) + memcpy(&netGen[i], &netGen[0], sizeof(EventGenRec)); + } //setup netGen var + + AtExit(CloseNets); + + { + int val1; + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + if ((connGen.fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) + die("socket"); + val1 = 1; + setsockopt(connGen.fd, SOL_SOCKET, SO_REUSEADDR, + (void *)&val1, sizeof(val1)); + if (bind(connGen.fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) + die("bind"); + if (listen(connGen.fd, 1) < 0) + die("listen"); + + AddEventGen(&connGen); + } //setup listening sock + + StartServer(); //server loop + return 0; } diff --git a/util.c b/util.c index b32592f..2f7c2e5 100644 --- a/util.c +++ b/util.c @@ -28,16 +28,12 @@ #include #include #include -#include 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; - static int myRandSeed = 1; static long baseTimeval; @@ -75,11 +71,10 @@ ExtFunc void Usage(void) " -a, --ascii\t\tUse ascii characters\n" " -C, --color=0\t\tDisable color\n" "\n" - " -w, --wait\t\tWait for connection\n" " -c, --connect \tInitiate connection\n" " -p, --port \tSet port number (default is %d)\n" "\n" - " -i, --speed \tSet the initial step-down interval, in seconds\n" + " -t, --team \tJoin a team (don't receive lines from your teammates)\n" " -l, --level \tBegin at a higher level (can be used as handicap)\n" " -k, --keys \tRemap keys (default is \"%s\" for cursors)\n" " -d, --dropmode\tDrops go into drop mode\n" @@ -136,14 +131,6 @@ ExtFunc void Rules(void) ///////////// RANDOM ///////////// -ExtFunc void InitUtil(void) -{ - SRandom(time(0)); - if (sigsetjmp(close_env, 1)) exit(0); - signal(SIGINT, CatchInt); - ResetBaseTime(); -} //InitUtil - /* * My really crappy random number generator follows * Should be more than sufficient for our purposes though @@ -208,11 +195,6 @@ ExtFunc void NormalizeTime(struct timeval *tv) } } -ExtFunc void CatchInt(int sig) -{ - siglongjmp(close_env, 1); -} - ExtFunc void CatchAlarm(int sig) { alarmGen.ready = 1; @@ -300,7 +282,7 @@ ExtFunc volatile void fatal(char *msg) { fprintf(stderr, "%s\n", msg); exit(1); -} +} //fatal ExtFunc void BlockSignals(MySigSet *saved, ...) { @@ -327,7 +309,7 @@ ExtFunc void BlockSignals(MySigSet *saved, ...) *saved = sigblock(set); #endif va_end(args); -} +} //BlockSignals ExtFunc void RestoreSignals(MySigSet *saved, MySigSet *set) { @@ -339,7 +321,7 @@ ExtFunc void RestoreSignals(MySigSet *saved, MySigSet *set) else sigsetmask(*set); #endif -} +} //RestoreSignals ///////////// EVENTS /////////////