home *** CD-ROM | disk | FTP | other *** search
/ Amiga ACS 1998 #6 / amigaacscoverdisc1998-061998.iso / games / descent / source / main / network.c < prev    next >
C/C++ Source or Header  |  1998-06-08  |  80KB  |  3,007 lines

  1. /*
  2. THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
  3. SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
  4. END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
  5. ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
  6. IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
  7. SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
  8. FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
  9. CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
  10. AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
  11. COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
  12. */
  13. /*
  14.  * $Source: f:/miner/source/main/rcs/network.c $
  15.  * $Revision: 2.11 $
  16.  * $Author: john $
  17.  * $Date: 1995/07/18 10:57:56 $
  18.  * 
  19.  * Routines for managing network play.
  20.  * 
  21.  */
  22.  
  23. #pragma off (unreferenced)
  24. static char rcsid[] = "$Id: network.c 2.11 1995/07/18 10:57:56 john Exp $";
  25. #pragma on (unreferenced)
  26.  
  27. #ifdef NETWORK
  28.  
  29. #include <i86.h>
  30. #include <dos.h>
  31. #include <stdio.h>
  32. #include <string.h>
  33. #include <stdlib.h>
  34. #include <conio.h>
  35.  
  36. #include "types.h"
  37. #include "args.h"
  38. #include "timer.h"
  39. #include "mono.h"
  40. #include "ipx.h"
  41. #include "newmenu.h"
  42. #include "key.h"
  43. #include "gauges.h"
  44. #include "object.h"
  45. #include "error.h"
  46. #include "netmisc.h"
  47. #include "laser.h"
  48. #include "gamesave.h"
  49. #include "gamemine.h"
  50. #include "player.h"
  51. #include "gameseq.h"
  52. #include "fireball.h"
  53. #include "network.h"
  54. #include "game.h"
  55. #include "multi.h"
  56. #include "endlevel.h"
  57. #include "palette.h"
  58. #include "fuelcen.h"
  59. #include "menu.h"
  60. #include "sounds.h"
  61. #include "text.h"
  62. #include "kmatrix.h"
  63. #include "newdemo.h"
  64. #include "multibot.h"
  65. #include "wall.h"
  66. #include "bm.h"
  67. #include "effects.h"
  68. #include "physics.h"
  69.  
  70. #ifdef SHAREWARE
  71. #define PID_REQUEST                     11
  72. #define PID_SYNC                        13
  73. #define PID_PDATA                        14
  74. #define PID_ADDPLAYER                15
  75. #define PID_DUMP                        17
  76. #define PID_ENDLEVEL                    18
  77. #define PID_QUIT_JOINING            20
  78. #define PID_OBJECT_DATA                21
  79. #define PID_GAME_LIST                22
  80. #define PID_GAME_INFO                24
  81. #else
  82. #define PID_REQUEST                     25
  83. #define PID_SYNC                        27
  84. #define PID_PDATA                        28
  85. #define PID_ADDPLAYER                29
  86. #define PID_DUMP                        31
  87. #define PID_ENDLEVEL                    32
  88. #define PID_QUIT_JOINING            34
  89. #define PID_OBJECT_DATA                35
  90. #define PID_GAME_LIST                36
  91. #define PID_GAME_INFO                37
  92. #endif
  93.  
  94. #define NETGAME_ANARCHY                0
  95. #define NETGAME_TEAM_ANARCHY        1
  96. #define NETGAME_ROBOT_ANARCHY        2
  97. #define NETGAME_COOPERATIVE        3
  98.  
  99. typedef struct endlevel_info {
  100.     ubyte                    type;
  101.     ubyte                    player_num;
  102.     byte                    connected;
  103.     short                    kill_matrix[MAX_PLAYERS][MAX_PLAYERS];
  104.     short                    kills;
  105.     short                    killed;
  106.     ubyte                    seconds_left;
  107. } endlevel_info;
  108.  
  109. #define MAX_ACTIVE_NETGAMES     4
  110. netgame_info Active_games[MAX_ACTIVE_NETGAMES];
  111. int num_active_games = 0;
  112.  
  113. int    Network_debug=0;
  114. int    Network_active=0;
  115.  
  116. int      Network_status = 0;
  117. int     Network_games_changed = 0;
  118.  
  119. int     Network_socket = 0;
  120. int    Network_allow_socket_changes = 0;
  121.  
  122. // For rejoin object syncing
  123.  
  124. int    Network_rejoined = 0;       // Did WE rejoin this game?
  125. int    Network_new_game = 0;         // Is this the first level of a new game?
  126. int    Network_send_objects = 0;  // Are we in the process of sending objects to a player?
  127. int     Network_send_objnum = -1;   // What object are we sending next?
  128. int    Network_player_added = 0;   // Is this a new player or a returning player?
  129. int    Network_send_object_mode = 0; // What type of objects are we sending, static or dynamic?
  130. sequence_packet    Network_player_rejoining; // Who is rejoining now?
  131.  
  132. fix    LastPacketTime[MAX_PLAYERS]; // For timeouts of idle/crashed players
  133.  
  134. int     PacketUrgent = 0;
  135.  
  136. frame_info     MySyncPack;
  137. ubyte         MySyncPackInitialized = 0;        // Set to 1 if the MySyncPack is zeroed.
  138. ushort         my_segments_checksum = 0;
  139.  
  140. sequence_packet My_Seq;
  141.  
  142. extern obj_position Player_init[MAX_PLAYERS];
  143.  
  144. #define DUMP_CLOSED 0
  145. #define DUMP_FULL 1
  146. #define DUMP_ENDLEVEL 2
  147. #define DUMP_DORK 3
  148. #define DUMP_ABORTED 4
  149. #define DUMP_CONNECTED 5
  150. #define DUMP_LEVEL 6
  151.  
  152. int network_wait_for_snyc();
  153.  
  154. void
  155. network_init(void)
  156. {
  157.     // So you want to play a netgame, eh?  Let's a get a few things
  158.     // straight
  159.  
  160.     int save_pnum = Player_num;
  161.  
  162.     memset(&Netgame, 0, sizeof(netgame_info));
  163.     memset(&My_Seq, 0, sizeof(sequence_packet));
  164.     My_Seq.type = PID_REQUEST;
  165.     memcpy(My_Seq.player.callsign, Players[Player_num].callsign, CALLSIGN_LEN+1);
  166.     memcpy(My_Seq.player.node, ipx_get_my_local_address(), 6);
  167.     memcpy(My_Seq.player.server, ipx_get_my_server_address(), 4 );
  168.  
  169.     for (Player_num = 0; Player_num < MAX_NUM_NET_PLAYERS; Player_num++)
  170.         init_player_stats_game();
  171.  
  172.     Player_num = save_pnum;        
  173.     multi_new_game();
  174.     Network_new_game = 1;
  175.     Fuelcen_control_center_destroyed = 0;
  176.     network_flush();
  177. }
  178.  
  179. #define ENDLEVEL_SEND_INTERVAL F1_0*2
  180. #define ENDLEVEL_IDLE_TIME    F1_0*10
  181.     
  182. void 
  183. network_endlevel_poll( int nitems, newmenu_item * menus, int * key, int citem )
  184. {
  185.     // Polling loop for End-of-level menu
  186.  
  187.     static fix t1 = 0;
  188.     int i = 0;
  189.     int num_ready = 0;
  190.     int num_escaped = 0;
  191.     int goto_secret = 0;
  192.  
  193.     int previous_state[MAX_NUM_NET_PLAYERS];
  194.     int previous_seconds_left;
  195.  
  196.     menus = menus;
  197.     citem = citem;
  198.     nitems = nitems;
  199.     key = key;
  200.  
  201.     // Send our endlevel packet at regular intervals
  202.  
  203.     if (timer_get_approx_seconds() > t1+ENDLEVEL_SEND_INTERVAL)
  204.     {
  205.         network_send_endlevel_packet();
  206.         t1 = timer_get_approx_seconds();
  207.     }
  208.  
  209.     for (i = 0; i < N_players; i++)
  210.         previous_state[i] = Players[i].connected;
  211.  
  212.     previous_seconds_left = Fuelcen_seconds_left;
  213.  
  214.     network_listen();
  215.  
  216.     for (i = 0; i < N_players; i++)
  217.     {
  218.         if (previous_state[i] != Players[i].connected)        
  219.         {
  220.             sprintf(menus[i].text, "%s %s", Players[i].callsign, CONNECT_STATES(Players[i].connected));
  221.             menus[i].redraw = 1;
  222.         }
  223.         if (Players[i].connected == 1)
  224.         {
  225.             // Check timeout for idle players
  226.             if (timer_get_approx_seconds() > LastPacketTime[i]+ENDLEVEL_IDLE_TIME)
  227.             {
  228.                 mprintf((0, "idle timeout for player %d.\n", i));
  229.                 Players[i].connected = 0;
  230.                 network_send_endlevel_sub(i);
  231.             }                
  232.         }
  233.  
  234.         if ((Players[i].connected != 1) && (Players[i].connected != 5) && (Players[i].connected != 6))
  235.             num_ready++;
  236.         if (Players[i].connected != 1)
  237.             num_escaped++;
  238.         if (Players[i].connected == 4)
  239.             goto_secret = 1;
  240.     }
  241.  
  242.     if (num_escaped == N_players) // All players are out of the mine
  243.     {
  244.         Fuelcen_seconds_left = -1;
  245.     }
  246.  
  247.     if (previous_seconds_left != Fuelcen_seconds_left)
  248.     {
  249.         if (Fuelcen_seconds_left < 0)
  250.         {
  251.             sprintf(menus[N_players].text, TXT_REACTOR_EXPLODED);
  252.             menus[N_players].redraw = 1;
  253.         }
  254.         else
  255.         {
  256.             sprintf(menus[N_players].text, "%s: %d %s  ", TXT_TIME_REMAINING, Fuelcen_seconds_left, TXT_SECONDS);
  257.             menus[N_players].redraw = 1;
  258.         }
  259.     }
  260.  
  261.     if (num_ready == N_players) // All players have checked in or are disconnected
  262.     {
  263.         if (goto_secret)
  264.             *key = -3;
  265.         else
  266.             *key = -2;
  267.     }
  268. }
  269.  
  270. void 
  271. network_endlevel_poll2( int nitems, newmenu_item * menus, int * key, int citem )
  272. {
  273.     // Polling loop for End-of-level menu
  274.  
  275.     static fix t1 = 0;
  276.     int i = 0;
  277.     int num_ready = 0;
  278.     int goto_secret = 0;
  279.  
  280.     menus = menus;
  281.     citem = citem;
  282.     nitems = nitems;
  283.     key = key;
  284.  
  285.     // Send our endlevel packet at regular intervals
  286.  
  287.     if (timer_get_approx_seconds() > t1+ENDLEVEL_SEND_INTERVAL)
  288.     {
  289.         network_send_endlevel_packet();
  290.         t1 = timer_get_approx_seconds();
  291.     }
  292.  
  293.     network_listen();
  294.  
  295.     for (i = 0; i < N_players; i++)
  296.     {
  297.         if ((Players[i].connected != 1) && (Players[i].connected != 5) && (Players[i].connected != 6))
  298.             num_ready++;
  299.         if (Players[i].connected == 4)
  300.             goto_secret = 1;
  301.     }
  302.  
  303.     if (num_ready == N_players) // All players have checked in or are disconnected
  304.     {
  305.         if (goto_secret)
  306.             *key = -3;
  307.         else
  308.             *key = -2;
  309.     }
  310. }
  311.  
  312. int
  313. network_endlevel(int *secret)
  314. {
  315.     // Do whatever needs to be done between levels
  316.  
  317.     newmenu_item m[MAX_NUM_NET_PLAYERS+1];
  318.     char menu_text[MAX_NUM_NET_PLAYERS+1][80];
  319.     int i, choice;
  320.  
  321.     char text[80];
  322.  
  323.     Function_mode = FMODE_MENU;
  324.  
  325.     network_flush();
  326.  
  327.     Network_status = NETSTAT_ENDLEVEL; // We are between levels
  328.  
  329.     network_listen();
  330.  
  331.     network_send_endlevel_packet();
  332.  
  333. newmenu:
  334.     // Setup menu text pointers and zero them
  335.     for (i=0; i<N_players; i++) 
  336.     {
  337.         m[i].type = NM_TYPE_TEXT;
  338.         m[i].text = menu_text[i];
  339.         sprintf(m[i].text, "%s %s", Players[i].callsign, CONNECT_STATES(Players[i].connected));
  340.         LastPacketTime[i] = timer_get_approx_seconds();
  341.     }
  342.     m[N_players].type = NM_TYPE_TEXT;
  343.     m[N_players].text = menu_text[N_players];
  344.  
  345.     if (Fuelcen_seconds_left < 0)
  346.         sprintf(m[N_players].text, TXT_REACTOR_EXPLODED);
  347.     else
  348.         sprintf(m[N_players].text, "%s: %d %s  ", TXT_TIME_REMAINING, Fuelcen_seconds_left, TXT_SECONDS);
  349.  
  350. menu:
  351.     sprintf(text, "%s\n%s", TXT_WAITING, TXT_ESC_ABORT);
  352.  
  353.     choice=newmenu_do3(NULL, text, N_players+1, m, network_endlevel_poll, 0, "STARS.PCX", 300, 160);
  354.  
  355.     if (choice==-1) {
  356.         newmenu_item m2[2];
  357.  
  358.         m2[0].type = m2[1].type = NM_TYPE_MENU;
  359.         m2[0].text = TXT_YES; m2[1].text = TXT_NO;
  360.         choice = newmenu_do1(NULL, TXT_SURE_LEAVE_GAME, 2, m2, network_endlevel_poll2, 1);
  361.         if (choice == 0)
  362.         {
  363.             Players[Player_num].connected = 0;
  364.             network_send_endlevel_packet();
  365.             network_send_endlevel_packet();
  366.             longjmp(LeaveGame,0);
  367.         }    
  368.         if (choice > -2)
  369.             goto newmenu;
  370.     }
  371.  
  372. //    kmatrix_view();
  373.  
  374.     if (choice > -2)
  375.         goto menu;
  376.     
  377.     if (choice == -3)
  378.         *secret = 1; // If any player went to the secret level, we go to the secret level
  379.  
  380.     network_send_endlevel_packet();
  381.     network_send_endlevel_packet();
  382.     MySyncPackInitialized = 0;
  383.  
  384.     network_update_netgame();
  385.  
  386.     return(0);
  387. }
  388.  
  389. int 
  390. can_join_netgame(netgame_info *game)
  391. {
  392.     // Can this player rejoin a netgame in progress?
  393.  
  394.     int i, num_players;
  395.  
  396.     if (game->game_status == NETSTAT_STARTING)
  397.         return 1;
  398.  
  399.     if (game->game_status != NETSTAT_PLAYING)
  400.         return 0;
  401.  
  402.     // Game is in progress, figure out if this guy can re-join it
  403.  
  404.     num_players = game->numplayers;
  405.  
  406.     if (!(game->game_flags & NETGAME_FLAG_CLOSED)) {
  407.         // Look for player that is not connected
  408.         if (game->numplayers < game->max_numplayers)
  409.             return 1;
  410.  
  411.         for (i = 0; i < num_players; i++) {
  412.             if (game->players[i].connected == 0)
  413.                 return 1;
  414.         }
  415.         return 0;
  416.     }
  417.  
  418.     // Search to see if we were already in this closed netgame in progress
  419.  
  420.  
  421.     for (i = 0; i < num_players; i++)
  422.         if ( (!stricmp(Players[Player_num].callsign, game->players[i].callsign)) &&
  423.               (!memcmp(My_Seq.player.node, game->players[i].node, 6)) &&
  424.               (!memcmp(My_Seq.player.server, game->players[i].server, 4)) )
  425.             break;
  426.  
  427.     if (i != num_players)
  428.         return 1;
  429.     return 0;
  430. }
  431.  
  432. void
  433. network_disconnect_player(int playernum)
  434. {
  435.     // A player has disconnected from the net game, take whatever steps are
  436.     // necessary 
  437.  
  438.     if (playernum == Player_num) 
  439.     {
  440.         Int3(); // Weird, see Rob
  441.         return;
  442.     }
  443.  
  444.     Players[playernum].connected = 0;
  445.     Netgame.players[playernum].connected = 0;
  446.  
  447. //    create_player_appearance_effect(&Objects[Players[playernum].objnum]);
  448.     multi_make_player_ghost(playernum);
  449.  
  450. #ifndef SHAREWARE
  451.     if (Newdemo_state == ND_STATE_RECORDING)
  452.         newdemo_record_multi_disconnect(playernum);
  453.  
  454.     multi_strip_robots(playernum);
  455. #endif
  456. }
  457.         
  458. void
  459. network_new_player(sequence_packet *their)
  460. {
  461.     int objnum;
  462.     int pnum;
  463.  
  464.     pnum = their->player.connected;
  465.  
  466.     Assert(pnum >= 0);
  467.     Assert(pnum < MaxNumNetPlayers);    
  468.     
  469.     objnum = Players[pnum].objnum;
  470.  
  471. #ifndef SHAREWARE
  472.     if (Newdemo_state == ND_STATE_RECORDING) {
  473.         int new_player;
  474.  
  475.         if (pnum == N_players)
  476.             new_player = 1;
  477.         else
  478.             new_player = 0;
  479.         newdemo_record_multi_connect(pnum, new_player, their->player.callsign);
  480.     }
  481. #endif
  482.  
  483.     memcpy(Players[pnum].callsign, their->player.callsign, CALLSIGN_LEN+1);
  484.     memcpy(Netgame.players[pnum].callsign, their->player.callsign, CALLSIGN_LEN+1);
  485.  
  486. #ifndef SHAREWARE
  487.     if ( (*(uint *)their->player.server) != 0 )
  488.         ipx_get_local_target( their->player.server, their->player.node, Players[pnum].net_address );
  489.     else
  490. #endif
  491.         memcpy(Players[pnum].net_address, their->player.node, 6);
  492.  
  493.     memcpy(Netgame.players[pnum].node, their->player.node, 6);
  494.     memcpy(Netgame.players[pnum].server, their->player.server, 4);
  495.  
  496.    Players[pnum].n_packets_got = 0;
  497.     Players[pnum].connected = 1;
  498.     Players[pnum].net_kills_total = 0;
  499.     Players[pnum].net_killed_total = 0;
  500.     memset(kill_matrix[pnum], 0, MAX_PLAYERS*sizeof(short)); 
  501.     Players[pnum].score = 0;
  502.     Players[pnum].flags = 0;
  503.  
  504.     if (pnum == N_players)
  505.     {
  506.         N_players++;
  507.         Netgame.numplayers = N_players;
  508.     }
  509.  
  510.     digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
  511.  
  512.     HUD_init_message("'%s' %s\n", their->player.callsign, TXT_JOINING);
  513.     
  514.     multi_make_ghost_player(pnum);
  515.  
  516. #ifndef SHAREWARE
  517.     multi_send_score();
  518. #endif
  519.  
  520. //    create_player_appearance_effect(&Objects[objnum]);
  521. }
  522.  
  523. void network_welcome_player(sequence_packet *their)
  524. {
  525.      // Add a player to a game already in progress
  526.     ubyte local_address[6];
  527.     int player_num;
  528.     int i;
  529.  
  530.     // Don't accept new players if we're ending this level.  Its safe to
  531.     // ignore since they'll request again later
  532.  
  533.     if ((Endlevel_sequence) || (Fuelcen_control_center_destroyed))
  534.     {
  535.         mprintf((0, "Ignored request from new player to join during endgame.\n"));
  536.         network_dump_player(their->player.server,their->player.node, DUMP_ENDLEVEL);
  537.         return; 
  538.     }
  539.  
  540.     if (Network_send_objects)
  541.     {
  542.         // Ignore silently, we're already responding to someone and we can't
  543.         // do more than one person at a time.  If we don't dump them they will
  544.         // re-request in a few seconds.
  545.         return;
  546.     }
  547.  
  548.     if (their->player.connected != Current_level_num)
  549.     {
  550.         mprintf((0, "Dumping player due to old level number.\n"));
  551.         network_dump_player(their->player.server, their->player.node, DUMP_LEVEL);
  552.         return;
  553.     }
  554.  
  555.     player_num = -1;
  556.     memset(&Network_player_rejoining, 0, sizeof(sequence_packet));
  557.     Network_player_added = 0;
  558.  
  559. #ifndef SHAREWARE
  560.     if ( (*(uint *)their->player.server) != 0 )
  561.         ipx_get_local_target( their->player.server, their->player.node, local_address );
  562.     else
  563. #endif
  564.         memcpy(local_address, their->player.node, 6);
  565.  
  566.     for (i = 0; i < N_players; i++)
  567.     {
  568.         if ( (!stricmp(Players[i].callsign, their->player.callsign )) && (!memcmp(Players[i].net_address,local_address, 6)) ) 
  569.         {
  570.             player_num = i;
  571.             break;
  572.         }
  573.     }
  574.  
  575.     if (player_num == -1)
  576.     {
  577.         // Player is new to this game
  578.  
  579.         if ( !(Netgame.game_flags & NETGAME_FLAG_CLOSED) && (N_players < MaxNumNetPlayers))
  580.         {
  581.             // Add player in an open slot, game not full yet
  582.  
  583.             player_num = N_players;
  584.             Network_player_added = 1;
  585.         }
  586.         else if (Netgame.game_flags & NETGAME_FLAG_CLOSED)
  587.         {
  588.             // Slots are open but game is closed
  589.  
  590.             network_dump_player(their->player.server, their->player.node, DUMP_CLOSED);
  591.             return;
  592.         }
  593.         else
  594.         {
  595.             // Slots are full but game is open, see if anyone is
  596.             // disconnected and replace the oldest player with this new one
  597.         
  598.             int oldest_player = -1;
  599.             fix oldest_time = timer_get_approx_seconds();
  600.  
  601.             Assert(N_players == MaxNumNetPlayers);
  602.  
  603.             for (i = 0; i < N_players; i++)
  604.             {
  605.                 if ( (!Players[i].connected) && (LastPacketTime[i] < oldest_time))
  606.                 {
  607.                     oldest_time = LastPacketTime[i];
  608.                     oldest_player = i;
  609.                 }
  610.             }
  611.  
  612.             if (oldest_player == -1)
  613.             {
  614.                 // Everyone is still connected 
  615.  
  616.                 network_dump_player(their->player.server, their->player.node, DUMP_FULL);
  617.                 return;
  618.             }
  619.             else
  620.             {    
  621.                 // Found a slot!
  622.  
  623.                 player_num = oldest_player;
  624.                 Network_player_added = 1;
  625.             }
  626.         }
  627.     }
  628.     else 
  629.     {
  630.         // Player is reconnecting
  631.         
  632.         if (Players[player_num].connected)
  633.         {
  634.             mprintf((0, "Extra REQUEST from player ignored.\n"));
  635.             return;
  636.         }
  637.  
  638. #ifndef SHAREWARE
  639.         if (Newdemo_state == ND_STATE_RECORDING)
  640.             newdemo_record_multi_reconnect(player_num);
  641. #endif
  642.  
  643.         Network_player_added = 0;
  644.  
  645.         digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
  646.  
  647.         HUD_init_message("'%s' %s", Players[player_num].callsign, TXT_REJOIN);
  648.     }
  649.  
  650.     // Send updated Objects data to the new/returning player
  651.  
  652.     Network_player_rejoining = *their;
  653.     Network_player_rejoining.player.connected = player_num;
  654.     Network_send_objects = 1;
  655.     Network_send_objnum = -1;
  656.  
  657.     network_send_objects();
  658. }
  659.  
  660. int network_objnum_is_past(int objnum)
  661. {
  662.     // determine whether or not a given object number has already been sent
  663.     // to a re-joining player.
  664.     
  665.     int player_num = Network_player_rejoining.player.connected;
  666.     int obj_mode = !((object_owner[objnum] == -1) || (object_owner[objnum] == player_num));
  667.  
  668.     if (!Network_send_objects)
  669.         return 0; // We're not sending objects to a new player
  670.  
  671.     if (obj_mode > Network_send_object_mode)
  672.         return 0;
  673.     else if (obj_mode < Network_send_object_mode)
  674.          return 1;
  675.     else if (objnum < Network_send_objnum)
  676.         return 1;
  677.     else
  678.         return 0;
  679. }
  680.  
  681. #define OBJ_PACKETS_PER_FRAME 1
  682.  
  683. #ifndef SHAREWARE
  684. void network_send_door_updates(void)
  685. {
  686.     // Send door status when new player joins
  687.     
  688.     int i;
  689.  
  690.     for (i = 0; i < Num_walls; i++)
  691.     {
  692.         if ((Walls[i].type == WALL_DOOR) && ((Walls[i].state == WALL_DOOR_OPENING) || (Walls[i].state == WALL_DOOR_WAITING)))
  693.             multi_send_door_open(Walls[i].segnum, Walls[i].sidenum);
  694.         else if ((Walls[i].type == WALL_BLASTABLE) && (Walls[i].flags & WALL_BLASTED))
  695.             multi_send_door_open(Walls[i].segnum, Walls[i].sidenum);
  696.         else if ((Walls[i].type == WALL_BLASTABLE) && (Walls[i].hps != WALL_HPS))
  697.             multi_send_hostage_door_status(i);
  698.     }
  699.  
  700. }    
  701.  
  702. void network_process_monitor_vector(int vector)
  703. {
  704.     int i, j;
  705.     int count = 0;
  706.     segment *seg;
  707.     
  708.     for (i=0; i <= Highest_segment_index; i++)
  709.     {
  710.         int tm, ec, bm;
  711.         seg = &Segments[i];
  712.         for (j = 0; j < 6; j++)
  713.         {
  714.             if ( ((tm = seg->sides[j].tmap_num2) != 0) &&
  715.                   ((ec = TmapInfo[tm&0x3fff].eclip_num) != -1) &&
  716.                    ((bm = Effects[ec].dest_bm_num) != -1) )
  717.             {
  718.                 if (vector & (1 << count))
  719.                 {
  720.                     seg->sides[j].tmap_num2 = bm | (tm&0xc000);
  721.                     mprintf((0, "Monitor %d blown up.\n", count));
  722.                 }
  723.                 else
  724.                     mprintf((0, "Monitor %d intact.\n", count));
  725.                 count++;
  726.                 Assert(count < 32);
  727.             }
  728.         }
  729.     }
  730. }
  731.  
  732. int network_create_monitor_vector(void)
  733. {
  734.     int i, j, k;
  735.     int num_blown_bitmaps = 0;
  736.     int monitor_num = 0;
  737.     int blown_bitmaps[7];
  738.     int vector = 0;
  739.     segment *seg;
  740.  
  741.     for (i=0; i < Num_effects; i++)
  742.     {
  743.         if (Effects[i].dest_bm_num > 0) {
  744.             for (j = 0; j < num_blown_bitmaps; j++)
  745.                 if (blown_bitmaps[j] == Effects[i].dest_bm_num)
  746.                     break;
  747.             if (j == num_blown_bitmaps)
  748.                 blown_bitmaps[num_blown_bitmaps++] = Effects[i].dest_bm_num;
  749.         }
  750.     }        
  751.         
  752.     for (i = 0; i < num_blown_bitmaps; i++)
  753.         mprintf((0, "Blown bitmap #%d = %d.\n", i, blown_bitmaps[i]));
  754.     
  755.     Assert(num_blown_bitmaps <= 7);
  756.  
  757.     for (i=0; i <= Highest_segment_index; i++)
  758.     {
  759.         int tm, ec;
  760.         seg = &Segments[i];
  761.         for (j = 0; j < 6; j++)
  762.         {
  763.             if ((tm = seg->sides[j].tmap_num2) != 0) 
  764.             {
  765.                 if ( ((ec = TmapInfo[tm&0x3fff].eclip_num) != -1) &&
  766.                        (Effects[ec].dest_bm_num != -1) )
  767.                 {
  768.                     mprintf((0, "Monitor %d intact.\n", monitor_num));
  769.                     monitor_num++;
  770.                     Assert(monitor_num < 32);
  771.                 }
  772.                 else
  773.                 {
  774.                     for (k = 0; k < num_blown_bitmaps; k++)
  775.                     {
  776.                         if ((tm&0x3fff) == blown_bitmaps[k])
  777.                         {
  778.                             mprintf((0, "Monitor %d destroyed.\n", monitor_num));
  779.                             vector |= (1 << monitor_num);
  780.                             monitor_num++;
  781.                             Assert(monitor_num < 32);
  782.                             break;
  783.                         }
  784.                     }
  785.                 }
  786.             }
  787.         }
  788.     }
  789.     mprintf((0, "Final monitor vector %x.\n", vector));
  790.     return(vector);
  791. }
  792. #endif
  793.  
  794. void network_stop_resync(sequence_packet *their)
  795. {
  796.     if ( (!memcmp(Network_player_rejoining.player.node, their->player.node, 6)) &&
  797.           (!memcmp(Network_player_rejoining.player.server, their->player.server, 4)) &&
  798.          (!stricmp(Network_player_rejoining.player.callsign, their->player.callsign)) )
  799.     {
  800.         mprintf((0, "Aborting resync for player %s.\n", their->player.callsign));
  801.         Network_send_objects = 0;
  802.         Network_send_objnum = -1;
  803.     }
  804. }
  805.  
  806. byte object_buffer[IPX_MAX_DATA_SIZE];
  807.  
  808. void network_send_objects(void)
  809. {
  810.     short remote_objnum;
  811.     byte owner;
  812.     int loc, i, h;
  813.  
  814.     static int obj_count = 0;
  815.     static int frame_num = 0;
  816.  
  817.     int obj_count_frame = 0;
  818.     int player_num = Network_player_rejoining.player.connected;
  819.  
  820.     // Send clear objects array trigger and send player num
  821.  
  822.     Assert(Network_send_objects != 0);
  823.     Assert(player_num >= 0);
  824.     Assert(player_num < MaxNumNetPlayers);
  825.  
  826.     if (Endlevel_sequence || Fuelcen_control_center_destroyed)
  827.     {
  828.         // Endlevel started before we finished sending the goods, we'll
  829.         // have to stop and try again after the level.
  830.  
  831.         network_dump_player(Network_player_rejoining.player.server,Network_player_rejoining.player.node, DUMP_ENDLEVEL);
  832.         Network_send_objects = 0; 
  833.         return;
  834.     }
  835.  
  836.     for (h = 0; h < OBJ_PACKETS_PER_FRAME; h++) // Do more than 1 per frame, try to speed it up without
  837.                                                               // over-stressing the receiver.
  838.     {
  839.         obj_count_frame = 0;
  840.         memset(object_buffer, 0, IPX_MAX_DATA_SIZE);
  841.         object_buffer[0] = PID_OBJECT_DATA;
  842.         loc = 3;
  843.     
  844.         if (Network_send_objnum == -1)
  845.         {
  846.             obj_count = 0;
  847.             Network_send_object_mode = 0;
  848. //            mprintf((0, "Sending object array to player %d.\n", player_num));
  849.             *(short *)(object_buffer+loc) = -1;    loc += 2;
  850.             object_buffer[loc] = player_num;         loc += 1;
  851.                                                     loc += 2; // Placeholder for remote_objnum, not used here
  852.             Network_send_objnum = 0;
  853.             obj_count_frame = 1;
  854.             frame_num = 0;
  855.         }
  856.         
  857.         for (i = Network_send_objnum; i <= Highest_object_index; i++)
  858.         {
  859.             if ((Objects[i].type != OBJ_POWERUP) && (Objects[i].type != OBJ_PLAYER) &&
  860.                  (Objects[i].type != OBJ_CNTRLCEN) && (Objects[i].type != OBJ_GHOST) &&
  861.                  (Objects[i].type != OBJ_ROBOT) && (Objects[i].type != OBJ_HOSTAGE))
  862.                 continue;
  863.             if ((Network_send_object_mode == 0) && ((object_owner[i] != -1) && (object_owner[i] != player_num)))
  864.                 continue;
  865.             if ((Network_send_object_mode == 1) && ((object_owner[i] == -1) || (object_owner[i] == player_num)))
  866.                 continue;
  867.     
  868.             if ( ((IPX_MAX_DATA_SIZE-1) - loc) < (sizeof(object)+5) )
  869.                 break; // Not enough room for another object
  870.  
  871.             obj_count_frame++;
  872.             obj_count++;
  873.     
  874.             remote_objnum = objnum_local_to_remote((short)i, &owner);
  875.             Assert(owner == object_owner[i]);
  876.  
  877.             *(short *)(object_buffer+loc) = i;                                loc += 2;
  878.             object_buffer[loc] = owner;                                            loc += 1;
  879.             *(short *)(object_buffer+loc) = remote_objnum;                 loc += 2;
  880.             memcpy(object_buffer+loc, &Objects[i], sizeof(object));    loc += sizeof(object);
  881. //            mprintf((0, "..packing object %d, remote %d\n", i, remote_objnum));
  882.         }
  883.  
  884.         if (obj_count_frame) // Send any objects we've buffered
  885.         {
  886.             frame_num++;
  887.     
  888.             Network_send_objnum = i;
  889.             object_buffer[1] = obj_count_frame;
  890.             object_buffer[2] = frame_num;
  891. //            mprintf((0, "Object packet %d contains %d objects.\n", frame_num, obj_count_frame));
  892.  
  893.             Assert(loc <= IPX_MAX_DATA_SIZE);
  894.  
  895.             ipx_send_internetwork_packet_data( object_buffer, loc, Network_player_rejoining.player.server, Network_player_rejoining.player.node );
  896.             // OLD ipx_send_packet_data(object_buffer, loc, &Network_player_rejoining.player.node);
  897.         }
  898.  
  899.         if (i > Highest_object_index)
  900.         {
  901.             if (Network_send_object_mode == 0)
  902.             {
  903.                 Network_send_objnum = 0;
  904.                 Network_send_object_mode = 1; // go to next mode
  905.             }
  906.             else 
  907.             {
  908.                 Assert(Network_send_object_mode == 1);
  909.  
  910.                 frame_num++;
  911.                 // Send count so other side can make sure he got them all
  912. //                mprintf((0, "Sending end marker in packet #%d.\n", frame_num));
  913.                 mprintf((0, "Sent %d objects.\n", obj_count));
  914.                 object_buffer[0] = PID_OBJECT_DATA;
  915.                 object_buffer[1] = 1;
  916.                 object_buffer[2] = frame_num;
  917.                 *(short *)(object_buffer+3) = -2;    
  918.                 *(short *)(object_buffer+6) = obj_count;
  919.                 //OLD ipx_send_packet_data(object_buffer, 8, &Network_player_rejoining.player.node);
  920.                 ipx_send_internetwork_packet_data(object_buffer, 8, Network_player_rejoining.player.server, Network_player_rejoining.player.node);
  921.             
  922.                 // Send sync packet which tells the player who he is and to start!
  923.                 network_send_rejoin_sync(player_num);
  924.  
  925.                 // Turn off send object mode
  926.                 Network_send_objnum = -1;
  927.                 Network_send_objects = 0;
  928.                 obj_count = 0;
  929.                 return;
  930.             } // mode == 1;
  931.         } // i > Highest_object_index
  932.     } // For PACKETS_PER_FRAME
  933. }
  934.  
  935. void network_send_rejoin_sync(int player_num)
  936. {
  937.     int i, j;
  938.  
  939.     Players[player_num].connected = 1; // connect the new guy
  940.     LastPacketTime[player_num] = timer_get_approx_seconds();
  941.  
  942.     if (Endlevel_sequence || Fuelcen_control_center_destroyed)
  943.     {
  944.         // Endlevel started before we finished sending the goods, we'll
  945.         // have to stop and try again after the level.
  946.  
  947.         network_dump_player(Network_player_rejoining.player.server,Network_player_rejoining.player.node, DUMP_ENDLEVEL);
  948.         Network_send_objects = 0; 
  949.         return;
  950.     }
  951.  
  952.     if (Network_player_added)
  953.     {
  954.         Network_player_rejoining.type = PID_ADDPLAYER;
  955.         Network_player_rejoining.player.connected = player_num;
  956.         network_new_player(&Network_player_rejoining);
  957.  
  958.         for (i = 0; i < N_players; i++)
  959.         {
  960.             if ((i != player_num) && (i != Player_num) && (Players[i].connected))
  961.                 ipx_send_packet_data( (ubyte *)&Network_player_rejoining, sizeof(sequence_packet), Netgame.players[i].server, Netgame.players[i].node, Players[i].net_address);
  962.         }
  963.     }    
  964.  
  965.     // Send sync packet to the new guy
  966.  
  967.     network_update_netgame();
  968.  
  969.     // Fill in the kill list
  970.     for (j=0; j<MAX_PLAYERS; j++)
  971.     {
  972.         for (i=0; i<MAX_PLAYERS;i++)
  973.             Netgame.kills[j][i] = kill_matrix[j][i];
  974.         Netgame.killed[j] = Players[j].net_killed_total;
  975.         Netgame.player_kills[j] = Players[j].net_kills_total;
  976. #ifndef SHAREWARE
  977.         Netgame.player_score[j] = Players[j].score;
  978. #endif
  979.     }    
  980.  
  981. #ifndef SHAREWARE
  982.     Netgame.level_time = Players[Player_num].time_level;
  983.     Netgame.monitor_vector = network_create_monitor_vector();
  984. #endif
  985.  
  986.     mprintf((0, "Sending rejoin sync packet!!!\n"));
  987.     
  988.      ipx_send_internetwork_packet_data( (ubyte *)&Netgame, sizeof(netgame_info), Network_player_rejoining.player.server, Network_player_rejoining.player.node );
  989.      ipx_send_internetwork_packet_data( (ubyte *)&Netgame, sizeof(netgame_info), Network_player_rejoining.player.server, Network_player_rejoining.player.node ); // repeat for safety
  990.      ipx_send_internetwork_packet_data( (ubyte *)&Netgame, sizeof(netgame_info), Network_player_rejoining.player.server, Network_player_rejoining.player.node ); // repeat for safety
  991.  
  992. #ifndef SHAREWARE
  993.     network_send_door_updates();
  994. #endif
  995.  
  996.     return;
  997. }
  998.  
  999. char * network_get_player_name( int objnum )
  1000. {
  1001.     if ( objnum < 0 ) return NULL; 
  1002.     if ( Objects[objnum].type != OBJ_PLAYER ) return NULL;
  1003.     if ( Objects[objnum].id >= MAX_PLAYERS ) return NULL;
  1004.     if ( Objects[objnum].id >= N_players ) return NULL;
  1005.     
  1006.     return Players[Objects[objnum].id].callsign;
  1007. }
  1008.  
  1009.  
  1010. void network_add_player(sequence_packet *p)
  1011. {
  1012.     int i;
  1013.     
  1014.     mprintf((0, "Got add player request!\n"));
  1015.  
  1016.     for (i=0; i<N_players; i++ )    {
  1017.         if ( !memcmp( Netgame.players[i].node, p->player.node, 6) && !memcmp(Netgame.players[i].server, p->player.server, 4))    
  1018.             return;        // already got them
  1019.     }
  1020.         
  1021.     if ( N_players >= MAX_PLAYERS )    
  1022.         return;        // too many of em
  1023.  
  1024.     memcpy( Netgame.players[N_players].callsign, p->player.callsign, CALLSIGN_LEN+1 );
  1025.     memcpy( Netgame.players[N_players].node, p->player.node, 6 );
  1026.     memcpy( Netgame.players[N_players].server, p->player.server, 4 );
  1027.     Netgame.players[N_players].connected = 1;
  1028.     Players[N_players].connected = 1;
  1029.     LastPacketTime[N_players] = timer_get_approx_seconds();
  1030.     N_players++;
  1031.     Netgame.numplayers = N_players;
  1032.  
  1033.     // Broadcast updated info
  1034.  
  1035.     network_send_game_info(NULL);
  1036. }
  1037.  
  1038. // One of the players decided not to join the game
  1039.  
  1040. void network_remove_player(sequence_packet *p)
  1041. {
  1042.     int i,pn;
  1043.     
  1044.     pn = -1;
  1045.     for (i=0; i<N_players; i++ )    {
  1046.         if (!memcmp(Netgame.players[i].node, p->player.node, 6) && !memcmp(Netgame.players[i].server, p->player.server, 4))        {
  1047.             pn = i;
  1048.             break;
  1049.         }
  1050.     }
  1051.     
  1052.     if (pn < 0 ) return;
  1053.  
  1054.     for (i=pn; i<N_players-1; i++ )    {
  1055.         memcpy( Netgame.players[i].callsign, Netgame.players[i+1].callsign, CALLSIGN_LEN+1 );
  1056.         memcpy( Netgame.players[i].node, Netgame.players[i+1].node, 6 );
  1057.         memcpy( Netgame.players[i].server, Netgame.players[i+1].server, 4 );
  1058.     }
  1059.         
  1060.     N_players--;
  1061.     Netgame.numplayers = N_players;
  1062.  
  1063.     // Broadcast new info
  1064.  
  1065.     network_send_game_info(NULL);
  1066.  
  1067. }
  1068.  
  1069. void
  1070. network_dump_player(ubyte * server, ubyte *node, int why)
  1071. {
  1072.     // Inform player that he was not chosen for the netgame
  1073.  
  1074.     sequence_packet temp;
  1075.  
  1076.     temp.type = PID_DUMP;
  1077.     memcpy(temp.player.callsign, Players[Player_num].callsign, CALLSIGN_LEN+1);
  1078.     temp.player.connected = why;
  1079.     ipx_send_internetwork_packet_data( (ubyte *)&temp, sizeof(sequence_packet), server, node);
  1080. }
  1081.  
  1082. void
  1083. network_send_game_list_request(void)
  1084. {
  1085.     // Send a broadcast request for game info
  1086.  
  1087.     sequence_packet me;
  1088.  
  1089.     mprintf((0, "Sending game_list request.\n"));
  1090.     memcpy( me.player.callsign, Players[Player_num].callsign, CALLSIGN_LEN+1 );
  1091.     memcpy( me.player.node, ipx_get_my_local_address(), 6 );
  1092.     memcpy( me.player.server, ipx_get_my_server_address(), 4 );
  1093.     me.type = PID_GAME_LIST;
  1094.     
  1095.     ipx_send_broadcast_packet_data( (ubyte *)&me, sizeof(sequence_packet) );
  1096. }
  1097.  
  1098. void
  1099. network_update_netgame(void)
  1100. {
  1101.     // Update the netgame struct with current game variables
  1102.  
  1103.     int i, j;
  1104.  
  1105.     if (Network_status == NETSTAT_STARTING)
  1106.         return;
  1107.  
  1108.     Netgame.numplayers = N_players;
  1109.     Netgame.game_status = Network_status;
  1110.     Netgame.max_numplayers = MaxNumNetPlayers;
  1111.  
  1112.     for (i = 0; i < MAX_NUM_NET_PLAYERS; i++) 
  1113.     {
  1114.         Netgame.players[i].connected = Players[i].connected;
  1115.         for(j = 0; j < MAX_NUM_NET_PLAYERS; j++)
  1116.             Netgame.kills[i][j] = kill_matrix[i][j];
  1117.         Netgame.killed[i] = Players[i].net_killed_total;
  1118.         Netgame.player_kills[i] = Players[i].net_kills_total;
  1119. #ifndef SHAREWARE
  1120.         Netgame.player_score[i] = Players[i].score;
  1121.         Netgame.player_flags[i] = (Players[i].flags & (PLAYER_FLAGS_BLUE_KEY | PLAYER_FLAGS_RED_KEY | PLAYER_FLAGS_GOLD_KEY));
  1122. #endif
  1123.     }
  1124.     Netgame.team_kills[0] = team_kills[0];
  1125.     Netgame.team_kills[1] = team_kills[1];
  1126.     Netgame.levelnum = Current_level_num;
  1127. }
  1128.  
  1129. void
  1130. network_send_endlevel_sub(int player_num)
  1131. {
  1132.     endlevel_info end;
  1133.     int i;
  1134.  
  1135.     // Send an endlevel packet for a player
  1136.     end.type         = PID_ENDLEVEL;
  1137.     end.player_num = player_num;
  1138.     end.connected    = Players[player_num].connected;
  1139.     end.kills        = Players[player_num].net_kills_total;
  1140.     end.killed        = Players[player_num].net_killed_total;
  1141.     memcpy(end.kill_matrix, kill_matrix[player_num], MAX_PLAYERS*sizeof(short));
  1142.  
  1143.     if (Players[player_num].connected == 1) // Still playing
  1144.     {
  1145.         Assert(Fuelcen_control_center_destroyed);
  1146.          end.seconds_left = Fuelcen_seconds_left;
  1147.     }
  1148.  
  1149. //    mprintf((0, "Sending endlevel packet.\n"));
  1150.  
  1151.     for (i = 0; i < N_players; i++)
  1152.     {    
  1153.         if ((i != Player_num) && (i!=player_num) && (Players[i].connected))
  1154.             ipx_send_packet_data((ubyte *)&end, sizeof(endlevel_info), Netgame.players[i].server, Netgame.players[i].node,Players[i].net_address);
  1155.     }
  1156. }
  1157.  
  1158. void
  1159. network_send_endlevel_packet(void)
  1160. {
  1161.     // Send an updated endlevel status to other hosts
  1162.  
  1163.     network_send_endlevel_sub(Player_num);
  1164. }
  1165.  
  1166. void
  1167. network_send_game_info(sequence_packet *their)
  1168. {
  1169.      // Send game info to someone who requested it
  1170.  
  1171.     char old_type, old_status;
  1172.  
  1173.     mprintf((0, "Sending game info.\n"));
  1174.  
  1175.     network_update_netgame(); // Update the values in the netgame struct
  1176.  
  1177.     old_type = Netgame.type;
  1178.     old_status = Netgame.game_status;
  1179.  
  1180.     Netgame.type = PID_GAME_INFO;
  1181.     if (Endlevel_sequence || Fuelcen_control_center_destroyed)
  1182.         Netgame.game_status = NETSTAT_ENDLEVEL;
  1183.  
  1184.     if (!their)
  1185.         ipx_send_broadcast_packet_data((ubyte *)&Netgame, sizeof(netgame_info));
  1186.     else    
  1187.         ipx_send_internetwork_packet_data((ubyte *)&Netgame, sizeof(netgame_info), their->player.server, their->player.node);
  1188.  
  1189.     Netgame.type = old_type;
  1190.     Netgame.game_status = old_status;
  1191. }    
  1192.  
  1193. int network_send_request(void)
  1194. {
  1195.     // Send a request to join a game 'Netgame'.  Returns 0 if we can join this
  1196.     // game, non-zero if there is some problem.
  1197.     int i;
  1198.  
  1199.     Assert(Netgame.numplayers > 0);
  1200.  
  1201.     for (i = 0; i < MAX_NUM_NET_PLAYERS; i++)
  1202.         if (Netgame.players[i].connected)
  1203.             break;
  1204.  
  1205.     Assert(i < MAX_NUM_NET_PLAYERS);
  1206.  
  1207.     mprintf((0, "Sending game enroll request to player %d.  Level = %d\n", i, Netgame.levelnum));
  1208.  
  1209. //    segments_checksum = netmisc_calc_checksum( Segments, sizeof(segment)*(Highest_segment_index+1) );     
  1210.  
  1211.     My_Seq.type = PID_REQUEST;
  1212.     My_Seq.player.connected = Current_level_num;
  1213.  
  1214.     ipx_send_internetwork_packet_data((ubyte *)&My_Seq, sizeof(sequence_packet), Netgame.players[i].server, Netgame.players[i].node);
  1215.     return i;
  1216. }
  1217.     
  1218. void network_process_gameinfo(ubyte *data)
  1219. {
  1220.     int i, j;
  1221.     netgame_info *new;
  1222.  
  1223.     new = (netgame_info *)data;        
  1224.  
  1225.     Network_games_changed = 1;
  1226.  
  1227.     mprintf((0, "Got game data for game %s.\n", new->game_name));
  1228.  
  1229.     for (i = 0; i < num_active_games; i++)
  1230.         if (!stricmp(Active_games[i].game_name, new->game_name) && !memcmp(Active_games[i].players[0].node, new->players[0].node, 6)
  1231.              && !memcmp(Active_games[i].players[0].server, new->players[0].server, 4))
  1232.             break;
  1233.  
  1234.     if (i == MAX_ACTIVE_NETGAMES)
  1235.     {
  1236.         mprintf((0, "Too many netgames.\n"));
  1237.         return;
  1238.     }
  1239.     memcpy(&Active_games[i], data, sizeof(netgame_info));
  1240.     if (i == num_active_games)
  1241.         num_active_games++;
  1242.  
  1243.     if (Active_games[i].numplayers == 0)
  1244.     {
  1245.         // Delete this game
  1246.         for (j = i; j < num_active_games-1; j++)
  1247.             memcpy(&Active_games[j], &Active_games[j+1], sizeof(netgame_info));
  1248.         num_active_games--;
  1249.     }
  1250. }
  1251.  
  1252. void network_process_dump(sequence_packet *their)
  1253. {
  1254.     // Our request for join was denied.  Tell the user why.
  1255.  
  1256.     mprintf((0, "Dumped by player %s, type %d.\n", their->player.callsign, their->player.connected));
  1257.  
  1258.     nm_messagebox(NULL, 1, TXT_OK, NET_DUMP_STRINGS(their->player.connected));
  1259.     Network_status = NETSTAT_MENU;
  1260.     
  1261. void network_process_request(sequence_packet *their)
  1262. {
  1263.     // Player is ready to receieve a sync packet
  1264.     int i;
  1265.  
  1266.     mprintf((0, "Player %s ready for sync.\n", their->player.callsign));
  1267.  
  1268.     for (i = 0; i < N_players; i++)
  1269.         if (!memcmp(their->player.server, Netgame.players[i].server, 4) && !memcmp(their->player.node, Netgame.players[i].node, 6) && (!stricmp(their->player.callsign, Netgame.players[i].callsign))) {
  1270.             Players[i].connected = 1;
  1271.             break;
  1272.         }
  1273. }
  1274.  
  1275. void network_process_packet(ubyte *data, int length )
  1276. {
  1277.     sequence_packet *their = (sequence_packet *)data;
  1278.  
  1279. //    mprintf( (0, "Got packet of length %d, type %d\n", length, their->type ));
  1280.     
  1281. //    if ( length < sizeof(sequence_packet) ) return;
  1282.  
  1283.     length = length;
  1284.  
  1285.     switch( their->type )    {
  1286.     
  1287.     case PID_GAME_INFO:
  1288.         mprintf((0, "GOT a PID_GAME_INFO!\n"));
  1289.         if (length != sizeof(netgame_info))
  1290.             mprintf((0, " Invalid size %d for netgame packet.\n", length));
  1291.         if (Network_status == NETSTAT_BROWSING)
  1292.             network_process_gameinfo(data);
  1293.         break;
  1294.     case PID_GAME_LIST:
  1295.         // Someone wants a list of games
  1296.         mprintf((0, "Got a PID_GAME_LIST!\n"));
  1297.         if ((Network_status == NETSTAT_PLAYING) || (Network_status == NETSTAT_STARTING) || (Network_status == NETSTAT_ENDLEVEL))
  1298.             if (network_i_am_master())
  1299.                 network_send_game_info(their);
  1300.         break;
  1301.     case PID_ADDPLAYER:
  1302.         mprintf( (0, "Got NEWPLAYER message from %s.\n", their->player.callsign));
  1303.         network_new_player(their);
  1304.         break;            
  1305.     case PID_REQUEST:
  1306.         mprintf( (0, "Got REQUEST from '%s'\n", their->player.callsign ));
  1307.         if (Network_status == NETSTAT_STARTING)    
  1308.         {
  1309.             // Someone wants to join our game!
  1310.             network_add_player(their);
  1311.         }
  1312.         else if (Network_status == NETSTAT_WAITING)
  1313.         {
  1314.             // Someone is ready to recieve a sync packet
  1315.             network_process_request(their);
  1316.         }
  1317.         else if (Network_status == NETSTAT_PLAYING)
  1318.         {
  1319.             // Someone wants to join a game in progress!
  1320.             network_welcome_player(their);
  1321.         }
  1322.         break;
  1323.     case PID_DUMP:    
  1324.         if (Network_status == NETSTAT_WAITING)
  1325.             network_process_dump(their);
  1326.         break;
  1327.     case PID_QUIT_JOINING:
  1328.         if (Network_status == NETSTAT_STARTING)
  1329.             network_remove_player( their );
  1330.         else if ((Network_status == NETSTAT_PLAYING) && (Network_send_objects))
  1331.             network_stop_resync( their );
  1332.         break;
  1333.     case PID_SYNC:    
  1334.         if (Network_status == NETSTAT_WAITING)    {
  1335.             network_read_sync_packet((netgame_info *)data);
  1336.         }
  1337.         break;
  1338.     case PID_PDATA:    
  1339.         if ((Game_mode&GM_NETWORK) && ((Network_status == NETSTAT_PLAYING)||(Network_status == NETSTAT_ENDLEVEL) )) { 
  1340.             network_read_pdata_packet((frame_info *)data);
  1341.         }
  1342.         break;
  1343.     case PID_OBJECT_DATA:
  1344.         if (Network_status == NETSTAT_WAITING) 
  1345.             network_read_object_packet(data);
  1346.         break;
  1347.     case PID_ENDLEVEL:
  1348.         if ((Network_status == NETSTAT_ENDLEVEL) || (Network_status == NETSTAT_PLAYING))
  1349.             network_read_endlevel_packet(data);
  1350.         else
  1351.             mprintf((0, "Junked endlevel packet.\n"));
  1352.         break;
  1353.     default:
  1354.         mprintf((0, "Ignoring invalid packet type.\n"));
  1355.         Int3(); // Invalid network packet type, see ROB
  1356.     }
  1357. }
  1358.  
  1359. #ifndef NDEBUG
  1360. void dump_segments()
  1361. {
  1362.     FILE * fp;
  1363.  
  1364.     fp = fopen( "TEST.DMP", "wb" );
  1365.     fwrite( Segments, sizeof(segment)*(Highest_segment_index+1),1, fp );     
  1366.     fclose(fp);
  1367.     mprintf( (0, "SS=%d\n", sizeof(segment) ));
  1368. }
  1369. #endif
  1370.  
  1371. void
  1372. network_read_endlevel_packet( ubyte *data )
  1373. {
  1374.     // Special packet for end of level syncing
  1375.  
  1376.     int playernum;
  1377.     endlevel_info *end;    
  1378.  
  1379.     end = (endlevel_info *)data;
  1380.  
  1381.     playernum = end->player_num;
  1382.     
  1383.     Assert(playernum != Player_num);
  1384.     Assert(playernum < N_players);
  1385.  
  1386.     if ((Network_status == NETSTAT_PLAYING) && (end->connected != 0))
  1387.         return; // Only accept disconnect packets if we're not out of the level yet
  1388.  
  1389.     Players[playernum].connected = end->connected;
  1390.     memcpy(&kill_matrix[playernum][0], end->kill_matrix, MAX_PLAYERS*sizeof(short));
  1391.     Players[playernum].net_kills_total = end->kills;
  1392.     Players[playernum].net_killed_total = end->killed;
  1393.  
  1394.     if ((Players[playernum].connected == 1) && (end->seconds_left < Fuelcen_seconds_left))
  1395.         Fuelcen_seconds_left = end->seconds_left;
  1396.  
  1397.     LastPacketTime[playernum] = timer_get_approx_seconds();
  1398.  
  1399. //    mprintf((0, "Got endlevel packet from player %d.\n", playernum));
  1400. }
  1401.  
  1402. void
  1403. network_pack_objects(void)
  1404. {
  1405.     // Switching modes, pack the object array
  1406.  
  1407.     special_reset_objects();
  1408. }                
  1409.  
  1410. int
  1411. network_verify_objects(int remote, int local)
  1412. {
  1413.     int i;
  1414.     int nplayers, got_controlcen=0;
  1415.  
  1416.     if ((remote-local) > 10)
  1417.         return(-1);
  1418.  
  1419.     if (Game_mode & GM_MULTI_ROBOTS)
  1420.         got_controlcen = 1;
  1421.  
  1422.     nplayers = 0;
  1423.  
  1424.     for (i = 0; i <= Highest_object_index; i++)
  1425.     {
  1426.         if ((Objects[i].type == OBJ_PLAYER) || (Objects[i].type == OBJ_GHOST))
  1427.             nplayers++;
  1428.         if (Objects[i].type == OBJ_CNTRLCEN)
  1429.             got_controlcen=1;
  1430.     }
  1431.  
  1432.     if (got_controlcen && (nplayers >= MaxNumNetPlayers))
  1433.         return(0);
  1434.  
  1435.     return(1);
  1436. }
  1437.     
  1438. void
  1439. network_read_object_packet( ubyte *data )
  1440. {
  1441.     // Object from another net player we need to sync with
  1442.  
  1443.     short objnum, remote_objnum;
  1444.     byte obj_owner;
  1445.     int segnum, i;
  1446.     object *obj;
  1447.  
  1448.     static int my_pnum = 0;
  1449.     static int mode = 0;
  1450.     static int object_count = 0;
  1451.     static int frame_num = 0;
  1452.     int nobj = data[1];
  1453.     int loc = 3;
  1454.     int remote_frame_num = data[2];
  1455.     
  1456.     frame_num++;
  1457.  
  1458. //    mprintf((0, "Object packet %d (remote #%d) contains %d objects.\n", frame_num, remote_frame_num, nobj));
  1459.  
  1460.     for (i = 0; i < nobj; i++)
  1461.     {
  1462.         objnum = *(short *)(data+loc);            loc += 2;
  1463.         obj_owner = data[loc];                        loc += 1;
  1464.         remote_objnum = *(short *)(data+loc);    loc += 2;
  1465.  
  1466.         if (objnum == -1) 
  1467.         {
  1468.             // Clear object array
  1469.             mprintf((0, "Clearing object array.\n"));
  1470.  
  1471.             init_objects();
  1472.             Network_rejoined = 1;
  1473.             my_pnum = obj_owner;
  1474.             change_playernum_to(my_pnum);
  1475.             mode = 1;
  1476.             object_count = 0;
  1477.             frame_num = 1;
  1478.         }
  1479.         else if (objnum == -2)
  1480.         {
  1481.             // Special debug checksum marker for entire send
  1482.             if (mode == 1)
  1483.             {
  1484.                 network_pack_objects();
  1485.                 mode = 0;
  1486.             }
  1487.             mprintf((0, "Objnum -2 found in frame local %d remote %d.\n", frame_num, remote_frame_num));
  1488.             mprintf((0, "Got %d objects, expected %d.\n", object_count, remote_objnum));
  1489.             if (remote_objnum != object_count) {
  1490.                 Int3();
  1491.             }
  1492.             if (network_verify_objects(remote_objnum, object_count))
  1493.             {
  1494.                 // Failed to sync up 
  1495.                 nm_messagebox(NULL, 1, TXT_OK, TXT_NET_SYNC_FAILED);
  1496.                 Network_status = NETSTAT_MENU;                
  1497.                 return;
  1498.             }
  1499.             frame_num = 0;
  1500.         }
  1501.         else 
  1502.         {
  1503.             if (frame_num != remote_frame_num)
  1504.                 Int3();
  1505.  
  1506.             object_count++;
  1507.             if ((obj_owner == my_pnum) || (obj_owner == -1)) 
  1508.             {
  1509.                 if (mode != 1)
  1510.                     Int3(); // SEE ROB
  1511.                 objnum = remote_objnum;
  1512.                 //if (objnum > Highest_object_index)
  1513.                 //{
  1514.                 //    Highest_object_index = objnum;
  1515.                 //    num_objects = Highest_object_index+1;
  1516.                 //}
  1517.             }
  1518.             else {
  1519.                 if (mode == 1)
  1520.                 {
  1521.                     network_pack_objects();
  1522.                     mode = 0;
  1523.                 }
  1524.                 objnum = obj_allocate();
  1525.             }
  1526.             if (objnum != -1) {
  1527.                 obj = &Objects[objnum];
  1528.                 if (obj->segnum != -1)
  1529.                     obj_unlink(objnum);
  1530.                 Assert(obj->segnum == -1);
  1531.                 Assert(objnum < MAX_OBJECTS);
  1532.                 memcpy(obj,data+loc,sizeof(object));        loc += sizeof(object);
  1533.                 segnum = obj->segnum;
  1534.                 obj->next = obj->prev = obj->segnum = -1;
  1535.                 obj->attached_obj = -1;
  1536.                 if (segnum > -1)
  1537.                     obj_link(obj-Objects,segnum);
  1538.                 if (obj_owner == my_pnum) 
  1539.                     map_objnum_local_to_local(objnum);
  1540.                 else if (obj_owner != -1)
  1541.                     map_objnum_local_to_remote(objnum, remote_objnum, obj_owner);
  1542.                 else
  1543.                     object_owner[objnum] = -1;
  1544.             }
  1545.         } // For a standard onbject
  1546.     } // For each object in packet
  1547. }
  1548.     
  1549. void network_sync_poll( int nitems, newmenu_item * menus, int * key, int citem )
  1550. {
  1551.     // Polling loop waiting for sync packet to start game
  1552.  
  1553.  
  1554.     static fix t1 = 0;
  1555.  
  1556.     menus = menus;
  1557.     citem = citem;
  1558.     nitems = nitems;
  1559.  
  1560.     network_listen();
  1561.  
  1562.     if (Network_status != NETSTAT_WAITING)    // Status changed to playing, exit the menu
  1563.         *key = -2;
  1564.  
  1565.     if (!Network_rejoined && (timer_get_approx_seconds() > t1+F1_0*2))
  1566.     {
  1567.         int i;
  1568.  
  1569.         // Poll time expired, re-send request
  1570.         
  1571.         t1 = timer_get_approx_seconds();
  1572.  
  1573.         mprintf((0, "Re-sending join request.\n"));
  1574.         i = network_send_request();
  1575.         if (i < 0)
  1576.             *key = -2;
  1577.     }
  1578. }
  1579.  
  1580. void network_start_poll( int nitems, newmenu_item * menus, int * key, int citem )
  1581. {
  1582.     int i,n,nm;
  1583.  
  1584.     key=key;
  1585.     citem=citem;
  1586.  
  1587.     Assert(Network_status == NETSTAT_STARTING);
  1588.  
  1589.     if (!menus[0].value) {
  1590.             menus[0].value = 1;
  1591.             menus[0].redraw = 1;
  1592.     }
  1593.  
  1594.     for (i=1; i<nitems; i++ )    {
  1595.         if ( (i>= N_players) && (menus[i].value) )    {
  1596.             menus[i].value = 0;
  1597.             menus[i].redraw = 1;
  1598.         }
  1599.     }
  1600.  
  1601.     nm = 0;
  1602.     for (i=0; i<nitems; i++ )    {
  1603.         if ( menus[i].value )    {
  1604.             nm++;
  1605.             if ( nm > N_players )    {
  1606.                 menus[i].value = 0;
  1607.                 menus[i].redraw = 1;
  1608.             }
  1609.         }
  1610.     }
  1611.  
  1612.     if ( nm > MaxNumNetPlayers )    {
  1613.         nm_messagebox( TXT_ERROR, 1, TXT_OK, "%s %d %s", TXT_SORRY_ONLY, MaxNumNetPlayers, TXT_NETPLAYERS_IN );
  1614.         // Turn off the last player highlighted
  1615.         for (i = N_players; i > 0; i--)
  1616.             if (menus[i].value == 1) 
  1617.             {
  1618.                 menus[i].value = 0;
  1619.                 menus[i].redraw = 1;
  1620.                 break;
  1621.             }
  1622.     }
  1623.  
  1624.     if (nitems > MAX_PLAYERS ) return;
  1625.     
  1626.     n = Netgame.numplayers;
  1627.     network_listen();
  1628.  
  1629.     if (n < Netgame.numplayers )     
  1630.     {
  1631.         sprintf( menus[N_players-1].text, "%d. %-16s", N_players, Netgame.players[N_players-1].callsign );
  1632.         menus[N_players-1].redraw = 1;
  1633.         if (N_players <= MaxNumNetPlayers)
  1634.         {
  1635.             menus[N_players-1].value = 1;
  1636.         }
  1637.     } 
  1638.     else if ( n > Netgame.numplayers )    
  1639.     {
  1640.         // One got removed...
  1641.         for (i=0; i<N_players; i++ )    
  1642.         {
  1643.             sprintf( menus[i].text, "%d. %-16s", i+1, Netgame.players[i].callsign );
  1644.             if (i < MaxNumNetPlayers)
  1645.                 menus[i].value = 1;
  1646.             else
  1647.                 menus[i].value = 0;
  1648.             menus[i].redraw = 1;
  1649.         }
  1650.         for (i=N_players; i<n; i++ )    
  1651.         {
  1652.             sprintf( menus[i].text, "%d. ", i+1 );        // Clear out the deleted entries...
  1653.             menus[i].value = 0;
  1654.             menus[i].redraw = 1;
  1655.         }
  1656.    }
  1657. }
  1658.  
  1659. int opt_cinvul;
  1660. int last_cinvul=0;
  1661. int opt_mode;
  1662.  
  1663. #pragma off (unreferenced)
  1664. void network_game_param_poll( int nitems, newmenu_item * menus, int * key, int citem )
  1665. {
  1666. #ifdef SHAREWARE
  1667.     return;
  1668. #else
  1669. #ifndef ROCKWELL_CODE
  1670.      if (menus[opt_mode+2].value && !menus[opt_mode+6].value) { 
  1671.         menus[opt_mode+6].value = 1;
  1672.         menus[opt_mode+6].redraw = 1;
  1673.     }
  1674.     if (menus[opt_mode+4].value) {
  1675.         if (!menus[opt_mode+7].value) {
  1676.             menus[opt_mode+7].value = 1;
  1677.             menus[opt_mode+7].redraw = 1;
  1678.         }
  1679.     }
  1680. #endif
  1681.     if ( last_cinvul != menus[opt_cinvul].value )    {
  1682.         sprintf( menus[opt_cinvul].text, "%s: %d %s", TXT_REACTOR_LIFE, menus[opt_cinvul].value*5, TXT_MINUTES_ABBREV );
  1683.         last_cinvul = menus[opt_cinvul].value;
  1684.         menus[opt_cinvul].redraw = 1;
  1685.     }        
  1686.  
  1687. #endif
  1688. }
  1689. #pragma on (unreferenced)
  1690.  
  1691. int network_get_game_params( char * game_name, int *mode, int *game_flags, int *level )
  1692. {
  1693.     int i;
  1694.     int opt, opt_name, opt_level, opt_closed, opt_difficulty;
  1695.     newmenu_item m[16];
  1696.     char name[NETGAME_NAME_LEN+1];
  1697.     char slevel[5];
  1698.     char level_text[32];
  1699.     char srinvul[32];
  1700.  
  1701. #ifndef SHAREWARE
  1702.     int new_mission_num;
  1703.     int anarchy_only;
  1704.  
  1705.     new_mission_num = multi_choose_mission(&anarchy_only);
  1706.  
  1707.     if (new_mission_num < 0)
  1708.         return -1;
  1709.  
  1710.     strcpy(Netgame.mission_name, Mission_list[new_mission_num].filename);
  1711.     strcpy(Netgame.mission_title, Mission_list[new_mission_num].mission_name);
  1712.     Netgame.control_invul_time = control_invul_time;
  1713. #endif
  1714.     
  1715.     sprintf( name, "%s%s", Players[Player_num].callsign, TXT_S_GAME );
  1716.     sprintf( slevel, "1" );
  1717.  
  1718.     opt = 0;
  1719.     m[opt].type = NM_TYPE_TEXT; m[opt].text = TXT_DESCRIPTION; opt++;
  1720.  
  1721.     opt_name = opt;
  1722.     m[opt].type = NM_TYPE_INPUT; m[opt].text = name; m[opt].text_len = NETGAME_NAME_LEN; opt++;
  1723.  
  1724.     sprintf(level_text, "%s (1-%d)", TXT_LEVEL_, Last_level);
  1725.     if (Last_secret_level < -1)
  1726.         sprintf(level_text+strlen(level_text)-1, ", S1-S%d)", -Last_secret_level);
  1727.     else if (Last_secret_level == -1)
  1728.         sprintf(level_text+strlen(level_text)-1, ", S1)");
  1729.  
  1730.     Assert(strlen(level_text) < 32);
  1731.  
  1732.     m[opt].type = NM_TYPE_TEXT; m[opt].text = level_text; opt++;
  1733.  
  1734.     opt_level = opt;
  1735.     m[opt].type = NM_TYPE_INPUT; m[opt].text = slevel; m[opt].text_len=4; opt++;
  1736.  
  1737. #ifdef ROCKWELL_CODE    
  1738.     opt_mode = 0;
  1739. #else
  1740.     opt_mode = opt;
  1741.     m[opt].type = NM_TYPE_TEXT; m[opt].text = TXT_MODE; opt++;
  1742.     m[opt].type = NM_TYPE_RADIO; m[opt].text = TXT_ANARCHY; m[opt].value=1; m[opt].group=0; opt++;
  1743.     m[opt].type = NM_TYPE_RADIO; m[opt].text = TXT_TEAM_ANARCHY; m[opt].value=0; m[opt].group=0; opt++;
  1744.     m[opt].type = NM_TYPE_RADIO; m[opt].text = TXT_ANARCHY_W_ROBOTS; m[opt].value=0; m[opt].group=0; opt++;
  1745.     m[opt].type = NM_TYPE_RADIO; m[opt].text = TXT_COOPERATIVE; m[opt].value=0; m[opt].group=0; opt++;
  1746. #endif
  1747.     m[opt].type = NM_TYPE_TEXT; m[opt].text = TXT_OPTIONS; opt++;
  1748.  
  1749.     opt_closed = opt;
  1750.     m[opt].type = NM_TYPE_CHECK; m[opt].text = TXT_CLOSED_GAME; m[opt].value=0; opt++;
  1751. #ifndef SHAREWARE
  1752. //    m[opt].type = NM_TYPE_CHECK; m[opt].text = TXT_SHOW_IDS; m[opt].value=0; opt++;
  1753.     m[opt].type = NM_TYPE_CHECK; m[opt].text = TXT_SHOW_ON_MAP; m[opt].value=0; opt++;
  1754. #endif
  1755.  
  1756.     opt_difficulty = opt;
  1757.     m[opt].type = NM_TYPE_SLIDER; m[opt].value=Player_default_difficulty; m[opt].text=TXT_DIFFICULTY; m[opt].min_value=0; m[opt].max_value=(NDL-1); opt++;
  1758.  
  1759. //    m[opt].type = NM_TYPE_TEXT; m[opt].text = "Reactor Invulnerability (mins)"; opt++;
  1760. //    opt_cinvul = opt;
  1761. //    sprintf( srinvul, "%d", control_invul_time );
  1762. //    m[opt].type = NM_TYPE_INPUT; m[opt].text = srinvul; m[opt].text_len=2; opt++;
  1763.         
  1764.     opt_cinvul = opt;
  1765.     sprintf( srinvul, "%s: %d %s", TXT_REACTOR_LIFE, 5*control_invul_time, TXT_MINUTES_ABBREV );
  1766.     last_cinvul = control_invul_time;
  1767.     m[opt].type = NM_TYPE_SLIDER; m[opt].value=control_invul_time; m[opt].text= srinvul; m[opt].min_value=0; m[opt].max_value=15; opt++;
  1768.  
  1769.     Assert(opt <= 16);
  1770.  
  1771. menu:
  1772.     i = newmenu_do1( NULL, TXT_NETGAME_SETUP, opt, m, network_game_param_poll, 1 );
  1773.     
  1774.     if ( i > -1 )    {
  1775.         int j;
  1776.  
  1777.         for (j = 0; j < num_active_games; j++)
  1778.             if (!stricmp(Active_games[j].game_name, name))
  1779.             {
  1780.                 nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_DUPLICATE_NAME);
  1781.                 goto menu;
  1782.             }
  1783.  
  1784.         strcpy( game_name, name );
  1785.         
  1786.  
  1787.         if (!strnicmp(slevel, "s", 1))
  1788.             *level = -atoi(slevel+1);
  1789.         else
  1790.             *level = atoi(slevel);
  1791.  
  1792.         if ((*level < Last_secret_level) || (*level > Last_level) || (*level == 0))
  1793.         {
  1794.             nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_LEVEL_OUT_RANGE );
  1795.             sprintf(slevel, "1");
  1796.             goto menu;
  1797.         }
  1798. #ifdef ROCKWELL_CODE    
  1799.         *mode = NETGAME_COOPERATIVE;
  1800. #else
  1801.         if ( m[opt_mode+1].value )
  1802.              *mode = NETGAME_ANARCHY;
  1803.         else if (m[opt_mode+2].value) {
  1804.             *mode = NETGAME_TEAM_ANARCHY;
  1805.         } else if (anarchy_only) {
  1806.             nm_messagebox(NULL, 1, TXT_OK, TXT_ANARCHY_ONLY_MISSION);
  1807.             m[opt_mode+2].value = 0;
  1808.             m[opt_mode+3].value = 0;
  1809.             m[opt_mode].value = 1;
  1810.             goto menu;
  1811.         } else if ( m[opt_mode+3].value )    
  1812.             *mode = NETGAME_ROBOT_ANARCHY;
  1813.         else if ( m[opt_mode+4].value ) 
  1814.             *mode = NETGAME_COOPERATIVE;
  1815.         else Int3(); // Invalid mode -- see Rob
  1816. #endif    // ifdef ROCKWELL
  1817.  
  1818.         if (m[opt_closed].value)
  1819.             *game_flags |= NETGAME_FLAG_CLOSED;
  1820. #ifndef SHAREWARE
  1821. //        if (m[opt_closed+1].value)
  1822. //            *game_flags |= NETGAME_FLAG_SHOW_ID;
  1823.         if (m[opt_closed+1].value)
  1824.             *game_flags |= NETGAME_FLAG_SHOW_MAP;
  1825. #endif
  1826.  
  1827.         Difficulty_level = m[opt_difficulty].value;
  1828.  
  1829.         //control_invul_time = atoi( srinvul )*60*F1_0;
  1830.         control_invul_time = m[opt_cinvul].value;
  1831.         Netgame.control_invul_time = control_invul_time*5*F1_0*60;
  1832.     }
  1833.     return i;
  1834. }
  1835.  
  1836. void
  1837. network_set_game_mode(int gamemode)
  1838. {
  1839.     Show_kill_list = 1;
  1840.  
  1841.     if ( gamemode == NETGAME_ANARCHY )
  1842.         Game_mode = GM_NETWORK;
  1843.     else if ( gamemode == NETGAME_ROBOT_ANARCHY )
  1844.         Game_mode = GM_NETWORK | GM_MULTI_ROBOTS;
  1845.     else if ( gamemode == NETGAME_COOPERATIVE ) 
  1846.         Game_mode = GM_NETWORK | GM_MULTI_COOP | GM_MULTI_ROBOTS;
  1847.     else if ( gamemode == NETGAME_TEAM_ANARCHY )
  1848.     {
  1849.         Game_mode = GM_NETWORK | GM_TEAM;
  1850.         Show_kill_list = 2;
  1851.     }
  1852.     else
  1853.         Int3();
  1854.     if (Game_mode & GM_MULTI_ROBOTS)
  1855.         MaxNumNetPlayers = 4;
  1856.     else
  1857.         MaxNumNetPlayers = 8;
  1858. }
  1859.  
  1860. int
  1861. network_find_game(void)
  1862. {
  1863.     // Find out whether or not there is space left on this socket
  1864.  
  1865.     fix t1;
  1866.  
  1867.     Network_status = NETSTAT_BROWSING;
  1868.  
  1869.     num_active_games = 0;
  1870.  
  1871.     show_boxed_message(TXT_WAIT);
  1872.  
  1873.     network_send_game_list_request();
  1874.     t1 = timer_get_approx_seconds() + F1_0*2;
  1875.  
  1876.     while (timer_get_approx_seconds() < t1) // Wait 3 seconds for replies
  1877.         network_listen();
  1878.  
  1879.     clear_boxed_message();
  1880.  
  1881. //    mprintf((0, "%s %d %s\n", TXT_FOUND, num_active_games, TXT_ACTIVE_GAMES));
  1882.  
  1883.     if (num_active_games < MAX_ACTIVE_NETGAMES)
  1884.         return 0;
  1885.     return 1;
  1886. }
  1887.     
  1888. void network_read_sync_packet( netgame_info * sp )
  1889. {    
  1890.     int i, j;
  1891.  
  1892.     char temp_callsign[CALLSIGN_LEN+1];
  1893.     
  1894.     // This function is now called by all people entering the netgame.
  1895.  
  1896.     // mprintf( (0, "%s %d\n", TXT_STARTING_NETGAME, sp->levelnum ));
  1897.  
  1898.     if (sp != &Netgame)
  1899.         memcpy( &Netgame, sp, sizeof(netgame_info) );
  1900.  
  1901.     N_players = sp->numplayers;
  1902.     Difficulty_level = sp->difficulty;
  1903.     Network_status = sp->game_status;
  1904.  
  1905.     Assert(Function_mode != FMODE_GAME);
  1906.  
  1907.     // New code, 11/27
  1908.  
  1909.     mprintf((1, "Netgame.checksum = %d, calculated checksum = %d.\n", Netgame.segments_checksum, my_segments_checksum));
  1910.  
  1911.     if (Netgame.segments_checksum != my_segments_checksum)
  1912.     {
  1913.         Network_status = NETSTAT_MENU;
  1914.         nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_NETLEVEL_NMATCH);
  1915. #ifdef NDEBUG
  1916.         return;
  1917. #endif
  1918.     }
  1919.  
  1920.     // Discover my player number
  1921.  
  1922.     memcpy(temp_callsign, Players[Player_num].callsign, CALLSIGN_LEN+1);
  1923.     
  1924.     Player_num = -1;
  1925.  
  1926.     for (i=0; i<MAX_NUM_NET_PLAYERS; i++ )    {
  1927.         Players[i].net_kills_total = 0;
  1928. //        Players[i].net_killed_total = 0;
  1929.     }
  1930.  
  1931.     for (i=0; i<N_players; i++ )    {
  1932.         if ((!memcmp( sp->players[i].node, My_Seq.player.node, 6 )) &&
  1933.              (!stricmp( sp->players[i].callsign, temp_callsign)) )    
  1934.         {
  1935.             Assert(Player_num == -1); // Make sure we don't find ourselves twice!  Looking for interplay reported bug
  1936.             change_playernum_to(i);
  1937.         }
  1938.         memcpy( Players[i].callsign, sp->players[i].callsign, CALLSIGN_LEN+1 );
  1939.  
  1940. #ifndef SHAREWARE
  1941.         if ( (*(uint *)sp->players[i].server) != 0 )
  1942.             ipx_get_local_target( sp->players[i].server, sp->players[i].node, Players[i].net_address );
  1943.         else
  1944. #endif
  1945.             memcpy( Players[i].net_address, sp->players[i].node, 6 );
  1946.  
  1947.         Players[i].n_packets_got=0;                // How many packets we got from them
  1948.         Players[i].n_packets_sent=0;                // How many packets we sent to them
  1949.         Players[i].connected = sp->players[i].connected;
  1950.         Players[i].net_kills_total += sp->player_kills[i];
  1951. #ifndef SHAREWARE
  1952.         if ((Network_rejoined) || (i != Player_num))
  1953.             Players[i].score = sp->player_score[i];
  1954. #endif        
  1955.         for (j = 0; j < MAX_NUM_NET_PLAYERS; j++)
  1956.         {
  1957.             kill_matrix[i][j] = sp->kills[i][j];
  1958.         }
  1959.     }
  1960.  
  1961.     if ( Player_num < 0 )    {
  1962.         Network_status = NETSTAT_MENU;
  1963.         return;
  1964.     }
  1965.  
  1966.     if (Network_rejoined) 
  1967.         for (i=0; i<N_players;i++)
  1968.             Players[i].net_killed_total = sp->killed[i];
  1969.  
  1970. #ifndef SHAREWARE
  1971.     if (Network_rejoined) {
  1972.         network_process_monitor_vector(sp->monitor_vector);
  1973.         Players[Player_num].time_level = sp->level_time;
  1974.     }
  1975. #endif
  1976.  
  1977.     team_kills[0] = sp->team_kills[0];
  1978.     team_kills[1] = sp->team_kills[1];
  1979.     
  1980.     Players[Player_num].connected = 1;
  1981.     Netgame.players[Player_num].connected = 1;
  1982.  
  1983.     if (!Network_rejoined)
  1984.         for (i=0; i<MaxNumNetPlayers; i++) {
  1985.             Objects[Players[i].objnum].pos = Player_init[Netgame.locations[i]].pos;
  1986.             Objects[Players[i].objnum].orient = Player_init[Netgame.locations[i]].orient;
  1987.              obj_relink(Players[i].objnum,Player_init[Netgame.locations[i]].segnum);
  1988.         }
  1989.  
  1990.     Objects[Players[Player_num].objnum].type = OBJ_PLAYER;
  1991.  
  1992.     Network_status = NETSTAT_PLAYING;
  1993.     Function_mode = FMODE_GAME;
  1994.     multi_sort_kill_list();
  1995.  
  1996. }
  1997.  
  1998. void
  1999. network_send_sync(void)
  2000. {
  2001.     int i, j, np;
  2002.  
  2003.     // Randomize their starting locations...
  2004.  
  2005.     srand( TICKER );
  2006.     for (i=0; i<MaxNumNetPlayers; i++ )    
  2007.     {
  2008.         if (Players[i].connected)
  2009.             Players[i].connected = 1; // Get rid of endlevel connect statuses
  2010.         if (Game_mode & GM_MULTI_COOP)
  2011.             Netgame.locations[i] = i;
  2012.         else {
  2013.             do 
  2014.             {
  2015.                 np = rand() % MaxNumNetPlayers;
  2016.                 for (j=0; j<i; j++ )    
  2017.                 {
  2018.                     if (Netgame.locations[j]==np)    
  2019.                     {
  2020.                         np =-1;
  2021.                         break;
  2022.                     }
  2023.                 }
  2024.             } while (np<0);
  2025.             // np is a location that is not used anywhere else..
  2026.             Netgame.locations[i]=np;
  2027. //            mprintf((0, "Player %d starting in location %d\n" ,i ,np ));
  2028.         }
  2029.     }
  2030.  
  2031.     // Push current data into the sync packet
  2032.  
  2033.     network_update_netgame();
  2034.     Netgame.game_status = NETSTAT_PLAYING;
  2035.     Netgame.type = PID_SYNC;
  2036.     Netgame.segments_checksum = my_segments_checksum;
  2037.  
  2038.     for (i=0; i<N_players; i++ )    {
  2039.         if ((!Players[i].connected) || (i == Player_num))
  2040.             continue;
  2041.  
  2042.         // Send several times, extras will be ignored
  2043.         ipx_send_internetwork_packet_data( (ubyte *)&Netgame, sizeof(netgame_info), Netgame.players[i].server, Netgame.players[i].node);
  2044.         ipx_send_internetwork_packet_data( (ubyte *)&Netgame, sizeof(netgame_info), Netgame.players[i].server, Netgame.players[i].node);
  2045.         ipx_send_internetwork_packet_data( (ubyte *)&Netgame, sizeof(netgame_info), Netgame.players[i].server, Netgame.players[i].node);
  2046.     }    
  2047.     network_read_sync_packet(&Netgame); // Read it myself, as if I had sent it
  2048. }
  2049.  
  2050. int
  2051. network_select_teams(void)
  2052. {
  2053. #ifndef SHAREWARE
  2054.     newmenu_item m[MAX_PLAYERS+4];
  2055.     int choice, opt, opt_team_b;
  2056.     ubyte team_vector = 0;
  2057.     char team_names[2][CALLSIGN_LEN+1];
  2058.     int i;
  2059.     int pnums[MAX_PLAYERS+2];
  2060.  
  2061.     // One-time initialization
  2062.  
  2063.     for (i = N_players/2; i < N_players; i++) // Put first half of players on team A
  2064.     {
  2065.         team_vector |= (1 << i);
  2066.     }
  2067.  
  2068.     sprintf(team_names[0], "%s", TXT_BLUE);
  2069.     sprintf(team_names[1], "%s", TXT_RED);
  2070.  
  2071.     // Here comes da menu
  2072. menu:
  2073.     m[0].type = NM_TYPE_INPUT; m[0].text = team_names[0]; m[0].text_len = CALLSIGN_LEN; 
  2074.  
  2075.     opt = 1;
  2076.     for (i = 0; i < N_players; i++)
  2077.     {
  2078.         if (!(team_vector & (1 << i)))
  2079.         {
  2080.             m[opt].type = NM_TYPE_MENU; m[opt].text = Netgame.players[i].callsign; pnums[opt] = i; opt++;
  2081.         }
  2082.     }
  2083.     opt_team_b = opt;
  2084.     m[opt].type = NM_TYPE_INPUT; m[opt].text = team_names[1]; m[opt].text_len = CALLSIGN_LEN; opt++;
  2085.     for (i = 0; i < N_players; i++)
  2086.     {
  2087.         if (team_vector & (1 << i))
  2088.         {
  2089.             m[opt].type = NM_TYPE_MENU; m[opt].text = Netgame.players[i].callsign; pnums[opt] = i; opt++;
  2090.         }
  2091.     }
  2092.     m[opt].type = NM_TYPE_TEXT; m[opt].text = ""; opt++;
  2093.     m[opt].type = NM_TYPE_MENU; m[opt].text = TXT_ACCEPT; opt++;
  2094.  
  2095.     Assert(opt <= MAX_PLAYERS+4);
  2096.     
  2097.     choice = newmenu_do(NULL, TXT_TEAM_SELECTION, opt, m, NULL);
  2098.  
  2099.     if (choice == opt-1)
  2100.     {
  2101.         if ((opt-2-opt_team_b < 2) || (opt_team_b == 1)) 
  2102.         {
  2103.             nm_messagebox(NULL, 1, TXT_OK, TXT_TEAM_MUST_ONE);
  2104.             goto menu;
  2105.         }
  2106.         
  2107.         Netgame.team_vector = team_vector;
  2108.         strcpy(Netgame.team_name[0], team_names[0]);
  2109.         strcpy(Netgame.team_name[1], team_names[1]);
  2110.         return 1;
  2111.     }
  2112.  
  2113.     else if ((choice > 0) && (choice < opt_team_b)) {
  2114.         team_vector |= (1 << pnums[choice]);
  2115.     }
  2116.     else if ((choice > opt_team_b) && (choice < opt-2)) {
  2117.         team_vector &= ~(1 << pnums[choice]);
  2118.     }
  2119.     else if (choice == -1)
  2120.         return 0;
  2121.     goto menu;
  2122. #else
  2123.     return 0;
  2124. #endif
  2125. }
  2126.  
  2127. int
  2128. network_select_players(void)
  2129. {
  2130.     int i, j;
  2131.     newmenu_item m[MAX_PLAYERS];
  2132.     char text[MAX_PLAYERS][25];
  2133.     char title[50];
  2134.     int save_nplayers;
  2135.  
  2136.     network_add_player( &My_Seq );
  2137.         
  2138.     for (i=0; i< MAX_PLAYERS; i++ )    {
  2139.         sprintf( text[i], "%d.  %-16s", i+1, "" );
  2140.         m[i].type = NM_TYPE_CHECK; m[i].text = text[i]; m[i].value = 0;
  2141.     }
  2142.  
  2143.     m[0].value = 1;                // Assume server will play...
  2144.  
  2145.     sprintf( text[0], "%d. %-16s", 1, Players[Player_num].callsign );
  2146.     sprintf( title, "%s %d %s", TXT_TEAM_SELECT, MaxNumNetPlayers, TXT_TEAM_PRESS_ENTER );
  2147.  
  2148. GetPlayersAgain:
  2149.     j=newmenu_do1( NULL, title, MAX_PLAYERS, m, network_start_poll, 1 );
  2150.  
  2151.     save_nplayers = N_players;
  2152.  
  2153.     if (j<0) 
  2154.     {
  2155.         // Aborted!                     
  2156.         // Dump all players and go back to menu mode
  2157.  
  2158. abort:
  2159.         for (i=1; i<N_players; i++)
  2160.             network_dump_player(Netgame.players[i].server,Netgame.players[i].node, DUMP_ABORTED);
  2161.  
  2162.         Netgame.numplayers = 0;
  2163.         network_send_game_info(0); // Tell everyone we're bailing
  2164.  
  2165.         Network_status = NETSTAT_MENU;
  2166.         return(0);
  2167.     }
  2168.  
  2169.     // Count number of players chosen
  2170.  
  2171.     N_players = 0;
  2172.     for (i=0; i<save_nplayers; i++ )
  2173.     {
  2174.         if (m[i].value)    
  2175.             N_players++;
  2176.     }
  2177.     
  2178.     if ( N_players > MaxNumNetPlayers) {
  2179.         nm_messagebox( TXT_ERROR, 1, TXT_OK, "%s %d %s", TXT_SORRY_ONLY, MaxNumNetPlayers, TXT_NETPLAYERS_IN );
  2180.         N_players = save_nplayers;
  2181.         goto GetPlayersAgain;
  2182.     }
  2183.  
  2184. #ifdef NDEBUG
  2185.     if ( N_players < 2 )     {
  2186.         nm_messagebox( TXT_ERROR, 1, TXT_OK, TXT_TEAM_ATLEAST_TWO );
  2187.         N_players = save_nplayers;
  2188.         goto GetPlayersAgain;
  2189.     }
  2190. #endif
  2191.  
  2192. #ifdef NDEBUG
  2193.     if ( (Netgame.gamemode == NETGAME_TEAM_ANARCHY) && (N_players < 3) ) {
  2194.         nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_TEAM_ATLEAST_THREE );
  2195.         N_players = save_nplayers;
  2196.         goto GetPlayersAgain;
  2197.     }
  2198. #endif
  2199.  
  2200.     // Remove players that aren't marked.
  2201.     N_players = 0;
  2202.     for (i=0; i<save_nplayers; i++ )    {
  2203.         if (m[i].value)    
  2204.         {
  2205.             if (i > N_players)
  2206.             {
  2207.                 memcpy(Netgame.players[N_players].callsign, Netgame.players[i].callsign, CALLSIGN_LEN+1);
  2208.                 memcpy(Netgame.players[N_players].node, Netgame.players[i].node, 6);
  2209.                 memcpy(Netgame.players[N_players].server, Netgame.players[i].server, 4);
  2210.             }
  2211.             Players[N_players].connected = 1;
  2212.             N_players++;
  2213.         }
  2214.         else
  2215.         {
  2216.             network_dump_player(Netgame.players[i].server,Netgame.players[i].node, DUMP_DORK);
  2217.         }
  2218.     }
  2219.  
  2220.     for (i = N_players; i < MAX_NUM_NET_PLAYERS; i++) {
  2221.         memset(Netgame.players[i].callsign, 0, CALLSIGN_LEN+1);
  2222.         memset(Netgame.players[i].node, 0, 6);
  2223.         memset(Netgame.players[i].server, 0, 4);
  2224.     }
  2225.  
  2226.     if (Netgame.gamemode == NETGAME_TEAM_ANARCHY)
  2227.         if (!network_select_teams())
  2228.             goto abort;
  2229.  
  2230.     return(1); 
  2231. }
  2232.  
  2233. void 
  2234. network_start_game(void)    
  2235. {
  2236.     int i;
  2237.     char game_name[NETGAME_NAME_LEN+1];
  2238.     int chosen_game_mode, game_flags, level;
  2239.  
  2240.     Assert( sizeof(frame_info) < IPX_MAX_DATA_SIZE );
  2241.  
  2242.     mprintf((0, "Using frame_info len %d, max %d.\n", sizeof(frame_info), IPX_MAX_DATA_SIZE));
  2243.  
  2244.     if ( !Network_active )
  2245.     {
  2246.         nm_messagebox(NULL, 1, TXT_OK, TXT_IPX_NOT_FOUND );
  2247.         return;
  2248.     }
  2249.  
  2250.     network_init();
  2251.     change_playernum_to(0);
  2252.  
  2253.     if (network_find_game())
  2254.     {
  2255.         nm_messagebox(NULL, 1, TXT_OK, TXT_NET_FULL);
  2256.         return;
  2257.     }
  2258.  
  2259.     game_flags = 0;
  2260.     i = network_get_game_params( game_name, &chosen_game_mode, &game_flags, &level );
  2261.  
  2262.     if (i<0) return;
  2263.  
  2264.     N_players = 0;
  2265.  
  2266. // LoadLevel(level); Old, no longer used.
  2267.  
  2268.     Netgame.difficulty = Difficulty_level;
  2269.     Netgame.gamemode = chosen_game_mode;
  2270.     Netgame.game_status = NETSTAT_STARTING;
  2271.     Netgame.numplayers = 0;
  2272.     Netgame.max_numplayers = MaxNumNetPlayers;
  2273.     Netgame.levelnum = level;
  2274.     Netgame.game_flags = game_flags;
  2275.     Netgame.protocol_version = MULTI_PROTO_VERSION;
  2276.  
  2277.     strcpy(Netgame.game_name, game_name);
  2278.     
  2279.     Network_status = NETSTAT_STARTING;
  2280.  
  2281.     network_set_game_mode(Netgame.gamemode);
  2282.  
  2283.     if(network_select_players())
  2284.     {
  2285.         StartNewLevel(Netgame.levelnum);
  2286.     }
  2287.     else
  2288.         Game_mode = GM_GAME_OVER;
  2289.     
  2290. }
  2291.  
  2292. void restart_net_searching(newmenu_item * m)
  2293. {
  2294.     int i;
  2295.     N_players = 0;
  2296.     num_active_games = 0;
  2297.  
  2298.     memset(Active_games, 0, sizeof(netgame_info)*MAX_ACTIVE_NETGAMES);
  2299.  
  2300.     for (i = 0; i < MAX_ACTIVE_NETGAMES; i++) {
  2301.         sprintf(m[(2*i)+1].text, "%d.                                       ", i+1);
  2302.         sprintf(m[(2*i)+2].text, " \n");
  2303.         m[(2*i)+1].redraw = 1;
  2304.         m[(2*i)+2].redraw = 1;
  2305.     }
  2306.     Network_games_changed = 1;    
  2307. }
  2308.  
  2309. void network_join_poll( int nitems, newmenu_item * menus, int * key, int citem )
  2310. {
  2311.     // Polling loop for Join Game menu
  2312.     static fix t1 = 0;
  2313.     int i, osocket;
  2314.  
  2315.     menus = menus;
  2316.     citem = citem;
  2317.     nitems = nitems;
  2318.     key = key;
  2319.  
  2320.     if (Network_allow_socket_changes )    {
  2321.         osocket = Network_socket;
  2322.     
  2323.         if ( *key==KEY_PAGEUP )     { Network_socket--; *key = 0; }
  2324.         if ( *key==KEY_PAGEDOWN )     { Network_socket++; *key = 0; }
  2325.     
  2326.         if ( Network_socket+IPX_DEFAULT_SOCKET > 0x8000 )
  2327.             Network_socket    = 0x8000 - IPX_DEFAULT_SOCKET;
  2328.     
  2329.         if ( Network_socket+IPX_DEFAULT_SOCKET < 0 )
  2330.             Network_socket    = IPX_DEFAULT_SOCKET;
  2331.     
  2332.         if (Network_socket != osocket )        {
  2333.             sprintf( menus[0].text, "%s %+d", TXT_CURRENT_IPX_SOCKET, Network_socket );
  2334.             menus[0].redraw = 1;
  2335.             mprintf(( 0, "Changing to socket %d\n", Network_socket ));
  2336.             network_listen();
  2337.             ipx_change_default_socket( IPX_DEFAULT_SOCKET + Network_socket );
  2338.             restart_net_searching(menus);
  2339.             network_send_game_list_request();
  2340.             return;
  2341.         }
  2342.     }
  2343.  
  2344.     if (timer_get_approx_seconds() > t1+F1_0*4)
  2345.     {
  2346.         t1 = timer_get_approx_seconds();
  2347.         network_send_game_list_request();
  2348.     }                
  2349.     
  2350.     network_listen();
  2351.  
  2352.     if (!Network_games_changed)
  2353.         return;
  2354.  
  2355.     Network_games_changed = 0;
  2356.  
  2357.     // Copy the active games data into the menu options
  2358.     for (i = 0; i < num_active_games; i++)
  2359.     {
  2360.         int game_status = Active_games[i].game_status;
  2361.         int j, nplayers = 0;
  2362.         char levelname[4];
  2363.  
  2364.         for (j = 0; j < Active_games[i].numplayers; j++)
  2365.             if (Active_games[i].players[j].connected)
  2366.                 nplayers++;
  2367.  
  2368.         if (Active_games[i].levelnum < 0)
  2369.             sprintf(levelname, "S%d", -Active_games[i].levelnum);
  2370.         else 
  2371.             sprintf(levelname, "%d", Active_games[i].levelnum);
  2372.  
  2373.         sprintf(menus[(2*i)+1].text, "%d. %s (%s)", i+1, Active_games[i].game_name, MODE_NAMES(Active_games[i].gamemode));
  2374.  
  2375.         if (game_status == NETSTAT_STARTING) 
  2376.         {
  2377.             sprintf(menus[(2*i)+2].text, "%s%s  %s%d\n", TXT_NET_FORMING, levelname, TXT_NET_PLAYERS, nplayers);
  2378.         }
  2379.         else if (game_status == NETSTAT_PLAYING)
  2380.         {
  2381.             if (can_join_netgame(&Active_games[i]))
  2382.                 sprintf(menus[(2*i)+2].text, "%s%s  %s%d\n", TXT_NET_JOIN, levelname, TXT_NET_PLAYERS, nplayers);
  2383.             else
  2384.                 sprintf(menus[(2*i)+2].text, "%s\n", TXT_NET_CLOSED);
  2385.         }
  2386.         else
  2387.             sprintf(menus[(2*i)+2].text, "%s\n", TXT_NET_BETWEEN);
  2388.  
  2389.         if (strlen(Active_games[i].mission_name) > 0)
  2390.             sprintf(menus[(2*i)+2].text+strlen(menus[(2*i)+2].text), "%s%s", TXT_MISSION, Active_games[i].mission_title);
  2391.  
  2392.         Assert(strlen(menus[(2*i)+2].text) < 70);
  2393.         menus[(2*i)+1].redraw = 1;
  2394.         menus[(2*i)+2].redraw = 1;
  2395.     }
  2396.  
  2397.     for (i = num_active_games; i < MAX_ACTIVE_NETGAMES; i++)
  2398.     {
  2399.         sprintf(menus[(2*i)+1].text, "%d.                                       ", i+1);
  2400.         sprintf(menus[(2*i)+2].text, " \n");
  2401.         menus[(2*i)+1].redraw = 1;
  2402.         menus[(2*i)+2].redraw = 1;
  2403.     }
  2404. }
  2405.  
  2406. int
  2407. network_wait_for_sync(void)
  2408. {
  2409.     char text[60];
  2410.     newmenu_item m[2];
  2411.     int i, choice;
  2412.     
  2413.     Network_status = NETSTAT_WAITING;
  2414.  
  2415.     m[0].type=NM_TYPE_TEXT; m[0].text = text;
  2416.     m[1].type=NM_TYPE_TEXT; m[1].text = TXT_NET_LEAVE;
  2417.     
  2418.     i = network_send_request();
  2419.  
  2420.     if (i < 0)
  2421.         return(-1);
  2422.  
  2423.     sprintf( m[0].text, "%s\n'%s' %s", TXT_NET_WAITING, Netgame.players[i].callsign, TXT_NET_TO_ENTER );
  2424.  
  2425. menu:    
  2426.     choice=newmenu_do( NULL, TXT_WAIT, 2, m, network_sync_poll );
  2427.  
  2428.     if (choice > -1)
  2429.         goto menu;
  2430.  
  2431.     if (Network_status != NETSTAT_PLAYING)    
  2432.     {
  2433.         sequence_packet me;
  2434.  
  2435. //        if (Network_status == NETSTAT_ENDLEVEL)
  2436. //        {
  2437. //             network_send_endlevel_packet(0);
  2438. //            longjmp(LeaveGame, 0);
  2439. //        }        
  2440.  
  2441.         mprintf((0, "Aborting join.\n"));
  2442.         me.type = PID_QUIT_JOINING;
  2443.         memcpy( me.player.callsign, Players[Player_num].callsign, CALLSIGN_LEN+1 );
  2444.         memcpy( me.player.node, ipx_get_my_local_address(), 6 );
  2445.         memcpy( me.player.server, ipx_get_my_server_address(), 4 );
  2446.         ipx_send_internetwork_packet_data( (ubyte *)&me, sizeof(sequence_packet), Netgame.players[0].server, Netgame.players[0].node );
  2447.         N_players = 0;
  2448.         Function_mode = FMODE_MENU;
  2449.         Game_mode = GM_GAME_OVER;
  2450.         return(-1);    // they cancelled        
  2451.     }
  2452.     return(0);
  2453. }
  2454.  
  2455. void 
  2456. network_request_poll( int nitems, newmenu_item * menus, int * key, int citem )
  2457. {
  2458.     // Polling loop for waiting-for-requests menu
  2459.  
  2460.     int i = 0;
  2461.     int num_ready = 0;
  2462.  
  2463.     menus = menus;
  2464.     citem = citem;
  2465.     nitems = nitems;
  2466.     key = key;
  2467.  
  2468.     // Send our endlevel packet at regular intervals
  2469.  
  2470. //    if (timer_get_approx_seconds() > t1+ENDLEVEL_SEND_INTERVAL)
  2471. //    {
  2472. //        network_send_endlevel_packet();
  2473. //        t1 = timer_get_approx_seconds();
  2474. //    }
  2475.  
  2476.     network_listen();
  2477.  
  2478.     for (i = 0; i < N_players; i++)
  2479.     {
  2480.         if ((Players[i].connected == 1) || (Players[i].connected == 0))
  2481.             num_ready++;
  2482.     }
  2483.  
  2484.     if (num_ready == N_players) // All players have checked in or are disconnected
  2485.     {
  2486.         *key = -2;
  2487.     }
  2488. }
  2489.  
  2490. void
  2491. network_wait_for_requests(void)
  2492. {
  2493.     // Wait for other players to load the level before we send the sync
  2494.     int choice, i;
  2495.     newmenu_item m[1];
  2496.     
  2497.     Network_status = NETSTAT_WAITING;
  2498.  
  2499.     m[0].type=NM_TYPE_TEXT; m[0].text = TXT_NET_LEAVE;
  2500.  
  2501.     mprintf((0, "Entered wait_for_requests : N_players = %d.\n", N_players));
  2502.  
  2503.     for (choice = 0; choice < N_players; choice++)
  2504.         mprintf((0, "Players[%d].connected = %d.\n", choice, Players[choice].connected));
  2505.  
  2506.     Network_status = NETSTAT_WAITING;
  2507.     network_flush();
  2508.  
  2509.     Players[Player_num].connected = 1;
  2510.  
  2511. menu:
  2512.     choice = newmenu_do(NULL, TXT_WAIT, 1, m, network_request_poll);    
  2513.  
  2514.     if (choice == -1)
  2515.     {
  2516.         // User aborted
  2517.         choice = nm_messagebox(NULL, 3, TXT_YES, TXT_NO, TXT_START_NOWAIT, TXT_QUITTING_NOW);
  2518.         if (choice == 2)
  2519.             return;
  2520.         if (choice != 0)
  2521.             goto menu;
  2522.         
  2523.         // User confirmed abort
  2524.         
  2525.         for (i=0; i < N_players; i++)
  2526.             if ((Players[i].connected != 0) && (i != Player_num))
  2527.                 network_dump_player(Netgame.players[i].server, Netgame.players[i].node, DUMP_ABORTED);
  2528.  
  2529.         longjmp(LeaveGame, 0);    
  2530.     }
  2531.     else if (choice != -2)
  2532.         goto menu;
  2533. }
  2534.  
  2535. int
  2536. network_level_sync(void)
  2537. {
  2538.      // Do required syncing between (before) levels
  2539.  
  2540.     int result;
  2541.  
  2542.     mprintf((0, "Player %d entering network_level_sync.\n", Player_num));
  2543.  
  2544.     MySyncPackInitialized = 0;
  2545.  
  2546. //    my_segments_checksum = netmisc_calc_checksum(Segments, sizeof(segment)*(Highest_segment_index+1));
  2547.  
  2548.     network_flush(); // Flush any old packets
  2549.  
  2550.     if (N_players == 0)
  2551.         result = network_wait_for_sync();
  2552.     else if (network_i_am_master())
  2553.     {
  2554.         network_wait_for_requests();
  2555.         network_send_sync();
  2556.         result = 0;
  2557.     }
  2558.     else
  2559.         result = network_wait_for_sync();
  2560.  
  2561.     if (result)
  2562.     {
  2563.         Players[Player_num].connected = 0;
  2564.         network_send_endlevel_packet();
  2565.         longjmp(LeaveGame, 0);
  2566.     }
  2567.     return(0);
  2568. }
  2569.  
  2570. void network_join_game()
  2571. {
  2572.     int choice, i;
  2573.     char menu_text[(MAX_ACTIVE_NETGAMES*2)+1][70];
  2574.     
  2575.     newmenu_item m[((MAX_ACTIVE_NETGAMES)*2)+1];
  2576.  
  2577.     if ( !Network_active )
  2578.     {
  2579.         nm_messagebox(NULL, 1, TXT_OK, TXT_IPX_NOT_FOUND);
  2580.         return;
  2581.     }
  2582.  
  2583.     network_init();
  2584.  
  2585.     N_players = 0;
  2586.  
  2587.     setjmp(LeaveGame);
  2588.  
  2589.     Network_status = NETSTAT_BROWSING; // We are looking at a game menu
  2590.     
  2591.     network_listen();  // Throw out old info
  2592.  
  2593.     network_send_game_list_request(); // broadcast a request for lists
  2594.  
  2595.     num_active_games = 0;
  2596.  
  2597.     memset(m, 0, sizeof(newmenu_item)*(MAX_ACTIVE_NETGAMES*2));
  2598.     memset(Active_games, 0, sizeof(netgame_info)*MAX_ACTIVE_NETGAMES);
  2599.     
  2600.     m[0].text = menu_text[0];
  2601.     m[0].type = NM_TYPE_TEXT;
  2602.     if (Network_allow_socket_changes)
  2603.         sprintf( m[0].text, "Current IPX Socket is default%+d", Network_socket );
  2604.     else
  2605.         sprintf( m[0].text, "" );
  2606.  
  2607.     for (i = 0; i < MAX_ACTIVE_NETGAMES; i++) {
  2608.         m[2*i+1].text = menu_text[2*i+1];
  2609.         m[2*i+2].text = menu_text[2*i+2];
  2610.         m[2*i+1].type = NM_TYPE_MENU;
  2611.         m[2*i+2].type = NM_TYPE_TEXT;
  2612.         sprintf(m[(2*i)+1].text, "%d.                                       ", i+1);
  2613.         sprintf(m[(2*i)+2].text, " \n");
  2614.         m[(2*i)+1].redraw = 1;
  2615.         m[(2*i)+2].redraw = 1;
  2616.     }
  2617.  
  2618.     Network_games_changed = 1;    
  2619. remenu:
  2620.     choice=newmenu_do1(NULL, TXT_NET_SEARCHING, (MAX_ACTIVE_NETGAMES)*2+1, m, network_join_poll, 0 );
  2621.  
  2622.     if (choice==-1)    {
  2623.         Network_status = NETSTAT_MENU;
  2624.         return;    // they cancelled        
  2625.     }        
  2626.     choice--;
  2627.     choice /= 2;
  2628.  
  2629.     if (choice >=num_active_games)
  2630.     {
  2631.         nm_messagebox(TXT_SORRY, 1, TXT_OK, TXT_INVALID_CHOICE);
  2632.         goto remenu;
  2633.     }
  2634.  
  2635.     // Choice has been made and looks legit
  2636.     if (Active_games[choice].game_status == NETSTAT_ENDLEVEL)
  2637.     {
  2638.         nm_messagebox(TXT_SORRY, 1, TXT_OK, TXT_NET_GAME_BETWEEN2);
  2639.         goto remenu;
  2640.     }
  2641.  
  2642.     if (Active_games[choice].protocol_version != MULTI_PROTO_VERSION)
  2643.     {
  2644.         nm_messagebox(TXT_SORRY, 1, TXT_OK, TXT_VERSION_MISMATCH);
  2645.         goto remenu;
  2646.     }
  2647.  
  2648. #ifndef SHAREWARE
  2649.     {    
  2650.         // Check for valid mission name
  2651.             mprintf((0, "Loading mission:%s.\n", Active_games[choice].mission_name));
  2652.             if (!load_mission_by_name(Active_games[choice].mission_name))
  2653.             {
  2654.                 nm_messagebox(NULL, 1, TXT_OK, TXT_MISSION_NOT_FOUND);
  2655.                 goto remenu;
  2656.             }
  2657.     }
  2658. #endif
  2659.  
  2660.     if (!can_join_netgame(&Active_games[choice]))
  2661.     {
  2662.         if (Active_games[choice].numplayers == Active_games[choice].max_numplayers)
  2663.             nm_messagebox(TXT_SORRY, 1, TXT_OK, TXT_GAME_FULL);
  2664.         else
  2665.             nm_messagebox(TXT_SORRY, 1, TXT_OK, TXT_IN_PROGRESS);
  2666.         goto remenu;
  2667.     }
  2668.  
  2669.     // Choice is valid, prepare to join in
  2670.  
  2671.     memcpy(&Netgame, &Active_games[choice], sizeof(netgame_info));
  2672.     Difficulty_level = Netgame.difficulty;
  2673.     MaxNumNetPlayers = Netgame.max_numplayers;
  2674.     change_playernum_to(1);
  2675.  
  2676.     network_set_game_mode(Netgame.gamemode);
  2677.  
  2678.     StartNewLevel(Netgame.levelnum);
  2679.  
  2680.     return;        // look ma, we're in a game!!!
  2681. }
  2682.  
  2683. void network_leave_game()
  2684. {
  2685.     network_do_frame(1, 1);
  2686.  
  2687.     if ((network_i_am_master()) && (Network_status == NETSTAT_STARTING))
  2688.     {
  2689.         Netgame.numplayers = 0;
  2690.         network_send_game_info(0);
  2691.     }
  2692.     
  2693.     Players[Player_num].connected = 0;
  2694.     network_send_endlevel_packet();
  2695.     change_playernum_to(0);
  2696.     Game_mode = GM_GAME_OVER;
  2697.     network_flush();
  2698. }
  2699.  
  2700. void network_flush()
  2701. {
  2702.     ubyte packet[IPX_MAX_DATA_SIZE];
  2703.  
  2704.     if (!Network_active)
  2705.         return;
  2706.  
  2707.     while (ipx_get_packet_data(packet) > 0)
  2708.         ;
  2709. }
  2710.  
  2711. void network_listen()
  2712. {
  2713.     int size;
  2714.     ubyte packet[IPX_MAX_DATA_SIZE];
  2715.  
  2716.     if (!Network_active) return;
  2717.  
  2718.     if (!(Game_mode & GM_NETWORK) && (Function_mode == FMODE_GAME))
  2719.         mprintf((0, "Calling network_listen() when not in net game.\n"));
  2720.  
  2721.     size = ipx_get_packet_data( packet );
  2722.     while ( size > 0 )    {
  2723.         network_process_packet( packet, size );
  2724.         size = ipx_get_packet_data( packet );
  2725.     }
  2726. }
  2727.  
  2728. void network_send_data( ubyte * ptr, int len, int urgent )
  2729. {
  2730.     char check;
  2731.  
  2732.     if (Endlevel_sequence)
  2733.         return;
  2734.  
  2735.     if (!MySyncPackInitialized)    {
  2736.         MySyncPackInitialized = 1;
  2737.         memset( &MySyncPack, 0, sizeof(frame_info) );
  2738.     }
  2739.     
  2740.     if (urgent)
  2741.         PacketUrgent = 1;
  2742.  
  2743.     if ((MySyncPack.data_size+len) > NET_XDATA_SIZE )    {
  2744.         check = ptr[0];
  2745.         network_do_frame(1, 0);
  2746.         if (MySyncPack.data_size != 0) {
  2747.             mprintf((0, "%d bytes were added to data by network_do_frame!\n", MySyncPack.data_size));
  2748.             Int3();
  2749.         }
  2750. //        Int3();        // Trying to send too much!
  2751. //        return;
  2752.         mprintf((0, "Packet overflow, sending additional packet, type %d len %d.\n", ptr[0], len));
  2753.         Assert(check == ptr[0]);
  2754.     }
  2755.  
  2756.     Assert(MySyncPack.data_size+len <= NET_XDATA_SIZE);
  2757.  
  2758.     memcpy( &MySyncPack.data[MySyncPack.data_size], ptr, len );
  2759.     MySyncPack.data_size += len;
  2760. }
  2761.  
  2762. void network_timeout_player(int playernum)
  2763. {
  2764.     // Remove a player from the game if we haven't heard from them in 
  2765.     // a long time.
  2766.     int i, n = 0;
  2767.  
  2768.     Assert(playernum < N_players);
  2769.     Assert(playernum > -1);
  2770.  
  2771.     network_disconnect_player(playernum);
  2772.     create_player_appearance_effect(&Objects[Players[playernum].objnum]);
  2773.  
  2774.     digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
  2775.  
  2776.     HUD_init_message("%s %s", Players[playernum].callsign, TXT_DISCONNECTING);
  2777.     for (i = 0; i < N_players; i++)
  2778.         if (Players[i].connected) 
  2779.             n++;
  2780.  
  2781.     if (n == 1)
  2782.     {
  2783.         nm_messagebox(NULL, 1, TXT_OK, TXT_YOU_ARE_ONLY);
  2784.     }
  2785. }
  2786.  
  2787. fix last_send_time = 0;
  2788. fix last_timeout_check = 0;
  2789.  
  2790. void network_do_frame(int force, int listen)
  2791. {
  2792.     int i;
  2793.  
  2794.     if (!(Game_mode&GM_NETWORK)) return;
  2795.  
  2796.     if ((Network_status != NETSTAT_PLAYING) || (Endlevel_sequence)) // Don't send postion during escape sequence...
  2797.         goto listen;
  2798.  
  2799.     last_send_time += FrameTime;
  2800.     last_timeout_check += FrameTime;
  2801.  
  2802.     // Send out packet 10 times per second maximum... unless they fire, then send more often...
  2803.     if ( (last_send_time>F1_0/10) || (Network_laser_fired) || force || PacketUrgent )    {        
  2804.         if ( Players[Player_num].connected )    {
  2805.             int objnum = Players[Player_num].objnum;
  2806.             PacketUrgent = 0;
  2807.  
  2808.             if (listen) {
  2809.                 multi_send_robot_frame(0);
  2810.                 multi_send_fire();        // Do firing if needed..
  2811.             }
  2812.  
  2813. //            mprintf((0, "Send packet, %f secs, %d bytes.\n", f2fl(last_send_time), MySyncPack.data_size));
  2814.  
  2815.             last_send_time = 0;
  2816.  
  2817.             MySyncPack.type                     = PID_PDATA;
  2818.             MySyncPack.playernum             = Player_num;
  2819. #ifdef SHAREWARE
  2820.             MySyncPack.objnum                 = Players[Player_num].objnum;
  2821. #endif
  2822.             MySyncPack.obj_segnum            = Objects[objnum].segnum;
  2823.             MySyncPack.obj_pos                = Objects[objnum].pos;
  2824.             MySyncPack.obj_orient            = Objects[objnum].orient;
  2825. #ifdef SHAREWARE
  2826.             MySyncPack.obj_phys_info        = Objects[objnum].mtype.phys_info;
  2827. #else
  2828.             MySyncPack.phys_velocity         = Objects[objnum].mtype.phys_info.velocity;
  2829.             MySyncPack.phys_rotvel            = Objects[objnum].mtype.phys_info.rotvel;
  2830. #endif
  2831.             MySyncPack.obj_render_type        = Objects[objnum].render_type;
  2832.             MySyncPack.level_num                = Current_level_num;
  2833.     
  2834.             for (i=0; i<N_players; i++ )    {
  2835.                 if ( (Players[i].connected) && (i!=Player_num ) )    {
  2836.                     MySyncPack.numpackets = Players[i].n_packets_sent++;
  2837. //                    mprintf((0, "sync pack is %d bytes long.\n", sizeof(frame_info)));
  2838.                     ipx_send_packet_data( (ubyte *)&MySyncPack, sizeof(frame_info)-NET_XDATA_SIZE+MySyncPack.data_size, Netgame.players[i].server, Netgame.players[i].node,Players[i].net_address );
  2839.                 }
  2840.             }
  2841.             MySyncPack.data_size = 0;        // Start data over at 0 length.
  2842.             if (Fuelcen_control_center_destroyed)
  2843.                 network_send_endlevel_packet();
  2844.             //mprintf( (0, "Packet has %d bytes appended (TS=%d)\n", MySyncPack.data_size, sizeof(frame_info)-NET_XDATA_SIZE+MySyncPack.data_size ));
  2845.         }
  2846.     }
  2847.  
  2848.     if (!listen)
  2849.         return;
  2850.  
  2851.     if ((last_timeout_check > F1_0) && !(Fuelcen_control_center_destroyed))
  2852.     {
  2853.         fix approx_time = timer_get_approx_seconds();
  2854.         // Check for player timeouts
  2855.         for (i = 0; i < N_players; i++)
  2856.         {
  2857.             if ((i != Player_num) && (Players[i].connected == 1))
  2858.             {
  2859.                 if ((LastPacketTime[i] == 0) || (LastPacketTime[i] > approx_time))
  2860.                 {
  2861.                     LastPacketTime[i] = approx_time;
  2862.                     continue;
  2863.                 }
  2864.                 if ((approx_time - LastPacketTime[i]) > (5*F1_0))
  2865.                     network_timeout_player(i);
  2866.             }
  2867.         }
  2868.         last_timeout_check = 0;
  2869.     }
  2870.  
  2871. listen:
  2872.     if (!listen)
  2873.     {
  2874.         MySyncPack.data_size = 0;
  2875.         return;
  2876.     }
  2877.     network_listen();
  2878.  
  2879.     if (Network_send_objects)
  2880.         network_send_objects();
  2881. }
  2882.  
  2883. int missed_packets = 0;
  2884.  
  2885. void network_consistency_error(void)
  2886. {
  2887.     static int count = 0;
  2888.  
  2889.     if (count++ < 10)
  2890.         return;
  2891.  
  2892.     Function_mode = FMODE_MENU;
  2893.     nm_messagebox(NULL, 1, TXT_OK, TXT_CONSISTENCY_ERROR);
  2894.     Function_mode = FMODE_GAME;
  2895.     count = 0;
  2896.     multi_quit_game = 1;
  2897.     multi_leave_menu = 1;
  2898.     multi_reset_stuff();
  2899. }
  2900.  
  2901. void network_read_pdata_packet(frame_info *pd )
  2902. {
  2903.     int TheirPlayernum = pd->playernum;
  2904. #ifdef SHAREWARE
  2905.     int TheirObjnum = pd->objnum;
  2906. #else
  2907.     int TheirObjnum = Players[pd->playernum].objnum;
  2908. #endif
  2909.     object * TheirObj = NULL;
  2910.  
  2911.     if (TheirPlayernum < 0) {
  2912.         Int3(); // This packet is bogus!!
  2913.         return;
  2914.     }
  2915.     if (!multi_quit_game && (TheirPlayernum >= N_players)) {
  2916.         Int3(); // We missed an important packet!
  2917.         network_consistency_error();
  2918.         return;
  2919.     }
  2920.     if (Endlevel_sequence || (Network_status == NETSTAT_ENDLEVEL) )    {
  2921.         int old_Endlevel_sequence = Endlevel_sequence;
  2922.         Endlevel_sequence = 1;
  2923.         if ( pd->data_size>0 )    {
  2924.             // pass pd->data to some parser function....
  2925.             multi_process_bigdata( pd->data, pd->data_size );
  2926.         }
  2927.         Endlevel_sequence = old_Endlevel_sequence;
  2928.         return;
  2929.     }
  2930. //    mprintf((0, "Gametime = %d, Frametime = %d.\n", GameTime, FrameTime));
  2931.  
  2932.     if ((byte)pd->level_num != Current_level_num)
  2933.     {
  2934.         mprintf((0, "Got frame packet from player %d wrong level %d!\n", pd->playernum, pd->level_num));
  2935.         return;
  2936.     }
  2937.  
  2938.     TheirObj = &Objects[TheirObjnum];
  2939.  
  2940.     //------------- Keep track of missed packets -----------------
  2941.     Players[TheirPlayernum].n_packets_got++;
  2942.     LastPacketTime[TheirPlayernum] = timer_get_approx_seconds();
  2943.  
  2944.     if  ( pd->numpackets != Players[TheirPlayernum].n_packets_got )    {
  2945.         missed_packets += pd->numpackets-Players[TheirPlayernum].n_packets_got;
  2946.  
  2947.         if ( missed_packets > 0 )    
  2948.             mprintf( (0, "Missed %d packets from player #%d (%d total)\n", pd->numpackets-Players[TheirPlayernum].n_packets_got, TheirPlayernum, missed_packets ));
  2949.         else
  2950.             mprintf( (0, "Got %d late packets from player #%d (%d total)\n", Players[TheirPlayernum].n_packets_got-pd->numpackets, TheirPlayernum, missed_packets ));
  2951.  
  2952.         Players[TheirPlayernum].n_packets_got = pd->numpackets;
  2953.     }
  2954.  
  2955.     //------------ Read the player's ship's object info ----------------------
  2956.     TheirObj->pos                = pd->obj_pos;
  2957.     TheirObj->orient            = pd->obj_orient;
  2958. #ifdef SHAREWARE
  2959.     TheirObj->mtype.phys_info        = pd->obj_mtype.phys_info;
  2960. #else
  2961.     TheirObj->mtype.phys_info.velocity = pd->phys_velocity;
  2962.     TheirObj->mtype.phys_info.rotvel = pd->phys_rotvel;
  2963. #endif
  2964.  
  2965.     if ((TheirObj->render_type != pd->obj_render_type) && (pd->obj_render_type == RT_POLYOBJ))
  2966.         multi_make_ghost_player(TheirPlayernum);
  2967.  
  2968.     obj_relink(TheirObjnum,pd->obj_segnum);
  2969.  
  2970.     if (TheirObj->movement_type == MT_PHYSICS)
  2971.         set_thrust_from_velocity(TheirObj);
  2972.  
  2973.     //------------ Welcome them back if reconnecting --------------
  2974.     if (!Players[TheirPlayernum].connected)    {
  2975.         Players[TheirPlayernum].connected = 1;
  2976.  
  2977. #ifndef SHAREWARE
  2978.         if (Newdemo_state == ND_STATE_RECORDING)
  2979.             newdemo_record_multi_reconnect(TheirPlayernum);
  2980. #endif
  2981.  
  2982.         multi_make_ghost_player(TheirPlayernum);
  2983.  
  2984.         create_player_appearance_effect(&Objects[TheirObjnum]);
  2985.  
  2986.         digi_play_sample( SOUND_HUD_MESSAGE, F1_0);
  2987.         HUD_init_message( "'%s' %s", Players[TheirPlayernum].callsign, TXT_REJOIN );
  2988.  
  2989. #ifndef SHAREWARE
  2990.         multi_send_score();
  2991. #endif
  2992.     }
  2993.  
  2994.     //------------ Parse the extra data at the end ---------------
  2995.  
  2996.     if ( pd->data_size>0 )    {
  2997.         // pass pd->data to some parser function....
  2998.         multi_process_bigdata( pd->data, pd->data_size );
  2999.     }
  3000. //    mprintf( (0, "Got packet with %d bytes on it!\n", pd->data_size ));
  3001.  
  3002. }
  3003.  
  3004. #endif
  3005. 
  3006.