1 /* Tetrinet for Linux, by Andrew Church <achurch@achurch.org>
2 * This program is public domain.
4 * Tetrinet main program.
7 /*************************************************************************/
18 /*************************************************************************/
20 int fancy
= 0; /* Fancy TTY graphics? */
21 int log
= 0; /* Log network traffic to file? */
22 char *logname
; /* Log filename */
23 int windows_mode
= 0; /* Try to be just like the Windows version? */
24 int noslide
= 0; /* Disallow piece sliding? */
26 int my_playernum
= -1; /* What player number are we? */
27 WinInfo winlist
[MAXWINLIST
]; /* Winners' list from server */
28 int server_sock
; /* Socket for server communication */
29 int dispmode
; /* Current display mode */
30 char *players
[6]; /* Player names (NULL for no such player) */
31 char *teams
[6]; /* Team names (NULL for not on a team) */
32 int playing_game
; /* Are we currently playing a game? */
33 int not_playing_game
; /* Are we currently watching people play a game? */
34 int game_paused
; /* Is the game currently paused? */
36 Interface
*io
; /* Input/output routines */
38 /*************************************************************************/
39 /*************************************************************************/
43 /*************************************************************************/
45 /* Parse a line from the server. Destroys the buffer it's given as a side
53 cmd
= strtok(buf
, " ");
58 } else if (strcmp(cmd
, "noconnecting") == 0) {
62 /* XXX not to stderr, please! -- we need to stay running w/o server */
63 fprintf(stderr
, "Server error: %s\n", s
);
66 } else if (strcmp(cmd
, "winlist") == 0) {
69 while (i
< MAXWINLIST
&& (s
= strtok(NULL
, " "))) {
79 strncpy(winlist
[i
].name
, s
, sizeof(winlist
[i
].name
)-1);
80 winlist
[i
].name
[sizeof(winlist
[i
].name
)] = 0;
81 winlist
[i
].points
= atoi(t
);
82 if ((t
= strchr(t
, ';')) != NULL
)
83 winlist
[i
].games
= atoi(t
+1);
87 winlist
[i
].name
[0] = 0;
88 if (dispmode
== MODE_WINLIST
)
91 } else if (strcmp(cmd
, "playernum") == 0) {
92 if (s
= strtok(NULL
, " "))
93 my_playernum
= atoi(s
);
94 /* Note: players[my_playernum-1] is set in init() */
96 } else if (strcmp(cmd
, "playerjoin") == 0) {
100 s
= strtok(NULL
, " ");
101 t
= strtok(NULL
, "");
105 if (player
< 0 || player
> 5)
107 players
[player
] = strdup(t
);
110 teams
[player
] = NULL
;
112 snprintf(buf
, sizeof(buf
), "*** %s is Now Playing", t
);
113 io
->draw_text(BUFFER_PLINE
, buf
);
114 if (dispmode
== MODE_FIELDS
)
117 } else if (strcmp(cmd
, "playerleave") == 0) {
121 s
= strtok(NULL
, " ");
125 if (player
< 0 || player
> 5 || !players
[player
])
127 snprintf(buf
, sizeof(buf
), "*** %s has Left", players
[player
]);
128 io
->draw_text(BUFFER_PLINE
, buf
);
129 free(players
[player
]);
130 players
[player
] = NULL
;
131 if (dispmode
== MODE_FIELDS
)
134 } else if (strcmp(cmd
, "team") == 0) {
138 s
= strtok(NULL
, " ");
139 t
= strtok(NULL
, "");
143 if (player
< 0 || player
> 5 || !players
[player
])
148 teams
[player
] = strdup(t
);
150 teams
[player
] = NULL
;
152 snprintf(buf
, sizeof(buf
), "*** %s is Now on Team %s", players
[player
], t
);
154 snprintf(buf
, sizeof(buf
), "*** %s is Now Alone", players
[player
]);
155 io
->draw_text(BUFFER_PLINE
, buf
);
157 } else if (strcmp(cmd
, "pline") == 0) {
159 char buf
[1024], *name
;
161 s
= strtok(NULL
, " ");
162 t
= strtok(NULL
, "");
167 playernum
= atoi(s
)-1;
168 if (playernum
== -1) {
171 if (playernum
< 0 || playernum
> 5 || !players
[playernum
])
173 name
= players
[playernum
];
175 snprintf(buf
, sizeof(buf
), "<%s> %s", name
, t
);
176 io
->draw_text(BUFFER_PLINE
, buf
);
178 } else if (strcmp(cmd
, "plineact") == 0) {
180 char buf
[1024], *name
;
182 s
= strtok(NULL
, " ");
183 t
= strtok(NULL
, "");
188 playernum
= atoi(s
)-1;
189 if (playernum
== -1) {
192 if (playernum
< 0 || playernum
> 5 || !players
[playernum
])
194 name
= players
[playernum
];
196 snprintf(buf
, sizeof(buf
), "* %s %s", name
, t
);
197 io
->draw_text(BUFFER_PLINE
, buf
);
199 } else if (strcmp(cmd
, "newgame") == 0) {
202 if (s
= strtok(NULL
, " "))
204 if (s
= strtok(NULL
, " "))
205 initial_level
= atoi(s
);
206 if (s
= strtok(NULL
, " "))
207 lines_per_level
= atoi(s
);
208 if (s
= strtok(NULL
, " "))
210 if (s
= strtok(NULL
, " "))
211 special_lines
= atoi(s
);
212 if (s
= strtok(NULL
, " "))
213 special_count
= atoi(s
);
214 if (s
= strtok(NULL
, " ")) {
215 special_capacity
= atoi(s
);
216 if (special_capacity
> MAX_SPECIALS
)
217 special_capacity
= MAX_SPECIALS
;
219 if (s
= strtok(NULL
, " ")) {
220 memset(piecefreq
, 0, sizeof(piecefreq
));
228 if (s
= strtok(NULL
, " ")) {
229 memset(specialfreq
, 0, sizeof(specialfreq
));
237 if (s
= strtok(NULL
, " "))
238 level_average
= atoi(s
);
239 if (s
= strtok(NULL
, " "))
242 for (i
= 0; i
< 6; i
++)
243 levels
[i
] = initial_level
;
244 memset(&fields
[my_playernum
-1], 0, sizeof(Field
));
246 io
->clear_text(BUFFER_GMSG
);
247 io
->clear_text(BUFFER_ATTDEF
);
251 io
->draw_text(BUFFER_PLINE
, "*** The Game Has Started");
253 } else if (strcmp(cmd
, "ingame") == 0) {
254 /* Sent when a player connects in the middle of a game */
258 s
= buf
+ sprintf(buf
, "f %d ", my_playernum
);
259 for (y
= 0; y
< FIELD_HEIGHT
; y
++) {
260 for (x
= 0; x
< FIELD_WIDTH
; x
++) {
261 fields
[my_playernum
-1][y
][x
] = rand()%5 + 1;
262 *s
++ = '0' + fields
[my_playernum
-1][y
][x
];
266 sputs(buf
, server_sock
);
268 not_playing_game
= 1;
270 } else if (strcmp(cmd
, "pause") == 0) {
271 if (s
= strtok(NULL
, " "))
272 game_paused
= atoi(s
);
274 io
->draw_text(BUFFER_PLINE
, "*** The Game Has Been Paused");
275 io
->draw_text(BUFFER_GMSG
, "*** The Game Has Been Paused");
277 io
->draw_text(BUFFER_PLINE
, "*** The Game Has Been Unpaused");
278 io
->draw_text(BUFFER_GMSG
, "*** The Game Has Been Unpaused");
281 } else if (strcmp(cmd
, "endgame") == 0) {
283 not_playing_game
= 0;
284 memset(fields
, 0, sizeof(fields
));
286 io
->clear_text(BUFFER_ATTDEF
);
287 io
->draw_text(BUFFER_PLINE
, "*** The Game Has Ended");
288 if (dispmode
== MODE_FIELDS
) {
290 io
->draw_own_field();
291 for (i
= 1; i
<= 6; i
++) {
292 if (i
!= my_playernum
)
293 io
->draw_other_field(i
);
297 } else if (strcmp(cmd
, "playerwon") == 0) {
298 /* Syntax: playerwon # -- sent when all but one player lose */
300 } else if (strcmp(cmd
, "playerlost") == 0) {
301 /* Syntax: playerlost # -- sent after playerleave on disconnect
302 * during a game, or when a player loses (sent by the losing
303 * player and from the server to all other players */
305 } else if (strcmp(cmd
, "f") == 0) { /* field */
306 int player
, x
, y
, tile
;
308 /* This looks confusing, but what it means is, ignore this message
309 * if a game isn't going on. */
310 if (!playing_game
&& !not_playing_game
)
312 if (!(s
= strtok(NULL
, " ")))
316 if (!(s
= strtok(NULL
, "")))
319 /* Set field directly */
320 char *ptr
= (char *) fields
[player
];
323 *ptr
++ = (*s
++) - '0';
325 case 'a': *ptr
++ = 6 + SPECIAL_A
; break;
326 case 'b': *ptr
++ = 6 + SPECIAL_B
; break;
327 case 'c': *ptr
++ = 6 + SPECIAL_C
; break;
328 case 'g': *ptr
++ = 6 + SPECIAL_G
; break;
329 case 'n': *ptr
++ = 6 + SPECIAL_N
; break;
330 case 'o': *ptr
++ = 6 + SPECIAL_O
; break;
331 case 'q': *ptr
++ = 6 + SPECIAL_Q
; break;
332 case 'r': *ptr
++ = 6 + SPECIAL_R
; break;
333 case 's': *ptr
++ = 6 + SPECIAL_S
; break;
337 /* Set specific locations on field */
345 fields
[player
][y
][x
] = tile
;
350 if (player
== my_playernum
-1)
351 io
->draw_own_field();
353 io
->draw_other_field(player
+1);
354 } else if (strcmp(cmd
, "lvl") == 0) {
357 if (!(s
= strtok(NULL
, " ")))
360 if (!(s
= strtok(NULL
, "")))
362 levels
[player
] = atoi(s
);
364 } else if (strcmp(cmd
, "sb") == 0) {
368 if (!(s
= strtok(NULL
, " ")))
371 if (!(type
= strtok(NULL
, " ")))
373 if (!(s
= strtok(NULL
, " ")))
376 do_special(type
, from
, to
);
378 } else if (strcmp(cmd
, "gmsg") == 0) {
379 if (!(s
= strtok(NULL
, "")))
381 io
->draw_text(BUFFER_GMSG
, s
);
386 /*************************************************************************/
387 /*************************************************************************/
389 static char partyline_buffer
[512];
390 static int partyline_pos
= 0;
392 #define curpos (partyline_buffer+partyline_pos)
394 /*************************************************************************/
396 /* Add a character to the partyline buffer. */
398 void partyline_input(int c
)
400 if (partyline_pos
< sizeof(partyline_buffer
) - 1) {
401 memmove(curpos
+1, curpos
, strlen(curpos
)+1);
402 partyline_buffer
[partyline_pos
++] = c
;
403 io
->draw_partyline_input(partyline_buffer
, partyline_pos
);
407 /*************************************************************************/
409 /* Delete the current character from the partyline buffer. */
411 void partyline_delete(void)
413 if (partyline_buffer
[partyline_pos
]) {
414 memmove(curpos
, curpos
+1, strlen(curpos
)-1+1);
415 io
->draw_partyline_input(partyline_buffer
, partyline_pos
);
419 /*************************************************************************/
421 /* Backspace a character from the partyline buffer. */
423 void partyline_backspace(void)
425 if (partyline_pos
> 0) {
431 /*************************************************************************/
433 /* Kill the entire partyline input buffer. */
435 void partyline_kill(void)
438 *partyline_buffer
= 0;
439 io
->draw_partyline_input(partyline_buffer
, partyline_pos
);
442 /*************************************************************************/
444 /* Move around the input buffer. Sign indicates direction; absolute value
445 * of 1 means one character, 2 means the whole line.
448 void partyline_move(int how
)
452 io
->draw_partyline_input(partyline_buffer
, partyline_pos
);
453 } else if (how
== -1 && partyline_pos
> 0) {
455 io
->draw_partyline_input(partyline_buffer
, partyline_pos
);
456 } else if (how
== 1 && partyline_buffer
[partyline_pos
]) {
458 io
->draw_partyline_input(partyline_buffer
, partyline_pos
);
459 } else if (how
== 2) {
460 partyline_pos
= strlen(partyline_buffer
);
461 io
->draw_partyline_input(partyline_buffer
, partyline_pos
);
465 /*************************************************************************/
467 /* Send the input line to the server. */
469 void partyline_enter(void)
473 if (*partyline_buffer
) {
474 if (strncasecmp(partyline_buffer
, "/me ", 4) == 0) {
475 sockprintf(server_sock
, "plineact %d %s", my_playernum
, partyline_buffer
+4);
476 snprintf(buf
, sizeof(buf
), "* %s %s", players
[my_playernum
-1], partyline_buffer
+4);
477 io
->draw_text(BUFFER_PLINE
, buf
);
478 } else if (strcasecmp(partyline_buffer
, "/start") == 0) {
479 sockprintf(server_sock
, "startgame 1 %d", my_playernum
);
480 } else if (strcasecmp(partyline_buffer
, "/end") == 0) {
481 sockprintf(server_sock
, "startgame 0 %d", my_playernum
);
482 } else if (strcasecmp(partyline_buffer
, "/pause") == 0) {
483 sockprintf(server_sock
, "pause 1 %d", my_playernum
);
484 } else if (strcasecmp(partyline_buffer
, "/unpause") == 0) {
485 sockprintf(server_sock
, "pause 0 %d", my_playernum
);
486 } else if (strncasecmp(partyline_buffer
, "/team", 5) == 0) {
487 if (strlen(partyline_buffer
) == 5)
488 strcpy(partyline_buffer
+5, " "); /* make it "/team " */
489 sockprintf(server_sock
, "team %d %s", my_playernum
, partyline_buffer
+6);
490 if (partyline_buffer
[6]) {
491 if (teams
[my_playernum
-1])
492 free(teams
[my_playernum
-1]);
493 teams
[my_playernum
-1] = strdup(partyline_buffer
+6);
494 snprintf(buf
, sizeof(buf
), "*** %s is Now on Team %s", players
[my_playernum
-1], partyline_buffer
+6);
495 io
->draw_text(BUFFER_PLINE
, buf
);
497 if (teams
[my_playernum
-1])
498 free(teams
[my_playernum
-1]);
499 teams
[my_playernum
-1] = NULL
;
500 snprintf(buf
, sizeof(buf
), "*** %s is Now Alone", players
[my_playernum
-1]);
501 io
->draw_text(BUFFER_PLINE
, buf
);
503 } else if (*partyline_buffer
== '/'
504 && partyline_buffer
[1] != 0 && partyline_buffer
[1] != ' ') {
505 char *s
= strtok(partyline_buffer
+1, " ");
508 snprintf(buf
, sizeof(buf
), "*** Unknown command: %s", s
);
509 io
->draw_text(BUFFER_PLINE
, buf
);
511 sockprintf(server_sock
, "pline %d %s", my_playernum
, partyline_buffer
);
512 snprintf(buf
, sizeof(buf
), "<%s> %s", players
[my_playernum
-1], partyline_buffer
);
513 io
->draw_text(BUFFER_PLINE
, buf
);
516 *partyline_buffer
= 0;
517 io
->draw_partyline_input(partyline_buffer
, partyline_pos
);
523 /*************************************************************************/
524 /*************************************************************************/
526 int init(int ac
, char **av
)
529 char *nick
= NULL
, *server
= NULL
;
535 int start_server
= 0; /* Start the server? (-server) */
536 int slide
= 0; /* Do we definitely want to slide? (-slide) */
539 /* If there's a DISPLAY variable set in the environment, default to
540 * Xwindows I/O, else default to terminal I/O. */
541 if (getenv("DISPLAY"))
542 io
= &xwin_interface
;
545 io
=&tty_interface
; /* because Xwin isn't done yet */
550 for (i
= 1; i
< ac
; i
++) {
552 if (strcmp(av
[i
], "-server") == 0) {
554 } else if (strcmp(av
[i
], "-fancy") == 0) {
556 } else if (strcmp(av
[i
], "-log") == 0) {
560 fprintf(stderr
, "Option -log requires an argument\n");
564 } else if (strcmp(av
[i
], "-noslide") == 0) {
566 } else if (strcmp(av
[i
], "-slide") == 0) {
568 } else if (strcmp(av
[i
], "-windows") == 0) {
572 fprintf(stderr
, "Unknown option %s\n", av
[i
]);
577 } else if (!server
) {
580 fprintf(stderr
, "Usage: %s <nick> <server>\n", av
[0]);
589 fprintf(stderr
, "Usage: %s <nick> <server>\n", av
[0]);
592 if (strlen(nick
) > 63) /* put a reasonable limit on nick length */
595 if ((server_sock
= conn(server
, 31457, ip
)) < 0) {
596 fprintf(stderr
, "Couldn't connect to server %s: %s\n",
597 server
, strerror(errno
));
600 sprintf(nickmsg
, "tetrisstart %s 1.13", nick
);
601 sprintf(iphashbuf
, "%d", ip
[0]*54 + ip
[1]*41 + ip
[2]*29 + ip
[3]*17);
602 /* buf[0] does not need to be initialized for this algorithm */
603 len
= strlen(nickmsg
);
604 for (i
= 0; i
< len
; i
++)
605 buf
[i
+1] = (((buf
[i
]&0xFF) + (nickmsg
[i
]&0xFF)) % 255) ^ iphashbuf
[i
% strlen(iphashbuf
)];
607 for (i
= 0; i
< len
; i
++)
608 sprintf(nickmsg
+i
*2, "%02X", buf
[i
] & 0xFF);
609 sputs(nickmsg
, server_sock
);
612 if (!sgets(buf
, sizeof(buf
), server_sock
)) {
613 fprintf(stderr
, "Server %s closed connection\n", server
);
614 disconn(server_sock
);
618 } while (my_playernum
< 0);
619 sockprintf(server_sock
, "team %d ", my_playernum
);
621 players
[my_playernum
-1] = strdup(nick
);
622 dispmode
= MODE_PARTYLINE
;
624 io
->setup_partyline();
629 /*************************************************************************/
631 int main(int ac
, char **av
)
635 if ((i
= init(ac
, av
)) != 0)
640 if (playing_game
&& !game_paused
)
641 timeout
= tetris_timeout();
644 i
= io
->wait_for_input(timeout
);
647 if (sgets(buf
, sizeof(buf
), server_sock
))
650 io
->draw_text(BUFFER_PLINE
, "*** Disconnected from Server");
653 } else if (i
== -2) {
654 tetris_timeout_action();
655 } else if (i
== 12) { /* Ctrl-L */
657 } else if (i
== K_F10
) {
658 break; /* out of main loop */
659 } else if (i
== K_F1
) {
660 if (dispmode
!= MODE_FIELDS
) {
661 dispmode
= MODE_FIELDS
;
664 } else if (i
== K_F2
) {
665 if (dispmode
!= MODE_PARTYLINE
) {
666 dispmode
= MODE_PARTYLINE
;
667 io
->setup_partyline();
669 } else if (i
== K_F3
) {
670 if (dispmode
!= MODE_WINLIST
) {
671 dispmode
= MODE_WINLIST
;
674 } else if (dispmode
== MODE_FIELDS
) {
676 } else if (dispmode
== MODE_PARTYLINE
) {
677 if (i
== 8 || i
== 127) /* Backspace or Delete */
678 partyline_backspace();
679 else if (i
== 4) /* Ctrl-D */
681 else if (i
== 21) /* Ctrl-U */
683 else if (i
== '\r' || i
== '\n')
685 else if (i
== K_LEFT
)
687 else if (i
== K_RIGHT
)
689 else if (i
== 1) /* Ctrl-A */
691 else if (i
== 5) /* Ctrl-E */
693 else if (i
>= 1 && i
<= 0xFF)
698 disconn(server_sock
);
702 /*************************************************************************/
704 #endif /* !SERVER_ONLY */
706 /*************************************************************************/