Server fixes, many visual improvements (event messages, piece shadow).
* 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
------------------------------------------------------------------------------
#
CC="gcc"
-COPT="-O -m486"
-CEXTRA=""
+COPT="-O"
+CEXTRA="-m486"
LEXTRA=""
CURSES_HACK=false
case "$opt" in
-g)
COPT="-g -O0"
-# CEXTRA="-Wall -Wstrict-prototypes"
+# CEXTRA="-Wall -Wstrict-prototypes -m486"
;;
-O*)
COPT="$opt"
--- /dev/null
+---- 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?)
+
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)
{
return BT_none;
else
return abs(board[scr][y][x]);
-}
+} //GetBlock
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;
RobotCmd(0, " %d", b);
}
RobotCmd(0, "\n");
- }
+ } //robot
changed[scr][y] = 0;
any = 1;
for (x = 0; c; (c >>= 1), (++x))
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];
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);
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;
}
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)
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
# 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 */
* Ctrl-C during initialization might leave the terminal in a bad state.
*/
BlockSignals(&oldMask, SIGINT, 0);
- initscr();
+ initscr(); //start curses
#ifdef CURSES_HACK
{
#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
}
} //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 &&
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
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();
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
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
+#include <setjmp.h>
static struct option options[] = {
{ "ascii", 2, 0, 'a' },
static char *hostStr;
static int paused = 0;
+static sigjmp_buf close_env;
+
ExtFunc void MapKeys(char *newKeys)
{
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;
}
{ //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));
} //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)
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:
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;
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:
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);
} //(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
} //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;
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;
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);
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;
}
#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)
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
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);
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)
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
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
{ //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
/*
#include <stdio.h>
#include <signal.h>
-#define version_string "0.5.810"
+#define version_string "0.7.819"
#define ExtFunc /* Marks functions that need prototypes */
#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;
char buf[512];
int bufSize, bufGoal;
} EventGenRec;
-EXT EventGenRec netGen[MAX_SCREENS];
MyEventType NetGenFunc(EventGenRec *gen, MyEvent *event);
#define SCF_fairRobot 000002
#define SCF_paused 000004
-EXT int totalPlayers;
-
typedef struct _Player {
int alive;
char name[16];
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;
#include <errno.h>
static MyEventType RobotGenFunc(EventGenRec *gen, MyEvent *event);
-
static EventGenRec robotGen =
{ NULL, 0, FT_read, -1, RobotGenFunc, EM_robot };
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);
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)
{
}
} //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;
}
} //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;
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)
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
"\n"
" -s, --seed <seed>\tStart with given random seed\n"
" -i, --speed <sec>\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);
}
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;
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;
if (sigsetjmp(close_env, 1)) exit(0);
signal(SIGINT, CatchInt);
port = DEFAULT_PORT;
+ Game.maxplayers = 8;
Game.initspeed = DEFAULT_INTERVAL;
Game.seed = time(0);
// 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();
// 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;
}
#include <sys/time.h>
#include <netdb.h>
#include <errno.h>
-#include <setjmp.h>
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;
" -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"
- " -i, --speed <sec>\tSet the initial step-down interval, in seconds\n"
+ " -t, --team <team>\tJoin a team (don't receive lines from your teammates)\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"
///////////// 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
}
}
-ExtFunc void CatchInt(int sig)
-{
- siglongjmp(close_env, 1);
-}
-
ExtFunc void CatchAlarm(int sig)
{
alarmGen.ready = 1;
{
fprintf(stderr, "%s\n", msg);
exit(1);
-}
+} //fatal
ExtFunc void BlockSignals(MySigSet *saved, ...)
{
*saved = sigblock(set);
#endif
va_end(args);
-}
+} //BlockSignals
ExtFunc void RestoreSignals(MySigSet *saved, MySigSet *set)
{
else
sigsetmask(*set);
#endif
-}
+} //RestoreSignals
///////////// EVENTS /////////////