1 /* Tetrinet for Linux, by Andrew Church <achurch@achurch.org>
2  * This program is public domain.
3  *
4  * Tetrinet main program.
5  */
6 
7 /*************************************************************************/
8 
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <time.h>
13 #include <errno.h>
14 #include "tetrinet.h"
15 #include "io.h"
16 #include "server.h"
17 #include "sockets.h"
18 #include "tetris.h"
19 #include "version.h"
20 
21 /*************************************************************************/
22 
23 int fancy = 0;		/* Fancy TTY graphics? */
24 int log = 0;		/* Log network traffic to file? */
25 char *logname;		/* Log filename */
26 int windows_mode = 0;	/* Try to be just like the Windows version? */
27 int noslide = 0;	/* Disallow piece sliding? */
28 int tetrifast = 0;	/* TetriFast mode? */
29 int cast_shadow = 1;	/* Make pieces cast shadow? */
30 
31 int my_playernum = -1;	/* What player number are we? */
32 char *my_nick;		/* And what is our nick? */
33 WinInfo winlist[MAXWINLIST];  /* Winners' list from server */
34 int server_sock;	/* Socket for server communication */
35 int dispmode;		/* Current display mode */
36 char *players[6];	/* Player names (NULL for no such player) */
37 char *teams[6];		/* Team names (NULL for not on a team) */
38 int playing_game;	/* Are we currently playing a game? */
39 int not_playing_game;	/* Are we currently watching people play a game? */
40 int game_paused;	/* Is the game currently paused? */
41 
42 Interface *io;		/* Input/output routines */
43 
44 /*************************************************************************/
45 /*************************************************************************/
46 
47 #ifndef SERVER_ONLY
48 
49 /*************************************************************************/
50 
51 /* Parse a line from the server.  Destroys the buffer it's given as a side
52  * effect.
53  */
54 
parse(char * buf)55 void parse(char *buf)
56 {
57     char *cmd, *s, *t;
58 
59     cmd = strtok(buf, " ");
60 
61     if (!cmd) {
62 	return;
63 
64     } else if (strcmp(cmd, "noconnecting") == 0) {
65 	s = strtok(NULL, "");
66 	if (!s)
67 	    s = "Unknown";
68 	/* XXX not to stderr, please! -- we need to stay running w/o server */
69 	fprintf(stderr, "Server error: %s\n", s);
70 	exit(1);
71 
72     } else if (strcmp(cmd, "winlist") == 0) {
73 	int i = 0;
74 
75 	while (i < MAXWINLIST && (s = strtok(NULL, " "))) {
76 	    t = strchr(s, ';');
77 	    if (!t)
78 		break;
79 	    *t++ = 0;
80 	    if (*s == 't')
81 		winlist[i].team = 1;
82 	    else
83 		winlist[i].team = 0;
84 	    s++;
85 	    strncpy(winlist[i].name, s, sizeof(winlist[i].name)-1);
86 	    winlist[i].name[sizeof(winlist[i].name)-1] = 0;
87 	    winlist[i].points = atoi(t);
88 	    if ((t = strchr(t, ';')) != NULL)
89 		winlist[i].games = atoi(t+1);
90 	    i++;
91 	}
92 	if (i < MAXWINLIST)
93 	    winlist[i].name[0] = 0;
94 	if (dispmode == MODE_WINLIST)
95 	    io->setup_winlist();
96 
97     } else if (strcmp(cmd, tetrifast ? ")#)(!@(*3" : "playernum") == 0) {
98 	if ((s = strtok(NULL, " ")))
99 	    my_playernum = atoi(s);
100 	/* Note: players[my_playernum-1] is set in init() */
101 	/* But that doesn't work when joining other channel. */
102 	players[my_playernum-1] = strdup(my_nick);
103 
104     } else if (strcmp(cmd, "playerjoin") == 0) {
105 	int player;
106 	char buf[1024];
107 
108 	s = strtok(NULL, " ");
109 	t = strtok(NULL, "");
110 	if (!s || !t)
111 	    return;
112 	player = atoi(s)-1;
113 	if (player < 0 || player > 5)
114 	    return;
115 	players[player] = strdup(t);
116 	if (teams[player]) {
117 	    free(teams[player]);
118 	    teams[player] = NULL;
119 	}
120 	snprintf(buf, sizeof(buf), "*** %s is Now Playing", t);
121 	io->draw_text(BUFFER_PLINE, buf);
122 	if (dispmode == MODE_FIELDS)
123 	    io->setup_fields();
124 
125     } else if (strcmp(cmd, "playerleave") == 0) {
126 	int player;
127 	char buf[1024];
128 
129 	s = strtok(NULL, " ");
130 	if (!s)
131 	    return;
132 	player = atoi(s)-1;
133 	if (player < 0 || player > 5 || !players[player])
134 	    return;
135 	snprintf(buf, sizeof(buf), "*** %s has Left", players[player]);
136 	io->draw_text(BUFFER_PLINE, buf);
137 	free(players[player]);
138 	players[player] = NULL;
139 	if (dispmode == MODE_FIELDS)
140 	    io->setup_fields();
141 
142     } else if (strcmp(cmd, "team") == 0) {
143 	int player;
144 	char buf[1024];
145 
146 	s = strtok(NULL, " ");
147 	t = strtok(NULL, "");
148 	if (!s)
149 	    return;
150 	player = atoi(s)-1;
151 	if (player < 0 || player > 5 || !players[player])
152 	    return;
153 	if (teams[player])
154 	    free(teams[player]);
155 	if (t)
156 	    teams[player] = strdup(t);
157 	else
158 	    teams[player] = NULL;
159 	if (t)
160 	    snprintf(buf, sizeof(buf), "*** %s is Now on Team %s", players[player], t);
161 	else
162 	    snprintf(buf, sizeof(buf), "*** %s is Now Alone", players[player]);
163 	io->draw_text(BUFFER_PLINE, buf);
164 
165     } else if (strcmp(cmd, "pline") == 0) {
166 	int playernum;
167 	char buf[1024], *name;
168 
169 	s = strtok(NULL, " ");
170 	t = strtok(NULL, "");
171 	if (!s)
172 	    return;
173 	if (!t)
174 	    t = "";
175 	playernum = atoi(s)-1;
176 	if (playernum == -1) {
177 	    name = "Server";
178 	} else {
179 	    if (playernum < 0 || playernum > 5 || !players[playernum])
180 		return;
181 	    name = players[playernum];
182 	}
183 	snprintf(buf, sizeof(buf), "<%s> %s", name, t);
184 	io->draw_text(BUFFER_PLINE, buf);
185 
186     } else if (strcmp(cmd, "plineact") == 0) {
187 	int playernum;
188 	char buf[1024], *name;
189 
190 	s = strtok(NULL, " ");
191 	t = strtok(NULL, "");
192 	if (!s)
193 	    return;
194 	if (!t)
195 	    t = "";
196 	playernum = atoi(s)-1;
197 	if (playernum == -1) {
198 	    name = "Server";
199 	} else {
200 	    if (playernum < 0 || playernum > 5 || !players[playernum])
201 		return;
202 	    name = players[playernum];
203 	}
204 	snprintf(buf, sizeof(buf), "* %s %s", name, t);
205 	io->draw_text(BUFFER_PLINE, buf);
206 
207     } else if (strcmp(cmd, tetrifast ? "*******" : "newgame") == 0) {
208 	int i;
209 
210 	if ((s = strtok(NULL, " ")))
211 	    /* stack height */;
212 	if ((s = strtok(NULL, " ")))
213 	    initial_level = atoi(s);
214 	if ((s = strtok(NULL, " ")))
215 	    lines_per_level = atoi(s);
216 	if ((s = strtok(NULL, " ")))
217 	    level_inc = atoi(s);
218 	if ((s = strtok(NULL, " ")))
219 	    special_lines = atoi(s);
220 	if ((s = strtok(NULL, " ")))
221 	    special_count = atoi(s);
222 	if ((s = strtok(NULL, " "))) {
223 	    special_capacity = atoi(s);
224 	    if (special_capacity > MAX_SPECIALS)
225 		special_capacity = MAX_SPECIALS;
226 	}
227 	if ((s = strtok(NULL, " "))) {
228 	    memset(piecefreq, 0, sizeof(piecefreq));
229 	    while (*s) {
230 		i = *s - '1';
231 		if (i >= 0 && i < 7)
232 		    piecefreq[i]++;
233 		s++;
234 	    }
235 	}
236 	if ((s = strtok(NULL, " "))) {
237 	    memset(specialfreq, 0, sizeof(specialfreq));
238 	    while (*s) {
239 		i = *s - '1';
240 		if (i >= 0 && i < 9)
241 		    specialfreq[i]++;
242 		s++;
243 	    }
244 	}
245 	if ((s = strtok(NULL, " ")))
246 	    level_average = atoi(s);
247 	if ((s = strtok(NULL, " ")))
248 	    old_mode = atoi(s);
249 	lines = 0;
250 	for (i = 0; i < 6; i++)
251 	    levels[i] = initial_level;
252 	memset(&fields[my_playernum-1], 0, sizeof(Field));
253 	specials[0] = -1;
254 	io->clear_text(BUFFER_GMSG);
255 	io->clear_text(BUFFER_ATTDEF);
256 	new_game();
257 	playing_game = 1;
258 	game_paused = 0;
259 	io->draw_text(BUFFER_PLINE, "*** The Game Has Started");
260 
261     } else if (strcmp(cmd, "ingame") == 0) {
262 	/* Sent when a player connects in the middle of a game */
263 	int x, y;
264 	char buf[1024], *s;
265 
266 	s = buf + sprintf(buf, "f %d ", my_playernum);
267 	for (y = 0; y < FIELD_HEIGHT; y++) {
268 	    for (x = 0; x < FIELD_WIDTH; x++) {
269 		fields[my_playernum-1][y][x] = rand()%5 + 1;
270 		*s++ = '0' + fields[my_playernum-1][y][x];
271 	    }
272 	}
273 	*s = 0;
274 	sputs(buf, server_sock);
275 	playing_game = 0;
276 	not_playing_game = 1;
277 
278     } else if (strcmp(cmd, "pause") == 0) {
279 	if ((s = strtok(NULL, " ")))
280 	    game_paused = atoi(s);
281 	if (game_paused) {
282 	    io->draw_text(BUFFER_PLINE, "*** The Game Has Been Paused");
283 	    io->draw_text(BUFFER_GMSG, "*** The Game Has Been Paused");
284 	} else {
285 	    io->draw_text(BUFFER_PLINE, "*** The Game Has Been Unpaused");
286 	    io->draw_text(BUFFER_GMSG, "*** The Game Has Been Unpaused");
287 	}
288 
289     } else if (strcmp(cmd, "endgame") == 0) {
290 	playing_game = 0;
291 	not_playing_game = 0;
292 	memset(fields, 0, sizeof(fields));
293 	specials[0] = -1;
294 	io->clear_text(BUFFER_ATTDEF);
295 	io->draw_text(BUFFER_PLINE, "*** The Game Has Ended");
296 	if (dispmode == MODE_FIELDS) {
297 	    int i;
298 	    io->draw_own_field();
299 	    for (i = 1; i <= 6; i++) {
300 		if (i != my_playernum)
301 		    io->draw_other_field(i);
302 	    }
303 	}
304 
305     } else if (strcmp(cmd, "playerwon") == 0) {
306 	/* Syntax: playerwon # -- sent when all but one player lose */
307 
308     } else if (strcmp(cmd, "playerlost") == 0) {
309 	/* Syntax: playerlost # -- sent after playerleave on disconnect
310 	 *     during a game, or when a player loses (sent by the losing
311 	 *     player and from the server to all other players */
312 
313     } else if (strcmp(cmd, "f") == 0) {   /* field */
314 	int player, x, y, tile;
315 
316 	/* This looks confusing, but what it means is, ignore this message
317 	 * if a game isn't going on. */
318 	if (!playing_game && !not_playing_game)
319 	    return;
320 	if (!(s = strtok(NULL, " ")))
321 	    return;
322 	player = atoi(s);
323 	player--;
324 	if (!(s = strtok(NULL, "")))
325 	    return;
326 	if (*s >= '0') {
327 	    /* Set field directly */
328 	    char *ptr = (char *) fields[player];
329 	    while (*s) {
330 		if (*s <= '5')
331 		    *ptr++ = (*s++) - '0';
332 		else switch (*s++) {
333 		    case 'a': *ptr++ = 6 + SPECIAL_A; break;
334 		    case 'b': *ptr++ = 6 + SPECIAL_B; break;
335 		    case 'c': *ptr++ = 6 + SPECIAL_C; break;
336 		    case 'g': *ptr++ = 6 + SPECIAL_G; break;
337 		    case 'n': *ptr++ = 6 + SPECIAL_N; break;
338 		    case 'o': *ptr++ = 6 + SPECIAL_O; break;
339 		    case 'q': *ptr++ = 6 + SPECIAL_Q; break;
340 		    case 'r': *ptr++ = 6 + SPECIAL_R; break;
341 		    case 's': *ptr++ = 6 + SPECIAL_S; break;
342 		}
343 	    }
344 	} else {
345 	    /* Set specific locations on field */
346 	    tile = 0;
347 	    while (*s) {
348 		if (*s < '0') {
349 		    tile = *s - '!';
350 		} else {
351 		    x = *s - '3';
352 		    y = (*++s) - '3';
353 		    fields[player][y][x] = tile;
354 		}
355 		s++;
356 	    }
357 	}
358 	if (player == my_playernum-1)
359 	    io->draw_own_field();
360 	else
361 	    io->draw_other_field(player+1);
362     } else if (strcmp(cmd, "lvl") == 0) {
363 	int player;
364 
365 	if (!(s = strtok(NULL, " ")))
366 	    return;
367 	player = atoi(s)-1;
368 	if (!(s = strtok(NULL, "")))
369 	    return;
370 	levels[player] = atoi(s);
371 
372     } else if (strcmp(cmd, "sb") == 0) {
373 	int from, to;
374 	char *type;
375 
376 	if (!(s = strtok(NULL, " ")))
377 	    return;
378 	to = atoi(s);
379 	if (!(type = strtok(NULL, " ")))
380 	    return;
381 	if (!(s = strtok(NULL, " ")))
382 	    return;
383 	from = atoi(s);
384 	do_special(type, from, to);
385 
386     } else if (strcmp(cmd, "gmsg") == 0) {
387 	if (!(s = strtok(NULL, "")))
388 	    return;
389 	io->draw_text(BUFFER_GMSG, s);
390 
391     }
392 }
393 
394 /*************************************************************************/
395 /*************************************************************************/
396 
397 static char partyline_buffer[512];
398 static int partyline_pos = 0;
399 
400 #define curpos	(partyline_buffer+partyline_pos)
401 
402 /*************************************************************************/
403 
404 /* Add a character to the partyline buffer. */
405 
partyline_input(int c)406 void partyline_input(int c)
407 {
408     if (partyline_pos < sizeof(partyline_buffer) - 1) {
409 	memmove(curpos+1, curpos, strlen(curpos)+1);
410 	partyline_buffer[partyline_pos++] = c;
411 	io->draw_partyline_input(partyline_buffer, partyline_pos);
412     }
413 }
414 
415 /*************************************************************************/
416 
417 /* Delete the current character from the partyline buffer. */
418 
partyline_delete(void)419 void partyline_delete(void)
420 {
421     if (partyline_buffer[partyline_pos]) {
422 	memmove(curpos, curpos+1, strlen(curpos)-1+1);
423 	io->draw_partyline_input(partyline_buffer, partyline_pos);
424     }
425 }
426 
427 /*************************************************************************/
428 
429 /* Backspace a character from the partyline buffer. */
430 
partyline_backspace(void)431 void partyline_backspace(void)
432 {
433     if (partyline_pos > 0) {
434 	partyline_pos--;
435 	partyline_delete();
436     }
437 }
438 
439 /*************************************************************************/
440 
441 /* Kill the entire partyline input buffer. */
442 
partyline_kill(void)443 void partyline_kill(void)
444 {
445     partyline_pos = 0;
446     *partyline_buffer = 0;
447     io->draw_partyline_input(partyline_buffer, partyline_pos);
448 }
449 
450 /*************************************************************************/
451 
452 /* Move around the input buffer.  Sign indicates direction; absolute value
453  * of 1 means one character, 2 means the whole line.
454  */
455 
partyline_move(int how)456 void partyline_move(int how)
457 {
458     if (how == -2) {
459 	partyline_pos = 0;
460 	io->draw_partyline_input(partyline_buffer, partyline_pos);
461     } else if (how == -1 && partyline_pos > 0) {
462 	partyline_pos--;
463 	io->draw_partyline_input(partyline_buffer, partyline_pos);
464     } else if (how == 1 && partyline_buffer[partyline_pos]) {
465 	partyline_pos++;
466 	io->draw_partyline_input(partyline_buffer, partyline_pos);
467     } else if (how == 2) {
468 	partyline_pos = strlen(partyline_buffer);
469 	io->draw_partyline_input(partyline_buffer, partyline_pos);
470     }
471 }
472 
473 /*************************************************************************/
474 
475 /* Send the input line to the server. */
476 
partyline_enter(void)477 void partyline_enter(void)
478 {
479     char buf[1024];
480 
481     if (*partyline_buffer) {
482 	if (strncasecmp(partyline_buffer, "/me ", 4) == 0) {
483 	    sockprintf(server_sock, "plineact %d %s", my_playernum, partyline_buffer+4);
484 	    snprintf(buf, sizeof(buf), "* %s %s", players[my_playernum-1], partyline_buffer+4);
485 	    io->draw_text(BUFFER_PLINE, buf);
486 	} else if (strcasecmp(partyline_buffer, "/start") == 0) {
487 	    sockprintf(server_sock, "startgame 1 %d", my_playernum);
488 	} else if (strcasecmp(partyline_buffer, "/end") == 0) {
489 	    sockprintf(server_sock, "startgame 0 %d", my_playernum);
490 	} else if (strcasecmp(partyline_buffer, "/pause") == 0) {
491 	    sockprintf(server_sock, "pause 1 %d", my_playernum);
492 	} else if (strcasecmp(partyline_buffer, "/unpause") == 0) {
493 	    sockprintf(server_sock, "pause 0 %d", my_playernum);
494 	} else if (strncasecmp(partyline_buffer, "/team", 5) == 0) {
495 	    if (strlen(partyline_buffer) == 5)
496 		strcpy(partyline_buffer+5, " ");  /* make it "/team " */
497 	    sockprintf(server_sock, "team %d %s", my_playernum, partyline_buffer+6);
498 	    if (partyline_buffer[6]) {
499 		if (teams[my_playernum-1])
500 		    free(teams[my_playernum-1]);
501 		teams[my_playernum-1] = strdup(partyline_buffer+6);
502 		snprintf(buf, sizeof(buf), "*** %s is Now on Team %s", players[my_playernum-1], partyline_buffer+6);
503 		io->draw_text(BUFFER_PLINE, buf);
504 	    } else {
505 		if (teams[my_playernum-1])
506 		    free(teams[my_playernum-1]);
507 		teams[my_playernum-1] = NULL;
508 		snprintf(buf, sizeof(buf), "*** %s is Now Alone", players[my_playernum-1]);
509 		io->draw_text(BUFFER_PLINE, buf);
510 	    }
511 	} else {
512 	    sockprintf(server_sock, "pline %d %s", my_playernum, partyline_buffer);
513 	    if (*partyline_buffer != '/'
514 		|| partyline_buffer[1] == 0 || partyline_buffer[1] == ' ') {
515 		/* We do not show server-side commands. */
516 		snprintf(buf, sizeof(buf), "<%s> %s", players[my_playernum-1], partyline_buffer);
517 		io->draw_text(BUFFER_PLINE, buf);
518 	    }
519 	}
520 	partyline_pos = 0;
521 	*partyline_buffer = 0;
522 	io->draw_partyline_input(partyline_buffer, partyline_pos);
523     }
524 }
525 
526 #undef curpos
527 
528 /*************************************************************************/
529 /*************************************************************************/
530 
help()531 void help()
532 {
533     fprintf(stderr,
534 "Tetrinet " VERSION " - Text-mode tetrinet client\n"
535 "\n"
536 "Usage: tetrinet [OPTION]... NICK SERVER\n"
537 "\n"
538 "Options (see README for details):\n"
539 "  -fancy       Use \"fancy\" TTY graphics.\n"
540 "  -fast        Connect to the server in the tetrifast mode.\n"
541 "  -log <file>  Log network traffic to the given file.\n"
542 "  -noshadow    Do not make the pieces cast shadow.\n"
543 "  -noslide     Do not allow pieces to \"slide\" after being dropped\n"
544 "               with the spacebar.\n"
545 "  -server      Start the server instead of the client.\n"
546 "  -shadow      Make the pieces cast shadow. Can speed up gameplay\n"
547 "               considerably, but it can be considered as cheating by\n"
548 "               some people since some other tetrinet clients lack this.\n"
549 "  -slide       Opposite of -noslide; allows pieces to \"slide\" after\n"
550 "               being dropped.  If both -slide and -noslide are given,\n"
551 "               -slide takes precedence.\n"
552 "  -windows     Behave as much like the Windows version of Tetrinet as\n"
553 "               possible. Implies -noslide and -noshadow.\n"
554 	   );
555 }
556 
init(int ac,char ** av)557 int init(int ac, char **av)
558 {
559     int i;
560     char *nick = NULL, *server = NULL;
561     char buf[1024];
562     char nickmsg[1024];
563     unsigned char ip[4];
564     char iphashbuf[32];
565     int len;
566 #ifdef BUILTIN_SERVER
567     int start_server = 0;   /* Start the server? (-server) */
568 #endif
569     int slide = 0;	    /* Do we definitely want to slide? (-slide) */
570 
571 
572     /* If there's a DISPLAY variable set in the environment, default to
573      * Xwindows I/O, else default to terminal I/O. */
574     /* if (getenv("DISPLAY"))
575 	io = &xwin_interface;
576     else
577 	io = &tty_interface; */
578     io=&tty_interface;  /* because Xwin isn't done yet */
579 
580     srand(time(NULL));
581     init_shapes();
582 
583     for (i = 1; i < ac; i++) {
584 	if (*av[i] == '-') {
585 #ifdef BUILTIN_SERVER
586 	    if (strcmp(av[i], "-server") == 0) {
587 		start_server = 1;
588 	    } else
589 #endif
590 	    if (strcmp(av[i], "-fancy") == 0) {
591 		fancy = 1;
592 	    } else if (strcmp(av[i], "-log") == 0) {
593 		log = 1;
594 		i++;
595 		if (i >= ac) {
596 		    fprintf(stderr, "Option -log requires an argument\n");
597 		    return 1;
598 		}
599 		logname = av[i];
600 	    } else if (strcmp(av[i], "-noslide") == 0) {
601 		noslide = 1;
602 	    } else if (strcmp(av[i], "-noshadow") == 0) {
603 		cast_shadow = 0;
604 	    } else if (strcmp(av[i], "-shadow") == 0) {
605 		cast_shadow = 1;
606 	    } else if (strcmp(av[i], "-slide") == 0) {
607 		slide = 1;
608 	    } else if (strcmp(av[i], "-windows") == 0) {
609 		windows_mode = 1;
610 		noslide = 1;
611 		cast_shadow = 0;
612 	    } else if (strcmp(av[i], "-fast") == 0) {
613 		tetrifast = 1;
614 	    } else {
615 		fprintf(stderr, "Unknown option %s\n", av[i]);
616 	        help();
617 		return 1;
618 	    }
619 	} else if (!nick) {
620 	    my_nick = nick = av[i];
621 	} else if (!server) {
622 	    server = av[i];
623 	} else {
624 	    help();
625 	    return 1;
626 	}
627     }
628     if (slide)
629 	noslide = 0;
630 #ifdef BUILTIN_SERVER
631     if (start_server)
632 	exit(server_main());
633 #endif
634     if (!server) {
635 	help();
636 	return 1;
637     }
638     if (strlen(nick) > 63)  /* put a reasonable limit on nick length */
639 	nick[63] = 0;
640 
641     if ((server_sock = conn(server, 31457, ip)) < 0) {
642 	fprintf(stderr, "Couldn't connect to server %s: %s\n",
643 		server, strerror(errno));
644 	return 1;
645     }
646     sprintf(nickmsg, "tetri%s %s 1.13", tetrifast ? "faster" : "sstart", nick);
647     sprintf(iphashbuf, "%d", ip[0]*54 + ip[1]*41 + ip[2]*29 + ip[3]*17);
648     /* buf[0] does not need to be initialized for this algorithm */
649     len = strlen(nickmsg);
650     for (i = 0; i < len; i++)
651 	buf[i+1] = (((buf[i]&0xFF) + (nickmsg[i]&0xFF)) % 255) ^ iphashbuf[i % strlen(iphashbuf)];
652     len++;
653     for (i = 0; i < len; i++)
654 	sprintf(nickmsg+i*2, "%02X", buf[i] & 0xFF);
655     sputs(nickmsg, server_sock);
656 
657     do {
658 	if (!sgets(buf, sizeof(buf), server_sock)) {
659 	    fprintf(stderr, "Server %s closed connection\n", server);
660 	    disconn(server_sock);
661 	    return 1;
662 	}
663 	parse(buf);
664     } while (my_playernum < 0);
665     sockprintf(server_sock, "team %d ", my_playernum);
666 
667     players[my_playernum-1] = strdup(nick);
668     dispmode = MODE_PARTYLINE;
669     io->screen_setup();
670     io->setup_partyline();
671 
672     return 0;
673 }
674 
675 /*************************************************************************/
676 
main(int ac,char ** av)677 int main(int ac, char **av)
678 {
679     int i;
680 
681     if ((i = init(ac, av)) != 0)
682 	return i;
683 
684     for (;;) {
685 	int timeout;
686 	if (playing_game && !game_paused)
687 	    timeout = tetris_timeout();
688 	else
689 	    timeout = -1;
690 	i = io->wait_for_input(timeout);
691 	if (i == -1) {
692 	    char buf[1024];
693 	    if (sgets(buf, sizeof(buf), server_sock))
694 		parse(buf);
695 	    else {
696 		io->draw_text(BUFFER_PLINE, "*** Disconnected from Server");
697 		break;
698 	    }
699 	} else if (i == -2) {
700 	    tetris_timeout_action();
701 	} else if (i == 12) {  /* Ctrl-L */
702 	    io->screen_redraw();
703 	} else if (i == K_F10) {
704 	    break;  /* out of main loop */
705 	} else if (i == K_F1) {
706 	    if (dispmode != MODE_FIELDS) {
707 		dispmode = MODE_FIELDS;
708 		io->setup_fields();
709 	    }
710 	} else if (i == K_F2) {
711 	    if (dispmode != MODE_PARTYLINE) {
712 		dispmode = MODE_PARTYLINE;
713 		io->setup_partyline();
714 	    }
715 	} else if (i == K_F3) {
716 	    if (dispmode != MODE_WINLIST) {
717 		dispmode = MODE_WINLIST;
718 		io->setup_winlist();
719 	    }
720 	} else if (dispmode == MODE_FIELDS) {
721 	    tetris_input(i);
722 	} else if (dispmode == MODE_PARTYLINE) {
723 	    if (i == 8 || i == 127)   /* Backspace or Delete */
724 		partyline_backspace();
725 	    else if (i == 4)    /* Ctrl-D */
726 		partyline_delete();
727 	    else if (i == 21)   /* Ctrl-U */
728 		partyline_kill();
729 	    else if (i == '\r' || i == '\n')
730 		partyline_enter();
731 	    else if (i == K_LEFT)
732 		partyline_move(-1);
733 	    else if (i == K_RIGHT)
734 		partyline_move(1);
735 	    else if (i == 1)    /* Ctrl-A */
736 		partyline_move(-2);
737 	    else if (i == 5)    /* Ctrl-E */
738 		partyline_move(2);
739 	    else if (i >= 1 && i <= 0xFF)
740 		partyline_input(i);
741 	}
742     }
743 
744     disconn(server_sock);
745     return 0;
746 }
747 
748 /*************************************************************************/
749 
750 #endif	/* !SERVER_ONLY */
751 
752 /*************************************************************************/
753