home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / src / exampleCode / games / IndiZone / vroom / client.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-08-02  |  42.5 KB  |  1,902 lines

  1. /*
  2.  * Copyright (C) 1994, Silicon Graphics, Inc.
  3.  * All Rights Reserved.
  4.  *
  5.  * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
  6.  * the contents of this file may not be disclosed to third parties, copied or
  7.  * duplicated in any form, in whole or in part, without the prior written
  8.  * permission of Silicon Graphics, Inc.
  9.  *
  10.  * RESTRICTED RIGHTS LEGEND:
  11.  * Use, duplication or disclosure by the Government is subject to restrictions
  12.  * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
  13.  * and Computer Software clause at DFARS 252.227-7013, and/or in similar or
  14.  * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
  15.  * rights reserved under the Copyright Laws of the United States.
  16.  */
  17. #include <stdio.h>
  18. #include <unistd.h>
  19. #include <fcntl.h>
  20. #include <sys/types.h>
  21. #include <sys/socket.h>
  22. #include <sys/time.h>
  23. #include <netinet/in.h>
  24. #include <arpa/inet.h>
  25. #include <netdb.h>
  26. #include <errno.h>
  27. #include <string.h>
  28. #include <malloc.h>
  29. #include <bstring.h>
  30.  
  31. #include <X11/Intrinsic.h>
  32. #include <X11/StringDefs.h>
  33. #include <Xm/Xm.h>
  34. #include <Xm/List.h>
  35. #include <GL/GLwDrawA.h>
  36. #include "Row.h"
  37.  
  38.  
  39. #include "vroom.h"
  40. #include "server.h"
  41. #include "client.h"
  42. #include "multicast.h"
  43. #include "playmode.h"
  44. #include "track.h"
  45. #include "ogl.h"
  46. #include "solo.h"
  47. #include "sound.h"
  48. #include "messages.h"
  49.  
  50. typedef void    (*ClientFunc)( void ) ;
  51.  
  52. #define    CLIENT_ST_INIT        0
  53. #define    CLIENT_ST_WAIT_TO_JOIN    1
  54. #define    CLIENT_ST_SELECT_TRACK    2
  55.  
  56. #define    WAIT_TO_JOIN_DELAY    (5.0f)    /* seconds */
  57.  
  58. typedef    struct _serverHost {
  59.     long            id ;
  60.     int            nClients ;
  61.     long            status ;
  62.     XmString        name ;
  63.     float            nextDeadLine ;
  64.     struct _serverHost    *next ;
  65.     } ServerHost ;
  66.  
  67. /* BEGIN PROTOTYPES -S client.c */
  68. static ServerHost *     addServerToList( long id, int n, long status ) ;
  69. static ServerHost *     checkForNewHost( outStatusPacket *bp ) ;
  70. static void             checkServers( void ) ;
  71. static void             clientLoadCourse( long courseId ) ;
  72. static void             clientNoOp( void ) ;
  73. static void             clientPostRace( void ) ;
  74. static void             clientPostTrial( void ) ;
  75. static void             clientPreRace( void ) ;
  76. static void             clientPreTrial( void ) ;
  77. static void             clientRace( void ) ;
  78. static void             clientSpectate( void ) ;
  79. static void             clientTrial( void ) ;
  80. static void             clientWaitToJoin( void ) ;
  81. static void             closeOutput( void ) ;
  82. static int              connectToServer( long id ) ;
  83. static XmString         createServerListString( long id, int nClients,
  84.                             long status ) ;
  85. static void             findSelf( long serverId, long playerId[] ) ;
  86. static ServerHost *     findServer( long id ) ;
  87. static int              processServerAckPacket( outAckPacket *packet,
  88.                             int size ) ;
  89. static int              processServerCoursePacket( outCoursePacket *packet,
  90.                             int size ) ;
  91. static int              processServerMsgPacket( outMsgPacket *packet,
  92.                             int size ) ;
  93. static int              processServerNamePacket( outNamePacket *packet,
  94.                             int size ) ;
  95. static int              processServerRacePacket( outRacePacket *packet,
  96.                             int size ) ;
  97. static int              processServerStatusPacket( outStatusPacket *packet,
  98.                             int size ) ;
  99. static void             removeServer( long id ) ;
  100. static void             resetToServerSearch( void ) ;
  101. static void             sendServerInfoPacket( int option, int choice ) ;
  102. static void             sendServerNamePacket( void ) ;
  103. static void             sendServerSpeedPacket( void ) ;
  104. static void             serverSelectionCB( Widget w, Widget list,
  105.                             XtPointer callData ) ;
  106. static void             serverStatsCB( Widget w, Widget list,
  107.                             XtPointer callData ) ;
  108. static void             updateSpeed( void ) ;
  109. /* END PROTOTYPES -S client.c */
  110.  
  111. char                teamPosition[MAX_PLAYERS] ;
  112. int                steering ;
  113. long                gameServerId = 0 ;
  114.  
  115. extern int            debugOn ;
  116. extern int            raceLaps ;
  117. extern int            errno ;
  118. extern int            h_errno ;
  119. extern int            nTracks ;
  120. extern int            nCars ;
  121. extern int            startLastLap ;
  122. extern char            *basename ;
  123. extern char            *myName ;
  124. extern float            currentTime ;
  125. extern float            startSoundTime ;
  126. extern float            speedFactor ;
  127. extern long            myHostId ;
  128. extern Widget            serverCourseForm ;
  129. extern Widget            courseVoteForm ;
  130. extern Widget            resultsForm ;
  131. extern Widget            previewForm ;
  132. extern Widget            serverForm ;
  133. extern Widget            mainOgl ;
  134. extern int            self ;
  135. extern PlayerStruct        player[] ;
  136. extern Car            cars[] ;
  137. extern Sfx            collideSfx ;
  138. extern Sfx            changeLaneSfx ;
  139. extern Sfx            crashSfx ;
  140. extern Sfx            lastlapSfx ;
  141. extern Sfx            motorSfx ;
  142. extern Sfx            squealSfx ;
  143. extern Sfx            startSfx ;
  144. extern Sfx            toneSfx ;
  145.  
  146. static ServerHost        *serverList = NULL ;
  147. static int            nServers = 0 ;
  148. static int            showRecord ;
  149. static Widget            serverSelector ;
  150. static Widget            serverSelectorList ;
  151. static int            inFd ;
  152. static struct sockaddr_in    inAddr ;
  153. static int            outFd = -1 ;
  154. static struct sockaddr_in    outAddr ;
  155. static int            inputLength ;
  156. static int            lookForSelf = 0 ;
  157. static Widget            serverCourseSelector = NULL ;
  158. static float            deadLineToServer = 0.0f ;
  159. static float            nextDeadLine = 0.0f ;
  160. static float            joinDeadLine = 0.0f ;
  161. static float            joinServerTime = 0.0f ;
  162. static ClientFunc        clientFunc ;
  163. static long            loadedCourseId = -1 ;
  164. static outRacePacket        racePacket ;
  165. static inSpeedPacket        speedPacket ;
  166. static int            newRacePacket = 0 ;
  167. static int            checkIds = 0 ;
  168. static long            serverStatus = 0 ;
  169. static int            activePlayer = 0 ;
  170.  
  171. static char            *serverStatusString[] = {
  172.                     "initializing",
  173.                     "course selection",
  174.                     "loading course",
  175.                     "preparing time trials",
  176.                     "running time trials",
  177.                     "finishing time trials",
  178.                     "preparing to race",
  179.                     "running race",
  180.                     "finishing race",
  181.                     "showing race results",
  182.                     "quitting",
  183.                     } ;
  184.  
  185.  
  186. /*------------------------------------------------------------------------------
  187.  * Make the chooser for selecting a server (team mode).
  188.  *----------------------------------------------------------------------------*/
  189. void
  190. createServerSelector(
  191.     Widget    parent
  192.     )
  193. {
  194.     int        n ;
  195.     Arg        args[10] ;
  196.     Widget        w ;
  197.     Widget        button[4] ;
  198.     static char    *labels[4] = { "Join", "Info", "Quit", "Help" } ;
  199.     static int    init = 0 ;
  200.     static char    *helpFiles[] = { "server.help", NULL } ;
  201.  
  202.     if( init )
  203.     {
  204.         return ;
  205.     }
  206.     init = 1 ;
  207.  
  208.     nServers = 0 ;
  209.  
  210.     n = 0 ;
  211.     XtSetArg( args[n], XmNtopAttachment, XmATTACH_FORM ) ; n++ ;
  212.     XtSetArg( args[n], XmNleftAttachment, XmATTACH_FORM ) ; n++ ;
  213.     XtSetArg( args[n], XmNrightAttachment, XmATTACH_FORM ) ; n++ ;
  214.     XtSetArg( args[n], XmNbottomAttachment, XmATTACH_FORM ) ; n++ ;
  215.     createSelectionList( "Select a game server:", parent,
  216.             &serverSelectorList, 4, button, labels, args, n ) ;
  217.  
  218.     XtAddCallback( serverSelectorList, XmNdefaultActionCallback,
  219.         (XtCallbackProc)serverSelectionCB, serverSelectorList ) ;
  220.     XtAddCallback( button[0], XmNactivateCallback,
  221.         (XtCallbackProc)serverSelectionCB, serverSelectorList ) ;
  222.     XtAddCallback( button[1], XmNactivateCallback,
  223.         (XtCallbackProc)serverStatsCB, serverSelectorList ) ;
  224.     XtAddCallback( button[2], XmNactivateCallback,
  225.         (XtCallbackProc)exitCB, NULL ) ;
  226.     XtAddCallback( button[2], XmNactivateCallback,
  227.         (XtCallbackProc)helpCB, helpFiles ) ;
  228.  
  229.     addServerToList( myHostId, 0, SERVER_ST_COURSE_SEL ) ;
  230.     XmListSelectPos( serverSelectorList, 1, True ) ;
  231. }
  232.  
  233.  
  234.  
  235. /*------------------------------------------------------------------------------
  236.  * Create a string for adding to the server list.
  237.  *----------------------------------------------------------------------------*/
  238. static XmString
  239. createServerListString(
  240.     long    id,
  241.     int    nClients,
  242.     long    status
  243.     )
  244. {
  245.     XmString    entry ;
  246.     char        buf[512] ;
  247.  
  248.     sprintf( buf, "%s - (%s, %d players)", hostNameFromId( id, 0 ),
  249.         ( status == SERVER_ST_COURSE_SEL && nClients < MAX_PLAYERS ) ?
  250.         "open" : "closed", nClients ) ;
  251.  
  252.     entry = XmStringCreateLocalized( buf ) ;
  253.  
  254.     return( entry ) ;
  255. }
  256.  
  257.  
  258.  
  259. /*------------------------------------------------------------------------------
  260.  * Server selection callback.
  261.  *----------------------------------------------------------------------------*/
  262. static void
  263. serverSelectionCB(
  264.     Widget        w,
  265.     Widget        list,
  266.     XtPointer    callData
  267.     )
  268. {
  269.     ServerHost    *server = serverList ;
  270.     char        *s ;
  271.     XmString    selectedServer ;
  272.  
  273.     if( ( s = getListSelectedString( list ) ) == NULL )
  274.     {
  275.         minorError( NULL, NULL,
  276.                 "Error determining server name." ) ;
  277.         return ;
  278.     }
  279.  
  280.     selectedServer = XmStringCreateLocalized( s ) ;
  281.     XtFree( s ) ;
  282.  
  283.     while( server != NULL )
  284.     {
  285.         if( XmStringCompare( server->name, selectedServer ) )
  286.         {
  287.             if( server->id == myHostId )
  288.             {
  289.                 initLocalServer() ;
  290.                 gameServerId = server->id ;
  291.                 lookForSelf = 0 ;
  292.                 self = 0 ;
  293.                 setWorkProc( VROOM_WP_FIND_SERVER, 0 ) ;
  294.             }
  295.             else
  296.             {
  297.                 outFd = connectToServer( server->id ) ;
  298.                 if( outFd != -1 )
  299.                 {
  300.                     gameServerId = server->id ;
  301.                     lookForSelf = 0 ;
  302.                     setWorkProc( VROOM_WP_RUN_CLIENT, 1 ) ;
  303.                     clientFunc = clientWaitToJoin ;
  304.                     loadedCourseId = -1 ;
  305.                     busyCursor() ;
  306.                     joinDeadLine = currentTime +
  307.                             WAIT_TO_JOIN_DELAY ;
  308.                     sendServerInfoPacket( CLIENT_INFO_JOIN,
  309.                                 0 ) ;
  310.                 }
  311.             }
  312.             XmStringFree( selectedServer ) ;
  313.             return ;
  314.         }
  315.         server = server->next ;
  316.     }
  317.  
  318.     XmStringFree( selectedServer ) ;
  319.     minorError( NULL, NULL, "Could not find server in list!" ) ;
  320. }
  321.  
  322.  
  323.  
  324. /*------------------------------------------------------------------------------
  325.  * Server stats callback.
  326.  *----------------------------------------------------------------------------*/
  327. static void
  328. serverStatsCB(
  329.     Widget        w,
  330.     Widget        list,
  331.     XtPointer    callData
  332.     )
  333. {
  334.     ServerHost    *server = serverList ;
  335.     char        *s ;
  336.     XmString    selectedServer ;
  337.  
  338.     if( ( s = getListSelectedString( list ) ) == NULL )
  339.     {
  340.         minorError( NULL, NULL,
  341.                 "Error determining server name." ) ;
  342.         return ;
  343.     }
  344.  
  345.     selectedServer = XmStringCreateLocalized( s ) ;
  346.     XtFree( s ) ;
  347.  
  348.     while( server != NULL )
  349.     {
  350.         if( XmStringCompare( server->name, selectedServer ) )
  351.         {
  352.             postInfo( NULL, NULL, "Status for %s:  %s.\n\n%d"
  353.                 " players.", hostNameFromId( server->id, 0 ),
  354.                 serverStatusString[server->status],
  355.                 server->nClients ) ;
  356.             XmStringFree( selectedServer ) ;
  357.             return ;
  358.         }
  359.         server = server->next ;
  360.     }
  361.  
  362.     minorError( NULL, NULL, "Could not find server in list!" ) ;
  363.     XmStringFree( selectedServer ) ;
  364. }
  365.  
  366.  
  367.  
  368. /*------------------------------------------------------------------------------
  369.  * Initialize the multicast listening port and start up a work process to
  370.  * listen for servers.
  371.  *----------------------------------------------------------------------------*/
  372. void
  373. listenForServers(
  374.     void
  375.     )
  376. {
  377.     int        port ;
  378.  
  379.     port = getPort( VROOM_CLIENT_INPUT_SERVICE, VROOM_CLIENT_INPUT_PORT ) ;
  380.  
  381.     if( ( inFd = openMulticastSocket( &inAddr, port, 0, 0, VROOM_GROUP,
  382.         NULL, "r" ) ) < 0 )
  383.     {
  384.         fatalError( "Could not open multicast socket for input." ) ;
  385.     }
  386.  
  387.     /*
  388.      * Turn on non-blocking I/O
  389.      */
  390.     if( fcntl( inFd, F_SETFL, FNDELAY ) < 0 ) {
  391.         fatalError( "fcntl F_SETFL, FNDELAY:\n  %s",
  392.                 strerror( errno ) ) ;
  393.         }
  394.  
  395.     setWorkProc( VROOM_WP_FIND_SERVER, 1 ) ;
  396. }
  397.  
  398.  
  399.  
  400. /*------------------------------------------------------------------------------
  401.  * Work proc to listen for servers.
  402.  *----------------------------------------------------------------------------*/
  403. void
  404. serverFind(
  405.     void
  406.     )
  407. {
  408.     int        cnt ;
  409.     int        id ;
  410.     outputPacket    packet ;
  411.     int        st = 0 ;
  412.     int        number ;
  413.     int        inAddrLen ;
  414.     int        socketEmpty = 0 ;
  415.     static int    newer = 0 ;
  416.  
  417.     while( !socketEmpty ) {
  418.         cnt = recvfrom( inFd, (char *)&packet,
  419.                     sizeof( outputPacket ), 0, &inAddr,
  420.                     &inAddrLen ) ;
  421.         if( cnt < 0 && errno == EWOULDBLOCK )
  422.         {
  423.             socketEmpty = 1 ;
  424.         }
  425.         else
  426.         {
  427.             switch( packet.base.type )
  428.             {
  429.                 case SERVER_P_COURSE :
  430.                 st = processServerCoursePacket( &packet.course,
  431.                                 cnt ) ;
  432.                 break ;
  433.  
  434.                 case SERVER_P_STATUS :
  435.                 st = processServerStatusPacket( &packet.status,
  436.                                 cnt ) ;
  437.                 break ;
  438.  
  439.                 case SERVER_P_ACK :
  440.                 st = processServerAckPacket( &packet.ack, cnt );
  441.                 break ;
  442.  
  443.                 case SERVER_P_RACE :
  444.                 if( cnt != sizeof( packet.race ) )
  445.                 {
  446.                     st = sizeof( packet.race ) ;
  447.                 }
  448.                 break ;
  449.  
  450.                 case SERVER_P_NAME :
  451.                 st = processServerNamePacket( &packet.name,
  452.                                 cnt ) ;
  453.                 break ;
  454.  
  455.                 case SERVER_P_MESSAGE :
  456.                 st = processServerMsgPacket( &packet.msg,
  457.                                 cnt ) ;
  458.                 break ;
  459.  
  460.                 default :
  461.                 if( ( packet.base.type & 0xffffff00 ) ==
  462.                     ( SERVER_PACKET_BASE_ID & 0xffffff00 )
  463.                     && ( packet.base.type & 0xfffffff0 ) >
  464.                     SERVER_PACKET_BASE_ID && newer == 0 )
  465.                 {
  466.                     newer = 1 ;
  467.                     postInfo( NULL, NULL,
  468.                         "An newer version of %s is"
  469.                         " being played on the net.\n\n",
  470.                         basename ) ;
  471.                 }
  472.                 else
  473.                 {
  474.                     printf( "wierd packet type 0x%08x\n",
  475.                         packet.base.type ) ;
  476.                 }
  477.                 break ;
  478.             }
  479.             if( st )
  480.             {
  481.                 fprintf( stderr, "received odd packet size of "
  482.                     "%d (type %d)! (id = 0x%08x)\n",
  483.                     cnt, packet.base.type, packet.base.id );
  484.                 st = 0 ;
  485.             }
  486.         }
  487.     }
  488.     checkServers() ;
  489. }
  490.  
  491.  
  492.  
  493. /*------------------------------------------------------------------------------
  494.  * Check if input host has been heard before.
  495.  *----------------------------------------------------------------------------*/
  496. static ServerHost *
  497. checkForNewHost(
  498.     outStatusPacket    *bp
  499.     )
  500. {
  501.     ServerHost    *server = serverList ;
  502.  
  503.     while( server != NULL )
  504.     {
  505.         if( server->id == bp->id )
  506.         {
  507.             return( server ) ;
  508.         }
  509.         else
  510.         {
  511.             server = server->next ;
  512.         }
  513.     }
  514.  
  515.     return( addServerToList( bp->id, bp->nPlayers, bp->status ) ) ;
  516. }
  517.  
  518.  
  519.  
  520. /*------------------------------------------------------------------------------
  521.  * Add a server to the server list.
  522.  *----------------------------------------------------------------------------*/
  523. static ServerHost *
  524. addServerToList(
  525.     long    id,
  526.     int    n,
  527.     long    status
  528.     )
  529. {
  530.     ServerHost    *server ;
  531.     XmString    serverName ;
  532.  
  533.     server = (ServerHost *)myMalloc( sizeof( ServerHost ) ) ;
  534.     server->id = id ;
  535.     server->next = serverList ;
  536.     server->status = status ;
  537.     server->nClients = n ;
  538.     server->nextDeadLine = currentTime + SERVER_TIMEOUT ;
  539.  
  540.     serverList = server ;
  541.  
  542.     nServers++ ;
  543.  
  544.     server->name = createServerListString( id, n, status ) ;
  545.  
  546.     XmListAddItem( serverSelectorList, server->name, 0 ) ;
  547.  
  548.     return( server ) ;
  549. }
  550.  
  551.  
  552.  
  553. /*------------------------------------------------------------------------------
  554.  * Try to establish a connection to the server.
  555.  *----------------------------------------------------------------------------*/
  556. static int
  557. connectToServer(
  558.     long    id
  559.     )
  560. {
  561.     int        sock ;
  562.     int        port ;
  563.     struct hostent    *hp ;
  564.     struct in_addr    sin ;
  565.  
  566.     if( ( sock = socket( AF_INET, SOCK_DGRAM, 0 ) ) < 0 )
  567.     {
  568.         minorError( NULL, NULL, "socket: %s", strerror( errno ) ) ;
  569.         return( -1 ) ;
  570.     }
  571.  
  572.     /*
  573.      * Initialize socket address to the server's address.
  574.      */
  575.     bzero( (char *)&outAddr, sizeof( outAddr ) ) ;
  576.     outAddr.sin_family = AF_INET ;
  577.  
  578.     sin.s_addr = id ;
  579.     if( ( hp = gethostbyaddr( &sin, sizeof( sin ), AF_INET ) ) == NULL )
  580.     {
  581.         minorError( NULL, NULL, "%s: %s", hostNameFromId( id, 1 ),
  582.                 hstrerror( h_errno ) ) ;
  583.         close( sock ) ;
  584.         return( -1 ) ;
  585.     }
  586.     bcopy( hp->h_addr, &(outAddr.sin_addr.s_addr), hp->h_length ) ;
  587.  
  588.     port = getPort( VROOM_SERVER_INPUT_SERVICE, VROOM_SERVER_INPUT_PORT ) ;
  589.  
  590.     outAddr.sin_port = htons( port ) ;
  591.  
  592.     /*
  593.      * Connect to the server.
  594.      */
  595.     if( connect( sock, &outAddr, sizeof( outAddr ) ) < 0 )
  596.     {
  597.         close( sock ) ;
  598.         perror( "Connect to server" ) ;
  599.         return( -1 ) ;
  600.     }
  601.  
  602.     return( sock ) ;
  603. }
  604.  
  605.  
  606.  
  607. /*------------------------------------------------------------------------------
  608.  * Client main loop.
  609.  *----------------------------------------------------------------------------*/
  610. void
  611. clientRun(
  612.     void
  613.     )
  614. {
  615.     clientFunc() ;
  616.  
  617.     if( currentTime > deadLineToServer )
  618.     {
  619.         sendServerInfoPacket( CLIENT_INFO_UPDATE, 0 ) ;
  620.     }
  621.  
  622.     newRacePacket = 0 ;
  623. }
  624.  
  625.  
  626.  
  627. /*------------------------------------------------------------------------------
  628.  * Function to wait to join a game.
  629.  *----------------------------------------------------------------------------*/
  630. static void
  631. clientWaitToJoin(
  632.     void
  633.     )
  634. {
  635.     if( currentTime > joinDeadLine )
  636.     {
  637.         gameServerId = 0 ;
  638.         activePlayer = 0 ;
  639.         lookForSelf = 0 ;
  640.         unbusyCursor() ;
  641.         setWorkProc( VROOM_WP_RUN_CLIENT, 0 ) ;
  642.         closeOutput() ;
  643.         minorError( NULL, NULL, "Server failed to "
  644.             "acknowledge connection.\nSelect "
  645.             "another server." ) ;
  646.     }
  647. }
  648.  
  649.  
  650.  
  651.  
  652. /*------------------------------------------------------------------------------
  653.  * Empty function to kill time.
  654.  *----------------------------------------------------------------------------*/
  655. static void
  656. clientNoOp(
  657.     void
  658.     )
  659. {
  660. }
  661.  
  662.  
  663.  
  664. /*------------------------------------------------------------------------------
  665.  * Send quit signal to server and close socket.
  666.  *----------------------------------------------------------------------------*/
  667. static void
  668. closeOutput(
  669.     void
  670.     )
  671. {
  672.     if( outFd != -1 )
  673.     {
  674.         sendServerInfoPacket( CLIENT_INFO_QUIT, 0 ) ;
  675.         close( outFd ) ;
  676.         outFd = -1 ;
  677.     }
  678. }
  679.  
  680.  
  681.  
  682. /*------------------------------------------------------------------------------
  683.  * Send a speed packet to the server.
  684.  *----------------------------------------------------------------------------*/
  685. static void
  686. sendServerSpeedPacket(
  687.     void
  688.     )
  689. {
  690.     speedPacket.playerNumber = self ;
  691.     write( outFd, (char *)&speedPacket, sizeof( speedPacket ) ) ;
  692. }
  693.  
  694.  
  695.  
  696. /*------------------------------------------------------------------------------
  697.  * Send an info packet to the server.
  698.  *----------------------------------------------------------------------------*/
  699. static void
  700. sendServerInfoPacket(
  701.     int    option,
  702.     int    choice
  703.     )
  704. {
  705.     inInfoPacket    packet ;
  706.  
  707.     packet.type = CLIENT_P_INFO ;
  708.     packet.id = myHostId ;
  709.     packet.option = option ;
  710.     packet.choice = choice ;
  711.  
  712.     write( outFd, (char *)&packet, sizeof( packet ) ) ;
  713.  
  714.     deadLineToServer = currentTime + CLIENT_UPDATE_FREQ ;
  715. }
  716.  
  717.  
  718.  
  719. /*------------------------------------------------------------------------------
  720.  * Inform server of my name.
  721.  *----------------------------------------------------------------------------*/
  722. static void
  723. sendServerNamePacket(
  724.     void
  725.     )
  726. {
  727.     inNamePacket    packet ;
  728.  
  729.     packet.type = CLIENT_P_NAME ;
  730.     packet.id = myHostId ;
  731.     strncpy( packet.name, myName, sizeof( packet.name ) ) ;
  732.  
  733.     write( outFd, (char *)&packet, sizeof( packet ) ) ;
  734.  
  735.     deadLineToServer = currentTime + CLIENT_UPDATE_FREQ ;
  736. }
  737.  
  738.  
  739.  
  740. /*------------------------------------------------------------------------------
  741.  * Send a text message to the server.
  742.  *----------------------------------------------------------------------------*/
  743. void
  744. sendServerMsgPacket(
  745.     char    *msg
  746.     )
  747. {
  748.     inMsgPacket    packet ;
  749.  
  750.     if( gameServerId != myHostId )
  751.     {
  752.         packet.type = CLIENT_P_MESSAGE ;
  753.         packet.id = myHostId ;
  754.         strncpy( packet.msg, msg, sizeof( packet.msg ) ) ;
  755.         packet.msg[sizeof( packet.msg ) - 1] = '\0' ;
  756.  
  757.         write( outFd, (char *)&packet, sizeof( packet ) ) ;
  758.  
  759.         deadLineToServer = currentTime + CLIENT_UPDATE_FREQ ;
  760.     }
  761.     else
  762.     {
  763.         broadcastMsgPacket( self, msg ) ;
  764.     }
  765. }
  766.  
  767.  
  768.  
  769. /*------------------------------------------------------------------------------
  770.  * Process a status packet from a server (in find server mode).
  771.  *----------------------------------------------------------------------------*/
  772. static int
  773. processServerStatusPacket(
  774.     outStatusPacket    *packet,
  775.     int        size
  776.     )
  777. {
  778.     int        i ;
  779.     int        *selList ;
  780.     int        selCnt ;
  781.     ServerHost    *server ;
  782.     XmString    oldListEntry ;
  783.  
  784.     if( size != sizeof( outStatusPacket ) )
  785.     {
  786.         return( -1 ) ;
  787.     }
  788.  
  789.     server = checkForNewHost( packet ) ;
  790.  
  791.     if( server->nClients != packet->nPlayers ||
  792.         server->status != packet->status )
  793.     {
  794.         if( server->id == gameServerId &&
  795.             packet->nPlayers != server->nClients )
  796.         {
  797.             updateServerCourseLabel( packet->nPlayers ) ;
  798.         }
  799.  
  800.         oldListEntry = server->name ;
  801.         server->nClients = packet->nPlayers ;
  802.         server->status = packet->status ;
  803.         server->name = createServerListString( server->id,
  804.                     server->nClients, server->status ) ;
  805.         XmListReplaceItems( serverSelectorList, &oldListEntry, 1,
  806.                 &(server->name) ) ;
  807.         if( !XmListGetSelectedPos( serverSelectorList, &selList,
  808.             &selCnt ) )
  809.         {
  810.             XmListSelectItem( serverSelectorList, server->name,
  811.                         True ) ;
  812.         }
  813.         else
  814.         {
  815.             XtFree( (char *)selList ) ;
  816.         }
  817.         XmStringFree( oldListEntry ) ;
  818.     }
  819.  
  820.     server->nextDeadLine = currentTime + SERVER_TIMEOUT ;
  821.  
  822.     if( server->id == gameServerId )
  823.     {
  824.         if( lookForSelf == 1 || checkIds == 1 )
  825.         {
  826.             findSelf( server->id, packet->playerId ) ;
  827.         }
  828.         serverStatus = packet->status ;
  829.     }
  830.  
  831.  
  832.     if( packet->status == SERVER_ST_QUIT )
  833.     {
  834.         if( packet->id == gameServerId )
  835.         {
  836.             resetToServerSearch() ;
  837.             minorError( startTeamModeCB, NULL,
  838.                 "Game server (%s) quit.\n"
  839.                 "Game aborted.  Choose another server.",
  840.                 hostNameFromId( server->id, 0 ) );
  841.         }
  842.         removeServer( packet->id ) ;
  843.     }
  844.  
  845.     return( 0 ) ;
  846. }
  847.  
  848.  
  849.  
  850. /*------------------------------------------------------------------------------
  851.  * Process an acknowledgement package from the server.
  852.  *----------------------------------------------------------------------------*/
  853. static int
  854. processServerAckPacket(
  855.     outAckPacket    *packet,
  856.     int        size
  857.     )
  858. {
  859.     ServerHost    *server ;
  860.  
  861.     if( size != sizeof( outAckPacket ) )
  862.     {
  863.         return( 1 ) ;
  864.     }
  865.  
  866.     if( ( server = findServer( packet->id ) ) != NULL )
  867.     {
  868.         server->nextDeadLine = currentTime + SERVER_TIMEOUT ;
  869.     }
  870.  
  871.     if( packet->id == gameServerId && ( packet->targetId == myHostId ||
  872.         packet->targetId == VROOM_ALL_PLAYERS ) )
  873.     {
  874.         switch( packet->status )
  875.         {
  876.             case ACK_ST_CANDIDATE :
  877.                 activePlayer = 1 ;
  878.                 busyCursor() ;
  879.                 sendServerInfoPacket( CLIENT_INFO_ACCEPT, 0 ) ;
  880.                 sendServerNamePacket() ;
  881.                 clientFunc = clientNoOp ;
  882.                 joinServerTime = currentTime ;
  883.                 setWorkProc( VROOM_WP_READ_SERVER, 1 ) ;
  884.                 setWorkProc( VROOM_WP_FIND_SERVER, 0 ) ;
  885.  
  886.                 serverCourseSelector =
  887.                      createServerCourseSelector(
  888.                             serverCourseForm ) ;
  889.                 if( server )
  890.                 {
  891.                     updateServerCourseLabel(
  892.                             server->nClients ) ;
  893.                 }
  894.                 createMessageArea() ;
  895.                 setAdminForm( serverCourseForm ) ;
  896.                 unbusyCursor() ;
  897.                 lookForSelf = 1 ;
  898.                 break ;
  899.  
  900.             case ACK_ST_WAIT :
  901.                 /*
  902.                  * Should put us into spectator mode until
  903.                  * such a time as the server can let us join.
  904.                  * Will have to add this later.
  905.                  */
  906.                 resetToServerSearch() ;
  907.                 minorError( startTeamModeCB, NULL,
  908.                     "%s is not accepting new\nplayers now."
  909.                     "  Try again later or try a\ndifferent "
  910.                     "server.",
  911.                     hostNameFromId( packet->id, 0 ) ) ;
  912.                 break ;
  913.  
  914.             case ACK_ST_DROP :
  915.                 /*
  916.                  * Dropped by server, probably due to poor
  917.                  * network connection.
  918.                  */
  919.                 resetToServerSearch() ;
  920.                 minorError( startTeamModeCB, NULL,
  921.                     "%s has dropped you.\n"
  922.                     "Try again later or try a\ndifferent "
  923.                     "server.",
  924.                     hostNameFromId( packet->id, 0 ) ) ;
  925.                 break ;
  926.  
  927.             case ACK_ST_COURSE_VOTE :
  928.                 if( packet->data == -1 )
  929.                 {
  930.                     minorError( NULL, NULL,  "The server "
  931.                         "does not recognize your course"
  932.                         "\nselection.  Choose another"
  933.                         "course." ) ;
  934.                 }
  935.                 else
  936.                 {
  937.                     busyCursor() ;
  938.                     setAdminForm( courseVoteForm ) ;
  939.                     unbusyCursor() ;
  940.                 }
  941.                 break ;
  942.  
  943.             case ACK_ST_COURSE_CHOSEN :
  944.                 if( packet->data != loadedCourseId )
  945.                 {
  946.                     clientLoadCourse( packet->data ) ;
  947.                 }
  948.                 break ;
  949.         }
  950.     }
  951.  
  952.     return( 0 ) ;
  953. }
  954.  
  955.  
  956.  
  957. /*------------------------------------------------------------------------------
  958.  * Process a name package from the server.
  959.  *----------------------------------------------------------------------------*/
  960. static int
  961. processServerNamePacket(
  962.     outNamePacket    *packet,
  963.     int        size
  964.     )
  965. {
  966.     int        i ;
  967.     ServerHost    *server ;
  968.  
  969.     if( size != sizeof( outNamePacket ) )
  970.     {
  971.         return( 1 ) ;
  972.     }
  973.  
  974.     if( ( server = findServer( packet->id ) ) != NULL )
  975.     {
  976.         server->nextDeadLine = currentTime + SERVER_TIMEOUT ;
  977.     }
  978.  
  979.     if( packet->id == gameServerId )
  980.     {
  981.         strncpy( cars[packet->nPlayer].name, packet->name,
  982.             sizeof( cars[packet->nPlayer].name ) ) ;
  983.     }
  984.  
  985.     return( 0 ) ;
  986. }
  987.  
  988.  
  989.  
  990. /*------------------------------------------------------------------------------
  991.  * Process a message package from the server.
  992.  *----------------------------------------------------------------------------*/
  993. static int
  994. processServerMsgPacket(
  995.     outMsgPacket    *packet,
  996.     int        size
  997.     )
  998. {
  999.     int        i ;
  1000.     ServerHost    *server ;
  1001.  
  1002.     if( size != sizeof( outMsgPacket ) )
  1003.     {
  1004.         return( 1 ) ;
  1005.     }
  1006.  
  1007.     if( ( server = findServer( packet->id ) ) != NULL )
  1008.     {
  1009.         server->nextDeadLine = currentTime + SERVER_TIMEOUT ;
  1010.     }
  1011.  
  1012.     if( packet->id == gameServerId )
  1013.     {
  1014.         postNewMessage( packet->nPlayer, packet->msg ) ;
  1015.     }
  1016.  
  1017.     return( 0 ) ;
  1018. }
  1019.  
  1020.  
  1021.  
  1022. /*------------------------------------------------------------------------------
  1023.  * Process a race course packet from a server (in game mode).
  1024.  *----------------------------------------------------------------------------*/
  1025. static int
  1026. processServerCoursePacket(
  1027.     outCoursePacket    *packet,
  1028.     int        size
  1029.     )
  1030. {
  1031.     ServerHost    *server ;
  1032.  
  1033.     if( size != sizeof( outCoursePacket ) )
  1034.     {
  1035.         return( -1 ) ;
  1036.     }
  1037.  
  1038.     if( packet->id == gameServerId && activePlayer )
  1039.     {
  1040.         if( addToCourseList( packet ) )
  1041.         {
  1042.             addCourseToSelectionList( serverCourseSelector,
  1043.                         packet->name ) ;
  1044.         }
  1045.     }
  1046.  
  1047.     if( ( server = findServer( packet->id ) ) != NULL )
  1048.     {
  1049.         server->nextDeadLine = currentTime + SERVER_TIMEOUT ;
  1050.     }
  1051.  
  1052.     return( 0 ) ;
  1053. }
  1054.  
  1055.  
  1056.  
  1057. /*------------------------------------------------------------------------------
  1058.  * Process a race packet from a server (in game mode).
  1059.  *----------------------------------------------------------------------------*/
  1060. static int
  1061. processServerRacePacket(
  1062.     outRacePacket    *packet,
  1063.     int        size
  1064.     )
  1065. {
  1066.     int        i ;
  1067.     int        n ;
  1068.     int        l ;
  1069.     long        statusChanges ;
  1070.     float        lapTime ;
  1071.     float        ds ;
  1072.     ServerHost    *server ;
  1073.  
  1074.     if( size != sizeof( outRacePacket ) )
  1075.     {
  1076.         return( -1 ) ;
  1077.     }
  1078.  
  1079.     if( packet->id == gameServerId )
  1080.     {
  1081.         newRacePacket = 1 ;
  1082.         bcopy( packet, &racePacket, sizeof( outRacePacket ) ) ;
  1083.         statusChanges = cars[self].status ^ racePacket.status[self] ;
  1084.         if( ( statusChanges & CAR_WIPE_OUT ) != 0 )
  1085.         {
  1086.             if( racePacket.status[self] & CAR_WIPE_OUT )
  1087.             {
  1088.                 showReason( racePacket.status[self] &
  1089.                         WIPE_OUT_REASON ) ;
  1090.                 sfxPlay( crashSfx ) ;
  1091.                 sfxSilenceLoop( squealSfx ) ;
  1092.                 sfxDisable( motorSfx ) ;
  1093.             }
  1094.             else if( cars[self].status & CAR_WIPE_OUT )
  1095.             {
  1096.                 setMessage( "Car set back on track." ) ;
  1097.                 sfxPlay( changeLaneSfx ) ;
  1098.                 sfxEnable( motorSfx ) ;
  1099.                 sfxPlayPitch( motorSfx, cars[self].desiredSpeed
  1100.                         * speedFactor ) ;
  1101.             }
  1102.         }
  1103.         if( ( statusChanges & CAR_SKID ) != 0 )
  1104.         {
  1105.             if( racePacket.status[self] & CAR_SKID )
  1106.             {
  1107.                 sfxPlay( squealSfx ) ;
  1108.             }
  1109.             else
  1110.             {
  1111.                 sfxSilenceLoop( squealSfx ) ;
  1112.             }
  1113.         }
  1114.         if( ( racePacket.status[self] & CAR_BUMP ) != 0 )
  1115.         {
  1116.             sfxPlay( collideSfx ) ;
  1117.         }
  1118.         ds = cars[self].desiredSpeed ;
  1119.         for( i = 0 ; i < MAX_PLAYERS ; i++ )
  1120.         {
  1121.             cars[i].status = racePacket.status[i] ;
  1122.             player[i].x = racePacket.x[i] ;
  1123.             player[i].y = racePacket.y[i] ;
  1124.             player[i].z = racePacket.z[i] ;
  1125.             player[i].thetaDeg = racePacket.thetaDeg[i] ;
  1126.             player[i].roll = racePacket.roll[i] ;
  1127.             player[i].headingDeg = racePacket.headingDeg[i] ;
  1128.             cars[i].totalDis = racePacket.totalDis[i] ;
  1129.             cars[i].desiredSpeed = racePacket.desiredSpeed[i] ;
  1130.             cars[i].lane = racePacket.lane[i] ;
  1131.             player[i].place = racePacket.position[i] ;
  1132.             teamPosition[player[i].place-1] = i ;
  1133.         }
  1134.         cars[self].desiredSpeed = ds ;
  1135.  
  1136.         if( packet->mode == SERVER_ST_PRE_RACE ||
  1137.             packet->mode == SERVER_ST_POST_TRIAL )
  1138.         {
  1139.             for( i = 0 ; i < MAX_PLAYERS ; i++ )
  1140.             {
  1141.                 cars[i].bestLapTime = racePacket.indTime[i] ;
  1142.             }
  1143.         }
  1144.  
  1145.         if( ( cars[self].status & ( CHANGE_LANES ) ) == steering )
  1146.         {
  1147.             if( steering )
  1148.             {
  1149.                 sfxPlay( changeLaneSfx ) ;
  1150.             }
  1151.             steering = 0 ;
  1152.         }
  1153.         if( ( cars[self].status & CAR_WIPE_OUT ) != 0 )
  1154.         {
  1155.             steering = 0 ;
  1156.         }
  1157.  
  1158.         i = ( (int)racePacket.totalDis[self] ) + 1 ;
  1159.         /*
  1160.          * Check to see to compute new best time trial lap time.
  1161.          */
  1162.         n = ( i - 1 ) / nTracks ;
  1163.         i = i % nTracks ;
  1164.         if( ( packet->mode == SERVER_ST_TRIAL ||
  1165.             packet->mode == SERVER_ST_POST_TRIAL ) && i == 1 &&
  1166.             cars[self].track->number == 0 )
  1167.         {
  1168.             lapTime = racePacket.time - racePacket.indTime[self] ;
  1169.             if( n == 1 || lapTime - cars[self].startLapTime <
  1170.                 cars[self].bestLapTime )
  1171.             {
  1172.                 cars[self].bestLapTime = lapTime -
  1173.                         cars[self].startLapTime ;
  1174.                 setMessage( "Best lap (unofficial): %s",
  1175.                     raceTimeString( cars[self].bestLapTime,
  1176.                             &l ) ) ;
  1177.             }
  1178.             cars[self].startLapTime = lapTime ;
  1179.         }
  1180.         cars[self].lap = n ;
  1181.         n = nTracks ;
  1182.         while( cars[self].track->number != i )
  1183.         {
  1184.             cars[self].track = cars[self].track->nextTrack ;
  1185.             if( --n < 0 )
  1186.             {
  1187.                 fatalError( "Bad track number computed from"
  1188.                     "server information.  Aborting." ) ;
  1189.             }
  1190.         }
  1191.     }
  1192.  
  1193.     return( 0 ) ;
  1194. }
  1195.  
  1196.  
  1197.  
  1198. /*------------------------------------------------------------------------------
  1199.  * Read input from the game server.
  1200.  *----------------------------------------------------------------------------*/
  1201. void
  1202. serverRead(
  1203.     void
  1204.     )
  1205. {
  1206.     int        cnt ;
  1207.     int        id ;
  1208.     outputPacket    packet ;
  1209.     int        st = 0 ;
  1210.     int        number ;
  1211.     int        inAddrLen ;
  1212.     int        socketEmpty = 0 ;
  1213.     static int    newer = 0 ;
  1214.  
  1215.     while( !socketEmpty ) {
  1216.         cnt = recvfrom( inFd, (char *)&packet,
  1217.                     sizeof( outputPacket ), 0, &inAddr,
  1218.                     &inAddrLen ) ;
  1219.         if( cnt < 0 && errno == EWOULDBLOCK )
  1220.         {
  1221.             socketEmpty = 1 ;
  1222.         }
  1223.         else
  1224.         {
  1225.             switch( packet.base.type )
  1226.             {
  1227.                 case SERVER_P_COURSE :
  1228.                 st = processServerCoursePacket( &packet.course,
  1229.                                 cnt ) ;
  1230.                 break ;
  1231.  
  1232.                 case SERVER_P_STATUS :
  1233.                 st = processServerStatusPacket( &packet.status,
  1234.                                 cnt ) ;
  1235.                 break ;
  1236.  
  1237.                 case SERVER_P_ACK :
  1238.                 st = processServerAckPacket( &packet.ack, cnt );
  1239.                 break ;
  1240.  
  1241.                 case SERVER_P_RACE :
  1242.                 st = processServerRacePacket( &packet.race,
  1243.                                 cnt ) ;
  1244.                 break ;
  1245.  
  1246.                 case SERVER_P_NAME :
  1247.                 st = processServerNamePacket( &packet.name,
  1248.                                 cnt ) ;
  1249.                 break ;
  1250.  
  1251.                 case SERVER_P_MESSAGE :
  1252.                 st = processServerMsgPacket( &packet.msg,
  1253.                                 cnt ) ;
  1254.                 break ;
  1255.  
  1256.                 default :
  1257.                 if( ( packet.base.type & 0xffffff00 ) ==
  1258.                     ( SERVER_PACKET_BASE_ID & 0xffffff00 )
  1259.                     && ( packet.base.type & 0xfffffff0 ) >
  1260.                     SERVER_PACKET_BASE_ID && newer == 0 )
  1261.                 {
  1262.                     newer = 1 ;
  1263.                     postInfo( NULL, NULL,
  1264.                         "An newer version of %s is"
  1265.                         " being played on the net.\n\n",
  1266.                         basename ) ;
  1267.                 }
  1268.                 else
  1269.                 {
  1270.                     printf( "wierd packet type 0x%08x\n",
  1271.                         packet.base.type ) ;
  1272.                 }
  1273.                 break ;
  1274.             }
  1275.             if( st )
  1276.             {
  1277.                 fprintf( stderr, "received odd packet size of "
  1278.                     "%d (type 0x%08x)! (id = 0x%08x)\n",
  1279.                     cnt, packet.base.type, packet.base.id );
  1280.                 st = 0 ;
  1281.             }
  1282.         }
  1283.     }
  1284.  
  1285.     checkServers() ;
  1286. }
  1287.  
  1288.  
  1289.  
  1290. /*------------------------------------------------------------------------------
  1291.  * Exit the game from client mode.
  1292.  *----------------------------------------------------------------------------*/
  1293. void
  1294. exitClientCB(
  1295.     Widget        w,
  1296.     XtPointer    clientData,
  1297.     XtPointer    callData
  1298.     )
  1299. {
  1300.     closeOutput() ;
  1301.     exitCB( w, clientData, callData ) ;
  1302. }
  1303.  
  1304.  
  1305.  
  1306. /*------------------------------------------------------------------------------
  1307.  * Locate a host in the server list.
  1308.  *----------------------------------------------------------------------------*/
  1309. static ServerHost *
  1310. findServer(
  1311.     long    id
  1312.     )
  1313. {
  1314.     ServerHost    *server = serverList ;
  1315.  
  1316.     while( server != NULL && server->id != id )
  1317.     {
  1318.         server = server->next ;
  1319.     }
  1320.  
  1321.     return( server ) ;
  1322. }
  1323.  
  1324.  
  1325.  
  1326. /*------------------------------------------------------------------------------
  1327.  * Check for server timeouts.
  1328.  *----------------------------------------------------------------------------*/
  1329. static void
  1330. checkServers(
  1331.     void
  1332.     )
  1333. {
  1334.     ServerHost    *server = serverList ;
  1335.     ServerHost    *prev ;
  1336.  
  1337.     if( currentTime > nextDeadLine )
  1338.     {
  1339.         if( serverList != NULL )
  1340.         {
  1341.             nextDeadLine = serverList->nextDeadLine ;
  1342.         }
  1343.         while( server != NULL )
  1344.         {
  1345.             if( server->id != myHostId &&
  1346.                 server->nextDeadLine < currentTime )
  1347.             {
  1348.                 prev = server ;
  1349.                 server = server->next ;
  1350.                 removeServer( prev->id ) ;
  1351.             }
  1352.             else
  1353.             {
  1354.                 nextDeadLine = MINFUNC( server->nextDeadLine,
  1355.                             nextDeadLine ) ;
  1356.                 server = server->next ;
  1357.             }
  1358.         }
  1359.     }
  1360. }
  1361.  
  1362.  
  1363.  
  1364. /*------------------------------------------------------------------------------
  1365.  * Remove a server from the server list.
  1366.  *----------------------------------------------------------------------------*/
  1367. static void
  1368. removeServer(
  1369.     long    id
  1370.     )
  1371. {
  1372.     int        *selList ;
  1373.     int        selCnt ;
  1374.     Widget        form ;
  1375.     ServerHost    *server = serverList ;
  1376.     ServerHost    *prev = NULL ;
  1377.     XmString    serverName ;
  1378.  
  1379.     while( server != NULL && server->id != id )
  1380.     {
  1381.         prev = server ;
  1382.         server = server->next ;
  1383.     }
  1384.  
  1385.     if( server->id == id )
  1386.     {
  1387.         if( prev == NULL )
  1388.         {
  1389.             serverList = server->next ;
  1390.         }
  1391.         else
  1392.         {
  1393.             prev->next = server->next ;
  1394.         }
  1395.         server->name = createServerListString( id, server->nClients,
  1396.                             server->status ) ;
  1397.         XmListDeleteItem( serverSelectorList, server->name ) ;
  1398.         XmStringFree( server->name ) ;
  1399.         if( !XmListGetSelectedPos( serverSelectorList, &selList,
  1400.             &selCnt ) )
  1401.         {
  1402.             XmListSelectPos( serverSelectorList, 1, True ) ;
  1403.         }
  1404.         else
  1405.         {
  1406.             XtFree( (char *)selList ) ;
  1407.         }
  1408.         free( server ) ;
  1409.         nServers-- ;
  1410.  
  1411.         if( id == gameServerId )
  1412.         {
  1413.             minorError( startTeamModeCB, NULL,
  1414.                 "Game server (%s) stopped\n"
  1415.                 "transmitting or responded too slowly.\n"
  1416.                 "Game aborted.  Choose another server.",
  1417.                 hostNameFromId( id, 0 ) );
  1418.             resetToServerSearch() ;
  1419.         }
  1420.     }
  1421. }
  1422.  
  1423.  
  1424.  
  1425. /*------------------------------------------------------------------------------
  1426.  * Callback for selecting a server course.
  1427.  *----------------------------------------------------------------------------*/
  1428. void
  1429. serverCourseSelectionCB(
  1430.     Widget        w,
  1431.     Widget        list,
  1432.     XtPointer    callData
  1433.     )
  1434. {
  1435.     char        *courseName ;
  1436.     outCoursePacket    *course ;
  1437.  
  1438.     if( ( courseName = getListSelectedString( list ) ) == NULL )
  1439.     {
  1440.         minorError( NULL, NULL,
  1441.                 "Error determining course file name." ) ;
  1442.     }
  1443.     else
  1444.     {
  1445.         course = findCourseInList( courseName ) ;
  1446.  
  1447.         if( course == NULL )
  1448.         {
  1449.             minorError( NULL, NULL, "Could not find selected "
  1450.                     "course `%s' in list.", courseName ) ;
  1451.         }
  1452.         else
  1453.         {
  1454.             if( gameServerId == myHostId )
  1455.             {
  1456.                 clientChoseCourse( myHostId,
  1457.                             course->courseId ) ;
  1458.             }
  1459.             else
  1460.             {
  1461.                 sendServerInfoPacket( CLIENT_INFO_COURSE_SEL,
  1462.                             course->courseId ) ;
  1463.             }
  1464.         }
  1465.         XtFree( courseName ) ;
  1466.     }
  1467. }
  1468.  
  1469.  
  1470.  
  1471. /*------------------------------------------------------------------------------
  1472.  * Course selected -- load it and prepare for time trials.
  1473.  *----------------------------------------------------------------------------*/
  1474. static void
  1475. clientLoadCourse(
  1476.     long    courseId
  1477.     )
  1478. {
  1479.     int    st ;
  1480.  
  1481.     if( ( st = loadTrackFromId( courseId ) ) == 0 )
  1482.     {
  1483.         sendServerInfoPacket( CLIENT_INFO_COURSE_ACK, courseId ) ;
  1484.         loadedCourseId = courseId ;
  1485.         cars[self].track = getTrack( 0 ) ;
  1486.         startTimeTrials( 0 ) ;
  1487.     }
  1488.     else if( st == -1 )
  1489.     {
  1490.         sendServerInfoPacket( CLIENT_INFO_COURSE_REQ, courseId ) ;
  1491.     }
  1492.     else
  1493.     {
  1494.         sendServerInfoPacket( CLIENT_INFO_QUIT, 0 ) ;
  1495.         fatalError( "Error creating race course.  Aborting." ) ;
  1496.     }
  1497. }
  1498.  
  1499.  
  1500.  
  1501. /*------------------------------------------------------------------------------
  1502.  * Initialize for networked game.
  1503.  *----------------------------------------------------------------------------*/
  1504. void
  1505. initTeamGame(
  1506.     void
  1507.     )
  1508. {
  1509.     sfxEnable( motorSfx ) ;
  1510.     sfxPlayPitch( motorSfx, 0.0f ) ;
  1511.     if( gameServerId != myHostId )
  1512.     {
  1513.         clientFunc = clientPreTrial ;
  1514.         startSoundTime = 2.0f ;
  1515.         checkIds = 1 ;
  1516.         initSoloCars( 1 ) ;
  1517.         nCars = 1 ;
  1518.         steering = 0 ;
  1519.         speedPacket.type = CLIENT_P_SPEED ;
  1520.         speedPacket.id = myHostId ;
  1521.         speedPacket.speed = 0.0f ;
  1522.     }
  1523.  
  1524.     setMessage( "Prepare to qualify." ) ;
  1525. }
  1526.  
  1527.  
  1528.  
  1529. /*------------------------------------------------------------------------------
  1530.  * Pre-trial loop.
  1531.  *----------------------------------------------------------------------------*/
  1532. static void
  1533. clientPreTrial(
  1534.     void
  1535.     )
  1536. {
  1537.     if( newRacePacket )
  1538.     {
  1539.         sfxPlayPitch( motorSfx, 0.5f * cars[self].desiredSpeed *
  1540.                 speedFactor ) ;
  1541.         if( racePacket.mode == SERVER_ST_TRIAL )
  1542.         {
  1543.             sfxPlay( startSfx ) ;
  1544.             sfxPlayPitch( motorSfx, cars[self].desiredSpeed *
  1545.                     speedFactor ) ;
  1546.             clientFunc = clientTrial ;
  1547.             cars[self].startLapTime = racePacket.time -
  1548.                         racePacket.indTime[self];
  1549.             setMessage( "Best of %d laps is used to qualify.",
  1550.                     VROOM_TRIAL_LAPS ) ;
  1551.         }
  1552.         else if( racePacket.time < startSoundTime )
  1553.         {
  1554.             sfxPlay( toneSfx ) ;
  1555.             startSoundTime -= 1.0f ;
  1556.         }
  1557.         drawIt( self ) ;
  1558.         drawPreTrialOverlay( racePacket.time, teamPosition, 1, 0 ) ;
  1559.         GLwDrawingAreaSwapBuffers( mainOgl ) ;
  1560.     }
  1561. }
  1562.  
  1563.  
  1564. /*------------------------------------------------------------------------------
  1565.  * Trial loop.
  1566.  *----------------------------------------------------------------------------*/
  1567. static void
  1568. clientTrial(
  1569.     void
  1570.     )
  1571. {
  1572.     int    l ;
  1573.  
  1574.     if( newRacePacket )
  1575.     {
  1576.         updateSpeed() ;
  1577.         drawIt( self ) ;
  1578.         drawTrialOverlay( racePacket.indTime[self], teamPosition ) ;
  1579.         GLwDrawingAreaSwapBuffers( mainOgl ) ;
  1580.  
  1581.         if( ( cars[self].status & CAR_FINISHED ) != 0 ||
  1582.             ( racePacket.mode != SERVER_ST_TRIAL &&
  1583.             racePacket.mode != SERVER_ST_POST_TRIAL ) )
  1584.         {
  1585.             clientFunc = clientPostTrial ;
  1586.             setMessage( "Waiting for all players to qualify.\n" ) ;
  1587.             sfxDisable( motorSfx ) ;
  1588.             sfxSilenceLoop( squealSfx ) ;
  1589.         }
  1590.     }
  1591. }
  1592.  
  1593.  
  1594.  
  1595. /*------------------------------------------------------------------------------
  1596.  * Loop for after time trials.
  1597.  *----------------------------------------------------------------------------*/
  1598. static void
  1599. clientPostTrial(
  1600.     void
  1601.     )
  1602. {
  1603.     int    i ;
  1604.  
  1605.     if( newRacePacket )
  1606.     {
  1607.         if( racePacket.mode != SERVER_ST_POST_TRIAL &&
  1608.             racePacket.mode != SERVER_ST_TRIAL )
  1609.         {
  1610.             clientFunc = clientPreRace ;
  1611.             i = teamPosition[0] ;
  1612.             showRecord = checkLapRecord( racePacket.indTime[i],
  1613.                         cars[i].name, 0 ) ;
  1614.             sfxEnable( motorSfx ) ;
  1615.             startSoundTime = 2.0f ;
  1616.             nCars = MAX_PLAYERS ;
  1617.             informPosition( player[self].place - 1 ) ;
  1618.         }
  1619.         drawIt( self ) ;
  1620.         drawPostTrialOverlay( racePacket.indTime[self], teamPosition ) ;
  1621.         GLwDrawingAreaSwapBuffers( mainOgl ) ;
  1622.  
  1623.     }
  1624. }
  1625.  
  1626.  
  1627.  
  1628. /*------------------------------------------------------------------------------
  1629.  * Loop for pre-race.
  1630.  *----------------------------------------------------------------------------*/
  1631. static void
  1632. clientPreRace(
  1633.     void
  1634.     )
  1635. {
  1636.     float    dTime ;
  1637.  
  1638.     if( newRacePacket )
  1639.     {
  1640.         dTime = racePacket.time ;
  1641.         sfxPlayPitch( motorSfx, 0.5f * cars[self].desiredSpeed *
  1642.                 speedFactor ) ;
  1643.         if( dTime <= 0.0f || racePacket.mode != SERVER_ST_PRE_RACE )
  1644.         {
  1645.             sfxPlay( startSfx ) ;
  1646.             sfxPlayPitch( motorSfx, cars[self].desiredSpeed *
  1647.                     speedFactor ) ;
  1648.             clientFunc = clientRace ;
  1649.             startLastLap = 0 ;
  1650.             setMessage( "May the best driver win." ) ;
  1651.             dTime = 0.0f ;
  1652.         }
  1653.         else if( racePacket.time < startSoundTime )
  1654.         {
  1655.             sfxPlay( toneSfx ) ;
  1656.             startSoundTime -= 1.0f ;
  1657.         }
  1658.         drawIt( 0 ) ;
  1659.         drawPreTrialOverlay( racePacket.time, teamPosition, 0,
  1660.                     showRecord ) ;
  1661.         GLwDrawingAreaSwapBuffers( mainOgl ) ;
  1662.     }
  1663. }
  1664.  
  1665.  
  1666.  
  1667. /*------------------------------------------------------------------------------
  1668.  * Loop for race.
  1669.  *----------------------------------------------------------------------------*/
  1670. static void
  1671. clientRace(
  1672.     void
  1673.     )
  1674. {
  1675.     int    n ;
  1676.     int    dLaps ;
  1677.  
  1678.     if( newRacePacket )
  1679.     {
  1680.         updateSpeed() ;
  1681.         if( ( cars[self].status & CAR_FINISHED ) != 0 )
  1682.         {
  1683.             clientFunc = clientPostRace ;
  1684.             sfxDisable( motorSfx ) ;
  1685.             sfxSilenceLoop( squealSfx ) ;
  1686.             showFinishMessage() ;
  1687.             drawIt( 0 ) ;
  1688.         }
  1689.         else
  1690.         {
  1691.             if( cars[self].lap == raceLaps - 1 &&
  1692.                 startLastLap == 0 )
  1693.             {
  1694.                 startLastLap = 1 ;
  1695.                 sfxPlay( lastlapSfx ) ;
  1696.             }
  1697.             drawIt( 0 ) ;
  1698.             if( player[self].place == 1 )
  1699.             {
  1700.                 dLaps = (int)( cars[self].totalDis -
  1701.                     cars[teamPosition[1]].totalDis ) /
  1702.                     nTracks ;
  1703.                 drawRaceOverlay( racePacket.time,
  1704.                     raceLaps - cars[self].lap - 1, dLaps, 1,
  1705.                     teamPosition ) ;
  1706.             }
  1707.             else
  1708.             {
  1709.                 n = teamPosition[0] ;
  1710.                 if( cars[n].status & CAR_FINISHED )
  1711.                 {
  1712.                     dLaps = raceLaps - cars[self].lap - 1 ;
  1713.                 }
  1714.                 else
  1715.                 {
  1716.                     dLaps = (int)( cars[n].totalDis -
  1717.                             cars[self].totalDis ) /
  1718.                             nTracks ;
  1719.                 }
  1720.                 drawRaceOverlay( racePacket.time,
  1721.                     raceLaps - cars[self].lap - 1, dLaps, 0,
  1722.                     teamPosition ) ;
  1723.             }
  1724.         }
  1725.         GLwDrawingAreaSwapBuffers( mainOgl ) ;
  1726.     }
  1727. }
  1728.  
  1729.  
  1730.  
  1731. /*------------------------------------------------------------------------------
  1732.  * Loop for post race.
  1733.  *----------------------------------------------------------------------------*/
  1734. static void
  1735. clientPostRace(
  1736.     void
  1737.     )
  1738. {
  1739.     int    i ;
  1740.  
  1741.     if( newRacePacket )
  1742.     {
  1743.         drawIt( 0 ) ;
  1744.         drawPostRaceOverlay( racePacket.indTime[self], racePacket.time,
  1745.                     teamPosition ) ;
  1746.         GLwDrawingAreaSwapBuffers( mainOgl ) ;
  1747.     }
  1748.  
  1749.     if( serverStatus == SERVER_ST_RESULTS )
  1750.     {
  1751.         clientFunc = clientNoOp ;
  1752.         for( i = 0 ; i < MAX_PLAYERS ; i++ )
  1753.         {
  1754.             cars[i].finishTime = racePacket.indTime[i] ;
  1755.         }
  1756.         showTeamResults( teamPosition ) ;
  1757.     }
  1758. }
  1759.  
  1760.  
  1761.  
  1762. /*------------------------------------------------------------------------------
  1763.  * Clean up from team mode when exitting.
  1764.  *----------------------------------------------------------------------------*/
  1765. void
  1766. teamExit(
  1767.     void
  1768.     )
  1769. {
  1770.     if( gameServerId != myHostId )
  1771.     {
  1772.         closeOutput() ;
  1773.     }
  1774.     else
  1775.     {
  1776.         closeServer() ;
  1777.     }
  1778. }
  1779.  
  1780.  
  1781.  
  1782. /*------------------------------------------------------------------------------
  1783.  * Reset everything to begin server search again.
  1784.  *----------------------------------------------------------------------------*/
  1785. static void
  1786. resetToServerSearch(
  1787.     void
  1788.     )
  1789. {
  1790.     busyCursor() ;
  1791.     gameServerId = 0 ;
  1792.     activePlayer = 0 ;
  1793.     checkIds = 0 ;
  1794.     lookForSelf = 0 ;
  1795.     closeOutput() ;
  1796.     clearCourseList() ;
  1797.     sfxDisable( motorSfx ) ;
  1798.     sfxSilenceLoop( squealSfx ) ;
  1799.     setWorkProc( VROOM_WP_FIND_SERVER, 1 ) ;
  1800.     setWorkProc( VROOM_WP_RUN_CLIENT, 0 ) ;
  1801.     setWorkProc( VROOM_WP_READ_SERVER, 0 ) ;
  1802.     unbusyCursor() ;
  1803. }
  1804.  
  1805.  
  1806.  
  1807. /*------------------------------------------------------------------------------
  1808.  * Find self in server's playerId list.
  1809.  *----------------------------------------------------------------------------*/
  1810. static void
  1811. findSelf(
  1812.     long    serverId,
  1813.     long    playerId[]
  1814.     )
  1815. {
  1816.     int    i ;
  1817.  
  1818.     self = -1 ;
  1819.     for( i = 0 ; i < MAX_PLAYERS ; i++ )
  1820.     {
  1821.         if( playerId[i] == myHostId )
  1822.         {
  1823.             self = i ;
  1824.             lookForSelf = 0 ;
  1825.             return ;
  1826.         }
  1827.     }
  1828.  
  1829.     if( self == -1 && currentTime > joinServerTime + 5.0f )
  1830.     {
  1831.         resetToServerSearch() ;
  1832.         minorError( startTeamModeCB, NULL,
  1833.             "Game server (%s) no longer\n"
  1834.             "recognizes you.  Game aborted.  "
  1835.             "Choose another server.",
  1836.             hostNameFromId( serverId, 0 ) );
  1837.     }
  1838. }
  1839.  
  1840.  
  1841.  
  1842. /*------------------------------------------------------------------------------
  1843.  * Reset for starting next team game.
  1844.  *----------------------------------------------------------------------------*/
  1845. void
  1846. restartTeamCB(
  1847.     Widget        w,
  1848.     XtPointer    clientData,
  1849.     XtPointer    callData
  1850.     )
  1851. {
  1852.     busyCursor() ;
  1853.  
  1854.     setAdminForm( serverCourseForm ) ;
  1855.  
  1856.     if( gameServerId != myHostId )
  1857.     {
  1858.         clientFunc = clientNoOp ;
  1859.         loadedCourseId = -1 ;
  1860.         lookForSelf = 1 ;
  1861.         nCars = 1 ;
  1862.     }
  1863.     else
  1864.     {
  1865.         setServerForNextGame() ;
  1866.     }
  1867.  
  1868.     unbusyCursor() ;
  1869. }
  1870.  
  1871.  
  1872.  
  1873. /*------------------------------------------------------------------------------
  1874.  * Loop for spectating.
  1875.  *----------------------------------------------------------------------------*/
  1876. static void
  1877. clientSpectate(
  1878.     void
  1879.     )
  1880. {
  1881. }
  1882.  
  1883.  
  1884.  
  1885. /*------------------------------------------------------------------------------
  1886.  * Check if need to send server new speed.
  1887.  *----------------------------------------------------------------------------*/
  1888. static void
  1889. updateSpeed(
  1890.     void
  1891.     )
  1892. {
  1893.     if( speedPacket.speed != cars[self].desiredSpeed ||
  1894.         ( speedPacket.steer != steering ) )
  1895.     {
  1896.         sfxPlayPitch( motorSfx, cars[self].desiredSpeed * speedFactor );
  1897.         speedPacket.speed = cars[self].desiredSpeed ;
  1898.         speedPacket.steer = steering ;
  1899.         sendServerSpeedPacket() ;
  1900.     }
  1901. }
  1902.