home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1994 March / Source_Code_CD-ROM_Walnut_Creek_March_1994.iso / compsrcs / games / volume14 / okbrdge2 / part07 < prev    next >
Encoding:
Internet Message Format  |  1993-01-26  |  54.8 KB

  1. Path: uunet!zephyr.ens.tek.com!master!saab!billr
  2. From: billr@saab.CNA.TEK.COM (Bill Randle)
  3. Newsgroups: comp.sources.games
  4. Subject: v14i085:  okbridge2 - computer-mediated bridge game, Part07/14
  5. Message-ID: <3524@master.CNA.TEK.COM>
  6. Date: 7 Sep 92 21:42:01 GMT
  7. Sender: news@master.CNA.TEK.COM
  8. Lines: 1887
  9. Approved: billr@saab.CNA.TEK.COM
  10.  
  11. Submitted-by: mclegg@cs.UCSD.EDU (Matthew Clegg)
  12. Posting-number: Volume 14, Issue 85
  13. Archive-name: okbridge2/Part07
  14. Supersedes: okbridge: Volume 13, Issue 16-22
  15. Environment: BSD-derived Unix, NeXT, curses, sockets
  16.  
  17.  
  18.  
  19. #! /bin/sh
  20. # This is a shell archive.  Remove anything before this line, then unpack
  21. # it by saving it into a file and typing "sh file".  To overwrite existing
  22. # files, type "sh file -c".  You can also feed this as standard input via
  23. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  24. # will see the following message at the end:
  25. #        "End of archive 7 (of 14)."
  26. # Contents:  MkDistrib network.c protocol.h
  27. # Wrapped by billr@saab on Mon Sep  7 14:33:37 1992
  28. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  29. if test -f 'MkDistrib' -a "${1}" != "-c" ; then 
  30.   echo shar: Will not clobber existing file \"'MkDistrib'\"
  31. else
  32. echo shar: Extracting \"'MkDistrib'\" \(576 characters\)
  33. sed "s/^X//" >'MkDistrib' <<'END_OF_FILE'
  34. X#!/bin/csh -f
  35. X#
  36. X# This shell script creates the compress okbridge tar file which
  37. X# is distributed.  Unfortunately, it seems that the "make" command
  38. X# on some systems is not smart enough to handle the work which is
  39. X# done by this file.
  40. X#
  41. Xsetenv MAJOR `grep 'major_rev.*=' types.h | sed 's@.*"\(.*\)".*@\1@'`
  42. Xsetenv MINOR `grep 'minor_rev.*=' types.h | sed 's@.*"\(.*\)".*@\1@'`
  43. Xsetenv OKFILENAME okbridge-$MAJOR$MINOR
  44. Xsetenv OKBDIR `pwd | sed "s@.*/\([^/]*\)@\1@"`
  45. Xcd .. 
  46. Xecho Ztar\'ing $OKBDIR into $OKFILENAME.tar.Z
  47. Xtar -cf $OKFILENAME.tar $OKBDIR
  48. Xcompress $OKFILENAME.tar
  49. END_OF_FILE
  50. if test 576 -ne `wc -c <'MkDistrib'`; then
  51.     echo shar: \"'MkDistrib'\" unpacked with wrong size!
  52. fi
  53. # end of 'MkDistrib'
  54. fi
  55. if test -f 'network.c' -a "${1}" != "-c" ; then 
  56.   echo shar: Will not clobber existing file \"'network.c'\"
  57. else
  58. echo shar: Extracting \"'network.c'\" \(33079 characters\)
  59. sed "s/^X//" >'network.c' <<'END_OF_FILE'
  60. X/* network.c
  61. X *
  62. X ! Copyright (C) 1990-1992 by Matthew Clegg.  All Rights Reserved
  63. X ! 
  64. X ! OKbridge is made available as a free service to the Internet.
  65. X ! Accordingly, the following restrictions are placed on its use:
  66. X ! 
  67. X ! 1.  OKbridge may not be modified in any way without the explicit 
  68. X !     permission of Matthew Clegg.  
  69. X ! 
  70. X ! 2.  OKbridge may not be used in any way for commercial advantage.
  71. X !     It may not be placed on for-profit networks or on for-profit
  72. X !     computer systems.  It may not be bundled as part of a package
  73. X !     or service provided by a for-profit organization.
  74. X ! 
  75. X ! If you have questions about restrictions on the use of OKbridge,
  76. X ! write to mclegg@cs.ucsd.edu.
  77. X ! 
  78. X ! DISCLAIMER:  The user of OKbridge accepts full responsibility for any
  79. X ! damage which may be caused by OKbridge.
  80. X *
  81. X * This is the implementation of the network module.  The network module
  82. X * provides two functions.  First it abstracts the socket communication
  83. X * into messages which are handled on the message queues, and second
  84. X * it provides a branch point between the server and client sides of
  85. X * the code.
  86. X *
  87. X */  
  88. X
  89. X/* The NO_TM_ZONE definition is for machines which do not have the
  90. X   field tm_zone defined in the struct tm returned by localtime.
  91. X   In this case, we use the struct timezone returned by gettimeofday
  92. X   to determine an offset from GMT.
  93. X*/
  94. X
  95. X#ifdef AIX
  96. X#define NO_TM_ZONE 1
  97. X#define NO_PWCOMMENT 1
  98. X#endif
  99. X
  100. X#ifdef HARRIS
  101. X#define NO_TM_ZONE 1
  102. X#endif
  103. X
  104. X/* The NO_PWCOMMENT definition is for machines which do not have the
  105. X   pw_comment field defined in struct passwd returned by getpwuid ().
  106. X*/
  107. X
  108. X#ifdef AIX
  109. X#define NO_PWCOMMENT 1
  110. X#endif
  111. X
  112. X#ifdef SGI
  113. X#define NO_TM_ZONE 1
  114. X#endif
  115. X
  116. X#include <ctype.h>
  117. X#include <sys/errno.h>
  118. X#include <sys/types.h>
  119. X#include <sys/socket.h>
  120. X#include <netinet/in.h>
  121. X#include <arpa/inet.h>
  122. X#include <netdb.h>
  123. X#include <pwd.h>
  124. X#include <stdio.h>
  125. X#include <string.h>
  126. X#include <sys/time.h>
  127. X#include <sys/resource.h>
  128. X#ifdef AIX
  129. X#include <sys/select.h>
  130. X#include <time.h>
  131. X#endif
  132. X
  133. X#define _NETWORK_ 
  134. X
  135. X#include "state.h"
  136. X#include "socket.h"
  137. X#include "display.h"
  138. X#include "gps.h"
  139. X#include "fds.h"
  140. X
  141. X#ifdef GCC
  142. Xextern bzero (), fprintf (), fflush (), accept ();
  143. Xextern time_t time ();
  144. X#endif
  145. X
  146. Xextern int errno;
  147. Xextern char *sys_errlist [];
  148. Xextern char *getlogin(), *getenv();
  149. Xextern char *strdup ();
  150. Xextern char *malloc ();
  151. Xextern void close ();
  152. Xextern int select ();
  153. Xextern int gethostname ();
  154. Xextern uid_t getuid ();
  155. X
  156. Xextern int Terminate_Program ();
  157. Xextern void restore_cursor ();
  158. Xextern void Broadcast_Comment ();
  159. Xextern void Generate_reset ();
  160. Xextern void Vacate_seat ();
  161. X
  162. X#ifndef index
  163. Xextern char *index();
  164. X#endif
  165. X
  166. X#ifdef LOGFILE
  167. X  FILE *net_log = NULL;
  168. X#endif
  169. X
  170. X#define DISCARD (void)
  171. X
  172. X#define RETRY_LIMIT 60
  173. X
  174. Xstatic Message message_free_list = NULL;
  175. Xstatic Connection connection_freelist = NULL;
  176. X
  177. Xstatic int listen_port = -1;
  178. X  /* The socket we have opened for listening for new connections. */
  179. Xstatic int server_port = -1;
  180. X  /* The socket opened by the client for listening to the server. */
  181. X
  182. Xextern char socket_error[];
  183. X  /* An error message buffer for recording socket errors. */
  184. X
  185. X/* Procedures for manipulating command queues: */
  186. X
  187. Xstatic message_queue Allocate_message_queue ()
  188. X{
  189. X  message_queue m;
  190. X
  191. X  m = (message_queue) malloc (sizeof(struct Message_queue_struct));
  192. X  m->head = m->tail = NULL;
  193. X  return (m);
  194. X}
  195. X
  196. Xint message_available (queue)
  197. X     message_queue queue;
  198. X/* Returns true if the given queue is nonempty. */
  199. X{
  200. X  return (queue->head != NULL);
  201. X}
  202. X
  203. XMessage dequeue_message (queue)
  204. X     message_queue queue;
  205. X/* Deletes the first message from the given queue and returns a pointer
  206. X   to that message.  
  207. X*/
  208. X{
  209. X  Message m;
  210. X
  211. X  if (!message_available(queue))
  212. X    return (NULL);
  213. X
  214. X  m = queue->head;
  215. X  queue->head = m->next;
  216. X  if (queue->head == NULL)
  217. X    queue->tail = NULL;
  218. X  return (m);
  219. X}
  220. X
  221. Xvoid enqueue_message (queue, m)
  222. X     message_queue queue; Message m;
  223. X/* Appends m to the list of messages stored in the given message queue. */
  224. X{
  225. X  m->next = NULL;
  226. X  if (queue->tail == NULL)
  227. X    queue->head = queue->tail = m;
  228. X  else {
  229. X    queue->tail->next = m;
  230. X    queue->tail = m;
  231. X  }
  232. X
  233. X}
  234. X
  235. XMessage allocate_message ()
  236. X/* Allocates a message structure and returns a pointer to the structure. */
  237. X{
  238. X  Message m;
  239. X
  240. X  if (message_free_list == NULL)
  241. X    m = (Message) malloc (sizeof(struct Message_struct));
  242. X  else {
  243. X    m = message_free_list;
  244. X    message_free_list = m->next;
  245. X  }
  246. X
  247. X  m->p.command_text[0] = '\0';
  248. X  m->p.player_no = 0;
  249. X  m->private = 0;
  250. X  m->loopback = 0;
  251. X  return (m);
  252. X}
  253. X
  254. Xvoid deallocate_message (m)
  255. X     Message m;
  256. X/* Returns the message structure m to the pool of free message structs. */
  257. X{
  258. X  m->next = message_free_list;
  259. X  message_free_list = m;
  260. X  m->p.player_no = 0;
  261. X}
  262. X
  263. Xvoid clear_message_queue (queue)
  264. X     message_queue queue;
  265. X/* Deletes all messages from the named queue. */
  266. X{
  267. X  Message m;
  268. X
  269. X  while (message_available(queue)) {
  270. X    m =  dequeue_message (queue);
  271. X    deallocate_message (m);
  272. X  }
  273. X}
  274. X
  275. Xvoid clear_all_message_queues (t)
  276. X     Table t;
  277. X/* Clears all of the message queues. */
  278. X{
  279. X  clear_message_queue (t->protocol_queue);
  280. X  clear_message_queue (t->conversation_queue);
  281. X  clear_message_queue (t->game_queue);
  282. X}
  283. X
  284. Xstatic Connection Allocate_Dummy_Connection ()
  285. X/* Allocates a new connection structure and fills in the fields with
  286. X   appropriate initial values.  Does not link the connection into any list. */
  287. X{
  288. X  Connection c;
  289. X
  290. X  if (connection_freelist == NULL)
  291. X    c = (Connection) malloc (sizeof(struct Connection_struct));
  292. X  else {
  293. X    c = connection_freelist;
  294. X    connection_freelist = c->inext;
  295. X  }
  296. X
  297. X  c->channel = 0;
  298. X  c->local   = 1;
  299. X  c->state   = CSTATE_CONNECTED;
  300. X  c->player_name[0] = '\0';
  301. X  c->fullname[0] = '\0';
  302. X  c->email[0] = '\0';
  303. X  c->registry[0] = '\0';
  304. X  c->seat    = PLAYER_OBS;
  305. X  c->table = NULL;
  306. X
  307. X  c->iprev = c->inext = c->oprev = c->onext = NULL;
  308. X  if (Connections == NULL)
  309. X    Connections = c;
  310. X
  311. X  return (c);
  312. X}
  313. X
  314. Xstatic Connection Allocate_Connection ()
  315. X/* Allocates a new connection structure, and fills in the pointer fields
  316. X   appropriately.  Links the connection into the Connection list. */
  317. X{
  318. X  Connection c = Allocate_Dummy_Connection ();
  319. X
  320. X  c->inext = Connections->inext;
  321. X  c->iprev = Connections;
  322. X  if (c->inext != NULL)
  323. X    c->inext->iprev = c;
  324. X  Connections->inext = c;
  325. X
  326. X  return (c);
  327. X}
  328. X
  329. Xstatic void Link_Output_Connection (p, c)
  330. X     Connection p, c;
  331. X/* Links the connection c into the list pointed to by p.  Since we
  332. X   always store dummy header elements in connection lists, p is guaranteed
  333. X   to be non-NULL, even in the case of an empty list.*/
  334. X{
  335. X
  336. X  c->oprev = p;
  337. X  c->onext = p->onext;
  338. X  if (c->onext != NULL)
  339. X    c->onext->oprev = c;
  340. X  p->onext = c;
  341. X
  342. X}
  343. X
  344. Xstatic void Unlink_Output_Connection (c)
  345. X     Connection c;
  346. X/* Unlinks the connection c from the list containing it. */
  347. X{
  348. X  if (c->onext != NULL)
  349. X    c->onext->oprev = c->oprev;
  350. X
  351. X  if (c->oprev != NULL)
  352. X    c->oprev->onext = c->onext;
  353. X
  354. X  c->oprev = c->onext = NULL;
  355. X}
  356. X
  357. Xstatic void Deallocate_Connection (c)
  358. X     Connection c;
  359. X/* Unlinks the connection c from any lists which may contain it and
  360. X   then returns the connection to the free store. */
  361. X{
  362. X  Unlink_Output_Connection (c);
  363. X  
  364. X  if (c->inext != NULL)
  365. X    c->inext->iprev = c->iprev;
  366. X  c->iprev->inext = c->inext;
  367. X
  368. X  c->inext = connection_freelist;
  369. X  connection_freelist = c;
  370. X}
  371. X
  372. Xvoid close_connection (c)
  373. X     Connection c;
  374. X/* void close_connection (Connection c) */
  375. X/* Closes the connection to c. */
  376. X{
  377. X  Table t = c->table;
  378. X#ifdef DEBUG
  379. X  char error_buf[100];
  380. X#endif
  381. X
  382. X  if (c->local)
  383. X    return;
  384. X
  385. X#ifdef DEBUG
  386. X  sprintf (error_buf, "CLOSED CONNECTION ON FD %d.", c->channel);
  387. X  Network_Comment (error_buf);
  388. X#endif 
  389. X
  390. X  Vacate_seat (c->table, c->seat);
  391. X
  392. X  if (c->channel > 0)
  393. X    close (c->channel);
  394. X
  395. X  Deallocate_Connection (c);
  396. X}
  397. X
  398. Xint No_connections (t)
  399. X     Table t;
  400. X/* Returns the number of connections to the table t. */
  401. X{
  402. X  int n;
  403. X  Connection c;
  404. X
  405. X  n = 0;
  406. X  FOREACH_PLAYER (c, t)
  407. X    n++;
  408. X  return (n);
  409. X}
  410. X
  411. XMessage loopback_message_unformatted (t, c, message)
  412. X     Table t; Connection c; char *message;
  413. X/* void loopback_message_unformatted (Table t, char *message); */
  414. X/* Constructs a message structure using the the given message.
  415. X   Assumes that the message is in the format which is used by the
  416. X   client in transmitting messages.  Appends the message structure
  417. X   to the protocol queue for the table t.
  418. X*/
  419. X{
  420. X  Message m = allocate_message ();
  421. X  strcpy (m->p.command_text, message);
  422. X  Parse_Command_for_Client (&(m->p));
  423. X  m->source = c;
  424. X  m->loopback = 1;
  425. X  enqueue_message (t->protocol_queue, m);
  426. X  return (m);
  427. X}
  428. X
  429. XMessage loopback_message (t, c, message)
  430. X     Table t; Connection c; char *message;
  431. X/* void loopback_message (Table t, Connection c, char *message); */
  432. X/* Constructs a message structure using the given source, source name
  433. X   and message text.  Appends the message structure to the protocol
  434. X   queue for the table t.
  435. X*/
  436. X{
  437. X  Message m = allocate_message ();
  438. X
  439. X  sprintf (m->p.command_text, "%s %s %s", seat_names[c->seat],
  440. X       c->player_name, message);
  441. X  m->source = c;
  442. X  m->loopback = 1;
  443. X  Parse_Command_for_Client (&(m->p));
  444. X  enqueue_message (t->protocol_queue, m);
  445. X  return (m);
  446. X}
  447. X
  448. Xint server_send_unformatted (c, message)
  449. X     Connection c; char *message;
  450. X/* Transmits the message to the given player.  Returns 0 if successful or 1
  451. X   if an error.  If an error occurs, then the connection is closed.
  452. X*/
  453. X{
  454. X  int status;                 /* return code from Select call. */
  455. X  int log;                    /* number of bytes transmitted. */
  456. X  int n;                      /* number of bytes to transmit. */
  457. X  struct fd_set wait_set;     /* A set representing the connections that
  458. X                 have been established. */
  459. X  struct timeval tm;          /* A timelimit of zero for polling for new
  460. X                 connections. */
  461. X  char buf [100];
  462. X
  463. X  if (c->local) {
  464. X/*    loopback_message_unformatted (c->table, c, message); */
  465. X    return (1);
  466. X  }
  467. X
  468. X  FD_ZERO (&wait_set);
  469. X  FD_SET (c->channel, &wait_set);
  470. X
  471. X  tm.tv_sec = 0;
  472. X  tm.tv_usec = 0;
  473. X  status = select (FD_SETSIZE, (fd_set *) 0, &wait_set, (fd_set *) 0, &tm);
  474. X
  475. X  if (status < 0) {
  476. X    sprintf (buf, "WRITE ERROR TO %s: %s", c->player_name, sys_errlist[errno]);
  477. X    Network_Comment (buf);
  478. X    shutdown (c->channel, 2);
  479. X    c->channel = 0;
  480. X    close_connection (c);
  481. X    return (1);
  482. X  } else if (status == 0) {
  483. X    sprintf (buf, "WRITE BLOCK FOR %s", c->player_name);
  484. X    Network_Comment (buf);
  485. X    shutdown (c->channel, 2);
  486. X    c->channel = 0;
  487. X    close_connection (c);
  488. X    return (1);
  489. X  }
  490. X
  491. X  n = strlen (message) + 1;
  492. X  do {
  493. X    log = fd_writeln (c->channel, message);
  494. X  } while ((log < 0) && (errno == EINTR));
  495. X
  496. X  if (log < n) {
  497. X    sprintf (buf, "WRITE BLOCK FOR %s", c->player_name);
  498. X    Network_Comment (buf);
  499. X    close_connection (c);
  500. X    return (1);
  501. X  }
  502. X
  503. X#ifdef LOGFILE
  504. X  fprintf (net_log, "%2d > %s\n", c->channel, message);
  505. X  fflush (net_log);
  506. X#endif    
  507. X  return (0);
  508. X}
  509. X
  510. Xint server_send (c, source, player_name, message)
  511. X     Connection c; int source; char *player_name, *message;
  512. X/* Formats a message and sends it across the given connection.
  513. X   Returns 0 if successful or 1 if an error.  If an error occurs,
  514. X   then the connection is closed. */
  515. X{
  516. X  char buf [120];
  517. X
  518. X  sprintf (buf, "%s %s %s", seat_names[source], player_name, message);
  519. X  return (server_send_unformatted (c, buf));
  520. X}
  521. X
  522. Xint May_receive (c, msg_type)
  523. X     Connection c; int msg_type;
  524. X/* Returns true if a message of type msg_type can be sent to channel c. */
  525. X{
  526. X  if (c == NULL)
  527. X    return (0);
  528. X  else if (c->state == CSTATE_PLAYING)
  529. X    return (1);
  530. X  else
  531. X    return (0);
  532. X}
  533. X
  534. Xvoid server_broadcast_unformatted (t, msg_type, message)
  535. X     Table t; int msg_type; char *message;
  536. X/* Broadcasts the message to all of the people sitting at table t who are
  537. X   entitled to receive the message. */
  538. X{
  539. X  Connection c, d;
  540. X  int local = 0;
  541. X
  542. X  c = t->Players->onext;
  543. X  while (c != NULL) {
  544. X    d = c->onext;
  545. X    if (May_receive (c, msg_type)) {
  546. X      if (c->local)
  547. X    local = 1;
  548. X      else
  549. X    server_send_unformatted (c, message);
  550. X    }
  551. X    c = d;
  552. X  }
  553. X
  554. X  if (local && (msg_type != CMD_BOARD) && (msg_type != CMD_RECORD))
  555. X    loopback_message_unformatted (t, MODERATOR(t), message);
  556. X}
  557. X
  558. Xvoid server_broadcast (t, source, player_name, msg_type, message)
  559. X     Table t; int source, msg_type; char *player_name, *message;
  560. X/* Formats and broadcasts the message to all of the people sitting at table t
  561. X   who are entitled to receive the message. */
  562. X{
  563. X  char buf [120];
  564. X
  565. X  sprintf (buf, "%s %s %s", seat_names[source], player_name, message);
  566. X  server_broadcast_unformatted (t, msg_type, buf);
  567. X}
  568. X
  569. Xvoid Relay_message (t, c, msg_type, message)
  570. X     Table t; Connection c; int msg_type; char *message;
  571. X/* Relays the message from c to all of the other players at table t who
  572. X   are allowed to receive the message. */
  573. X{
  574. X  Connection p, q;
  575. X
  576. X  p = t->Players->onext;
  577. X  while (p != NULL) {
  578. X    q = p->onext;
  579. X    if ((p->channel != c->channel) && May_receive (p, msg_type))
  580. X      server_send_unformatted (p, message);
  581. X    p = q;
  582. X  }
  583. X}
  584. X
  585. XMessage send_message (t, msg_type, message)
  586. X     Table t; int msg_type; char *message;
  587. X/* If in server mode, sends a message to each of the clients.
  588. X   If in client mode, sends a message to the server. */
  589. X{
  590. X  char message_buf [120];
  591. X  Message m;
  592. X
  593. X  m = loopback_message (t, Local_Player_Connection, message);
  594. X
  595. X  if (client_mode) {
  596. X    sprintf (message_buf, "%s %s", local_player_name, message);
  597. X    fd_writeln (server_port, message_buf);
  598. X#ifdef LOGFILE
  599. X  fprintf (net_log, "   > %s\n", message_buf);
  600. X  fflush (net_log);
  601. X#endif    
  602. X  }
  603. X
  604. X  return (m);
  605. X}
  606. X
  607. Xvoid send_server_message (t, msg_type, message)
  608. X     Table t; int msg_type; char *message;
  609. X/* Sends a message which originates from the 'SERVER'.
  610. X   Should only be used if in server mode.
  611. X*/
  612. X{
  613. X  server_broadcast (t, PLAYER_SERVER, seat_names[PLAYER_SERVER], 
  614. X            msg_type, message);
  615. X}
  616. X
  617. Xvoid send_private_message (c, message)
  618. X     Connection c; char *message;
  619. X/* static void send_private_message (Connection c, char *message) */
  620. X/* Only to be used in server mode.  Sends a message from the 'server'
  621. X   to the connection c.  If the connection c is the local player,
  622. X   then appends the message to the protocol queue of the table of
  623. X   the local player.
  624. X*/
  625. X{
  626. X  Message m;
  627. X
  628. X  if (c->local) {
  629. X    m = loopback_message (c->table, MODERATOR(c->table), message);
  630. X    m->private = 1;
  631. X  } else
  632. X    server_send (c, PLAYER_SERVER, seat_names[PLAYER_SERVER], message);
  633. X}
  634. X
  635. Xstatic void Broadcast_Inline_Data (t, msg_type, buf)
  636. X     Table t;
  637. X     int msg_type;
  638. X     char *buf;
  639. X/* Broadcasts the contents of buf to each of the connections at table t. */
  640. X{
  641. X  Connection c, d;
  642. X
  643. X  if (server_mode) {
  644. X    c = t->Players->onext;
  645. X    while (c != NULL) {
  646. X      d = c->onext;
  647. X      if (May_receive (c, msg_type)) {
  648. X    if (!c->local)
  649. X      server_send_unformatted (c, buf);
  650. X      }
  651. X      c = d;
  652. X    }
  653. X  } else
  654. X    fd_writeln (server_port, buf);
  655. X}
  656. X
  657. Xstatic void Relay_Inline_Data (t, f, msg_type, buf)
  658. X     Table t;
  659. X     Connection f;
  660. X     int msg_type;
  661. X     char *buf;
  662. X/* Broadcasts the contents of buf to each of the connections at table
  663. X   t EXCEPT f. */
  664. X{
  665. X  Connection c, d;
  666. X
  667. X  c = t->Players->onext;
  668. X  while (c != NULL) {
  669. X    d = c->onext;
  670. X    if (May_receive (c, msg_type)) {
  671. X      if ((c != f) && !c->local)
  672. X    server_send_unformatted (c, buf);
  673. X    }
  674. X    c = d;
  675. X  }
  676. X}
  677. X
  678. Xvoid Transmit_board (t, b)
  679. X     Table t;
  680. X     Board *b;
  681. X/* If we are the server, then transmits the board b to each of the players
  682. X   at the table t.  If we are a client, then transmits the board b to
  683. X   the server. */
  684. X{
  685. X  char buf1[100], buf2[100];
  686. X
  687. X  Encode_board (b, buf1, buf2);
  688. X  Broadcast_Inline_Data (t, CMD_BOARD, buf1);
  689. X  Broadcast_Inline_Data (t, CMD_BOARD, buf2);
  690. X}
  691. X
  692. Xvoid Transmit_play_record (t, p)
  693. X     Table t;
  694. X     Play_record *p;
  695. X/* If we are the server, then transmits the play record p to each of the
  696. X   players at the table t.  If we are a client, then transmits the play
  697. X   record p to the server. */
  698. X{
  699. X  char buf1[100], buf2[100], buf3[100];
  700. X
  701. X  Encode_play_record (p, buf1, buf2, buf3);
  702. X  Broadcast_Inline_Data (t, CMD_RECORD, buf1);
  703. X  Broadcast_Inline_Data (t, CMD_RECORD, buf2);
  704. X  Broadcast_Inline_Data (t, CMD_RECORD, buf3);
  705. X}
  706. X
  707. Xvoid Relay_board (t, c, b)
  708. X     Table t;
  709. X     Connection c;
  710. X     Board *b;
  711. X/* Relays the in-line board data from the connection c to the other players
  712. X   at the table t.
  713. X*/
  714. X{
  715. X  char buf1[100], buf2[100];
  716. X
  717. X  Encode_board (b, buf1, buf2);
  718. X  Relay_Inline_Data (t, c, CMD_BOARD, buf1);
  719. X  Relay_Inline_Data (t, c, CMD_BOARD, buf2);
  720. X}
  721. X
  722. X
  723. Xvoid Relay_play_record (t, c, p)
  724. X     Table t;
  725. X     Connection c;
  726. X     Play_record *p;
  727. X/* Relays the in-line play record from the connection c to the other players
  728. X   at the table t.
  729. X*/
  730. X{
  731. X  char buf1[100], buf2[100], buf3[100];
  732. X
  733. X  Encode_play_record (p, buf1, buf2, buf3);
  734. X  Relay_Inline_Data (t, c, CMD_RECORD, buf1);
  735. X  Relay_Inline_Data (t, c, CMD_RECORD, buf2);
  736. X  Relay_Inline_Data (t, c, CMD_RECORD, buf3);
  737. X}
  738. X
  739. XBoard *Receive_board (c)
  740. X     Connection c;
  741. X/* Receives a board as in-line data from the connection c. */
  742. X{
  743. X  char buf1[100], buf2[100];
  744. X  int status;
  745. X  Board *b;
  746. X  int chan;
  747. X
  748. X  if (client_mode)
  749. X    chan = server_port;
  750. X  else
  751. X    chan = c->channel;
  752. X
  753. X  status = fd_readln (chan, buf1, 100);
  754. X  if (status <= 0)
  755. X    return (NULL);
  756. X
  757. X  status = fd_readln(chan, buf2, 100);
  758. X  if (status <= 0)
  759. X    return (NULL);
  760. X
  761. X  b = Decode_board (buf1, buf2);
  762. X  return (b);
  763. X}
  764. X
  765. XPlay_record *Receive_play_record (c)
  766. X     Connection c;
  767. X/* Receives a play record as in-line data from the connection c. */
  768. X{
  769. X  char buf1[100], buf2[100], buf3[100];
  770. X  int status;
  771. X  Play_record *p;
  772. X  int chan;
  773. X
  774. X  if (client_mode)
  775. X    chan = server_port;
  776. X  else
  777. X    chan = c->channel;
  778. X
  779. X  status = fd_readln (chan, buf1, 100);
  780. X  if (status < 0)
  781. X    return (NULL);
  782. X
  783. X  status = fd_readln (chan, buf2, 100);
  784. X  if (status < 0)
  785. X    return (NULL);
  786. X
  787. X  status = fd_readln (chan, buf3, 100);
  788. X  if (status < 0)
  789. X    return (NULL);
  790. X
  791. X  p = Decode_play_record (buf1, buf2, buf3);
  792. X  return (p);
  793. X}
  794. X
  795. Xstatic Table Allocate_Table ()
  796. X/* Allocates a table structure and initializes it. */
  797. X{
  798. X  Table t;
  799. X  int i;
  800. X
  801. X  t = (Table) malloc(sizeof(struct Table_struct));
  802. X
  803. X  for (i = 0; i < PLAYER_TYPES; i++) {
  804. X/*    t->Seats[i].player_name[0] = '\0'; */
  805. X    sprintf (t->Seats[i].player_name, "%s", seat_names[i]);
  806. X    t->Seats[i].occupied = 0;
  807. X    t->Seats[i].connection = NULL;
  808. X  }
  809. X
  810. X  t->protocol_queue = Allocate_message_queue ();
  811. X  t->conversation_queue = Allocate_message_queue ();
  812. X  t->game_queue = Allocate_message_queue ();
  813. X
  814. X  /* The first element of the connections list is used to represent
  815. X     the server for the table. */
  816. X  t->Players = Allocate_Dummy_Connection ();
  817. X  t->Players->local = 1;
  818. X  t->Players->seat  = PLAYER_SERVER;
  819. X  t->Players->table = t;
  820. X  sprintf (t->Players->player_name, "MOD");
  821. X
  822. X  t->table_no = 1;
  823. X  t->game_mode = STARTUP_MODE;
  824. X  t->playing_mode = CLUB_PLAYING_MODE;
  825. X  t->board = NULL;
  826. X  t->play_record = NULL;
  827. X  t->above_line[0] = t->above_line[1] = 0;
  828. X  t->below_line[0] = t->below_line[1] = 0;
  829. X  
  830. X  if (Table_List == NULL) {
  831. X    Table_List = t;
  832. X    t->prev = t->next = NULL;
  833. X  } else {
  834. X    t->next = Table_List->next;
  835. X    if (t->next != NULL)
  836. X      t->next->prev = t;
  837. X    t->prev = Table_List;
  838. X    t->prev->next = t;
  839. X  }
  840. X
  841. X  return (t);
  842. X}
  843. X     
  844. Xvoid Initialize_Table_List ()
  845. X/* Initializes the array of tables. */
  846. X{
  847. X  Table_List = Local_table = Allocate_Table ();
  848. X  Local_Player_Connection = Allocate_Connection ();
  849. X  
  850. X  Local_Player_Connection->channel = 0;
  851. X  Local_Player_Connection->local   = 1;
  852. X  Local_Player_Connection->spectator = 0;
  853. X  Local_Player_Connection->seat    = local_player;
  854. X  Local_Player_Connection->table   = Local_table;
  855. X  sprintf (Local_Player_Connection->player_name, "%s", local_player_name);
  856. X
  857. X  if (IS_PLAYER(local_player)) {
  858. X    Local_table->Seats[local_player].connection = Local_Player_Connection;
  859. X    Local_table->Seats[local_player].occupied = 1;
  860. X    sprintf (Local_table->Seats[local_player].player_name, "%s", 
  861. X         local_player_name);
  862. X  }
  863. X
  864. X  Link_Output_Connection (Local_table->Players, Local_Player_Connection);
  865. X}
  866. X
  867. Xvoid Initialize_Network_Logfile ()
  868. X{
  869. X#ifdef LOGFILE
  870. X  char buf[80];
  871. X
  872. X  sprintf (buf, "%s.log", local_player_name);
  873. X  net_log = fopen (buf, "w");
  874. X  if (net_log == NULL) {
  875. X    sprintf (buf, "Error opening net_log file %s: %s", buf,
  876. X         sys_errlist[errno]);
  877. X    fflush (net_log);
  878. X    Terminate_Program (buf);
  879. X  }
  880. X#endif
  881. X}
  882. X
  883. Xvoid Initialize_Network ()
  884. X{
  885. X  struct hostent *he;
  886. X  struct passwd *pw_entry;
  887. X  char *my_addr, *user, *name, *p;
  888. X  char buf[100];
  889. X
  890. X#ifdef NO_TM_ZONE
  891. X  struct timeval tp;
  892. X  struct timezone tzp;
  893. X#else
  894. X  time_t current_time;
  895. X  struct tm *decoded_time;
  896. X#endif
  897. X
  898. X  gethostname (Host_name, 100);
  899. X
  900. X  if (!Host_IP_is_known) {
  901. X    he = gethostbyname (Host_name);
  902. X    if (he != NULL) {
  903. X      sprintf (Host_name, "%s", he->h_name);
  904. X/*    in = (struct in_addr *) he->h_addr_list[0];
  905. X      my_addr = inet_ntoa(*in);
  906. X*/
  907. X      my_addr = inet_ntoa(**((struct in_addr **) he->h_addr_list));
  908. X      if (my_addr == NULL) my_addr = "--";
  909. X      sprintf (Host_IP, "%s", my_addr);
  910. X      Host_IP_is_known = 1;
  911. X    } else
  912. X      sprintf (Host_IP, "--");
  913. X  }
  914. X
  915. X  pw_entry = getpwuid (getuid());
  916. X  if (pw_entry != NULL) {
  917. X    sprintf (User_name, "%s", pw_entry->pw_name);
  918. X#ifndef NO_PWCOMMENT
  919. X    if (pw_entry->pw_comment != NULL) {
  920. X      sprintf (User_fullname, "%s", pw_entry->pw_gecos);
  921. X      if ((p = index(User_fullname, ',')) != NULL)
  922. X    *p = '\0';
  923. X    } else 
  924. X#endif
  925. X    if ((name = getenv("NAME")) != NULL)
  926. X      sprintf (User_fullname, "%s", name);
  927. X    else
  928. X      sprintf (User_fullname, "%s", User_name);
  929. X  } else {
  930. X    user = getlogin ();
  931. X    if (user == NULL)
  932. X      user = getenv("USER");
  933. X    if (user == NULL)
  934. X      user = "unknown";
  935. X    sprintf (User_name, "%s", user);
  936. X    name = getenv("NAME");
  937. X    if (name != NULL)
  938. X      sprintf (User_fullname, "%s", name);
  939. X    else if (local_player_full_name != NULL)
  940. X      sprintf (User_fullname, "%s", local_player_full_name);
  941. X    else
  942. X      sprintf (User_fullname, "%s", User_name);
  943. X  }
  944. X
  945. X  if (local_player_email == NULL) {
  946. X    sprintf (buf, "%s@%s", pw_entry->pw_name, Host_name);
  947. X    local_player_email = strdup (buf);
  948. X  }
  949. X
  950. X  if (Timezone_name == NULL) {
  951. X#ifdef NO_TM_ZONE
  952. X    gettimeofday (&tp, &tzp);
  953. X    if (tzp.tz_minuteswest < 0)
  954. X      sprintf (buf, "GMT+%d:%02d", 
  955. X           (-tzp.tz_minuteswest)/60, (-tzp.tz_minuteswest)%60);
  956. X    else
  957. X      sprintf (buf, "GMT-%d:%02d", 
  958. X           tzp.tz_minuteswest/60, tzp.tz_minuteswest%60);
  959. X    Timezone_name = strdup (buf);
  960. X#else
  961. X    time (¤t_time);
  962. X    decoded_time = localtime (¤t_time);
  963. X    Timezone_name = strdup(decoded_time->tm_zone);
  964. X#endif
  965. X  }
  966. X
  967. X  if (Registration == NULL)
  968. X    Registration = strdup (local_player_email);
  969. X
  970. X  /* Allocate a dummy head for the connections list. */
  971. X  Connections = Allocate_Dummy_Connection ();
  972. X
  973. X  Initialize_Network_Logfile ();
  974. X  Initialize_Table_List ();
  975. X  
  976. X  client_mode = server_mode = 0;
  977. X  server_port = listen_port = 0;
  978. X}
  979. X
  980. Xvoid Close_all_connections ()
  981. X{
  982. X  Connection c, d;
  983. X  Table t;
  984. X  int i;
  985. X
  986. X  if (server_mode) {
  987. X    if (listen_port > 0) {
  988. X      close (listen_port);
  989. X      listen_port = 0;
  990. X    }
  991. X
  992. X    for (c = Connections->inext; c != NULL; ) {
  993. X      d = c->inext;
  994. X      if (!c->local) {
  995. X    close_connection (c);
  996. X    Vacate_seat (c->table, c->seat);
  997. X      }
  998. X      c = d;
  999. X    }
  1000. X
  1001. X    GPS_End_Server_Mode ();
  1002. X
  1003. X  } else if (client_mode) {
  1004. X    for (t = Table_List; t != NULL; t = t->next)
  1005. X      for (i = 0; i < 4; i++)
  1006. X    if ((t != Local_table) || (i != local_player))
  1007. X      Vacate_seat (t, i);
  1008. X    if (server_port > 0)
  1009. X      close(server_port);
  1010. X  }
  1011. X
  1012. X/*  Initialize_Table_List (); */
  1013. X  server_mode = client_mode = 0;
  1014. X  server_port = listen_port = 0;
  1015. X}
  1016. X
  1017. Xvoid Switch_Table (c, new_table)
  1018. X     Connection c;
  1019. X     Table new_table;
  1020. X/* Unlinks the connection c from the current table and links it into
  1021. X   the new table.
  1022. X*/
  1023. X{
  1024. X  Unlink_Output_Connection (c);
  1025. X  Link_Output_Connection (new_table->Players, c);
  1026. X}
  1027. X
  1028. Xvoid Setup_server ()
  1029. X/* Initializes the data structures for the server. */
  1030. X{
  1031. X  char comment_buf [80];
  1032. X  int retries;
  1033. X  time_t current_time;
  1034. X  struct tm *decoded_time;
  1035. X
  1036. X  retries = 0;
  1037. X  do {
  1038. X    listen_port = open_port (network_port);
  1039. X    retries = retries + 1;
  1040. X    if (listen_port <= 0)
  1041. X      network_port++;
  1042. X  } while ((listen_port <= 0) && (retries < 10));
  1043. X
  1044. X  if (listen_port <= 0) {
  1045. X    Network_Comment (socket_error);
  1046. X    Network_Comment ("ERROR IN SETTING UP SERVER.");
  1047. X    return;
  1048. X  }
  1049. X
  1050. X  sprintf (comment_buf, "ENTERING SERVER MODE ON PORT %d", network_port);
  1051. X  Network_Comment (comment_buf);
  1052. X
  1053. X  sprintf (PLAYER_NAME(Local_table, PLAYER_SERVER), "%s", local_player_name);
  1054. X
  1055. X  server_mode = 1;
  1056. X  time (¤t_time);
  1057. X  decoded_time = localtime (¤t_time);
  1058. X
  1059. X  sprintf (Server_Start_time, "%2d:%02d %s", decoded_time->tm_hour,
  1060. X       decoded_time->tm_min, Timezone_name);
  1061. X
  1062. X  if (local_player_full_name != NULL)
  1063. X    sprintf (Local_Player_Connection->fullname, "%s", local_player_full_name);
  1064. X  else if (strlen(User_fullname) > 0)
  1065. X    sprintf (Local_Player_Connection->fullname, "%s", User_fullname);
  1066. X
  1067. X  if (local_player_email != NULL)
  1068. X    sprintf (Local_Player_Connection->email, "%s", local_player_email);
  1069. X  sprintf (Local_Player_Connection->registry, "%s", Registration);
  1070. X
  1071. X  GPS_Broadcast_Server ();
  1072. X  Local_Player_Connection->state = CSTATE_PLAYING;
  1073. X
  1074. X}
  1075. X
  1076. Xvoid Assign_seat ();
  1077. X
  1078. Xvoid Attempt_to_connect (seat_requested)
  1079. X     int seat_requested;
  1080. X/* Attempts to connect to the server named as server_name.  If the
  1081. X * connection is successful, then initializes the Table data structure
  1082. X * appropriately and sends an initial handshaking message.
  1083. X */
  1084. X{
  1085. X  int status;  /* Return code from client_init call. */
  1086. X  char message_buf [80];
  1087. X
  1088. X  sprintf (message_buf, "CONNECTING TO SERVER AT %s, PORT %d.", server_name,
  1089. X       network_port);
  1090. X  Network_Comment (message_buf);
  1091. X  restore_cursor ();
  1092. X
  1093. X  status = client_init (server_name, network_port, 3);
  1094. X  if (status < 0) {
  1095. X    Network_Comment (socket_error);
  1096. X    return;
  1097. X  }
  1098. X
  1099. X  Network_Comment ("CONNECTION ESTABLISHED.");
  1100. X  server_port = status;
  1101. X  client_mode = 1;
  1102. X
  1103. X  /* Now we construct the initial handshake message that we send
  1104. X     to the server.  This message is of the form:
  1105. X       HELLO <ver> <player-name>
  1106. X     where
  1107. X       <ver>  is the current version of the program, 
  1108. X       <player-name> is the name of the local player.
  1109. X  */
  1110. X  sprintf (message_buf, "%s HELLO %s %s %s", local_player_name,
  1111. X       major_revision_level, local_player_name,
  1112. X       seat_names[seat_requested]);
  1113. X  fd_writeln (server_port, message_buf);
  1114. X#ifdef LOGFILE
  1115. X  fprintf (net_log, "   > %s\n", message_buf);
  1116. X  fflush (net_log);
  1117. X#endif    
  1118. X
  1119. X  local_player = PLAYER_OBS;
  1120. X  (void) Assign_seat (Local_table, Local_Player_Connection, PLAYER_OBS);
  1121. X  Local_Player_Connection->state = CSTATE_CONNECTED;
  1122. X  Display_Player_Position();
  1123. X
  1124. X  if (local_player_full_name != NULL)
  1125. X    Send_fullname (Local_table, local_player_full_name);
  1126. X  else if (strlen(User_fullname) > 0)
  1127. X    Send_fullname (Local_table, User_fullname);
  1128. X
  1129. X  if (local_player_email != NULL)
  1130. X    Send_email (Local_table, local_player_email);
  1131. X/*  Send_registry (Local_table, Registration); */
  1132. X}
  1133. X
  1134. Xvoid Accept_new_connection ()
  1135. X/* Given that a new connection is waiting to be accepted, makes
  1136. X   the connection and begins processing for it.
  1137. X */
  1138. X{
  1139. X  Connection c;
  1140. X  int fd_conn;  /* file descriptor for the connection. */
  1141. X  struct sockaddr net_addr;
  1142. X  int addrlen;
  1143. X#ifdef DEBUG
  1144. X  char error_buf[80];
  1145. X#endif
  1146. X
  1147. X  addrlen = sizeof(struct sockaddr);
  1148. X  fd_conn = accept (listen_port, &net_addr, &addrlen);
  1149. X  if (fd_conn < 0) {
  1150. X    sprintf (socket_error, "Connection Error: %s", sys_errlist[errno]);
  1151. X    Network_Comment (socket_error);
  1152. X    return;
  1153. X  }
  1154. X
  1155. X#ifdef LOGFILE
  1156. X  fprintf (net_log, "Connection established. fd %d.\n", fd_conn);
  1157. X  fflush (net_log);
  1158. X#endif
  1159. X
  1160. X  c = Allocate_Connection ();
  1161. X  c->local     = 0;
  1162. X  c->channel   = fd_conn;
  1163. X  c->spectator = 0;
  1164. X  c->state     = CSTATE_CONNECTED;
  1165. X  c->seat      = PLAYER_OBS;
  1166. X  c->table     = Local_table;
  1167. X  Link_Output_Connection (Local_table->Players, c);
  1168. X
  1169. X#ifdef DEBUG
  1170. X  sprintf (error_buf, "ACCEPTED A NEW CONNECTION FOR FD %d (%d).",
  1171. X       c->channel, listen_port);
  1172. X  Network_Comment (error_buf);
  1173. X#endif
  1174. X
  1175. X}
  1176. X
  1177. Xstatic void Get_network_message (c, channel)
  1178. X     Connection c; int channel;
  1179. X/* static void Get_network_message (Connection c) */
  1180. X/* Gets a message from connection c, parses it, and processes it. */
  1181. X{
  1182. X  int buflen;                 /* return code from fd_readln */
  1183. X  Message m;                  /* The message which was read from the
  1184. X                 network. */
  1185. X  char msg_buf[100];
  1186. X
  1187. X  m = allocate_message ();
  1188. X  buflen = fd_readln (channel, m->p.command_text, BUFFER_LENGTH);
  1189. X
  1190. X  if (buflen <= 0) {
  1191. X    deallocate_message (m);
  1192. X    if (server_mode) {
  1193. X      sprintf (msg_buf, "THE NETWORK CONNECTION WITH %s HAS BEEN LOST.",
  1194. X           c->player_name);
  1195. X      close_connection (c);
  1196. X      Broadcast_Comment (msg_buf);
  1197. X    } else {
  1198. X      server_port = 0;
  1199. X      Network_Comment 
  1200. X    ("THE NETWORK CONNECTION WITH THE SERVER HAS BEEN LOST.");
  1201. X      Generate_reset (RESET_DISCONNECT);
  1202. X    }
  1203. X  } else {
  1204. X    m->source = c;
  1205. X    if (server_mode) {
  1206. X      Parse_Command_for_Server (&(m->p));
  1207. X      m->p.player_no = c->seat;
  1208. X    } else {
  1209. X      Parse_Command_for_Client (&(m->p));
  1210. X    }
  1211. X
  1212. X    /* If we are receiving a BOARD or a RECORD message, then we must also
  1213. X       recieve the associated in-line data. */
  1214. X    if (m->p.command == CMD_BOARD)
  1215. X      m->p.data.board.record = Receive_board (c);
  1216. X    else if (m->p.command == CMD_RECORD)
  1217. X      m->p.data.record.play = Receive_play_record (c);
  1218. X
  1219. X    enqueue_message (c->table->protocol_queue, m);
  1220. X#ifdef LOGFILE
  1221. X  fprintf (net_log, "%2d < %s\n", channel, m->p.command_text);
  1222. X  fflush (net_log);
  1223. X#endif
  1224. X  }
  1225. X}
  1226. X
  1227. Xstatic void Wait_for_keyboard_event ()
  1228. X{
  1229. X  int status;                 /* return code from Select call. */
  1230. X  struct fd_set wait_set;     /* A set representing the connections that
  1231. X                 have been established. */
  1232. X
  1233. X  do {
  1234. X    FD_ZERO (&wait_set);
  1235. X    FD_SET (fileno(stdin), &wait_set);
  1236. X  
  1237. X    if (GPS_request_in_progress && (GPS_socket > 0))
  1238. X      FD_SET (GPS_socket, &wait_set);
  1239. X    
  1240. X    status = select (FD_SETSIZE, &wait_set, (fd_set *) 0, (fd_set *) 0,
  1241. X             (struct timeval *) 0);
  1242. X
  1243. X    if ((status < 0) && (errno != EINTR)) {
  1244. X      sprintf (socket_error, "Error in select: %s", sys_errlist[errno]);
  1245. X      Network_Comment (socket_error);
  1246. X    }
  1247. X  } while (status < 0);
  1248. X}
  1249. X
  1250. Xstatic void Client_wait_for_network_event ()
  1251. X{
  1252. X  int status;                 /* return code from Select call. */
  1253. X  struct fd_set wait_set;     /* A set representing the connections that
  1254. X                 have been established. */
  1255. X
  1256. X  do {
  1257. X    FD_ZERO (&wait_set);
  1258. X    FD_SET (fileno(stdin), &wait_set);
  1259. X    FD_SET (server_port,   &wait_set);
  1260. X
  1261. X    if (GPS_request_in_progress && (GPS_socket > 0))
  1262. X      FD_SET (GPS_socket, &wait_set);
  1263. X    
  1264. X    status = select (FD_SETSIZE, &wait_set, (fd_set *) 0, (fd_set *) 0, 
  1265. X             (struct timeval *) 0);
  1266. X  
  1267. X    if ((status < 0) && (errno != EINTR)) {
  1268. X      sprintf (socket_error, "Error in select: %s", sys_errlist[errno]);
  1269. X      Network_Comment (socket_error);
  1270. X    }
  1271. X  } while (status < 0);
  1272. X
  1273. X  if (FD_ISSET(server_port, &wait_set))
  1274. X    Get_network_message (Local_Player_Connection, server_port);
  1275. X}
  1276. X
  1277. Xstatic void Server_wait_for_network_event ()
  1278. X{
  1279. X  Connection c, d;
  1280. X  int status;                 /* return code from Select call. */
  1281. X  struct fd_set wait_set;     /* A set representing the connections that
  1282. X                 have been established. */
  1283. X  int connection_available, data_available;
  1284. X
  1285. X  data_available = 0;
  1286. X  do {
  1287. X    FD_ZERO (&wait_set);
  1288. X    
  1289. X    /* Set bits in the wait_set corresponding to the open socket 
  1290. X       descriptors which we have at the moment. */
  1291. X    FOREACH_CONNECTION (c) {
  1292. X      if (!c->local)
  1293. X    FD_SET (c->channel, &wait_set);
  1294. X    }
  1295. X    
  1296. X    FD_SET (fileno(stdin), &wait_set);
  1297. X    if (listen_port > 0)
  1298. X      FD_SET (listen_port, &wait_set);
  1299. X
  1300. X    if (GPS_request_in_progress && (GPS_socket > 0))
  1301. X      FD_SET (GPS_socket, &wait_set);
  1302. X    
  1303. X    status = select (FD_SETSIZE, &wait_set, (fd_set *) 0, (fd_set *) 0, 
  1304. X             (struct timeval *) 0);
  1305. X
  1306. X    connection_available = 0;
  1307. X    if (status < 0) {
  1308. X      if (errno != EINTR) {
  1309. X    sprintf (socket_error, "Error in select: %s", sys_errlist[errno]);
  1310. X    Network_Comment (socket_error);
  1311. X      }
  1312. X      data_available = 0;
  1313. X    } else {
  1314. X      connection_available = ((listen_port > 0) 
  1315. X                  && FD_ISSET(listen_port, &wait_set));
  1316. X      if (connection_available)
  1317. X    data_available = status - 1;
  1318. X      else
  1319. X    data_available = 1;
  1320. X    } 
  1321. X
  1322. X    if (connection_available)
  1323. X      Accept_new_connection ();
  1324. X
  1325. X  } while (!data_available);
  1326. X
  1327. X  c = Connections->inext;
  1328. X  while (c != NULL) {
  1329. X    d = c->inext;
  1330. X    if (!c->local && FD_ISSET(c->channel, &wait_set)) {
  1331. X    FD_CLR(c->channel, &wait_set);
  1332. X    Get_network_message (c, c->channel);
  1333. X      }
  1334. X    c = d;
  1335. X  }
  1336. X
  1337. X}
  1338. X
  1339. Xvoid Wait_for_network_event ()
  1340. X/* Waits for an input event to occur.  If a network event occurs, then
  1341. X   parses the message and places it onto the proper player queue.
  1342. X*/
  1343. X{
  1344. X  if (server_mode)
  1345. X    Server_wait_for_network_event ();
  1346. X  else if (client_mode)
  1347. X    Client_wait_for_network_event ();
  1348. X  else
  1349. X    Wait_for_keyboard_event ();
  1350. X
  1351. X#ifdef MDEBUG
  1352. X  if (!malloc_verify ()) {
  1353. X    Moderator_Comment ("A MEMORY ALLOCATION ERROR HAS BEEN DETECTED!");
  1354. X    access_error_routine ();
  1355. X  }
  1356. X#endif
  1357. X}
  1358. X
  1359. END_OF_FILE
  1360. if test 33079 -ne `wc -c <'network.c'`; then
  1361.     echo shar: \"'network.c'\" unpacked with wrong size!
  1362. fi
  1363. # end of 'network.c'
  1364. fi
  1365. if test -f 'protocol.h' -a "${1}" != "-c" ; then 
  1366.   echo shar: Will not clobber existing file \"'protocol.h'\"
  1367. else
  1368. echo shar: Extracting \"'protocol.h'\" \(18086 characters\)
  1369. sed "s/^X//" >'protocol.h' <<'END_OF_FILE'
  1370. X/* command.h -- Data structures and procedures used for communicating
  1371. X *              between the client and server.
  1372. X *
  1373. X ! Copyright (C) 1990-1992 by Matthew Clegg.  All Rights Reserved
  1374. X ! 
  1375. X ! OKbridge is made available as a free service to the Internet.
  1376. X ! Accordingly, the following restrictions are placed on its use:
  1377. X ! 
  1378. X ! 1.  OKbridge may not be modified in any way without the explicit 
  1379. X !     permission of Matthew Clegg.  
  1380. X ! 
  1381. X ! 2.  OKbridge may not be used in any way for commercial advantage.
  1382. X !     It may not be placed on for-profit networks or on for-profit
  1383. X !     computer systems.  It may not be bundled as part of a package
  1384. X !     or service provided by a for-profit organization.
  1385. X ! 
  1386. X ! If you have questions about restrictions on the use of OKbridge,
  1387. X ! write to mclegg@cs.ucsd.edu.
  1388. X ! 
  1389. X ! DISCLAIMER:  The user of OKbridge accepts full responsibility for any
  1390. X ! damage which may be caused by OKbridge.
  1391. X *
  1392. X * This module defines the data structures and procedures used for
  1393. X * communicating with between the client and server.  When a command
  1394. X * is received from a remote machine, this module unpacks it into
  1395. X * a data structure representing the command.  When we wish to send
  1396. X * a command to a remote machine, we invoke a procedure which is
  1397. X * tailored for that command. 
  1398. X */
  1399. X
  1400. X#define PROTOCOL_INCLUDED
  1401. X
  1402. Xenum
  1403. X{
  1404. X  CMD_ERROR = 0,
  1405. X  CMD_ACK,
  1406. X  CMD_ALERT,
  1407. X  CMD_BEGIN,
  1408. X  CMD_BID,
  1409. X  CMD_BOARD,
  1410. X  CMD_CC,
  1411. X  CMD_CLAIM,
  1412. X  CMD_CLAIMREQ,
  1413. X  CMD_CLAIMRESP,
  1414. X  CMD_COMMENT,
  1415. X  CMD_CONNERR,
  1416. X  CMD_DEAL,
  1417. X  CMD_ECHO,
  1418. X  CMD_EMAIL,
  1419. X  CMD_END,
  1420. X  CMD_FULLNAME,
  1421. X  CMD_HELLO,
  1422. X  CMD_MODE,
  1423. X  CMD_NAME,
  1424. X  CMD_PING,
  1425. X  CMD_PLAY,
  1426. X  CMD_PLAYREQ,
  1427. X  CMD_QUIT,
  1428. X  CMD_RECONNECT,
  1429. X  CMD_RECORD,
  1430. X  CMD_REGISTRY,
  1431. X  CMD_RESET,
  1432. X  CMD_SCORE,
  1433. X  CMD_SEAT,
  1434. X  CMD_SEATERR,
  1435. X  CMD_SEATPOS,
  1436. X  CMD_SEATREQ,
  1437. X  CMD_SERVEREQ,
  1438. X  CMD_SKIP,
  1439. X  CMD_SKIPACK,
  1440. X  CMD_SPEC,
  1441. X  CMD_TABLE,
  1442. X  CMD_TABLEREQ,
  1443. X  CMD_TALK,
  1444. X  CMD_USEREC,
  1445. X  CMD_WAKEUP,
  1446. X  CMD_WHO,
  1447. X  CMD_WHOIS,
  1448. X  CMD_WHORESP,
  1449. X
  1450. X  CMD_MAX
  1451. X};
  1452. X
  1453. X#define TALK_RCPT_LHO  0
  1454. X#define TALK_RCPT_RHO  1
  1455. X#define TALK_RCPT_OPPS 2
  1456. X#define TALK_RCPT_SPEC 3
  1457. X#define TALK_RCPT_ALL  4
  1458. X
  1459. X#define DUMMY  int
  1460. X
  1461. Xtypedef struct Connection_struct *Conn;
  1462. Xtypedef struct Board_struct *board_ptr;
  1463. Xtypedef struct Play_record_struct *play_ptr;
  1464. Xtypedef struct Table_struct *Table_ptr;
  1465. X
  1466. Xtypedef struct player_command_struct {
  1467. X  int command;
  1468. X  int player_no;
  1469. X  name_buffer    player_name;
  1470. X  command_buffer command_text;
  1471. X  union {
  1472. X    struct { message_buffer message;    }  error;
  1473. X    struct { name_buffer   player_name;
  1474. X         int           position; }     ack;
  1475. X    int                                    alert;
  1476. X    DUMMY                                  begin;
  1477. X    struct { int level, alert; }           bid;
  1478. X    struct { name_buffer   board_name;
  1479. X         int           board_no; 
  1480. X         board_ptr     record; }       board;
  1481. X    message_buffer                         cc;
  1482. X    struct { int no_tricks; }              claim;
  1483. X    struct { int no_tricks; }              claimreq;
  1484. X    int                                    claimresp;
  1485. X    message_buffer                         comment;
  1486. X    message_buffer                         connerr;
  1487. X    DUMMY                                  deal;
  1488. X    struct { name_buffer ping_source; }    echo;
  1489. X    struct { message_buffer addr; }        email;
  1490. X    DUMMY                                  end;
  1491. X    struct { message_buffer  name; }       fullname;
  1492. X    struct { name_buffer  version;
  1493. X         name_buffer  player_name; 
  1494. X         int          seat_req; }      hello;
  1495. X    int                                    mode;
  1496. X    struct { name_buffer  new_name; }      name;
  1497. X    DUMMY                                  ping;
  1498. X    int                                    play;
  1499. X    struct { int  card;
  1500. X             int  play_no; }               playreq;
  1501. X    DUMMY                                  quit;
  1502. X    struct {name_buffer ip;
  1503. X            int         port;
  1504. X            int         seat; }            reconnect;
  1505. X    struct { name_buffer board_name;
  1506. X         int         board_no;
  1507. X         play_ptr    play; }           record;
  1508. X    struct { message_buffer id; }          registry;
  1509. X    DUMMY                                  reset;
  1510. X    struct { int above[2];
  1511. X         int below[2]; }               score;
  1512. X    struct { name_buffer player_name;
  1513. X             int         old_pos; 
  1514. X             int         new_pos; }        seat;
  1515. X    struct { int free_seats[3]; }          seaterr;
  1516. X    int                                    seatpos;
  1517. X    int                                    seatreq;
  1518. X    struct { message_buffer command; }     servereq;
  1519. X    DUMMY                                  skip;
  1520. X    DUMMY                                  skipack;
  1521. X    DUMMY                                  spec;
  1522. X    int                                    table;
  1523. X    int                                    tablereq;
  1524. X    struct { int recipients;
  1525. X             message_buffer  message; }    talk;
  1526. X    struct { name_buffer north;
  1527. X         name_buffer east;
  1528. X         name_buffer south;
  1529. X         name_buffer west; }           userec;
  1530. X    struct { name_buffer recipient; }      wakeup;
  1531. X    DUMMY                                  who;
  1532. X    struct { name_buffer name; }           whois;
  1533. X    struct { name_buffer recipient;
  1534. X         message_buffer message; }     whoresp;
  1535. X  } data;
  1536. X} *player_command;
  1537. X
  1538. X
  1539. X/* The following routines define the interface for sending messages
  1540. X * to remote processes. */
  1541. X
  1542. Xvoid Send_ack ();
  1543. Xvoid Send_alert ();
  1544. Xvoid Send_begin ();
  1545. Xvoid Send_bid ();
  1546. Xvoid Send_board ();
  1547. Xvoid Send_cc ();
  1548. Xvoid Send_claim ();
  1549. Xvoid Send_claimreq ();
  1550. Xvoid Send_claimresp ();
  1551. Xvoid Send_comment ();
  1552. Xvoid Send_deal ();
  1553. Xvoid Send_echo ();
  1554. Xvoid Send_email ();
  1555. Xvoid Send_end ();
  1556. Xvoid Send_fullname ();
  1557. Xvoid Send_hello ();
  1558. Xvoid Send_mode ();
  1559. Xvoid Send_name ();
  1560. Xvoid Send_ping ();
  1561. Xvoid Send_play ();
  1562. Xvoid Send_playreq ();
  1563. Xvoid Send_quit ();
  1564. Xvoid Send_reconnect ();
  1565. Xvoid Send_record ();
  1566. Xvoid Send_registry ();
  1567. Xvoid Send_reset ();
  1568. Xvoid Send_seat  ();
  1569. Xvoid Send_seaterr ();
  1570. Xvoid Send_seatreq ();
  1571. Xvoid Send_score ();
  1572. Xvoid Send_servereq ();
  1573. Xvoid Send_skip ();
  1574. Xvoid Send_skipack ();
  1575. Xvoid Send_spec ();
  1576. Xvoid Send_tablereq ();
  1577. Xvoid Send_table ();
  1578. Xvoid Send_talk ();
  1579. Xvoid Send_userec ();
  1580. Xvoid Send_wakeup ();
  1581. Xvoid Send_who ();
  1582. Xvoid Send_whois ();
  1583. Xvoid Send_whoresp ();
  1584. X
  1585. X/* The following two procedures are for parsing messages which have 
  1586. X * received from remote hosts.  The difference is that in the first
  1587. X * procedure, we assume that the identifier of the sender is prefixed
  1588. X * to the message.  Both procedures assume that the input buffer is
  1589. X * stored in the player command record which is passed to the procedure.
  1590. X * Both return 0 if no errors were encountered in parsing the message
  1591. X * and -1 if an error was encountered.
  1592. X */
  1593. X
  1594. Xint Parse_Command_for_Client ();
  1595. Xint Parse_Command_for_Server ();
  1596. X
  1597. X
  1598. X/* DESCRIPTION OF PROTOCOL.
  1599. X
  1600. XAll messages transmitted by okbridge are ASCII strings terminated by
  1601. Xthe characters "\015\012" (^M^J).  Each message is interpreted and
  1602. Xconverted into a data structure with the help of the parser module.
  1603. XEach message has a header which identifies the origin of the message.
  1604. XThis is followed by a keyword which identifies the message type.
  1605. XFollowing the keyword, there may be additional type-specific
  1606. Xparameters.  The fields in a message are separated by blanks.
  1607. X
  1608. XWhen a client sends a message to the server, it is of the form
  1609. X  <client-name> <message-type> <parameters>,
  1610. Xwhere
  1611. X  <client-name>  is the 8-character nickname of the client,
  1612. X  <message-type> is a keyword identifying the type of the message,
  1613. X  <parameters>   is an optional list of message-specific parameters.
  1614. X
  1615. XWhen a server sends a message to a client, it is of the form
  1616. X  <position> <source> <message-type> <parameters>
  1617. Xwhere
  1618. X  <position>     is the name of the position occupied by the person
  1619. X                 who originated the message.  May be North, South,
  1620. X                 East, West, Mod or Obs.
  1621. X  <source>       is the nickname of the person who originated the message.
  1622. X  <message-type> is a keyword identifying the type of the message.
  1623. X  <parameters>   is an optional list of message-specific parameters.
  1624. X
  1625. XTo see the sequence of messages transmitted by the program, compile
  1626. Xwith the -DLOGFILE option.
  1627. X
  1628. XThere are three kinds of messages: protocol messages, conversational
  1629. Xmessages and game messages.  A protocol message affects the
  1630. Xcommunications behavior of the program, and is typically handled
  1631. Xdifferently according to whether we are operating in client mode or in
  1632. Xserver mode.  For example, the initial handshakes which are performed
  1633. Xwhen a new client connects to the server are protocol messages.
  1634. XConversational messages may affect the state of the game, but not
  1635. Xdirectly.  Examples of asynchronous messages are talk messages and
  1636. Xclaim requests.  Game messages convey changes to the state of the
  1637. Xgame.  Examples of game messages are deals, bids and plays.
  1638. X
  1639. XAll messages are first received by the network module.  This module
  1640. Xthen parses the message, creating a data structure as defined in
  1641. Xprotocol.h to represent the message.  The message is then put onto the
  1642. Xprotocol queue associated to the table.  The protocol queue is handled
  1643. Xby the cs module.  After a message from the protocol queue is handled,
  1644. Xit may be put onto the conversational queue.  The conversational queue
  1645. Xis handled by the conversation module.  After a message from the
  1646. Xconversational queue is handled, it may finally be put onto the game
  1647. Xqueue.  The game queue is handled by the bridge module.
  1648. X
  1649. X
  1650. XProtocol Messages
  1651. X-------- --------
  1652. X
  1653. X   HELLO <version> <player-name> <position>
  1654. X     When a client first establishes a connection to the server, it sends
  1655. X     a hello message identifying itself to the server.  If the server
  1656. X     recognizes the client as using a compatible version of okbridge,
  1657. X     then the HELLO message is relayed to the other clients.
  1658. X     <version>     := a string identifying the major revision level
  1659. X     <player-name> := an eight character name identifying the player
  1660. X     <position>    := the seat requested by the player
  1661. X
  1662. X   ACK <player-name> <position>
  1663. X     After verifying that the client is using the same version of okbridge,
  1664. X     the server sends to the client an ACK message for itself and for each
  1665. X     of the other clients, identifying all of the current connections.
  1666. X     <player-name>  := an eight character name identifying the player
  1667. X     <position>     := the current position of the player.
  1668. X
  1669. X   CONNERR <message>
  1670. X     Used by the server to transmit error messages in case the client
  1671. X     is not using the same version of okbridge.  The message is followed
  1672. X     by closing the socket connection.
  1673. X
  1674. X   ECHO <ping-source>
  1675. X     A message sent in response to a PING request.
  1676. X
  1677. X   PING
  1678. X     Sends a message which is automatically echoed by each of the other
  1679. X     players.  The purpose of this message is to determine how quickly
  1680. X     the networks are operating.
  1681. X
  1682. X   QUIT
  1683. X     Transmitted by a player as the final message before closing the
  1684. X     socket connection.
  1685. X
  1686. X   RECONNECT <ip-name-or-number> <port-number> <seat-name>
  1687. X     Transmitted by the server to a player, indicating that the player
  1688. X     should reconnect to a new server at the given ip/port and sit
  1689. X     at the specified seat.
  1690. X
  1691. X   SEAT <old-seat> <new-seat> <player-name>
  1692. X     Transmitted by the server to indicate that a player has changed seats.
  1693. X     If this message names the local player, it should be ignored.
  1694. X     Otherwise, problems may arise when two people choose the same name.
  1695. X
  1696. X   SEATERR <seat1> <seat2> <seat3>
  1697. X     Transmitted by the server to indicate that the requested seat
  1698. X     is not available.  The seats <seat1>, <seat2>, <seat3> list the
  1699. X     currently free seats.
  1700. X
  1701. X   SEATPOS <seat-assignment>
  1702. X     Indicates the seat which is assigned to the local player.
  1703. X     This is a mandatory assignment, and may not necessarily be the
  1704. X     seat which was requested.  Note that when a player makes a
  1705. X     seat request, he will receive a SEATPOS assignment and also
  1706. X     a SEAT message which is broadcast to everyone.
  1707. X
  1708. X   SEATREQ <seat-name>
  1709. X     Requests a seat assignment from the server.
  1710. X
  1711. X   SERVEREQ <command-name>
  1712. X     Makes a special request to the server which is not covered by
  1713. X     the other message types.  The server is free to the <command-name>
  1714. X     in an implementation-specific way.
  1715. X
  1716. X   TABLEREQ <requested-table-number>
  1717. X     Transmitted by a player to make a request to sit at the requested
  1718. X     table.  
  1719. X
  1720. X   TABLE <assigned-table-number>
  1721. X     Transmitted by the server to the table number assigned to the local
  1722. X     player.
  1723. X
  1724. X   
  1725. XConversational Messages
  1726. X-------------- --------
  1727. X
  1728. X   COMMENT <message>
  1729. X     Carries a message to each of the players which is from the MODERATOR.
  1730. X
  1731. X   CC <convention-card>
  1732. X     Conveys the convention card which is in use by the team of the person
  1733. X     sending the message.
  1734. X     
  1735. X   CLAIMREQ n
  1736. X     Transmits a request by the declarer to claim n additional tricks.
  1737. X     The defenders should each respond with a CLAIMRESP message.
  1738. X
  1739. X   CLAIMRESP YES|NO
  1740. X     Transmits a defender's response to a claim request message.
  1741. X
  1742. X   EMAIL <email-address>
  1743. X     Transmits a player's email address to the server.
  1744. X
  1745. X   FULLNAME <full-name>
  1746. X     Transmits a players full name to the server.
  1747. X
  1748. X   NAME <new-name>
  1749. X     Informs the other players that the local player has changed his
  1750. X     or her name.
  1751. X
  1752. X   REGISTRY <identification>
  1753. X     Registers a unique identity for the player with the server.
  1754. X     This is independent of the name supplied with the NAME command,
  1755. X     which may change.
  1756. X   
  1757. X   RESET
  1758. X     Resets the program to its initial state.  Can only be used by server.
  1759. X
  1760. X   SPEC
  1761. X     Sent by a player to indicate that s/he has entered spectator mode.
  1762. X
  1763. X   TALK <recipients> <message>
  1764. X     Sends a message to the <recipients> from a player.
  1765. X
  1766. X   WAKEUP [<player-name>|ALL]
  1767. X     Sends a wake-up signal.  If <player-name> is specified, then
  1768. X     only that player receives the signal.  Otherwise, everyone receives
  1769. X     the signal.
  1770. X
  1771. X   WHO
  1772. X     Asks the server to generate a brief list of the people who are
  1773. X     currently connected.
  1774. X
  1775. X   WHOIS <player-name>
  1776. X     Asks the player with name <player-name> to identify him/herself.
  1777. X     The identification should consist of the player's full name and
  1778. X     email address.
  1779. X
  1780. X   WHORESP <recipient> <ident>
  1781. X     A response to the WHOIS message, <recipient> is the name of the
  1782. X     player who issued the WHOIS message and <ident> is the identifying
  1783. X     message returned by the target of the WHOIS message.
  1784. X
  1785. X
  1786. XGame Messages
  1787. X---- --------
  1788. X
  1789. X   ALERT <formal-mode>
  1790. X     Alerts the most recent bid by the partner of the transmitting player.
  1791. X     <formal-mode> is a boolean flag indicating whether or not the
  1792. X     bid should be displayed to partner as well.
  1793. X
  1794. X   BEGIN
  1795. X     Sent by the server to announce that bidding should begin.
  1796. X
  1797. X   BID <bid-name> [ALERT]
  1798. X     Reports a bid to the other players.  The keyword ALERT appears
  1799. X     if the bidder has alerted his own bid.
  1800. X
  1801. X   BOARD <board-name> <sequence-number>
  1802. X     Initiates the transmission of a board for play.  The board name
  1803. X     and number are recorded along with the board itself in the local
  1804. X     database of boards.  The board itself is then transmitted as
  1805. X     in-line data following the message.  This board is then made the
  1806. X     Local_board, so it will be used in subsequent play.
  1807. X     Any subsequent RECORD messages apply to this board.
  1808. X
  1809. X   CLAIM n
  1810. X     Claims n of the remaining tricks.  Can only be used by the declarer
  1811. X     when it is his turn to play.  
  1812. X
  1813. X   DEAL
  1814. X     Sent by the server to announce the beginning of a new hand.
  1815. X     This message will be followed by a BOARD message plus one or more
  1816. X     RECORD messages from the server.  Bidding will be allowed to
  1817. X     commence when the server sends a BEGIN command.
  1818. X
  1819. X   END
  1820. X     Sent by the server at the end of each hand to cause each client
  1821. X     to re-enter the initial mode.
  1822. X
  1823. X   MODE CLUB|PRACTICE|FORMAL
  1824. X     Sets the playing mode.  Club mode is the usual mode of play.
  1825. X     Practice mode is used by two or more people to practice bidding
  1826. X     and planning play.  In practice mode, the following behaviors occur:
  1827. X       1. During bidding, the players who are sitting may bid.  When it
  1828. X          is time for a bid to be made from an unoccupied seat, the program
  1829. X          automatically supplies a bid of "PASS".
  1830. X       2. During play, the cards are revealed to everyone, and anyone may
  1831. X          specify the next card to be played.
  1832. X       3. A hand may be ended with the /claim command.  Confirmation is not
  1833. X          requested for claims.
  1834. X     In formal mode, the following actions are taken:
  1835. X       1. Alerts are not shown to the partner of the person making the alert.
  1836. X       2. Talk messages are not transmitted to partner.
  1837. X       3. The dummy is not displayed to anyone until the opening lead has
  1838. X          been made.
  1839. X       4. The bidding cannot be reviewed after play has begun.
  1840. X
  1841. X   PLAY <card-name>
  1842. X     Reports a play of a card to the other players.
  1843. X
  1844. X   PLAYREQ <card-name> <sequence-number>
  1845. X     Requests from the server that the given card be played for the
  1846. X     given play number.  The PLAYREQ message is needed only during
  1847. X     practice mode, when multiple players may attempt to play 
  1848. X     simultaneously.
  1849. X
  1850. X   RECORD <board-name> <sequence-number>
  1851. X     Initiates the transmission of a record of play.  The record itself
  1852. X     is transmitted as in-line data following the message.  This record
  1853. X     pertains to the board having the given name and number.  If this is
  1854. X     the name of the current board.
  1855. X
  1856. X   SCORE <above-ns> <above-ew> <below-ns> <below-ew>
  1857. X     A message sent by the server to indicate the current scoring totals.
  1858. X     This may be independent of the scores which have been computed for
  1859. X     the hands that have been played.
  1860. X
  1861. X   SKIP
  1862. X     Ends the current hand prematurely, causing all players to return
  1863. X     to STARTUP_MODE.  Does not record a score for the hand.
  1864. X
  1865. X   SKIPACK
  1866. X     Sent by each player in response to a skip message, indicating that
  1867. X     the local player has reset his state to startup mode.
  1868. X
  1869. X   USEREC <north-name> <east-name> <south-name> <west-name>
  1870. X     A message sent by the server to indicate which play record should
  1871. X     is being used in the current hand.
  1872. X*/
  1873. END_OF_FILE
  1874. if test 18086 -ne `wc -c <'protocol.h'`; then
  1875.     echo shar: \"'protocol.h'\" unpacked with wrong size!
  1876. fi
  1877. # end of 'protocol.h'
  1878. fi
  1879. echo shar: End of archive 7 \(of 14\).
  1880. cp /dev/null ark7isdone
  1881. MISSING=""
  1882. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ; do
  1883.     if test ! -f ark${I}isdone ; then
  1884.     MISSING="${MISSING} ${I}"
  1885.     fi
  1886. done
  1887. if test "${MISSING}" = "" ; then
  1888.     echo You have unpacked all 14 archives.
  1889.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  1890. else
  1891.     echo You still need to unpack the following archives:
  1892.     echo "        " ${MISSING}
  1893. fi
  1894. ##  End of shell archive.
  1895. exit 0
  1896.