2 * Netris -- A free networked version of T*tris
3 * Copyright (C) 1994,1995,1996 Mark H. Weaver <mhw@netris.org>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 * $Id: game.c,v 1.39 1999/05/16 06:56:27 mhw Exp $
27 #include <sys/types.h>
28 #include <netinet/in.h>
30 enum { KT_left, KT_right, KT_rotright, KT_rotleft, KT_drop, KT_down,
31 KT_toggleSpy, KT_pause, KT_faster, KT_redraw, KT_numKeys };
33 static char *keyNames[KT_numKeys+1] = {
34 "Left", "Right", "RotRight", "RotLeft", "Drop", "Down",
35 "ToggleSpy", "Pause", "Faster", "Redraw", NULL };
37 static char *gameNames[GT_len] = { "OnePlayer", "ClassicTwo" };
39 static char keyTable[KT_numKeys+1];
40 static int dropModeEnable = 0;
41 static char *robotProg;
43 ExtFunc void MapKeys(char *newKeys)
49 /* XXX assumptions about ASCII encoding here */
50 for (i = k = 0; newKeys[i] && k < KT_numKeys; i++,k++) {
51 if (newKeys[i] == '^' && newKeys[i+1])
52 keyTable[k] = toupper(newKeys[++i]) - ('A' - 1);
54 keyTable[k] = newKeys[i];
56 memset(used, 0, sizeof(used));
57 for (k = 0; k < KT_numKeys; k++) {
58 ch = (unsigned char) keyTable[k];
60 if (iscntrl(ch) && ch < ' ')
61 sprintf(scratch, "Ctrl-%c", ch + ('A' - 1));
63 sprintf(scratch, "\"%c\"", ch);
65 sprintf(scratch, "0x%X", ch);
67 fprintf(stderr, "Duplicate key mappings:\n");
69 fprintf(stderr, " %s mapped to both %s and %s\n",
70 scratch, keyNames[used[ch]-1], keyNames[k]);
78 ExtFunc int StartNewPiece(int scr, Shape *shape)
81 curShape[scr] = nextShape[scr];
82 nextShape[scr] = shape;
85 curShape[scr] = shape;
86 curY[scr] = boardVisible[scr] + 4;
87 curX[scr] = boardWidth[scr] / 2;
88 while (!ShapeVisible(curShape[scr], scr, curY[scr], curX[scr]))
90 if (!ShapeFits(curShape[scr], scr, curY[scr], curX[scr]))
92 PlotShape(curShape[scr], scr, curY[scr], curX[scr], 1);
96 ExtFunc void OneGame(int scr, int scr2)
99 int linesCleared, changed = 0;
100 int totalDrops = 0, totalLines = 0, totalAdds = 0;
101 int spied = 0, spying = 0, dropMode = 0;
102 int oldPaused = 0, paused = 0, pausedByMe = 0, pausedByThem = 0;
108 speed = stepDownInterval;
115 UpdateOpponentDisplay();
118 SetITimer(speed, speed);
120 RobotCmd(0, "GameType %s\n", gameNames[game]);
121 RobotCmd(0, "BoardSize 0 %d %d\n",
122 boardVisible[scr], boardWidth[scr]);
124 RobotCmd(0, "BoardSize 1 %d %d\n",
125 boardVisible[scr2], boardWidth[scr2]);
126 RobotCmd(0, "Opponent 1 %s %s\n", opponentName, opponentHost);
127 if (opponentFlags & SCF_usingRobot)
128 RobotCmd(0, "OpponentFlag 1 robot\n");
129 if (opponentFlags & SCF_fairRobot)
130 RobotCmd(0, "OpponentFlag 1 fairRobot\n");
132 RobotCmd(0, "TickLength %.3f\n", speed / 1.0e6);
133 RobotCmd(0, "BeginGame\n");
136 nextShape[scr] = ChooseOption(stdOptions);
137 while (StartNewPiece(scr, ChooseOption(stdOptions))) {
138 ShowScore(scr, totalDrops, totalLines, totalAdds);
139 if (robotEnable && !fairRobot)
140 RobotCmd(1, "NewPiece %d\n", ++pieceCount);
145 shapeNum = ShapeToNetNum(curShape[scr]);
146 data[0] = hton2(shapeNum);
147 SendPacket(scr, NP_newPiece, sizeof(data), data);
150 changed = RefreshBoard(scr) || changed;
152 changed = RefreshBoard(scr2) || changed;
158 switch (WaitMyEvent(&event, EM_any)) {
160 if (!MovePiece(scr, -1, 0))
163 SendPacket(scr, NP_down, 0, NULL);
166 p = strchr(keyTable, tolower(event.u.key));
169 RobotCmd(1, "UserKey %d %s\n",
170 (int)(unsigned char)event.u.key,
171 p ? keyNames[key] : "?");
177 if (paused && (key != KT_pause) && (key != KT_redraw))
181 if (MovePiece(scr, 0, -1) && spied)
182 SendPacket(scr, NP_left, 0, NULL);
185 if (MovePiece(scr, 0, 1) && spied)
186 SendPacket(scr, NP_right, 0, NULL);
189 if (RotatePiece(scr, 0) && spied)
190 SendPacket(scr, NP_rotleft, 0, NULL);
193 if (RotatePiece(scr, 1) && spied)
194 SendPacket(scr, NP_rotright, 0, NULL);
197 if (MovePiece(scr, -1, 0) && spied)
198 SendPacket(scr, NP_down, 0, NULL);
199 SetITimer(speed, speed);
202 spying = (!spying) && (scr2 >= 0);
205 if (DropPiece(scr) > 0) {
207 SendPacket(scr, NP_drop, 0, NULL);
208 if (dropModeEnable == 2)
209 SetITimer(speed, 1); //instantdrop
211 SetITimer(speed, speed);
213 dropMode = dropModeEnable;
216 pausedByMe = !pausedByMe;
217 if (game == GT_classicTwo) {
220 data[0] = hton2(pausedByMe);
221 SendPacket(scr, NP_pause, sizeof(data), data);
223 paused = pausedByMe || pausedByThem;
225 RobotCmd(1, "Pause %d %d\n", pausedByMe,
227 ShowPause(pausedByMe, pausedByThem);
231 if (game != GT_onePlayer)
234 SetITimer(speed, SetITimer(0, 0));
239 ScheduleFullRedraw();
244 if (dropMode && DropPiece(scr) > 0) {
246 SendPacket(scr, NP_drop, 0, NULL);
247 SetITimer(speed, speed);
254 cmd = event.u.robot.data;
255 if ((p = strchr(cmd, ' ')))
258 p = cmd + strlen(cmd);
259 for (key = 0; keyNames[key]; ++key)
260 if (!strcmp(keyNames[key], cmd) &&
261 (fairRobot || (1 == sscanf(p, "%d", &num) &&
264 if (!strcmp(cmd, "Message")) {
271 switch(event.u.net.type) {
277 memcpy(data, event.u.net.data, sizeof(data[0]));
278 column = Random(0, boardWidth[scr]);
279 data[1] = hton2(column);
280 InsertJunk(scr, ntoh2(data[0]), column);
282 SendPacket(scr, NP_insertJunk, sizeof(data), data);
283 // Message("Opponent added %d lines");
292 memcpy(data, event.u.net.data, sizeof(data));
293 shapeNum = ntoh2(data[0]);
294 StartNewPiece(scr2, NetNumToShape(shapeNum));
298 MovePiece(scr2, -1, 0);
301 MovePiece(scr2, 0, -1);
304 MovePiece(scr2, 0, 1);
307 RotatePiece(scr2, 0);
310 RotatePiece(scr2, 1);
316 ClearFullLines(scr2);
322 memcpy(data, event.u.net.data, sizeof(data));
323 InsertJunk(scr2, ntoh2(data[0]), ntoh2(data[1]));
330 memcpy(data, event.u.net.data, sizeof(data));
331 pausedByThem = ntoh2(data[0]);
332 paused = pausedByMe || pausedByThem;
334 RobotCmd(1, "Pause %d %d\n", pausedByMe,
336 ShowPause(pausedByMe, pausedByThem);
350 if (paused != oldPaused) {
352 pauseTimeLeft = SetITimer(0, 0);
354 SetITimer(speed, pauseTimeLeft);
362 if ((linesCleared = ClearFullLines(scr)) > 0) {
363 totalLines += linesCleared;
364 totalAdds += linesCleared - (linesCleared < 4);
366 if (linesCleared > 0 && spied)
367 SendPacket(scr, NP_clear, 0, NULL);
368 if (game == GT_classicTwo && linesCleared > 1) {
372 junkLines = linesCleared - (linesCleared < 4);
373 data[0] = hton2(junkLines);
374 SendPacket(scr, NP_giveJunk, sizeof(data), data);
381 ExtFunc int main(int argc, char **argv)
383 int initConn = 0, waitConn = 0, ch;
384 char *hostStr = NULL, *portStr = NULL;
386 standoutEnable = colorEnable = 1;
387 stepDownInterval = DEFAULT_INTERVAL;
388 MapKeys(DEFAULT_KEYS);
389 while ((ch = getopt(argc, argv, "hHRs:r:Fk:c:wodDSCp:i:")) != -1)
402 stepDownInterval = atof(optarg) * 1e6;
405 initSeed = atoi(optarg);
406 myFlags |= SCF_setSeed;
411 myFlags |= SCF_usingRobot;
415 myFlags |= SCF_fairRobot;
445 if (optind < argc || (initConn && waitConn)) {
449 if (fairRobot && !robotEnable)
450 fatal("You can't use the -F option without the -r option");
453 InitRobot(robotProg);
456 if (initConn || waitConn) {
459 game = GT_classicTwo;
461 InitiateConnection(hostStr, portStr);
463 WaitForConnection(portStr);
468 data[0] = hton4(MAJOR_VERSION);
469 data[1] = hton4(PROTOCOL_VERSION);
470 SendPacket(0, NP_version, sizeof(data), data);
471 if (WaitMyEvent(&event, EM_net) != E_net)
472 fatal("Network negotiation failed");
473 memcpy(data, event.u.net.data, sizeof(data));
474 major = ntoh4(data[0]);
475 protocolVersion = ntoh4(data[1]);
476 if (event.u.net.type != NP_version || major < MAJOR_VERSION)
477 fatal("Your opponent is using an old, incompatible version\n"
478 "of Netris. They should get the latest version.");
479 if (major > MAJOR_VERSION)
480 fatal("Your opponent is using an newer, incompatible version\n"
481 "of Netris. Get the latest version.");
482 if (protocolVersion > PROTOCOL_VERSION)
483 protocolVersion = PROTOCOL_VERSION;
485 if (protocolVersion < 3 && stepDownInterval != DEFAULT_INTERVAL)
486 fatal("Your opponent's version of Netris predates the -i option.\n"
487 "For fairness, you shouldn't use the -i option either.");
493 if (protocolVersion >= 3)
496 len = sizeof(netint4[2]);
497 if ((myFlags & SCF_setSeed))
503 data[0] = hton4(myFlags);
504 data[1] = hton4(seed);
505 data[2] = hton4(stepDownInterval);
506 SendPacket(0, NP_startConn, len, data);
507 if (WaitMyEvent(&event, EM_net) != E_net ||
508 event.u.net.type != NP_startConn)
509 fatal("Network negotiation failed");
510 memcpy(data, event.u.net.data, len);
511 opponentFlags = ntoh4(data[0]);
512 seed = ntoh4(data[1]);
514 if ((opponentFlags & SCF_setSeed) != (myFlags & SCF_setSeed))
515 fatal("If one player sets the random number seed, "
517 if ((myFlags & SCF_setSeed) && seed != initSeed)
518 fatal("Both players have set the random number seed, "
519 "and they are unequal.");
520 if (protocolVersion >= 3 && stepDownInterval != ntoh4(data[2]))
521 fatal("Your opponent is using a different step-down "
522 "interval (-i).\nYou must both use the same one.");
530 userName = getenv("LOGNAME");
531 if (!userName || !userName[0])
532 userName = getenv("USER");
533 if (!userName || !userName[0])
534 strcpy(userName, "???");
535 len = strlen(userName)+1;
536 if (len > sizeof(opponentName))
537 len = sizeof(opponentName);
538 SendPacket(0, NP_userName, len, userName);
539 if (WaitMyEvent(&event, EM_net) != E_net ||
540 event.u.net.type != NP_userName)
541 fatal("Network negotiation failed");
542 strncpy(opponentName, event.u.net.data,
543 sizeof(opponentName)-1);
544 opponentName[sizeof(opponentName)-1] = 0;
545 for (i = 0; opponentName[i]; ++i)
546 if (!isprint(opponentName[i]))
547 opponentName[i] = '?';
548 for (i = 0; opponentHost[i]; ++i)
549 if (!isprint(opponentHost[i]))
550 opponentHost[i] = '?';