]> icculus.org git repositories - btb/d2x.git/blob - main/network.c
use fixed 20fps wiggle
[btb/d2x.git] / main / network.c
1 /* $Id: network.c,v 1.19 2003-07-25 05:08:08 btb Exp $ */
2 /*
3 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
4 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
5 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
6 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
7 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
8 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
9 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
10 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
11 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
12 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
13 */
14
15 #ifdef HAVE_CONFIG_H
16 #include <conf.h>
17 #endif
18
19 #ifdef RCS
20 static char rcsid[] = "$Id: network.c,v 1.19 2003-07-25 05:08:08 btb Exp $";
21 #endif
22
23 #define PATCH12
24
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdlib.h>
28
29 #include "pstypes.h"
30 #include "strutil.h"
31 #include "args.h"
32 #include "timer.h"
33 #include "mono.h"
34 #include "ipx.h"
35 #include "newmenu.h"
36 #include "key.h"
37 #include "gauges.h"
38 #include "object.h"
39 #include "error.h"
40 #include "laser.h"
41 #include "gamesave.h"
42 #include "gamemine.h"
43 #include "player.h"
44 #include "gameseq.h"
45 #include "fireball.h"
46 #include "network.h"
47 #include "game.h"
48 #include "multi.h"
49 #include "endlevel.h"
50 #include "palette.h"
51 #include "cntrlcen.h"
52 #include "powerup.h"
53 #include "menu.h"
54 #include "sounds.h"
55 #include "text.h"
56 #include "kmatrix.h"
57 #include "newdemo.h"
58 #include "multibot.h"
59 #include "wall.h"
60 #include "bm.h"
61 #include "effects.h"
62 #include "physics.h"
63 #include "switch.h"
64 #include "automap.h"
65 #include "byteswap.h"
66 #include "netmisc.h"
67 #include "kconfig.h"
68 #include "playsave.h"
69 #include "cfile.h"
70
71 #ifdef MACINTOSH
72 #include <Events.h>
73 #include <Errors.h>     // for appletalk networking errors
74 #include "appltalk.h"
75 #endif
76
77 #define LHX(x)          ((x)*(MenuHires?2:1))
78 #define LHY(y)          ((y)*(MenuHires?2.4:1))
79
80 /* the following are the possible packet identificators.
81  * they are stored in the "type" field of the packet structs.
82  * they are offset 4 bytes from the beginning of the raw IPX data
83  * because of the "driver's" ipx_packetnum (see linuxnet.c).
84  */
85 #define PID_LITE_INFO       43 // 0x2B lite game info
86 #define PID_SEND_ALL_GAMEINFO 44 // 0x2C plz send more than lite only
87 #define PID_PLAYERSINFO     45 // 0x2D here's my name & personal data
88 #define PID_REQUEST         46 // 0x2E may i join, plz send sync
89 #define PID_SYNC            47 // 0x2F master says: enter mine now!
90 #define PID_PDATA           48 // 0x30
91 #define PID_ADDPLAYER       49
92
93 #define PID_DUMP            51 // 0x33 you can't join this game
94 #define PID_ENDLEVEL        52
95
96 #define PID_QUIT_JOINING    54
97 #define PID_OBJECT_DATA     55 // array of bots, players, powerups, ...
98 #define PID_GAME_LIST       56 // 0x38 give me the list of your games
99 #define PID_GAME_INFO       57 // 0x39 here's a game i've started
100 #define PID_PING_SEND       58
101 #define PID_PING_RETURN     59
102 #define PID_GAME_UPDATE     60 // inform about new player/team change
103 #define PID_ENDLEVEL_SHORT  61
104 #define PID_NAKED_PDATA     62
105 #define PID_GAME_PLAYERS    63
106 #define PID_NAMES_RETURN    64 // 0x40
107
108
109 #define NETGAME_ANARCHY         0
110 #define NETGAME_TEAM_ANARCHY    1
111 #define NETGAME_ROBOT_ANARCHY   2
112 #define NETGAME_COOPERATIVE     3
113 #define NETGAME_CAPTURE_FLAG    4
114 #define NETGAME_HOARD           5
115 #define NETGAME_TEAM_HOARD      6
116
117 /* The following are values for NetSecurityFlag */
118 #define NETSECURITY_OFF                 0
119 #define NETSECURITY_WAIT_FOR_PLAYERS    1
120 #define NETSECURITY_WAIT_FOR_GAMEINFO   2
121 #define NETSECURITY_WAIT_FOR_SYNC       3
122 /* The NetSecurityNum and the "Security" field of the network structs
123  * identifies a netgame. It is a random number chosen by the network master
124  * (the one that did "start netgame").
125  */
126
127 // MWA -- these structures are aligned -- please save me sanity and
128 // headaches by keeping alignment if these are changed!!!!  Contact
129 // me for info.
130
131 typedef struct endlevel_info {
132         ubyte                               type;
133         ubyte                               player_num;
134         byte                                connected;
135         ubyte                               seconds_left;
136         short                              kill_matrix[MAX_PLAYERS][MAX_PLAYERS];
137         short                               kills;
138         short                               killed;
139 } endlevel_info;
140
141 typedef struct endlevel_info_short {
142         ubyte                               type;
143         ubyte                               player_num;
144         byte                                connected;
145         ubyte                               seconds_left;
146 } endlevel_info_short;
147
148 // WARNING!!! This is the top part of netgame_info...if that struct changes,
149 //      this struct much change as well.  ie...they are aligned and the join system will
150 // not work without it.
151
152 // MWA  if this structure changes -- please make appropriate changes to receive_netgame_info
153 // code for macintosh in netmisc.c
154
155 typedef struct lite_info {
156         ubyte                           type;
157         int                             Security;
158         char                            game_name[NETGAME_NAME_LEN+1];
159         char                            mission_title[MISSION_NAME_LEN+1];
160         char                            mission_name[9];
161         int                             levelnum;
162         ubyte                           gamemode;
163         ubyte                           RefusePlayers;
164         ubyte                           difficulty;
165         ubyte                           game_status;
166         ubyte                           numplayers;
167         ubyte                           max_numplayers;
168         ubyte                           numconnected;
169         ubyte                           game_flags;
170         ubyte                           protocol_version;
171         ubyte                           version_major;
172         ubyte                           version_minor;
173         ubyte                           team_vector;
174 } __pack__ lite_info;
175
176 // IF YOU CHANGE THE SIZE OF ANY OF THE FOLLOWING STRUCTURES
177 // MAKE THE MACINTOSH DEFINES BE THE SAME SIZE AS THE PC OR
178 // I WILL HAVE TO HURT YOU VERY BADLY!!!  -- MWA
179
180 #ifdef MACINTOSH
181 #define NETGAME_INFO_SIZE       ( Network_game_type == IPX_GAME?355:sizeof(netgame_info) )
182 #define ALLNETPLAYERSINFO_SIZE  ( Network_game_type == IPX_GAME?317:sizeof(AllNetPlayers_info) )
183 #define LITE_INFO_SIZE          ( Network_game_type == IPX_GAME?72:sizeof(lite_info) )
184 #define SEQUENCE_PACKET_SIZE    ( Network_game_type == IPX_GAME?34:sizeof(sequence_packet) )
185 #define FRAME_INFO_SIZE         ( Network_game_type == IPX_GAME?541:sizeof(frame_info) )
186 #define IPX_SHORT_INFO_SIZE     ( 490 )
187 #else
188 #define NETGAME_INFO_SIZE       sizeof(netgame_info)
189 #define ALLNETPLAYERSINFO_SIZE  sizeof(AllNetPlayers_info)
190 #define LITE_INFO_SIZE          sizeof(lite_info)
191 #define SEQUENCE_PACKET_SIZE    sizeof(sequence_packet)
192 #define FRAME_INFO_SIZE         sizeof(frame_info)
193 #define IPX_SHORT_INFO_SIZE     sizeof(short_frame_info)
194 #endif
195
196 #define MAX_ACTIVE_NETGAMES     12
197
198 netgame_info Active_games[MAX_ACTIVE_NETGAMES];
199 AllNetPlayers_info ActiveNetPlayers[MAX_ACTIVE_NETGAMES];
200 AllNetPlayers_info *TempPlayersInfo,TempPlayersBase;
201 int NamesInfoSecurity=-1;
202
203 // MWAnetgame_info *TempNetInfo; 
204 netgame_info TempNetInfo;
205
206 extern void multi_send_drop_marker (int player,vms_vector position,char messagenum,char text[]);
207 extern void multi_send_kill_goal_counts();
208 extern int newmenu_dotiny( char * title, char * subtitle, int nitems, newmenu_item * item, void (*subfunction)(int nitems,newmenu_item * items, int * last_key, int citem) );
209
210 void network_process_naked_pdata (char *,int);
211 extern void multi_send_robot_controls(char);
212
213 void network_flush();
214 void network_listen();
215 void network_update_netgame();
216 void network_check_for_old_version(char pnum);
217 void network_send_objects();
218 void network_send_rejoin_sync(int player_num);
219 void network_send_game_info(sequence_packet *their);
220 void network_send_endlevel_short_sub(int from_player_num, int to_player);
221 void network_read_sync_packet(netgame_info * sp, int rsinit);
222 int  network_wait_for_playerinfo();
223 void network_process_pdata(char *data);
224 void network_read_object_packet(ubyte *data );
225 void network_read_endlevel_packet(ubyte *data );
226 void network_read_endlevel_short_packet(ubyte *data );
227 void network_ping(ubyte flat, int pnum);
228 void network_handle_ping_return(ubyte pnum);
229 void network_process_names_return(char *data);
230 void network_send_player_names(sequence_packet *their);
231 int  network_choose_connect();
232 void network_more_game_options();
233 void network_count_powerups_in_mine();
234 int  network_wait_for_all_info(int choice);
235 void network_AdjustMaxDataSize();
236 void network_do_big_wait(int choice);
237 void network_send_extras();
238 void network_read_pdata_packet(frame_info *pd);
239 void network_read_pdata_short_packet(short_frame_info *pd);
240
241 void ClipRank(signed char *rank);
242 void DoRefuseStuff(sequence_packet *their);
243 int  GetNewPlayerNumber(sequence_packet *their);
244 void SetAllAllowablesTo(int on);
245
246 int num_active_games = 0;
247 int PacketsPerSec=10;
248 int MaxXDataSize=NET_XDATA_SIZE;
249
250 int     Netlife_kills=0, Netlife_killed=0;
251
252 int     Network_debug=0;
253 int     Network_active=0;
254
255 int     Network_status = 0;
256 int     Network_games_changed = 0;
257
258 int     Network_socket = 0;
259 int     Network_allow_socket_changes = 1;
260
261 int     NetSecurityFlag=NETSECURITY_OFF;
262 int     NetSecurityNum=0;
263 int     Network_sending_extras=0;
264 int     VerifyPlayerJoined=-1;
265 int     Player_joining_extras=-1;  // This is so we know who to send 'latecomer' packets to.
266                                                                                           // We could just send updates to everyone but that kills the sender!   
267
268 // For rejoin object syncing
269
270 int     Network_rejoined = 0;       // Did WE rejoin this game?
271 int     Network_new_game = 0;            // Is this the first level of a new game?
272 int     Network_send_objects = 0;  // Are we in the process of sending objects to a player?
273 int     Network_send_objnum = -1;   // What object are we sending next?
274 int     Network_player_added = 0;   // Is this a new player or a returning player?
275 int     Network_send_object_mode = 0; // What type of objects are we sending, static or dynamic?
276 sequence_packet Network_player_rejoining; // Who is rejoining now?
277
278 fix     LastPacketTime[MAX_PLAYERS]; // For timeouts of idle/crashed players
279
280 int     PacketUrgent = 0;
281 int     NetGameType=0;
282 int     TotalMissedPackets=0,TotalPacketsGot=0;
283
284 frame_info      MySyncPack,UrgentSyncPack;
285 ubyte           MySyncPackInitialized = 0;              // Set to 1 if the MySyncPack is zeroed.
286 ushort          my_segments_checksum = 0;
287
288 sequence_packet My_Seq;
289 char WantPlayersInfo=0;
290 char WaitingForPlayerInfo=0;
291
292 char *RankStrings[]={"(unpatched) ","Cadet ","Ensign ","Lieutenant ","Lt.Commander ",
293                      "Commander ","Captain ","Vice Admiral ","Admiral ","Demigod "};
294
295 extern obj_position Player_init[MAX_PLAYERS];
296
297 extern int force_cockpit_redraw;
298
299 // reasons for a packet with type PID_DUMP
300 #define DUMP_CLOSED     0 // no new players allowed after game started
301 #define DUMP_FULL       1 // player cound maxed out
302 #define DUMP_ENDLEVEL   2
303 #define DUMP_DORK       3
304 #define DUMP_ABORTED    4
305 #define DUMP_CONNECTED  5 // never used
306 #define DUMP_LEVEL      6
307 #define DUMP_KICKED     7 // never used
308
309 extern ubyte Version_major,Version_minor;
310 extern ubyte SurfingNet;
311 extern char MaxPowerupsAllowed[MAX_POWERUP_TYPES];
312 extern char PowerupsInMine[MAX_POWERUP_TYPES];
313
314 extern void multi_send_stolen_items();
315
316 int network_wait_for_snyc();
317 extern void multi_send_wall_status (int,ubyte,ubyte,ubyte);
318 extern void multi_send_wall_status_specific (int,int,ubyte,ubyte,ubyte);
319
320 extern void game_disable_cheats();
321
322 char IWasKicked=0;
323 #ifdef NETPROFILING
324 FILE *SendLogFile,*RecieveLogFile;
325 int TTSent[100],TTRecv[100];
326 #endif
327
328 extern int Final_boss_is_dead;
329
330 // following is network stuff for appletalk
331
332 void network_dump_appletalk_player(ubyte node, ushort net, ubyte socket, int why);
333
334 #define NETWORK_OEM 0x10
335
336 #ifdef MACINTOSH
337
338 int Network_game_type;                                  // used to tell IPX vs. appletalk games
339
340 #define MAX_ZONES               255
341 #define MAX_ZONE_LENGTH         33
342 #define DEFAULT_ZONE_NAME       "\p*"
343 #define MAX_REGISTER_TRIES      5                                       // maximum time we will try and register a netgame
344 char Network_zone_name[MAX_ZONE_LENGTH];                // zone name game being played in for appletalk
345 char Zone_names[MAX_ZONES][MAX_ZONE_LENGTH];    // total list of zones that we can see
346 ubyte appletalk_use_broadcast = 0;
347 ubyte Network_game_validated = 0;               // validates a game before being able to join
348 ubyte Network_game_validate_choice = 0;
349 ubyte Network_appletalk_type = 0;               // type of appletalk network game is being played on
350
351 #define ETHERTALK_TYPE  0
352 #define LOCALTALK_TYPE  1
353 #define OTHERTALK_TYPE  2
354
355
356 void network_release_registered_game(void);
357
358 #endif
359
360 void
361 network_init(void)
362 {
363   
364         // So you want to play a netgame, eh?  Let's a get a few things
365         // straight
366
367    int t;
368         int save_pnum = Player_num;
369
370         game_disable_cheats();
371    IWasKicked=0;
372    Final_boss_is_dead=0;
373    NamesInfoSecurity=-1;
374
375
376    #ifdef NETPROFILING
377            OpenSendLog();
378                 OpenRecieveLog(); 
379         #endif
380         
381         for (t=0;t<MAX_POWERUP_TYPES;t++)
382                 {
383                         MaxPowerupsAllowed[t]=0;
384                         PowerupsInMine[t]=0;
385                 }
386
387    TotalMissedPackets=0; TotalPacketsGot=0;
388
389         memset(&Netgame, 0, sizeof(netgame_info));
390         memset(&NetPlayers,0,sizeof(AllNetPlayers_info));
391         memset(&My_Seq, 0, sizeof(sequence_packet));
392         My_Seq.type = PID_REQUEST;
393         memcpy(My_Seq.player.callsign, Players[Player_num].callsign, CALLSIGN_LEN+1);
394
395    #if defined (D2_OEM)
396            Version_minor|=NETWORK_OEM;
397         #endif
398  
399         My_Seq.player.version_major=Version_major;
400         My_Seq.player.version_minor=Version_minor;
401    My_Seq.player.rank=GetMyNetRanking();        
402  
403         if (Network_game_type == IPX_GAME) {
404                 memcpy(My_Seq.player.network.ipx.node, ipx_get_my_local_address(), 6);
405                 memcpy(My_Seq.player.network.ipx.server, ipx_get_my_server_address(), 4 );
406         #ifdef MACINTOSH
407         } else {
408                 My_Seq.player.network.appletalk.node = appletalk_get_my_node();
409                 My_Seq.player.network.appletalk.net = appletalk_get_my_net();
410                 My_Seq.player.network.appletalk.socket = appletalk_get_my_socket();
411         #endif
412         }
413 #ifdef MACINTOSH
414         My_Seq.player.computer_type = MAC;
415 #else
416         My_Seq.player.computer_type = DOS;
417 #endif
418
419         for (Player_num = 0; Player_num < MAX_NUM_NET_PLAYERS; Player_num++)
420                 init_player_stats_game();
421
422         Player_num = save_pnum;         
423         multi_new_game();
424         Network_new_game = 1;
425         Control_center_destroyed = 0;
426         network_flush();
427
428         Netgame.PacketsPerSec=10;
429
430    if ((t=FindArg("-packets")))
431     {
432      Netgame.PacketsPerSec=atoi(Args[t+1]);
433      if (Netgame.PacketsPerSec<1)
434       Netgame.PacketsPerSec=1;
435      else if (Netgame.PacketsPerSec>20)
436       Netgame.PacketsPerSec=20;
437      mprintf ((0,"Will send %d packets per second",Netgame.PacketsPerSec));
438     }
439    if (FindArg("-shortpackets"))
440     {
441      Netgame.ShortPackets=1;
442      mprintf ((0,"Will send short packets.\n"));
443     }
444 }
445
446 int
447 network_i_am_master(void)
448 {
449         // I am the lowest numbered player in this game?
450
451         int i;
452
453         if (!(Game_mode & GM_NETWORK))
454                 return (Player_num == 0);
455
456         for (i = 0; i < Player_num; i++)
457                 if (Players[i].connected)
458                         return 0;
459         return 1;
460 }
461
462 int network_who_is_master(void)
463 {
464         // Who is the master of this game?
465
466         int i;
467
468         if (!(Game_mode & GM_NETWORK))
469                 return (Player_num == 0);
470
471         for (i = 0; i < N_players; i++)
472                 if (Players[i].connected)
473                         return i;
474         return Player_num;
475 }
476 int network_how_many_connected()
477  {
478   int num=0,i;
479  
480         for (i = 0; i < N_players; i++)
481                 if (Players[i].connected)
482                         num++;
483    return (num);
484  }
485
486 #define ENDLEVEL_SEND_INTERVAL  (F1_0*2)
487 #define ENDLEVEL_IDLE_TIME      (F1_0*20)
488 /*
489 void
490 network_endlevel_poll( int nitems, newmenu_item * menus, int * key, int citem )
491 {
492         // Polling loop for End-of-level menu
493
494         static fix t1 = 0;
495         int i = 0;
496         int num_ready = 0;
497         int num_escaped = 0;
498         int goto_secret = 0;
499
500         int previous_state[MAX_NUM_NET_PLAYERS];
501         int previous_seconds_left;
502
503         menus = menus;
504         citem = citem;
505         nitems = nitems;
506         key = key;
507
508         // Send our endlevel packet at regular intervals
509
510         if (timer_get_approx_seconds() > (t1+ENDLEVEL_SEND_INTERVAL))
511         {
512                 network_send_endlevel_packet();
513                 t1 = timer_get_approx_seconds();
514         }
515
516         for (i = 0; i < N_players; i++)
517                 previous_state[i] = Players[i].connected;
518
519         previous_seconds_left = Countdown_seconds_left;
520
521         network_listen();
522
523         for (i = 0; i < N_players; i++)
524         {
525                 if (Players[i].connected == 1)
526                 {
527                         // Check timeout for idle players
528                         if (timer_get_approx_seconds() > LastPacketTime[i]+ENDLEVEL_IDLE_TIME)
529                         {
530                                 mprintf((0, "idle timeout for player %d.\n", i));
531                                 Players[i].connected = 0;
532                                 network_send_endlevel_sub(i);
533                         }                               
534                 }
535
536                 if ((Players[i].connected != 1) && (Players[i].connected != 5) && (Players[i].connected != 6))
537                         num_ready++;
538                 if (Players[i].connected != 1)
539                         num_escaped++;
540                 if (Players[i].connected == 4)
541                         goto_secret = 1;
542         }
543
544         if (num_escaped == N_players) // All players are out of the mine
545         {
546                 Countdown_seconds_left = -1;
547         }
548
549
550         if (num_ready == N_players) // All players have checked in or are disconnected
551         {
552                 if (goto_secret)
553                         *key = -3;
554                 else
555                         *key = -2;
556         }
557 } */
558   
559 void 
560 network_endlevel_poll2( int nitems, newmenu_item * menus, int * key, int citem )
561 {
562         // Polling loop for End-of-level menu
563
564         static fix t1 = 0;
565         int i = 0;
566         int num_ready = 0;
567         int goto_secret = 0;
568
569         menus = menus;
570         citem = citem;
571         nitems = nitems;
572         key = key;
573
574         // Send our endlevel packet at regular intervals
575
576         if (timer_get_approx_seconds() > (t1+ENDLEVEL_SEND_INTERVAL))
577         {
578                 network_send_endlevel_packet();
579                 t1 = timer_get_approx_seconds();
580         }
581
582 //   mprintf ((0,"Trying to listen!\n"));
583         network_listen();
584
585         for (i = 0; i < N_players; i++)
586         {
587                 if ((Players[i].connected != 1) && (Players[i].connected != 5) && (Players[i].connected != 6))
588                         num_ready++;
589                 if (Players[i].connected == 4)
590                         goto_secret = 1;                                        
591         }
592
593         if (num_ready == N_players) // All players have checked in or are disconnected
594         {
595                 if (goto_secret)
596                         *key = -3;
597                 else
598                         *key = -2;
599         }
600 }
601
602
603 extern fix StartAbortMenuTime;
604
605 void 
606 network_endlevel_poll3( int nitems, newmenu_item * menus, int * key, int citem )
607 {
608         // Polling loop for End-of-level menu
609
610    int num_ready=0,i;
611  
612         menus = menus;
613         citem = citem;
614         nitems = nitems;
615         key = key;
616
617         if (timer_get_approx_seconds() > (StartAbortMenuTime+(F1_0 * 8)))
618     *key=-2;
619
620
621         network_listen();
622
623
624         for (i = 0; i < N_players; i++)
625                 if ((Players[i].connected != 1) && (Players[i].connected != 5) && (Players[i].connected != 6))
626                         num_ready++;
627
628         if (num_ready == N_players) // All players have checked in or are disconnected
629                         *key = -2;
630
631 }
632
633
634 int
635 network_endlevel(int *secret)
636 {
637         // Do whatever needs to be done between levels
638
639    int i;
640
641    *secret=0;
642
643         //network_flush();
644
645         Network_status = NETSTAT_ENDLEVEL; // We are between levels
646
647         network_listen();
648
649         network_send_endlevel_packet();
650
651         for (i=0; i<N_players; i++) 
652         {
653                 LastPacketTime[i] = timer_get_approx_seconds();
654         }
655    
656         network_send_endlevel_packet();
657         network_send_endlevel_packet();
658         MySyncPackInitialized = 0;
659
660         network_update_netgame();
661
662         return(0);
663 }
664
665 int 
666 can_join_netgame(netgame_info *game,AllNetPlayers_info *people)
667 {
668         // Can this player rejoin a netgame in progress?
669
670         int i, num_players;
671
672         if (game->game_status == NETSTAT_STARTING)
673      return 1;
674
675         if (game->game_status != NETSTAT_PLAYING)
676     {
677       mprintf ((0,"Error: Can't join because game_status !=NETSTAT_PLAYING\n"));
678                 return 0;
679     }
680
681    if (game->version_major==0 && Version_major>0)
682     {
683            mprintf ((0,"Error:Can't join because version majors don't match!\n"));
684                 return (0);
685     }
686
687         if (game->version_major>0 && Version_major==0)
688     {
689            mprintf ((0,"Error:Can't join because version majors2 don't match!\n"));
690                 return (0);
691     }
692
693         // Game is in progress, figure out if this guy can re-join it
694
695         num_players = game->numplayers;
696
697         if (!(game->game_flags & NETGAME_FLAG_CLOSED)) {
698                 // Look for player that is not connected
699                 
700                 if (game->numconnected==game->max_numplayers)
701                  return (2);
702
703 //      mprintf ((0,"Refuse = %d\n",game->RefusePlayers));
704                 
705                 if (game->RefusePlayers)
706                  return (3);
707                 
708                 if (game->numplayers < game->max_numplayers)
709                         return 1;
710
711                 if (game->numconnected<num_players)
712                         return 1;
713                      
714         }
715
716         if (people==NULL)
717     {
718       mprintf ((0,"Error! Can't join because people==NULL!\n"));
719                 return 0;
720     }
721         
722         // Search to see if we were already in this closed netgame in progress
723
724         for (i = 0; i < num_players; i++) {
725                 if (Network_game_type == IPX_GAME) {
726                         if ( (!stricmp(Players[Player_num].callsign, people->players[i].callsign)) &&
727                                   (!memcmp(My_Seq.player.network.ipx.node, people->players[i].network.ipx.node, 6)) &&
728                                   (!memcmp(My_Seq.player.network.ipx.server, people->players[i].network.ipx.server, 4)) )
729                                 break;
730                 } else {
731                         if ( (!stricmp(Players[Player_num].callsign, people->players[i].callsign)) &&
732                                   (My_Seq.player.network.appletalk.node == people->players[i].network.appletalk.node) &&
733                                   (My_Seq.player.network.appletalk.net == people->players[i].network.appletalk.net) )
734                                 break;
735                 }
736         }
737
738         if (i != num_players)
739                 return 1;
740  
741    mprintf ((0,"Error: Can't join because at end of list!\n"));
742         return 0;
743 }
744
745 void
746 network_disconnect_player(int playernum)
747 {
748         // A player has disconnected from the net game, take whatever steps are
749         // necessary 
750
751         if (playernum == Player_num) 
752         {
753                 Int3(); // Weird, see Rob
754                 return;
755         }
756
757         Players[playernum].connected = 0;
758    NetPlayers.players[playernum].connected = 0;
759    if (VerifyPlayerJoined==playernum)
760           VerifyPlayerJoined=-1;
761
762 //      create_player_appearance_effect(&Objects[Players[playernum].objnum]);
763         multi_make_player_ghost(playernum);
764
765         if (Newdemo_state == ND_STATE_RECORDING)
766                 newdemo_record_multi_disconnect(playernum);
767
768         multi_strip_robots(playernum);
769 }
770                 
771 void
772 network_new_player(sequence_packet *their)
773 {
774         int objnum;
775         int pnum;
776
777         pnum = their->player.connected;
778
779         Assert(pnum >= 0);
780         Assert(pnum < MaxNumNetPlayers);        
781         
782         objnum = Players[pnum].objnum;
783
784         if (Newdemo_state == ND_STATE_RECORDING) {
785                 int new_player;
786
787                 if (pnum == N_players)
788                         new_player = 1;
789                 else
790                         new_player = 0;
791                 newdemo_record_multi_connect(pnum, new_player, their->player.callsign);
792         }
793
794         memcpy(Players[pnum].callsign, their->player.callsign, CALLSIGN_LEN+1);
795         memcpy(NetPlayers.players[pnum].callsign, their->player.callsign, CALLSIGN_LEN+1);
796         
797
798    ClipRank (&their->player.rank);
799    NetPlayers.players[pnum].rank=their->player.rank;
800         NetPlayers.players[pnum].version_major=their->player.version_major;
801         NetPlayers.players[pnum].version_minor=their->player.version_minor;
802    network_check_for_old_version(pnum);
803
804         if (Network_game_type == IPX_GAME) {
805                 if ( (*(uint *)their->player.network.ipx.server) != 0 )
806                         ipx_get_local_target( their->player.network.ipx.server, their->player.network.ipx.node, Players[pnum].net_address );
807                 else
808                         memcpy(Players[pnum].net_address, their->player.network.ipx.node, 6);
809         
810                 memcpy(NetPlayers.players[pnum].network.ipx.node, their->player.network.ipx.node, 6);
811                 memcpy(NetPlayers.players[pnum].network.ipx.server, their->player.network.ipx.server, 4);
812         } else {
813                 NetPlayers.players[pnum].network.appletalk.node = their->player.network.appletalk.node;
814                 NetPlayers.players[pnum].network.appletalk.net = their->player.network.appletalk.net;
815                 NetPlayers.players[pnum].network.appletalk.socket = their->player.network.appletalk.socket;
816         }
817
818         Players[pnum].n_packets_got = 0;
819         Players[pnum].connected = 1;
820         Players[pnum].net_kills_total = 0;
821         Players[pnum].net_killed_total = 0;
822         memset(kill_matrix[pnum], 0, MAX_PLAYERS*sizeof(short)); 
823         Players[pnum].score = 0;
824         Players[pnum].flags = 0;
825         Players[pnum].KillGoalCount=0;
826
827         if (pnum == N_players)
828         {
829                 N_players++;
830                 Netgame.numplayers = N_players;
831         }
832
833         digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
834
835    ClipRank (&their->player.rank);
836    
837    if (FindArg("-norankings"))
838           HUD_init_message("'%s' %s\n",their->player.callsign, TXT_JOINING);
839    else   
840      HUD_init_message("%s'%s' %s\n",RankStrings[their->player.rank],their->player.callsign, TXT_JOINING);
841         
842         multi_make_ghost_player(pnum);
843
844         multi_send_score();
845         multi_sort_kill_list();
846
847 //      create_player_appearance_effect(&Objects[objnum]);
848 }
849
850 char RefuseThisPlayer=0,WaitForRefuseAnswer=0,RefuseTeam;
851 char RefusePlayerName[12];
852 fix RefuseTimeLimit=0;
853
854 void network_welcome_player(sequence_packet *their)
855 {
856         // Add a player to a game already in progress
857         ubyte local_address[6];
858         int player_num;
859         int i;
860
861    WaitForRefuseAnswer=0;
862
863         if (FindArg("-NoMatrixCheat"))
864         {
865                 if ((their->player.version_minor & 0x0F) < 3)
866                 {
867                                         network_dump_player(their->player.network.ipx.server, their->player.network.ipx.node, DUMP_DORK);
868                                         return;
869                 }
870         }
871
872         if (HoardEquipped())
873         {
874    // If hoard game, and this guy isn't D2 Christmas (v1.2), dump him
875
876            if ((Game_mode & GM_HOARD) && ((their->player.version_minor & 0x0F)<2))
877                 {
878                         if (Network_game_type == IPX_GAME)
879                                 network_dump_player(their->player.network.ipx.server, their->player.network.ipx.node, DUMP_DORK);
880                         #ifdef MACINTOSH
881                         else
882                                 network_dump_appletalk_player(their->player.network.appletalk.node, their->player.network.appletalk.net, their->player.network.appletalk.socket, DUMP_DORK);
883                         #endif
884                         return;
885                 }
886         }
887
888         // Don't accept new players if we're ending this level.  Its safe to
889         // ignore since they'll request again later
890
891         if ((Endlevel_sequence) || (Control_center_destroyed))
892         {
893                 mprintf((0, "Ignored request from new player to join during endgame.\n"));
894                 if (Network_game_type == IPX_GAME)
895                         network_dump_player(their->player.network.ipx.server,their->player.network.ipx.node, DUMP_ENDLEVEL);
896                 #ifdef MACINTOSH
897                 else
898                         network_dump_appletalk_player(their->player.network.appletalk.node, their->player.network.appletalk.net, their->player.network.appletalk.socket, DUMP_ENDLEVEL);
899                 #endif
900                 return; 
901         }
902
903         if (Network_send_objects || Network_sending_extras)
904         {
905                 // Ignore silently, we're already responding to someone and we can't
906                 // do more than one person at a time.  If we don't dump them they will
907                 // re-request in a few seconds.
908                 return;
909         }
910
911         if (their->player.connected != Current_level_num)
912         {
913                 mprintf((0, "Dumping player due to old level number.\n"));
914                 if (Network_game_type == IPX_GAME)
915                         network_dump_player(their->player.network.ipx.server, their->player.network.ipx.node, DUMP_LEVEL);
916                 #ifdef MACINTOSH
917                 else
918                         network_dump_appletalk_player(their->player.network.appletalk.node, their->player.network.appletalk.net, their->player.network.appletalk.socket, DUMP_LEVEL);
919                 #endif
920                 return;
921         }
922
923         player_num = -1;
924         memset(&Network_player_rejoining, 0, sizeof(sequence_packet));
925         Network_player_added = 0;
926
927         if (Network_game_type == IPX_GAME) {
928                 if ( (*(uint *)their->player.network.ipx.server) != 0 )
929                         ipx_get_local_target( their->player.network.ipx.server, their->player.network.ipx.node, local_address );
930                 else
931                         memcpy(local_address, their->player.network.ipx.node, 6);
932         }
933
934         for (i = 0; i < N_players; i++)
935         {
936                 if ( (Network_game_type == IPX_GAME) && (!stricmp(Players[i].callsign, their->player.callsign )) && (!memcmp(Players[i].net_address,local_address, 6)) ) 
937                 {
938                         player_num = i;
939                         break;
940                 }
941 #ifdef MACINTOSH                // note link to above if
942                         else if ( (!stricmp(Players[i].callsign, their->player.callsign)) &&
943                                     (NetPlayers.players[i].network.appletalk.node == their->player.network.appletalk.node) &&
944                                         (NetPlayers.players[i].network.appletalk.net == their->player.network.appletalk.net)) {
945                         player_num = i;
946                         break;
947                 }
948 #endif
949         }
950
951         if (player_num == -1)
952         {
953                 // Player is new to this game
954
955                 if ( !(Netgame.game_flags & NETGAME_FLAG_CLOSED) && (N_players < MaxNumNetPlayers))
956                 {
957                         // Add player in an open slot, game not full yet
958
959                         player_num = N_players;
960                         Network_player_added = 1;
961                 }
962                 else if (Netgame.game_flags & NETGAME_FLAG_CLOSED)
963                 {
964                         // Slots are open but game is closed
965
966                         if (Network_game_type == IPX_GAME)
967                                 network_dump_player(their->player.network.ipx.server, their->player.network.ipx.node, DUMP_CLOSED);
968                         #ifdef MACINTOSH
969                         else
970                                 network_dump_appletalk_player(their->player.network.appletalk.node, their->player.network.appletalk.net, their->player.network.appletalk.socket, DUMP_CLOSED);
971                         #endif
972                         return;
973                 }
974                 else
975                 {
976                         // Slots are full but game is open, see if anyone is
977                         // disconnected and replace the oldest player with this new one
978                 
979                         int oldest_player = -1;
980                         fix oldest_time = timer_get_approx_seconds();
981
982                         Assert(N_players == MaxNumNetPlayers);
983
984                         for (i = 0; i < N_players; i++)
985                         {
986                                 if ( (!Players[i].connected) && (LastPacketTime[i] < oldest_time))
987                                 {
988                                         oldest_time = LastPacketTime[i];
989                                         oldest_player = i;
990                                 }
991                         }
992
993                         if (oldest_player == -1)
994                         {
995                                 // Everyone is still connected 
996
997                                 if (Network_game_type == IPX_GAME)
998                                         network_dump_player(their->player.network.ipx.server, their->player.network.ipx.node, DUMP_FULL);
999                                 #ifdef MACINTOSH
1000                                 else
1001                                         network_dump_appletalk_player(their->player.network.appletalk.node, their->player.network.appletalk.net, their->player.network.appletalk.socket, DUMP_FULL);
1002                                 #endif
1003                                 return;
1004                         }
1005                         else
1006                         {       
1007                                 // Found a slot!
1008
1009                                 player_num = oldest_player;
1010                                 Network_player_added = 1;
1011                         }
1012                 }
1013         }
1014         else 
1015         {
1016                 // Player is reconnecting
1017                 
1018                 if (Players[player_num].connected)
1019                 {
1020                         mprintf((0, "Extra REQUEST from player ignored.\n"));
1021                         return;
1022                 }
1023
1024                 if (Newdemo_state == ND_STATE_RECORDING)
1025                         newdemo_record_multi_reconnect(player_num);
1026
1027                 Network_player_added = 0;
1028
1029                 digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
1030                 
1031                 if (FindArg("-norankings"))
1032                         HUD_init_message("'%s' %s", Players[player_num].callsign, TXT_REJOIN);
1033                 else
1034                         HUD_init_message("%s'%s' %s", RankStrings[NetPlayers.players[player_num].rank],Players[player_num].callsign, TXT_REJOIN);
1035         }
1036
1037         Players[player_num].KillGoalCount=0;
1038
1039         // Send updated Objects data to the new/returning player
1040
1041         
1042         Network_player_rejoining = *their;
1043         Network_player_rejoining.player.connected = player_num;
1044         Network_send_objects = 1;
1045         Network_send_objnum = -1;
1046
1047         network_send_objects();
1048 }
1049
1050 int network_objnum_is_past(int objnum)
1051 {
1052         // determine whether or not a given object number has already been sent
1053         // to a re-joining player.
1054         
1055         int player_num = Network_player_rejoining.player.connected;
1056         int obj_mode = !((object_owner[objnum] == -1) || (object_owner[objnum] == player_num));
1057
1058         if (!Network_send_objects)
1059                 return 0; // We're not sending objects to a new player
1060
1061         if (obj_mode > Network_send_object_mode)
1062                 return 0;
1063         else if (obj_mode < Network_send_object_mode)
1064                 return 1;
1065         else if (objnum < Network_send_objnum)
1066                 return 1;
1067         else
1068                 return 0;
1069 }
1070
1071 #define OBJ_PACKETS_PER_FRAME 1
1072 extern void multi_send_active_door(char);
1073 extern void multi_send_door_open_specific(int,int,int,ubyte);
1074
1075
1076 void network_send_door_updates(int pnum)
1077 {
1078         // Send door status when new player joins
1079         
1080         int i;
1081    
1082    pnum=pnum;
1083
1084 //   Assert (pnum>-1 && pnum<N_players);
1085
1086         for (i = 0; i < Num_walls; i++)
1087         {
1088       if ((Walls[i].type == WALL_DOOR) && ((Walls[i].state == WALL_DOOR_OPENING) || (Walls[i].state == WALL_DOOR_WAITING) || (Walls[i].state == WALL_DOOR_OPEN)))
1089                         multi_send_door_open_specific(pnum,Walls[i].segnum, Walls[i].sidenum,Walls[i].flags);
1090                 else if ((Walls[i].type == WALL_BLASTABLE) && (Walls[i].flags & WALL_BLASTED))
1091                         multi_send_door_open_specific(pnum,Walls[i].segnum, Walls[i].sidenum,Walls[i].flags);
1092                 else if ((Walls[i].type == WALL_BLASTABLE) && (Walls[i].hps != WALL_HPS))
1093                         multi_send_hostage_door_status(i);
1094                 else
1095                         multi_send_wall_status_specific(pnum,i,Walls[i].type,Walls[i].flags,Walls[i].state);
1096         }
1097 }
1098
1099 extern vms_vector MarkerPoint[];
1100 void network_send_markers()
1101  {
1102   // send marker positions/text to new player
1103
1104   
1105   int i;
1106   
1107   for (i=0;i<8;i++)
1108    {
1109     if (MarkerObject[(i*2)]!=-1)
1110      multi_send_drop_marker (i,MarkerPoint[(i*2)],0,MarkerMessage[i*2]);
1111     if (MarkerObject[(i*2)+1]!=-1)
1112      multi_send_drop_marker (i,MarkerPoint[(i*2)+1],1,MarkerMessage[(i*2)+1]);
1113    }
1114  }
1115
1116 void network_process_monitor_vector(int vector)
1117 {
1118         int i, j;
1119         int count = 0;
1120         segment *seg;
1121         
1122         for (i=0; i <= Highest_segment_index; i++)
1123         {
1124                 int tm, ec, bm;
1125                 seg = &Segments[i];
1126                 for (j = 0; j < 6; j++)
1127                 {
1128                         if ( ((tm = seg->sides[j].tmap_num2) != 0) &&
1129                                   ((ec = TmapInfo[tm&0x3fff].eclip_num) != -1) &&
1130                                   ((bm = Effects[ec].dest_bm_num) != -1) )
1131                         {
1132                                 if (vector & (1 << count))
1133                                 {
1134                                         seg->sides[j].tmap_num2 = bm | (tm&0xc000);
1135                                 //      mprintf((0, "Monitor %d blown up.\n", count));
1136                                 }
1137                                 //else
1138                                   //    mprintf((0, "Monitor %d intact.\n", count));
1139                                 count++;
1140                                 Assert(count < 32);
1141                         }
1142                 }
1143         }
1144 }
1145
1146 int network_create_monitor_vector(void)
1147 {
1148         int i, j, k;
1149         int num_blown_bitmaps = 0;
1150         int monitor_num = 0;
1151         #define NUM_BLOWN_BITMAPS 20
1152         int blown_bitmaps[NUM_BLOWN_BITMAPS];
1153         int vector = 0;
1154         segment *seg;
1155
1156         for (i=0; i < Num_effects; i++)
1157         {
1158                 if (Effects[i].dest_bm_num > 0) {
1159                         for (j = 0; j < num_blown_bitmaps; j++)
1160                                 if (blown_bitmaps[j] == Effects[i].dest_bm_num)
1161                                         break;
1162                         if (j == num_blown_bitmaps) {
1163                                 blown_bitmaps[num_blown_bitmaps++] = Effects[i].dest_bm_num;
1164                                 Assert(num_blown_bitmaps < NUM_BLOWN_BITMAPS);
1165                         }
1166                 }
1167         }               
1168                 
1169 //      for (i = 0; i < num_blown_bitmaps; i++)
1170 //              mprintf((0, "Blown bitmap #%d = %d.\n", i, blown_bitmaps[i]));
1171
1172         for (i=0; i <= Highest_segment_index; i++)
1173         {
1174                 int tm, ec;
1175                 seg = &Segments[i];
1176                 for (j = 0; j < 6; j++)
1177                 {
1178                         if ((tm = seg->sides[j].tmap_num2) != 0) 
1179                         {
1180                                 if ( ((ec = TmapInfo[tm&0x3fff].eclip_num) != -1) &&
1181                                           (Effects[ec].dest_bm_num != -1) )
1182                                 {
1183                                 //      mprintf((0, "Monitor %d intact.\n", monitor_num));
1184                                         monitor_num++;
1185                                         Assert(monitor_num < 32);
1186                                 }
1187                                 else
1188                                 {
1189                                         for (k = 0; k < num_blown_bitmaps; k++)
1190                                         {
1191                                                 if ((tm&0x3fff) == blown_bitmaps[k])
1192                                                 {
1193                                                         //mprintf((0, "Monitor %d destroyed.\n", monitor_num));
1194                                                         vector |= (1 << monitor_num);
1195                                                         monitor_num++;
1196                                                         Assert(monitor_num < 32);
1197                                                         break;
1198                                                 }
1199                                         }
1200                                 }
1201                         }
1202                 }
1203         }
1204   //    mprintf((0, "Final monitor vector %x.\n", vector));
1205         return(vector);
1206 }
1207
1208 void network_stop_resync(sequence_packet *their)
1209 {
1210         if (Network_game_type == IPX_GAME) {
1211                 if ( (!memcmp(Network_player_rejoining.player.network.ipx.node, their->player.network.ipx.node, 6)) &&
1212                           (!memcmp(Network_player_rejoining.player.network.ipx.server, their->player.network.ipx.server, 4)) &&
1213                      (!stricmp(Network_player_rejoining.player.callsign, their->player.callsign)) )
1214                 {
1215                         mprintf((0, "Aborting resync for player %s.\n", their->player.callsign));
1216                         Network_send_objects = 0;
1217                         Network_sending_extras=0;
1218                         Network_rejoined=0;
1219                         Player_joining_extras=-1;
1220                         Network_send_objnum = -1;
1221                 }
1222         } else {
1223                 if ( (Network_player_rejoining.player.network.appletalk.node == their->player.network.appletalk.node) &&
1224                          (Network_player_rejoining.player.network.appletalk.net == their->player.network.appletalk.net) &&
1225                          (!stricmp(Network_player_rejoining.player.callsign, their->player.callsign)) )
1226                 {
1227                         mprintf((0, "Aborting resync for player %s.\n", their->player.callsign));
1228                         Network_send_objects = 0;
1229                         Network_sending_extras=0;
1230                         Network_rejoined=0;
1231                         Player_joining_extras=-1;
1232                         Network_send_objnum = -1;
1233                 }
1234         }
1235 }
1236
1237 byte object_buffer[IPX_MAX_DATA_SIZE];
1238
1239 void network_send_objects(void)
1240 {
1241         short remote_objnum;
1242         byte owner;
1243         int loc, i, h;
1244
1245         static int obj_count = 0;
1246         static int frame_num = 0;
1247
1248         int obj_count_frame = 0;
1249         int player_num = Network_player_rejoining.player.connected;
1250
1251         // Send clear objects array trigger and send player num
1252
1253         Assert(Network_send_objects != 0);
1254         Assert(player_num >= 0);
1255         Assert(player_num < MaxNumNetPlayers);
1256
1257         if (Endlevel_sequence || Control_center_destroyed)
1258         {
1259                 // Endlevel started before we finished sending the goods, we'll
1260                 // have to stop and try again after the level.
1261
1262                 if (Network_game_type == IPX_GAME)
1263                         network_dump_player(Network_player_rejoining.player.network.ipx.server,Network_player_rejoining.player.network.ipx.node, DUMP_ENDLEVEL);
1264                 #ifdef MACINTOSH
1265                 else
1266                         network_dump_appletalk_player(Network_player_rejoining.player.network.appletalk.node,Network_player_rejoining.player.network.appletalk.net, Network_player_rejoining.player.network.appletalk.socket, DUMP_ENDLEVEL);
1267                 #endif
1268                 Network_send_objects = 0; 
1269                 return;
1270         }
1271
1272         for (h = 0; h < OBJ_PACKETS_PER_FRAME; h++) // Do more than 1 per frame, try to speed it up without
1273                                                                                                                           // over-stressing the receiver.
1274         {
1275                 obj_count_frame = 0;
1276                 memset(object_buffer, 0, IPX_MAX_DATA_SIZE);
1277                 object_buffer[0] = PID_OBJECT_DATA;
1278                 loc = 3;
1279         
1280                 if (Network_send_objnum == -1)
1281                 {
1282                         obj_count = 0;
1283                         Network_send_object_mode = 0;
1284 //       mprintf((0, "Sending object array to player %d.\n", player_num));
1285                         *(short *)(object_buffer+loc) = INTEL_SHORT(-1);            loc += 2;
1286                         object_buffer[loc] = player_num;                            loc += 1;
1287                         /* Placeholder for remote_objnum, not used here */          loc += 2;
1288                         Network_send_objnum = 0;
1289                         obj_count_frame = 1;
1290                         frame_num = 0;
1291                 }
1292                 
1293                 for (i = Network_send_objnum; i <= Highest_object_index; i++)
1294                 {
1295                         if ((Objects[i].type != OBJ_POWERUP) && (Objects[i].type != OBJ_PLAYER) &&
1296                                  (Objects[i].type != OBJ_CNTRLCEN) && (Objects[i].type != OBJ_GHOST) &&
1297                                  (Objects[i].type != OBJ_ROBOT) && (Objects[i].type != OBJ_HOSTAGE) &&
1298                                  !(Objects[i].type==OBJ_WEAPON && Objects[i].id==PMINE_ID))
1299                                 continue;
1300                         if ((Network_send_object_mode == 0) && ((object_owner[i] != -1) && (object_owner[i] != player_num)))
1301                                 continue;
1302                         if ((Network_send_object_mode == 1) && ((object_owner[i] == -1) || (object_owner[i] == player_num)))
1303                                 continue;
1304
1305                         if ( ((IPX_MAX_DATA_SIZE-1) - loc) < (sizeof(object)+5) )
1306                                 break; // Not enough room for another object
1307
1308                         obj_count_frame++;
1309                         obj_count++;
1310         
1311                         remote_objnum = objnum_local_to_remote((short)i, &owner);
1312                         Assert(owner == object_owner[i]);
1313
1314                         *(short *)(object_buffer+loc) = INTEL_SHORT((short)i);      loc += 2;
1315                         object_buffer[loc] = owner;                                 loc += 1;
1316                         *(short *)(object_buffer+loc) = INTEL_SHORT(remote_objnum); loc += 2;
1317                         memcpy(&object_buffer[loc], &Objects[i], sizeof(object));
1318                         if (Network_game_type == IPX_GAME)
1319                                 swap_object((object *)&object_buffer[loc]);
1320                         loc += sizeof(object);
1321 //                      mprintf((0, "..packing object %d, remote %d\n", i, remote_objnum));
1322                 }
1323
1324                 if (obj_count_frame) // Send any objects we've buffered
1325                 {
1326                         frame_num++;
1327         
1328                         Network_send_objnum = i;
1329                         object_buffer[1] = obj_count_frame;  object_buffer[2] = frame_num;
1330 //       mprintf((0, "Object packet %d contains %d objects.\n", frame_num, obj_count_frame));
1331
1332                         Assert(loc <= IPX_MAX_DATA_SIZE);
1333
1334                         if (Network_game_type == IPX_GAME)
1335                                 ipx_send_internetwork_packet_data( object_buffer, loc, Network_player_rejoining.player.network.ipx.server, Network_player_rejoining.player.network.ipx.node );
1336                         #ifdef MACINTOSH
1337                         else
1338                                 appletalk_send_packet_data( object_buffer, loc, Network_player_rejoining.player.network.appletalk.node,
1339                                         Network_player_rejoining.player.network.appletalk.net,
1340                                         Network_player_rejoining.player.network.appletalk.socket);
1341                         #endif
1342
1343                         // OLD ipx_send_packet_data(object_buffer, loc, &Network_player_rejoining.player.node);
1344                 }
1345
1346                 if (i > Highest_object_index)
1347                 {
1348                         if (Network_send_object_mode == 0)
1349                         {
1350                                 Network_send_objnum = 0;
1351                                 Network_send_object_mode = 1; // go to next mode
1352                         }
1353                         else 
1354                         {
1355                                 Assert(Network_send_object_mode == 1); 
1356
1357                                 frame_num++;
1358                                 // Send count so other side can make sure he got them all
1359 //                              mprintf((0, "Sending end marker in packet #%d.\n", frame_num));
1360                                 mprintf((0, "Sent %d objects.\n", obj_count));
1361                                 object_buffer[0] = PID_OBJECT_DATA;
1362                                 object_buffer[1] = 1;
1363                                 object_buffer[2] = frame_num;
1364                                 *(short *)(object_buffer+3) = INTEL_SHORT(-2);
1365                                 *(short *)(object_buffer+6) = INTEL_SHORT(obj_count);
1366                                 //OLD ipx_send_packet_data(object_buffer, 8, &Network_player_rejoining.player.node);
1367                                 if (Network_game_type == IPX_GAME)
1368                                         ipx_send_internetwork_packet_data(object_buffer, 8, Network_player_rejoining.player.network.ipx.server, Network_player_rejoining.player.network.ipx.node);
1369                                 #ifdef MACINTOSH
1370                                 else
1371                                         appletalk_send_packet_data( object_buffer, 8, Network_player_rejoining.player.network.appletalk.node,
1372                                                                                                 Network_player_rejoining.player.network.appletalk.net,
1373                                                                                                 Network_player_rejoining.player.network.appletalk.socket);
1374                                 #endif
1375                                 
1376                         
1377                                 // Send sync packet which tells the player who he is and to start!
1378                                 network_send_rejoin_sync(player_num);
1379                                 mprintf ((0,"VerfiyPlayerJoined is now set to %d\n",player_num));
1380                                 VerifyPlayerJoined=player_num;
1381
1382                                 // Turn off send object mode
1383                                 Network_send_objnum = -1;
1384                                 Network_send_objects = 0;
1385                                 obj_count = 0;
1386
1387                                 //if (!network_i_am_master ())
1388                                 // Int3();  // Bad!! Get Jason.  Someone goofy is trying to get ahold of the game!
1389
1390                                 Network_sending_extras=40; // start to send extras
1391                            Player_joining_extras=player_num;
1392
1393                                 return;
1394                         } // mode == 1;
1395                 } // i > Highest_object_index
1396         } // For PACKETS_PER_FRAME
1397 }
1398
1399 extern void multi_send_powerup_update();
1400
1401 void network_send_rejoin_sync(int player_num)
1402 {
1403         int i, j;
1404
1405         Players[player_num].connected = 1; // connect the new guy
1406         LastPacketTime[player_num] = timer_get_approx_seconds();
1407
1408         if (Endlevel_sequence || Control_center_destroyed)
1409         {
1410                 // Endlevel started before we finished sending the goods, we'll
1411                 // have to stop and try again after the level.
1412
1413                 if (Network_game_type == IPX_GAME)
1414                         network_dump_player(Network_player_rejoining.player.network.ipx.server,Network_player_rejoining.player.network.ipx.node, DUMP_ENDLEVEL);
1415                 #ifdef MACINTOSH
1416                 else
1417                         network_dump_appletalk_player(Network_player_rejoining.player.network.appletalk.node,Network_player_rejoining.player.network.appletalk.net, Network_player_rejoining.player.network.appletalk.socket, DUMP_ENDLEVEL);
1418                 #endif
1419                 Network_send_objects = 0; 
1420                 Network_sending_extras=0;
1421                 return;
1422         }
1423
1424         if (Network_player_added)
1425         {
1426                 Network_player_rejoining.type = PID_ADDPLAYER;
1427                 Network_player_rejoining.player.connected = player_num;
1428                 network_new_player(&Network_player_rejoining);
1429
1430                 for (i = 0; i < N_players; i++)
1431                 {
1432                         if ((i != player_num) && (i != Player_num) && (Players[i].connected))
1433                                 if (Network_game_type == IPX_GAME) {
1434                                         send_sequence_packet( Network_player_rejoining, NetPlayers.players[i].network.ipx.server, NetPlayers.players[i].network.ipx.node, Players[i].net_address);
1435                                 #ifdef MACINTOSH
1436                                 } else {
1437                                         appletalk_send_packet_data( (ubyte *)&Network_player_rejoining, sizeof(sequence_packet), NetPlayers.players[i].network.appletalk.node,
1438                                                                                                 NetPlayers.players[i].network.appletalk.net, NetPlayers.players[i].network.appletalk.socket);
1439                                 #endif
1440                                 }
1441                 }
1442         }       
1443
1444         // Send sync packet to the new guy
1445
1446         network_update_netgame();
1447
1448         // Fill in the kill list
1449         for (j=0; j<MAX_PLAYERS; j++)
1450         {
1451                 for (i=0; i<MAX_PLAYERS;i++)
1452                         Netgame.kills[j][i] = kill_matrix[j][i];
1453                 Netgame.killed[j] = Players[j].net_killed_total;
1454                 Netgame.player_kills[j] = Players[j].net_kills_total;
1455                 Netgame.player_score[j] = Players[j].score;
1456         }       
1457
1458         Netgame.level_time = Players[Player_num].time_level;
1459         Netgame.monitor_vector = network_create_monitor_vector();
1460
1461         mprintf((0, "Sending rejoin sync packet!!!\n"));
1462
1463         if (Network_game_type == IPX_GAME) {
1464                 send_internetwork_full_netgame_packet(Network_player_rejoining.player.network.ipx.server, Network_player_rejoining.player.network.ipx.node);
1465                 send_netplayers_packet(Network_player_rejoining.player.network.ipx.server, Network_player_rejoining.player.network.ipx.node);
1466         #ifdef MACINTOSH
1467         } else {
1468                 appletalk_send_packet_data( (ubyte *)&Netgame, sizeof(netgame_info), Network_player_rejoining.player.network.appletalk.node,
1469                                                                         Network_player_rejoining.player.network.appletalk.net,
1470                                                                         Network_player_rejoining.player.network.appletalk.socket);
1471                 appletalk_send_packet_data( (ubyte *)&NetPlayers, sizeof(AllNetPlayers_info), Network_player_rejoining.player.network.appletalk.node,
1472                                                                         Network_player_rejoining.player.network.appletalk.net,
1473                                                                         Network_player_rejoining.player.network.appletalk.socket);
1474         #endif
1475         }
1476         return;
1477 }
1478 void resend_sync_due_to_packet_loss_for_allender ()
1479 {
1480    int i,j;
1481
1482    mprintf ((0,"I'm resending a sync packet! VPJ=%d\n",VerifyPlayerJoined));
1483   
1484         network_update_netgame();
1485
1486         // Fill in the kill list
1487         for (j=0; j<MAX_PLAYERS; j++)
1488         {
1489                 for (i=0; i<MAX_PLAYERS;i++)
1490                         Netgame.kills[j][i] = kill_matrix[j][i];
1491                 Netgame.killed[j] = Players[j].net_killed_total;
1492                 Netgame.player_kills[j] = Players[j].net_kills_total;
1493                 Netgame.player_score[j] = Players[j].score;
1494         }       
1495
1496         Netgame.level_time = Players[Player_num].time_level;
1497         Netgame.monitor_vector = network_create_monitor_vector();
1498
1499         if (Network_game_type == IPX_GAME) {
1500                 send_internetwork_full_netgame_packet(Network_player_rejoining.player.network.ipx.server, Network_player_rejoining.player.network.ipx.node);
1501                 send_netplayers_packet(Network_player_rejoining.player.network.ipx.server, Network_player_rejoining.player.network.ipx.node);
1502         #ifdef MACINTOSH
1503         } else {
1504                 appletalk_send_packet_data( (ubyte *)&Netgame, sizeof(netgame_info), Network_player_rejoining.player.network.appletalk.node,
1505                                                                         Network_player_rejoining.player.network.appletalk.net,
1506                                                                         Network_player_rejoining.player.network.appletalk.socket);
1507                 appletalk_send_packet_data( (ubyte *)&NetPlayers, sizeof(AllNetPlayers_info), Network_player_rejoining.player.network.appletalk.node,
1508                                                                         Network_player_rejoining.player.network.appletalk.net,
1509                                                                         Network_player_rejoining.player.network.appletalk.socket);
1510         #endif
1511         }
1512 }
1513
1514
1515 char * network_get_player_name( int objnum )
1516 {
1517         if ( objnum < 0 ) return NULL; 
1518         if ( Objects[objnum].type != OBJ_PLAYER ) return NULL;
1519         if ( Objects[objnum].id >= MAX_PLAYERS ) return NULL;
1520         if ( Objects[objnum].id >= N_players ) return NULL;
1521         
1522         return Players[Objects[objnum].id].callsign;
1523 }
1524
1525
1526 void network_add_player(sequence_packet *p)
1527 {
1528         int i;
1529         
1530         mprintf((0, "Got add player request!\n"));
1531
1532         for (i=0; i<N_players; i++ )    {
1533                 if (Network_game_type == IPX_GAME) {
1534                         if ( !memcmp( NetPlayers.players[i].network.ipx.node, p->player.network.ipx.node, 6) && !memcmp(NetPlayers.players[i].network.ipx.server, p->player.network.ipx.server, 4)) 
1535                                 return;         // already got them
1536                 } else {
1537                         if ( (NetPlayers.players[i].network.appletalk.node == p->player.network.appletalk.node) &&
1538                                  (NetPlayers.players[i].network.appletalk.net == p->player.network.appletalk.net))
1539                                         return;
1540                 }
1541         }
1542
1543         if (Network_game_type == IPX_GAME) {
1544                 memcpy( NetPlayers.players[N_players].network.ipx.node, p->player.network.ipx.node, 6 );
1545                 memcpy( NetPlayers.players[N_players].network.ipx.server, p->player.network.ipx.server, 4 );
1546         } else {
1547                 NetPlayers.players[N_players].network.appletalk.node = p->player.network.appletalk.node;
1548                 NetPlayers.players[N_players].network.appletalk.net = p->player.network.appletalk.net;
1549                 NetPlayers.players[N_players].network.appletalk.socket = p->player.network.appletalk.socket;
1550         }
1551    
1552    ClipRank (&p->player.rank);
1553   
1554         memcpy( NetPlayers.players[N_players].callsign, p->player.callsign, CALLSIGN_LEN+1 );
1555         NetPlayers.players[N_players].version_major=p->player.version_major;
1556         NetPlayers.players[N_players].version_minor=p->player.version_minor;
1557    NetPlayers.players[N_players].rank=p->player.rank;
1558         NetPlayers.players[N_players].connected = 1;
1559    network_check_for_old_version (N_players);
1560
1561         Players[N_players].KillGoalCount=0;
1562         Players[N_players].connected = 1;
1563         LastPacketTime[N_players] = timer_get_approx_seconds();
1564         N_players++;
1565         Netgame.numplayers = N_players;
1566
1567         // Broadcast updated info
1568
1569    mprintf ((0,"sending_game_info!\n"));
1570         network_send_game_info(NULL);
1571 }
1572
1573 // One of the players decided not to join the game
1574
1575 void network_remove_player(sequence_packet *p)
1576 {
1577         int i,pn;
1578         
1579         pn = -1;
1580         for (i=0; i<N_players; i++ )    {
1581                 if (Network_game_type == IPX_GAME) {
1582                         if (!memcmp(NetPlayers.players[i].network.ipx.node, p->player.network.ipx.node, 6) && !memcmp(NetPlayers.players[i].network.ipx.server, p->player.network.ipx.server, 4)) {
1583                                 pn = i;
1584                                 break;
1585                         }
1586                 } else {
1587                         if ( (NetPlayers.players[i].network.appletalk.node == p->player.network.appletalk.node) && (NetPlayers.players[i].network.appletalk.net == p->player.network.appletalk.net) ) {
1588                                 pn = i;
1589                                 break;
1590                         }
1591                 }
1592         }
1593         
1594         if (pn < 0 ) return;
1595
1596         for (i=pn; i<N_players-1; i++ ) {
1597                 if (Network_game_type == IPX_GAME) {
1598                         memcpy( NetPlayers.players[i].network.ipx.node, NetPlayers.players[i+1].network.ipx.node, 6 );
1599                         memcpy( NetPlayers.players[i].network.ipx.server, NetPlayers.players[i+1].network.ipx.server, 4 );
1600                 } else {
1601                         NetPlayers.players[i].network.appletalk.node = NetPlayers.players[i+1].network.appletalk.node;
1602                         NetPlayers.players[i].network.appletalk.net = NetPlayers.players[i+1].network.appletalk.net;
1603                         NetPlayers.players[i].network.appletalk.socket = NetPlayers.players[i+1].network.appletalk.socket;
1604                 }
1605                 memcpy( NetPlayers.players[i].callsign, NetPlayers.players[i+1].callsign, CALLSIGN_LEN+1 );
1606                 NetPlayers.players[i].version_major=NetPlayers.players[i+1].version_major;
1607                 NetPlayers.players[i].version_minor=NetPlayers.players[i+1].version_minor;
1608
1609            NetPlayers.players[i].rank=NetPlayers.players[i+1].rank;
1610                 ClipRank (&NetPlayers.players[i].rank);
1611            network_check_for_old_version(i);    
1612         }
1613                 
1614         N_players--;
1615         Netgame.numplayers = N_players;
1616
1617         // Broadcast new info
1618
1619         network_send_game_info(NULL);
1620
1621 }
1622
1623 void
1624 network_dump_player(ubyte * server, ubyte *node, int why)
1625 {
1626         // Inform player that he was not chosen for the netgame
1627
1628         sequence_packet temp;
1629
1630         temp.type = PID_DUMP;
1631         memcpy(temp.player.callsign, Players[Player_num].callsign, CALLSIGN_LEN+1);
1632         temp.player.connected = why;
1633         if (Network_game_type == IPX_GAME) {
1634                 send_internetwork_sequence_packet(temp, server, node);
1635         } else {
1636                 Int3();
1637         }
1638 }
1639
1640 #ifdef MACINTOSH
1641 void network_dump_appletalk_player(ubyte node, ushort net, ubyte socket, int why)
1642 {
1643         sequence_packet temp;
1644
1645         temp.type = PID_DUMP;
1646         memcpy(temp.player.callsign, Players[Player_num].callsign, CALLSIGN_LEN+1);
1647         temp.player.connected = why;
1648         if (Network_game_type == APPLETALK_GAME) {
1649                 appletalk_send_packet_data( (ubyte *)&temp, sizeof(sequence_packet), node, net, socket );
1650         } else {
1651                 Int3();
1652         }
1653 }
1654 #endif
1655
1656 void
1657 network_send_game_list_request()
1658 {
1659         // Send a broadcast request for game info
1660
1661         sequence_packet me;
1662
1663         mprintf((0, "Sending game_list request.\n"));
1664         me.type = PID_GAME_LIST;
1665         memcpy( me.player.callsign, Players[Player_num].callsign, CALLSIGN_LEN+1 );
1666
1667         if (Network_game_type == IPX_GAME) {
1668                 memcpy( me.player.network.ipx.node, ipx_get_my_local_address(), 6 );
1669                 memcpy( me.player.network.ipx.server, ipx_get_my_server_address(), 4 );
1670
1671                 send_broadcast_sequence_packet(me);
1672         #ifdef MACINTOSH
1673         } else {
1674                 me.player.network.appletalk.node = appletalk_get_my_node();
1675                 me.player.network.appletalk.net = appletalk_get_my_net();
1676                 me.player.network.appletalk.socket = appletalk_get_my_socket();
1677                 appletalk_send_game_info( (ubyte *)&me, sizeof(sequence_packet), Network_zone_name );
1678         #endif
1679         }
1680 }
1681
1682 void network_send_all_info_request(char type,int which_security)
1683 {
1684         // Send a broadcast request for game info
1685
1686         sequence_packet me;
1687
1688         mprintf((0, "Sending all_info request.\n"));
1689    
1690         me.Security=which_security;
1691         me.type = type;
1692         memcpy( me.player.callsign, Players[Player_num].callsign, CALLSIGN_LEN+1 );
1693         
1694         if (Network_game_type == IPX_GAME) {
1695                 memcpy( me.player.network.ipx.node, ipx_get_my_local_address(), 6 );
1696                 memcpy( me.player.network.ipx.server, ipx_get_my_server_address(), 4 );
1697
1698                 send_broadcast_sequence_packet(me);
1699         #ifdef MACINTOSH
1700         } else {
1701                 me.player.network.appletalk.node = appletalk_get_my_node();
1702                 me.player.network.appletalk.net = appletalk_get_my_net();
1703                 me.player.network.appletalk.socket = appletalk_get_my_socket();
1704                 appletalk_send_game_info( (ubyte *)&me, sizeof(sequence_packet), Network_zone_name );
1705         #endif
1706         }
1707 }
1708
1709
1710 void
1711 network_update_netgame(void)
1712 {
1713         // Update the netgame struct with current game variables
1714
1715         int i, j;
1716
1717    Netgame.numconnected=0;
1718    for (i=0;i<N_players;i++)
1719          if (Players[i].connected)
1720                 Netgame.numconnected++;
1721
1722 // This is great: D2 1.0 and 1.1 ignore upper part of the game_flags field of
1723 //      the lite_info struct when you're sitting on the join netgame screen.  We can
1724 //      "sneak" Hoard information into this field.  This is better than sending 
1725 //      another packet that could be lost in transit.
1726
1727
1728         if (HoardEquipped())
1729         {
1730                 if (Game_mode & GM_HOARD)
1731                 {
1732                         Netgame.game_flags |=NETGAME_FLAG_HOARD;
1733                         if (Game_mode & GM_TEAM)
1734                                 Netgame.game_flags |=NETGAME_FLAG_TEAM_HOARD;
1735                 }
1736         }
1737  
1738         if (Network_status == NETSTAT_STARTING)
1739                 return;
1740
1741         Netgame.numplayers = N_players;
1742         Netgame.game_status = Network_status;
1743         Netgame.max_numplayers = MaxNumNetPlayers;
1744
1745         for (i = 0; i < MAX_NUM_NET_PLAYERS; i++) 
1746         {
1747                 NetPlayers.players[i].connected = Players[i].connected;
1748                 for(j = 0; j < MAX_NUM_NET_PLAYERS; j++)
1749                         Netgame.kills[i][j] = kill_matrix[i][j];
1750
1751                 Netgame.killed[i] = Players[i].net_killed_total;
1752                 Netgame.player_kills[i] = Players[i].net_kills_total;
1753                 Netgame.player_score[i] = Players[i].score;
1754                 Netgame.player_flags[i] = (Players[i].flags & (PLAYER_FLAGS_BLUE_KEY | PLAYER_FLAGS_RED_KEY | PLAYER_FLAGS_GOLD_KEY));
1755         }
1756         Netgame.team_kills[0] = team_kills[0];
1757         Netgame.team_kills[1] = team_kills[1];
1758         Netgame.levelnum = Current_level_num;
1759
1760  
1761 }
1762
1763 void
1764 network_send_endlevel_sub(int player_num)
1765 {
1766         endlevel_info end;
1767         int i, j;
1768
1769         // Send an endlevel packet for a player
1770         end.type                = PID_ENDLEVEL;
1771         end.player_num = player_num;
1772         end.connected   = Players[player_num].connected;
1773         end.kills               = INTEL_SHORT(Players[player_num].net_kills_total);
1774         end.killed              = INTEL_SHORT(Players[player_num].net_killed_total);
1775         memcpy(end.kill_matrix, kill_matrix[player_num], MAX_PLAYERS*sizeof(short));
1776 #ifdef WORDS_BIGENDIAN
1777         for (i = 0; i < MAX_PLAYERS; i++)
1778                 for (j = 0; j < MAX_PLAYERS; j++)
1779                         end.kill_matrix[i][j] = INTEL_SHORT(end.kill_matrix[i][j]);
1780 #else
1781         j = j;          // to satisfy compiler
1782 #endif
1783
1784         if (Players[player_num].connected == 1) // Still playing
1785         {
1786                 Assert(Control_center_destroyed);
1787                 end.seconds_left = Countdown_seconds_left;
1788         }
1789
1790 //      mprintf((0, "Sending endlevel packet.\n"));
1791
1792         for (i = 0; i < N_players; i++)
1793         {       
1794                 if ((i != Player_num) && (i!=player_num) && (Players[i].connected)) {
1795                   if (Players[i].connected==1)
1796                          network_send_endlevel_short_sub(player_num,i);
1797                   else {
1798                         if (Network_game_type == IPX_GAME)
1799                                 ipx_send_packet_data((ubyte *)&end, sizeof(endlevel_info), NetPlayers.players[i].network.ipx.server, NetPlayers.players[i].network.ipx.node,Players[i].net_address);
1800                         #ifdef MACINTOSH
1801                         else
1802                                 appletalk_send_packet_data( (ubyte *)&end, sizeof(endlevel_info), NetPlayers.players[i].network.appletalk.node,
1803                                                                                         NetPlayers.players[i].network.appletalk.net,
1804                                                                                         NetPlayers.players[i].network.appletalk.socket);
1805                         #endif
1806                         }       
1807                 }
1808         }
1809 }
1810
1811 /* Send an updated endlevel status to other hosts */
1812 void
1813 network_send_endlevel_packet(void)
1814 {
1815         network_send_endlevel_sub(Player_num);
1816 }
1817
1818
1819 /* Send an endlevel packet for a player */
1820 void
1821 network_send_endlevel_short_sub(int from_player_num,int to_player)
1822 {
1823         endlevel_info_short end;
1824
1825         end.type = PID_ENDLEVEL_SHORT;
1826         end.player_num = from_player_num;
1827         end.connected = Players[from_player_num].connected;
1828         end.seconds_left = Countdown_seconds_left;
1829
1830
1831         if (Players[from_player_num].connected == 1) // Still playing
1832         {
1833                 Assert(Control_center_destroyed);
1834         }
1835
1836         if ((to_player != Player_num) && (to_player!=from_player_num) && (Players[to_player].connected))
1837         {
1838                 mprintf((0, "Sending short endlevel packet to %s.\n",Players[to_player].callsign));
1839                 if (Network_game_type == IPX_GAME)
1840                         ipx_send_packet_data((ubyte *)&end, sizeof(endlevel_info_short), NetPlayers.players[to_player].network.ipx.server, NetPlayers.players[to_player].network.ipx.node,Players[to_player].net_address);
1841                 #ifdef MACINTOSH
1842                 else
1843                         appletalk_send_packet_data( (ubyte *)&end, sizeof(endlevel_info_short), NetPlayers.players[to_player].network.appletalk.node,
1844                                                                                 NetPlayers.players[to_player].network.appletalk.net,
1845                                                                                 NetPlayers.players[to_player].network.appletalk.socket);
1846                 #endif
1847         }
1848 }
1849
1850 extern fix ThisLevelTime;
1851
1852 void
1853 network_send_game_info(sequence_packet *their)
1854 {
1855         // Send game info to someone who requested it
1856
1857         char old_type, old_status;
1858    fix timevar;
1859    int i;
1860
1861         mprintf((0, "Sending game info.\n"));
1862
1863         network_update_netgame(); // Update the values in the netgame struct
1864
1865         old_type = Netgame.type;
1866         old_status = Netgame.game_status;
1867
1868         Netgame.type = PID_GAME_INFO;
1869    NetPlayers.type = PID_PLAYERSINFO;
1870
1871    NetPlayers.Security=Netgame.Security;
1872         Netgame.version_major=Version_major;
1873         Netgame.version_minor=Version_minor;
1874
1875         if (Endlevel_sequence || Control_center_destroyed)
1876                 Netgame.game_status = NETSTAT_ENDLEVEL;
1877
1878         if (Netgame.PlayTimeAllowed)
1879     {
1880                 timevar=i2f (Netgame.PlayTimeAllowed*5*60);
1881                 i=f2i(timevar-ThisLevelTime);
1882                 if (i<30)
1883                         Netgame.game_status=NETSTAT_ENDLEVEL;
1884         }       
1885
1886         if (!their) {
1887                 if (Network_game_type == IPX_GAME) {
1888                         send_broadcast_full_netgame_packet();
1889                         send_broadcast_netplayers_packet();
1890                 } // nothing to do for appletalk games I think....
1891         } else {
1892                 if (Network_game_type == IPX_GAME) {
1893                         send_internetwork_full_netgame_packet(their->player.network.ipx.server, their->player.network.ipx.node);
1894                         send_netplayers_packet(their->player.network.ipx.server, their->player.network.ipx.node);
1895                 #ifdef MACINTOSH
1896                 } else {
1897                         appletalk_send_packet_data( (ubyte *)&Netgame, sizeof(netgame_info), their->player.network.appletalk.node,
1898                                                                                 their->player.network.appletalk.net, their->player.network.appletalk.socket );
1899                         appletalk_send_packet_data( (ubyte *)&NetPlayers, sizeof(AllNetPlayers_info), their->player.network.appletalk.node,
1900                                                                                 their->player.network.appletalk.net, their->player.network.appletalk.socket );
1901                 #endif
1902                 }
1903         }  
1904
1905         Netgame.type = old_type;
1906         Netgame.game_status = old_status;
1907 }       
1908
1909 void network_send_lite_info(sequence_packet *their)
1910 {
1911         // Send game info to someone who requested it
1912
1913         char old_type, old_status,oldstatus;
1914
1915         mprintf((0, "Sending lite game info.\n"));
1916
1917         network_update_netgame(); // Update the values in the netgame struct
1918
1919         old_type = Netgame.type;
1920         old_status = Netgame.game_status;
1921
1922         Netgame.type = PID_LITE_INFO;
1923
1924         if (Endlevel_sequence || Control_center_destroyed)
1925                 Netgame.game_status = NETSTAT_ENDLEVEL;
1926
1927 // If hoard mode, make this game look closed even if it isn't
1928    if (HoardEquipped())
1929         {
1930                 if (Game_mode & GM_HOARD)
1931                 {
1932                         oldstatus=Netgame.game_status;
1933                         Netgame.game_status=NETSTAT_ENDLEVEL;
1934                         Netgame.gamemode=NETGAME_CAPTURE_FLAG;
1935                         if (oldstatus==NETSTAT_ENDLEVEL)
1936                                 Netgame.game_flags|=NETGAME_FLAG_REALLY_ENDLEVEL;
1937                         if (oldstatus==NETSTAT_STARTING)
1938                                 Netgame.game_flags|=NETGAME_FLAG_REALLY_FORMING;
1939                 }
1940         }
1941
1942         if (!their) {
1943                 if (Network_game_type == IPX_GAME) {
1944                         send_broadcast_lite_netgame_packet();
1945                 }               // nothing to do for appletalk I think....
1946         } else {
1947                 if (Network_game_type == IPX_GAME) {
1948                         send_internetwork_lite_netgame_packet(their->player.network.ipx.server, their->player.network.ipx.node);
1949                 #ifdef MACINTOSH
1950                 } else {
1951                         appletalk_send_packet_data( (ubyte *)&Netgame, sizeof(lite_info), their->player.network.appletalk.node,
1952                                                                                 their->player.network.appletalk.net, their->player.network.appletalk.socket );
1953                 #endif
1954                 }
1955         }  
1956
1957         //  Restore the pre-hoard mode
1958         if (HoardEquipped())
1959         {
1960                 if (Game_mode & GM_HOARD)
1961                 {
1962                         if (!(Game_mode & GM_TEAM))
1963                            Netgame.gamemode=NETGAME_HOARD;
1964                         else
1965                            Netgame.gamemode=NETGAME_TEAM_HOARD;
1966                         Netgame.game_flags&=~NETGAME_FLAG_REALLY_ENDLEVEL;
1967                         Netgame.game_flags&=~NETGAME_FLAG_REALLY_FORMING;
1968                         Netgame.game_flags&=~NETGAME_FLAG_TEAM_HOARD;
1969                 }
1970         }
1971
1972         Netgame.type = old_type;
1973         Netgame.game_status = old_status;
1974
1975 }       
1976
1977 /* Send game info to all players in this game */
1978 void network_send_netgame_update()
1979 {
1980         char old_type, old_status;
1981         int i;
1982
1983         mprintf((0, "Sending updated game info.\n"));
1984
1985         network_update_netgame(); // Update the values in the netgame struct
1986
1987         old_type = Netgame.type;
1988         old_status = Netgame.game_status;
1989
1990         Netgame.type = PID_GAME_UPDATE;
1991
1992         if (Endlevel_sequence || Control_center_destroyed)
1993                 Netgame.game_status = NETSTAT_ENDLEVEL;
1994  
1995         for (i=0; i<N_players; i++ )    {
1996                 if ( (Players[i].connected) && (i!=Player_num ) )       {
1997                         if (Network_game_type == IPX_GAME) {
1998                                 send_lite_netgame_packet(NetPlayers.players[i].network.ipx.server, NetPlayers.players[i].network.ipx.node, Players[i].net_address);
1999                         #ifdef MACINTOSH
2000                         } else {
2001                                 appletalk_send_packet_data( (ubyte *)&Netgame, sizeof(lite_info), NetPlayers.players[i].network.appletalk.node,
2002                                                                                         NetPlayers.players[i].network.appletalk.net, NetPlayers.players[i].network.appletalk.socket );
2003                         #endif
2004                         }
2005                 }
2006         }
2007
2008         Netgame.type = old_type;
2009         Netgame.game_status = old_status;
2010 }       
2011                           
2012 int network_send_request(void)
2013 {
2014         // Send a request to join a game 'Netgame'.  Returns 0 if we can join this
2015         // game, non-zero if there is some problem.
2016         int i;
2017
2018         if (Netgame.numplayers < 1)
2019          return 1;
2020
2021         for (i = 0; i < MAX_NUM_NET_PLAYERS; i++)
2022           if (NetPlayers.players[i].connected)
2023               break;
2024
2025         Assert(i < MAX_NUM_NET_PLAYERS);
2026
2027         mprintf((0, "Sending game enroll request to player %d (%s). Serv=%x Node=%x Level=%d\n", i, Players[i].callsign,NetPlayers.players[i].network.ipx.server,NetPlayers.players[i].network.ipx.node,Netgame.levelnum));
2028
2029 //      segments_checksum = netmisc_calc_checksum( Segments, sizeof(segment)*(Highest_segment_index+1) );       
2030
2031         My_Seq.type = PID_REQUEST;
2032         My_Seq.player.connected = Current_level_num;
2033
2034         if (Network_game_type == IPX_GAME) {
2035                 send_internetwork_sequence_packet(My_Seq, NetPlayers.players[i].network.ipx.server, NetPlayers.players[i].network.ipx.node);
2036         #ifdef MACINTOSH
2037         } else {
2038                 appletalk_send_packet_data( (ubyte *)&My_Seq, sizeof(sequence_packet), NetPlayers.players[i].network.appletalk.node,
2039                                                                         NetPlayers.players[i].network.appletalk.net,
2040                                                                         NetPlayers.players[i].network.appletalk.socket);
2041         #endif
2042         }
2043
2044         return i;
2045 }
2046
2047 int SecurityCheck=0;
2048         
2049 void network_process_gameinfo(ubyte *data)
2050 {
2051         int i, j;
2052         netgame_info *new = (netgame_info *)data;
2053 #ifdef WORDS_BIGENDIAN
2054         netgame_info tmp_info;
2055
2056         if (Network_game_type == IPX_GAME)  {
2057                 receive_netgame_packet(data, &tmp_info, 0);                     // get correctly aligned structure
2058                 new = &tmp_info;
2059         }
2060 #endif
2061         
2062
2063    WaitingForPlayerInfo=0;
2064
2065    if (new->Security !=TempPlayersInfo->Security)
2066     {
2067      Int3();     // Get Jason
2068      return;     // If this first half doesn't go with the second half
2069     }
2070
2071         Network_games_changed = 1;
2072
2073    //mprintf((0, "Got game data for game %s.\n", new->game_name));
2074
2075    Assert (TempPlayersInfo!=NULL);
2076
2077         for (i = 0; i < num_active_games; i++)
2078          {
2079           //mprintf ((0,"GAMEINFO: Game %d is %s!\n",i,Active_games[i].game_name));
2080           
2081           if (!stricmp(Active_games[i].game_name, new->game_name) && 
2082                                   Active_games[i].Security==new->Security)
2083                break;
2084          }
2085
2086         if (i == MAX_ACTIVE_NETGAMES)
2087         {
2088                 mprintf((0, "Too many netgames.\n"));
2089                 return;
2090         }
2091         
2092 // MWA  memcpy(&Active_games[i], data, sizeof(netgame_info));
2093         memcpy(&Active_games[i], (ubyte *)new, sizeof(netgame_info));
2094         memcpy (&ActiveNetPlayers[i],TempPlayersInfo,sizeof(AllNetPlayers_info));
2095
2096    if (SecurityCheck)
2097          if (Active_games[i].Security==SecurityCheck)
2098                 SecurityCheck=-1;
2099
2100         //mprintf ((0,"Recieved %d unique games...\n",i));
2101
2102         if (i == num_active_games)
2103                 num_active_games++;
2104
2105         if (Active_games[i].numplayers == 0)
2106         {
2107          mprintf ((0,"DELETING THIS GAME!!!\n"));       
2108                 // Delete this game
2109                 for (j = i; j < num_active_games-1; j++)
2110         {
2111              memcpy(&Active_games[j], &Active_games[j+1], sizeof(netgame_info));
2112            memcpy (&ActiveNetPlayers[j],&ActiveNetPlayers[j+1],sizeof(AllNetPlayers_info));
2113         }
2114                 num_active_games--;
2115                 SecurityCheck=0;
2116         }
2117 }
2118
2119 void network_process_lite_info(ubyte *data)
2120 {
2121         int i, j;
2122         lite_info *new = (lite_info *)data;
2123 #ifdef WORDS_BIGENDIAN
2124         lite_info tmp_info;
2125
2126         if (Network_game_type == IPX_GAME) {
2127                 receive_netgame_packet(data, (netgame_info *)&tmp_info, 1);
2128                 new = &tmp_info;
2129         }
2130 #endif
2131
2132         Network_games_changed = 1;
2133
2134    //mprintf((0, "Got game data for game %s.\n", new->game_name));
2135
2136         for (i = 0; i < num_active_games; i++)
2137     {
2138       //mprintf ((0,"GAMEINFO: Game %d is %s!\n",i,Active_games[i].game_name));
2139       
2140       if ((!stricmp(Active_games[i].game_name, new->game_name)) && 
2141                          Active_games[i].Security==new->Security)
2142                                 break;
2143     }
2144
2145         if (i == MAX_ACTIVE_NETGAMES)
2146         {
2147                 mprintf((0, "Too many netgames.\n"));
2148                 return;
2149         }
2150         
2151         memcpy(&Active_games[i], (ubyte *)new, sizeof(lite_info));
2152
2153 // See if this is really a Hoard game
2154 // If so, adjust all the data accordingly
2155
2156         if (HoardEquipped())
2157         {
2158                 if (Active_games[i].game_flags & NETGAME_FLAG_HOARD)
2159                 {
2160                         Active_games[i].gamemode=NETGAME_HOARD;                                   
2161                         Active_games[i].game_status=NETSTAT_PLAYING;
2162                         
2163                         if (Active_games[i].game_flags & NETGAME_FLAG_TEAM_HOARD)
2164                                 Active_games[i].gamemode=NETGAME_TEAM_HOARD;                                      
2165                         if (Active_games[i].game_flags & NETGAME_FLAG_REALLY_ENDLEVEL)
2166                                 Active_games[i].game_status=NETSTAT_ENDLEVEL;
2167                         if (Active_games[i].game_flags & NETGAME_FLAG_REALLY_FORMING)
2168                                 Active_games[i].game_status=NETSTAT_STARTING;
2169                 }
2170         }
2171
2172    //mprintf ((0,"Recieved %d unique games...\n",i));
2173
2174         if (i == num_active_games)
2175                 num_active_games++;
2176
2177         if (Active_games[i].numplayers == 0)
2178     {
2179            mprintf ((0,"DELETING THIS GAME!!!\n"));     
2180                 // Delete this game
2181                 for (j = i; j < num_active_games-1; j++)
2182         {
2183              memcpy(&Active_games[j], &Active_games[j+1], sizeof(netgame_info));
2184         }
2185                 num_active_games--;
2186         }
2187 }
2188
2189 void network_process_dump(sequence_packet *their)
2190 {
2191         // Our request for join was denied.  Tell the user why.
2192
2193         char temp[40];
2194         int i;
2195
2196         mprintf((0, "Dumped by player %s, type %d.\n", their->player.callsign, their->player.connected));
2197
2198    if (their->player.connected!=7)
2199                 nm_messagebox(NULL, 1, TXT_OK, NET_DUMP_STRINGS(their->player.connected));
2200         else
2201                 {
2202                  for (i=0;i<N_players;i++)
2203                         if (!stricmp (their->player.callsign,Players[i].callsign))
2204                         {
2205                                 if (i!=network_who_is_master())
2206                                 {
2207                                         HUD_init_message ("%s attempted to kick you out.",their->player.callsign);
2208                                 }
2209                                 else
2210                                 {
2211                                   sprintf (temp,"%s has kicked you out!",their->player.callsign);
2212                                   nm_messagebox(NULL, 1, TXT_OK, &temp);
2213                             if (Network_status==NETSTAT_PLAYING)
2214                                   {
2215                                         IWasKicked=1;
2216                                         multi_leave_game();     
2217                                   }
2218                                  else
2219                                         Network_status = NETSTAT_MENU;
2220                       }
2221                    }
2222                 }
2223 }       
2224 void network_process_request(sequence_packet *their)
2225 {
2226         // Player is ready to receieve a sync packet
2227         int i;
2228
2229         mprintf((0, "Player %s ready for sync.\n", their->player.callsign));
2230
2231         for (i = 0; i < N_players; i++) {
2232                 if (Network_game_type == IPX_GAME) {
2233                         if (!memcmp(their->player.network.ipx.server, NetPlayers.players[i].network.ipx.server, 4) && !memcmp(their->player.network.ipx.node, NetPlayers.players[i].network.ipx.node, 6) && (!stricmp(their->player.callsign, NetPlayers.players[i].callsign))) {
2234                                 Players[i].connected = 1;
2235                                 break;
2236                         }
2237                 } else {
2238                         if ( (their->player.network.appletalk.node == NetPlayers.players[i].network.appletalk.node) && (their->player.network.appletalk.net == NetPlayers.players[i].network.appletalk.net) && (!stricmp(their->player.callsign, NetPlayers.players[i].callsign)) ) {
2239                                 Players[i].connected = 1;
2240                                 break;
2241                         }
2242                 }
2243         }                       
2244 }
2245
2246 #define REFUSE_INTERVAL F1_0 * 8
2247 extern void multi_reset_object_texture (object *);
2248
2249 void network_process_packet(ubyte *data, int length )
2250 {
2251         sequence_packet *their = (sequence_packet *)data;
2252 #ifdef WORDS_BIGENDIAN
2253         sequence_packet tmp_packet;
2254
2255         if (Network_game_type == IPX_GAME) {
2256                 receive_sequence_packet(data, &tmp_packet);
2257                 their = &tmp_packet;                                            // reassign their to point to correctly alinged structure
2258         }
2259 #endif
2260
2261    //mprintf( (0, "Got packet of length %d, type %d\n", length, their->type ));
2262         
2263 //      if ( length < sizeof(sequence_packet) ) return;
2264
2265         length = length;
2266
2267         switch( data[0] )       {
2268         
2269          case PID_GAME_INFO:            // Jason L. says we can safely ignore this type.
2270                 break;
2271         
2272      case PID_PLAYERSINFO:
2273
2274                 mprintf ((0,"Got a PID_PLAYERSINFO!\n"));
2275
2276                 if (Network_status==NETSTAT_WAITING)
2277                 {
2278                         if (Network_game_type == IPX_GAME)
2279                                 receive_netplayers_packet(data, &TempPlayersBase);
2280                         else
2281                                 memcpy (&TempPlayersBase,data,sizeof(AllNetPlayers_info));
2282
2283                         if (TempPlayersBase.Security!=Netgame.Security)
2284                          {
2285                           mprintf ((0,"Bad security for PLAYERSINFO\n"));
2286                           break;
2287                          }      
2288                 
2289                         mprintf ((0,"Got a waiting PID_PLAYERSINFO!\n"));
2290                         if (length!=ALLNETPLAYERSINFO_SIZE)
2291                         {
2292                                 mprintf ((0,"Invalid size for netplayers packet!\n"));
2293                                 return;
2294                         }
2295
2296                         TempPlayersInfo=&TempPlayersBase;
2297                         WaitingForPlayerInfo=0;
2298                         NetSecurityNum=TempPlayersInfo->Security;
2299                         NetSecurityFlag=NETSECURITY_WAIT_FOR_SYNC;
2300            }
2301
2302      break;
2303
2304    case PID_LITE_INFO:
2305
2306          if (length != LITE_INFO_SIZE)
2307                  {
2308                   mprintf ((0,"WARNING! Recieved invalid size for PID_LITE_INFO\n"));
2309                   return;
2310                  }
2311  
2312          if (Network_status == NETSTAT_BROWSING)
2313                 network_process_lite_info (data);
2314     break;
2315
2316         case PID_GAME_LIST:
2317                 // Someone wants a list of games
2318
2319            if (length != SEQUENCE_PACKET_SIZE)
2320                  {
2321                   mprintf ((0,"WARNING! Recieved invalid size for PID_GAME_LIST\n"));
2322                   return;
2323                  }
2324                         
2325                 mprintf((0, "Got a PID_GAME_LIST!\n"));
2326                 if ((Network_status == NETSTAT_PLAYING) || (Network_status == NETSTAT_STARTING) || (Network_status == NETSTAT_ENDLEVEL))
2327                         if (network_i_am_master())
2328                                 network_send_lite_info(their);
2329                 break;
2330
2331
2332         case PID_SEND_ALL_GAMEINFO:
2333
2334            if (length != SEQUENCE_PACKET_SIZE)
2335                  {
2336                   mprintf ((0,"WARNING! Recieved invalid size for PID_SEND_ALL_GAMEINFO\n"));
2337                   return;
2338                  }
2339
2340                 if ((Network_status == NETSTAT_PLAYING) || (Network_status == NETSTAT_STARTING) || (Network_status == NETSTAT_ENDLEVEL))
2341                         if (network_i_am_master() && their->Security==Netgame.Security)
2342                                 network_send_game_info(their);
2343                 break;
2344         
2345         case PID_ADDPLAYER:
2346
2347                 mprintf( (0, "Got NEWPLAYER message from %s.\n", their->player.callsign));
2348                 
2349            if (length != SEQUENCE_PACKET_SIZE)
2350                  {
2351                   mprintf ((0,"WARNING! Recieved invalid size for PID_ADDPLAYER\n"));
2352                   return;
2353                  }
2354                 mprintf( (0, "Got NEWPLAYER message from %s.\n", their->player.callsign));
2355                 network_new_player(their);
2356                 break;                  
2357         case PID_REQUEST:
2358            if (length != SEQUENCE_PACKET_SIZE)
2359                  {
2360                   mprintf ((0,"WARNING! Recieved invalid size for PID_REQUEST\n"));
2361                   return;
2362                  }
2363
2364                 mprintf( (0, "Got REQUEST from '%s'\n", their->player.callsign ));
2365                 if (Network_status == NETSTAT_STARTING) 
2366                 {
2367                         // Someone wants to join our game!
2368                         network_add_player(their);
2369                 }
2370                 else if (Network_status == NETSTAT_WAITING)
2371                 {
2372                         // Someone is ready to recieve a sync packet
2373                         network_process_request(their);
2374                 }
2375                 else if (Network_status == NETSTAT_PLAYING)
2376                 {
2377                         // Someone wants to join a game in progress!
2378                         if (Netgame.RefusePlayers)
2379                 DoRefuseStuff (their);
2380                    else 
2381                                 network_welcome_player(their);
2382                 }
2383                 break;
2384         case PID_DUMP:  
2385
2386            if (length != SEQUENCE_PACKET_SIZE)
2387                  {
2388                   mprintf ((0,"WARNING! Recieved invalid size for PID_DUMP\n"));
2389                   return;
2390                  }
2391   
2392                 if (Network_status == NETSTAT_WAITING || Network_status==NETSTAT_PLAYING )
2393                         network_process_dump(their);
2394                 break;
2395         case PID_QUIT_JOINING:
2396
2397            if (length != SEQUENCE_PACKET_SIZE)
2398                  {
2399                   mprintf ((0,"WARNING! Recieved invalid size for PID_QUIT_JOINING\n"));
2400                   return;
2401                  }
2402                 if (Network_status == NETSTAT_STARTING)
2403                         network_remove_player( their );
2404                 else if ((Network_status == NETSTAT_PLAYING) && (Network_send_objects))
2405                         network_stop_resync( their );
2406                 break;
2407         case PID_SYNC:  
2408
2409                 mprintf ((0,"Got a sync packet! Network_status=%d\n",NETSTAT_WAITING));
2410
2411                 if (Network_status == NETSTAT_WAITING)  {
2412
2413                         if (Network_game_type == IPX_GAME)
2414                                 receive_full_netgame_packet(data, &TempNetInfo);
2415                         else
2416                                 memcpy((ubyte *)&(TempNetInfo), data, sizeof(netgame_info));
2417
2418                         if (TempNetInfo.Security!=Netgame.Security)
2419                          {
2420                                 mprintf ((0,"Bad security on sync packet.\n"));
2421                                 break;
2422                          }
2423
2424                         if (NetSecurityFlag==NETSECURITY_WAIT_FOR_SYNC)
2425                          {
2426                           if (TempNetInfo.Security==TempPlayersInfo->Security)
2427                           {
2428                                 network_read_sync_packet (&TempNetInfo,0);
2429                                 NetSecurityFlag=0;
2430                                 NetSecurityNum=0;
2431                           }     
2432                          }
2433                         else
2434                          {      
2435                         NetSecurityFlag=NETSECURITY_WAIT_FOR_PLAYERS;
2436                         NetSecurityNum=TempNetInfo.Security;
2437
2438                         if ( network_wait_for_playerinfo())
2439                                 network_read_sync_packet((netgame_info *)data,0);
2440  
2441                         NetSecurityFlag=0;
2442                         NetSecurityNum=0;
2443                          }
2444                 }
2445                 break;
2446
2447         case PID_PDATA: 
2448                 if ((Game_mode&GM_NETWORK) && ((Network_status == NETSTAT_PLAYING)||(Network_status == NETSTAT_ENDLEVEL) || Network_status==NETSTAT_WAITING)) { 
2449                         network_process_pdata((char *)data);
2450                 }
2451                 break;
2452    case PID_NAKED_PDATA:
2453                 if ((Game_mode&GM_NETWORK) && ((Network_status == NETSTAT_PLAYING)||(Network_status == NETSTAT_ENDLEVEL) || Network_status==NETSTAT_WAITING)) 
2454                         network_process_naked_pdata((char *)data,length);
2455            break;
2456
2457         case PID_OBJECT_DATA:
2458                 if (Network_status == NETSTAT_WAITING) 
2459                         network_read_object_packet(data);
2460                 break;
2461         case PID_ENDLEVEL:
2462                 if ((Network_status == NETSTAT_ENDLEVEL) || (Network_status == NETSTAT_PLAYING))
2463                         network_read_endlevel_packet(data);
2464                 else
2465                         mprintf((0, "Junked endlevel packet.\n"));
2466                 break;
2467         case PID_ENDLEVEL_SHORT:
2468                 if ((Network_status == NETSTAT_ENDLEVEL) || (Network_status == NETSTAT_PLAYING))
2469                         network_read_endlevel_short_packet(data);
2470                 else
2471                         mprintf((0, "Junked short endlevel packet!\n"));
2472                 break;
2473
2474         case PID_GAME_UPDATE:
2475                 mprintf ((0,"Got a GAME_UPDATE!\n"));
2476                         
2477                 if (Network_status==NETSTAT_PLAYING)
2478                  {
2479                         if (Network_game_type == IPX_GAME)
2480                                 receive_lite_netgame_packet(data, &TempNetInfo);
2481                         else
2482                                 memcpy((ubyte *)&TempNetInfo, data, sizeof(lite_info) );
2483                   if (TempNetInfo.Security==Netgame.Security)
2484                          memcpy (&Netgame,(ubyte *)&TempNetInfo,sizeof(lite_info));
2485                  }
2486                 if (Game_mode & GM_TEAM)
2487                  {
2488                         int i;
2489
2490                         for (i=0;i<N_players;i++)
2491                          if (Players[i].connected)
2492                         multi_reset_object_texture (&Objects[Players[i].objnum]);
2493                     reset_cockpit();
2494                  }
2495            break;
2496    case PID_PING_SEND:
2497                 network_ping (PID_PING_RETURN,data[1]);
2498                 break;
2499    case PID_PING_RETURN:
2500                 network_handle_ping_return(data[1]);  // data[1] is player who told us of their ping time
2501                 break;
2502    case PID_NAMES_RETURN:
2503                 if (Network_status==NETSTAT_BROWSING && NamesInfoSecurity!=-1)
2504                   network_process_names_return (data);
2505                 break;
2506         case PID_GAME_PLAYERS:
2507                 // Someone wants a list of players in this game
2508
2509            if (length != SEQUENCE_PACKET_SIZE)
2510                  {
2511                   mprintf ((0,"WARNING! Recieved invalid size for PID_GAME_PLAYERS\n"));
2512                   return;
2513                  }
2514                         
2515                 mprintf((0, "Got a PID_GAME_PLAYERS!\n"));
2516                 if ((Network_status == NETSTAT_PLAYING) || (Network_status == NETSTAT_STARTING) || (Network_status == NETSTAT_ENDLEVEL))
2517                         if (network_i_am_master() && their->Security==Netgame.Security)
2518                                 network_send_player_names(their);
2519                 break;
2520
2521         default:
2522                 mprintf((0, "Ignoring invalid packet type.\n"));
2523                 Int3(); // Invalid network packet type, see ROB
2524            break;
2525         }
2526 }
2527
2528 #ifndef NDEBUG
2529 void dump_segments()
2530 {
2531         FILE * fp;
2532
2533         fp = fopen( "TEST.DMP", "wb" );
2534         fwrite( Segments, sizeof(segment)*(Highest_segment_index+1),1, fp );    
2535         fclose(fp);
2536         mprintf( (0, "SS=%d\n", sizeof(segment) ));
2537 }
2538 #endif
2539
2540
2541 void
2542 network_read_endlevel_packet( ubyte *data )
2543 {
2544         // Special packet for end of level syncing
2545         int playernum;
2546         endlevel_info *end = (endlevel_info *)data;
2547 #ifdef WORDS_BIGENDIAN
2548         int i, j;
2549
2550         for (i = 0; i < MAX_PLAYERS; i++)
2551                 for (j = 0; j < MAX_PLAYERS; j++)
2552                         end->kill_matrix[i][j] = INTEL_SHORT(end->kill_matrix[i][j]);
2553         end->kills = INTEL_SHORT(end->kills);
2554         end->killed = INTEL_SHORT(end->killed);
2555 #endif
2556
2557         playernum = end->player_num;
2558         
2559         Assert(playernum != Player_num);
2560     
2561         if (playernum>=N_players)
2562          {              
2563                 Int3(); // weird, but it an happen in a coop restore game
2564                 return; // if it happens in a coop restore, don't worry about it
2565          }
2566
2567         if ((Network_status == NETSTAT_PLAYING) && (end->connected != 0))
2568                 return; // Only accept disconnect packets if we're not out of the level yet
2569
2570         Players[playernum].connected = end->connected;
2571         memcpy(&kill_matrix[playernum][0], end->kill_matrix, MAX_PLAYERS*sizeof(short));
2572         Players[playernum].net_kills_total = end->kills;
2573         Players[playernum].net_killed_total = end->killed;
2574
2575         if ((Players[playernum].connected == 1) && (end->seconds_left < Countdown_seconds_left))
2576                 Countdown_seconds_left = end->seconds_left;
2577
2578         LastPacketTime[playernum] = timer_get_approx_seconds();
2579
2580 //      mprintf((0, "Got endlevel packet from player %d.\n", playernum));
2581 }
2582
2583 void
2584 network_read_endlevel_short_packet( ubyte *data )
2585 {
2586         // Special packet for end of level syncing
2587
2588         int playernum;
2589         endlevel_info_short *end;       
2590
2591         end = (endlevel_info_short *)data;
2592
2593         playernum = end->player_num;
2594         
2595         Assert(playernum != Player_num);
2596     
2597         if (playernum>=N_players)
2598          {              
2599                 Int3(); // weird, but it can happen in a coop restore game
2600                 return; // if it happens in a coop restore, don't worry about it
2601          }
2602
2603         if ((Network_status == NETSTAT_PLAYING) && (end->connected != 0))
2604          {
2605                 //mprintf ((0,"Returning early for short_endlevel\n"));
2606                 return; // Only accept disconnect packets if we're not out of the level yet
2607          }
2608
2609         Players[playernum].connected = end->connected;
2610
2611         if ((Players[playernum].connected == 1) && (end->seconds_left < Countdown_seconds_left))
2612                 Countdown_seconds_left = end->seconds_left;
2613
2614         LastPacketTime[playernum] = timer_get_approx_seconds();
2615 }
2616
2617
2618 void
2619 network_pack_objects(void)
2620 {
2621         // Switching modes, pack the object array
2622
2623         special_reset_objects();
2624 }                               
2625
2626 int
2627 network_verify_objects(int remote, int local)
2628 {
2629         int i;
2630         int nplayers, got_controlcen=0;
2631
2632    mprintf ((0,"NETWORK:remote=%d local=%d\n",remote,local));
2633
2634         if ((remote-local) > 10)
2635                 return(-1);
2636
2637         if (Game_mode & GM_MULTI_ROBOTS)
2638                 got_controlcen = 1;
2639
2640         nplayers = 0;
2641
2642         for (i = 0; i <= Highest_object_index; i++)
2643         {
2644                 if ((Objects[i].type == OBJ_PLAYER) || (Objects[i].type == OBJ_GHOST))
2645                         nplayers++;
2646                 if (Objects[i].type == OBJ_CNTRLCEN)
2647                         got_controlcen=1;
2648         }
2649
2650     if (got_controlcen && (MaxNumNetPlayers<=nplayers))
2651                 return(0);
2652
2653         return(1);
2654 }
2655
2656 void
2657 network_read_object_packet( ubyte *data )
2658 {
2659         // Object from another net player we need to sync with
2660
2661         short objnum, remote_objnum;
2662         byte obj_owner;
2663         int segnum, i;
2664         object *obj;
2665
2666         static int my_pnum = 0;
2667         static int mode = 0;
2668         static int object_count = 0;
2669         static int frame_num = 0;
2670         int nobj = data[1];
2671         int loc = 3;
2672         int remote_frame_num = data[2];
2673         
2674         frame_num++;
2675
2676 //      mprintf((0, "Object packet %d (remote #%d) contains %d objects.\n", frame_num, remote_frame_num, nobj));
2677
2678         for (i = 0; i < nobj; i++)
2679         {
2680                 objnum = INTEL_SHORT( *(short *)(data+loc) );                   loc += 2;
2681                 obj_owner = data[loc];                                          loc += 1;
2682                 remote_objnum = INTEL_SHORT( *(short *)(data+loc) );            loc += 2;
2683
2684                 if (objnum == -1) 
2685                 {
2686                         // Clear object array
2687                         mprintf((0, "Clearing object array.\n"));
2688
2689                         init_objects();
2690                         Network_rejoined = 1;
2691                         my_pnum = obj_owner;
2692                         change_playernum_to(my_pnum);
2693                         mode = 1;
2694                         object_count = 0;
2695                         frame_num = 1;
2696                 }
2697                 else if (objnum == -2)
2698                 {
2699                         // Special debug checksum marker for entire send
2700                         if (mode == 1)
2701                         {
2702                                 network_pack_objects();
2703                                 mode = 0;
2704                         }
2705                         mprintf((0, "Objnum -2 found in frame local %d remote %d.\n", frame_num, remote_frame_num));
2706                         mprintf((0, "Got %d objects, expected %d.\n", object_count, remote_objnum));
2707                         if (remote_objnum != object_count) {
2708                                 Int3();
2709                         }
2710                         if (network_verify_objects(remote_objnum, object_count))
2711                         {
2712                                 // Failed to sync up 
2713                                 nm_messagebox(NULL, 1, TXT_OK, TXT_NET_SYNC_FAILED);
2714                                 Network_status = NETSTAT_MENU;                          
2715                                 return;
2716                         }
2717                         frame_num = 0;
2718                 }
2719                 else 
2720                 {
2721                         if (frame_num != remote_frame_num)
2722                                 Int3();
2723                         mprintf ((0,"Got a type 3 object packet!\n"));
2724                         object_count++;
2725                         if ((obj_owner == my_pnum) || (obj_owner == -1)) 
2726                         {
2727                                 if (mode != 1)
2728                                         Int3(); // SEE ROB
2729                                 objnum = remote_objnum;
2730                                 //if (objnum > Highest_object_index)
2731                                 //{
2732                                 //      Highest_object_index = objnum;
2733                                 //      num_objects = Highest_object_index+1;
2734                                 //}
2735                         }
2736                         else {
2737                                 if (mode == 1)
2738                                 {
2739                                         network_pack_objects();
2740                                         mode = 0;
2741                                 }
2742                                 objnum = obj_allocate();
2743                         }
2744                         if (objnum != -1) {
2745                                 obj = &Objects[objnum];
2746                                 if (obj->segnum != -1)
2747                                         obj_unlink(objnum);
2748                                 Assert(obj->segnum == -1);
2749                                 Assert(objnum < MAX_OBJECTS);
2750                                 memcpy(obj, &data[loc], sizeof(object));
2751                                 if (Network_game_type == IPX_GAME)
2752                                         swap_object(obj);
2753                                 loc += sizeof(object);
2754                                 segnum = obj->segnum;
2755                                 obj->next = obj->prev = obj->segnum = -1;
2756                                 obj->attached_obj = -1;
2757                                 if (segnum > -1)
2758                                         obj_link(obj-Objects,segnum);
2759                                 if (obj_owner == my_pnum) 
2760                                         map_objnum_local_to_local(objnum);
2761                                 else if (obj_owner != -1)
2762                                         map_objnum_local_to_remote(objnum, remote_objnum, obj_owner);
2763                                 else
2764                                         object_owner[objnum] = -1;
2765                         }
2766                 } // For a standard onbject
2767         } // For each object in packet
2768 }
2769         
2770 /* Polling loop waiting for sync packet to start game
2771  * after having sent request
2772  */
2773 void network_sync_poll( int nitems, newmenu_item * menus, int * key, int citem )
2774 {
2775
2776         static fix t1 = 0;
2777
2778         menus = menus;
2779         citem = citem;
2780         nitems = nitems;
2781
2782         network_listen();
2783
2784         if (Network_status != NETSTAT_WAITING)  // Status changed to playing, exit the menu
2785                 *key = -2;
2786
2787         if (!Network_rejoined && (timer_get_approx_seconds() > t1+F1_0*2))
2788         {
2789                 int i;
2790
2791                 // Poll time expired, re-send request
2792                 
2793                 t1 = timer_get_approx_seconds();
2794
2795                 mprintf((0, "Re-sending join request.\n"));
2796                 i = network_send_request();
2797                 if (i < 0)
2798                         *key = -2;
2799         }
2800 }
2801
2802 void network_start_poll( int nitems, newmenu_item * menus, int * key, int citem )
2803 {
2804         int i,n,nm;
2805
2806         key=key;
2807         citem=citem;
2808
2809         Assert(Network_status == NETSTAT_STARTING);
2810
2811         if (!menus[0].value) {
2812                         menus[0].value = 1;
2813                         menus[0].redraw = 1;
2814         }
2815
2816         for (i=1; i<nitems; i++ )       {
2817                 if ( (i>= N_players) && (menus[i].value) )      {
2818                         menus[i].value = 0;
2819                         menus[i].redraw = 1;
2820                 }
2821         }
2822
2823         nm = 0;
2824         for (i=0; i<nitems; i++ )       {
2825                 if ( menus[i].value )   {
2826                         nm++;
2827                         if ( nm > N_players )   {
2828                                 menus[i].value = 0;
2829                                 menus[i].redraw = 1;
2830                         }
2831                 }
2832         }
2833
2834         if ( nm > MaxNumNetPlayers )    {
2835                 nm_messagebox( TXT_ERROR, 1, TXT_OK, "%s %d %s", TXT_SORRY_ONLY, MaxNumNetPlayers, TXT_NETPLAYERS_IN );
2836                 // Turn off the last player highlighted
2837                 for (i = N_players; i > 0; i--)
2838                         if (menus[i].value == 1) 
2839                         {
2840                                 menus[i].value = 0;
2841                                 menus[i].redraw = 1;
2842                                 break;
2843                         }
2844         }
2845
2846 //       if (nitems > MAX_PLAYERS ) return; 
2847         
2848         n = Netgame.numplayers;
2849         network_listen();
2850
2851         if (n < Netgame.numplayers )    
2852         {
2853                 digi_play_sample (SOUND_HUD_MESSAGE,F1_0);
2854
2855       mprintf ((0,"More players are printed!"));
2856                 if (FindArg("-norankings"))
2857               sprintf( menus[N_players-1].text, "%d. %-20s", N_players,NetPlayers.players[N_players-1].callsign );
2858                 else
2859               sprintf( menus[N_players-1].text, "%d. %s%-20s", N_players, RankStrings[NetPlayers.players[N_players-1].rank],NetPlayers.players[N_players-1].callsign );
2860
2861                 menus[N_players-1].redraw = 1;
2862                 if (N_players <= MaxNumNetPlayers)
2863                 {
2864                         menus[N_players-1].value = 1;
2865                 }
2866         } 
2867         else if ( n > Netgame.numplayers )      
2868         {
2869                 // One got removed...
2870
2871       digi_play_sample (SOUND_HUD_KILL,F1_0);
2872   
2873                 for (i=0; i<N_players; i++ )    
2874                 {
2875          
2876          if (FindArg("-norankings"))    
2877                  sprintf( menus[i].text, "%d. %-20s", i+1, NetPlayers.players[i].callsign );
2878          else
2879                  sprintf( menus[i].text, "%d. %s%-20s", i+1, RankStrings[NetPlayers.players[i].rank],NetPlayers.players[i].callsign );
2880                         if (i < MaxNumNetPlayers)
2881                                 menus[i].value = 1;
2882                         else
2883                                 menus[i].value = 0;
2884                         menus[i].redraw = 1;
2885                 }
2886                 for (i=N_players; i<n; i++ )    
2887                 {
2888                         sprintf( menus[i].text, "%d. ", i+1 );          // Clear out the deleted entries...
2889                         menus[i].value = 0;
2890                         menus[i].redraw = 1;
2891                 }
2892    }
2893 }
2894
2895 int opt_cinvul, opt_team_anarchy, opt_coop, opt_show_on_map, opt_closed,opt_maxnet;
2896 int last_cinvul=0,last_maxnet,opt_team_hoard;
2897 int opt_refuse,opt_capture;
2898
2899 void network_game_param_poll( int nitems, newmenu_item * menus, int * key, int citem )
2900 {
2901         static int oldmaxnet=0;
2902
2903         if (((HoardEquipped() && menus[opt_team_hoard].value) || (menus[opt_team_anarchy].value || menus[opt_capture].value)) && !menus[opt_closed].value && !menus[opt_refuse].value) { 
2904                 menus[opt_refuse].value = 1;
2905                 menus[opt_refuse].redraw = 1;
2906                 menus[opt_refuse-1].value = 0;
2907                 menus[opt_refuse-1].redraw = 1;
2908                 menus[opt_refuse-2].value = 0;
2909                 menus[opt_refuse-2].redraw = 1;
2910         }
2911
2912         #ifndef MACINTOSH
2913         if (menus[opt_coop].value)
2914         #else
2915         if ( (menus[opt_coop].value) || ((Network_game_type == APPLETALK_GAME) && (Network_appletalk_type == LOCALTALK_TYPE) && (menus[opt_coop-1].value)) )
2916         #endif
2917         {
2918                 oldmaxnet=1;
2919
2920                 if (menus[opt_maxnet].value>2) 
2921                 {
2922                     menus[opt_maxnet].value=2;
2923                     menus[opt_maxnet].redraw=1;
2924                 }
2925
2926                 if (menus[opt_maxnet].max_value>2)
2927                 {
2928                     menus[opt_maxnet].max_value=2;
2929                     menus[opt_maxnet].redraw=1;
2930                 }
2931
2932                 #ifdef MACINTOSH
2933                 if ( (Network_game_type == APPLETALK_GAME) && (Network_appletalk_type == LOCALTALK_TYPE) ) {
2934                         if (menus[opt_maxnet].value > 0) {
2935                             menus[opt_maxnet].value=0;
2936                             menus[opt_maxnet].redraw=1;
2937                         }
2938                         if (menus[opt_maxnet].max_value > 0) {
2939                                 menus[opt_maxnet].max_value=0;
2940                                 menus[opt_maxnet].redraw=1;
2941                         }
2942                 }
2943                 #endif
2944
2945              if (!Netgame.game_flags & NETGAME_FLAG_SHOW_MAP) 
2946                         Netgame.game_flags |= NETGAME_FLAG_SHOW_MAP;
2947
2948                 if (Netgame.PlayTimeAllowed || Netgame.KillGoal)
2949                 {
2950                     Netgame.PlayTimeAllowed=0;
2951                     Netgame.KillGoal=0;
2952                 }
2953
2954         }
2955         else // if !Coop game
2956     {
2957                 if (oldmaxnet)
2958                 {
2959                     oldmaxnet=0;
2960                         menus[opt_maxnet].value=6;
2961                         menus[opt_maxnet].max_value=6;
2962
2963                     #ifdef MACINTOSH
2964                         if ( (Network_game_type == APPLETALK_GAME) && (Network_appletalk_type == LOCALTALK_TYPE) ) {
2965                                 menus[opt_maxnet].value=1;
2966                                 menus[opt_maxnet].max_value=1;
2967                         }
2968                         #endif
2969                           
2970                 }
2971         }         
2972
2973     if ( last_maxnet != menus[opt_maxnet].value )   
2974         {
2975                 sprintf( menus[opt_maxnet].text, "Maximum players: %d", menus[opt_maxnet].value+2 );
2976                 last_maxnet = menus[opt_maxnet].value;
2977                 menus[opt_maxnet].redraw = 1;
2978         }               
2979  }
2980
2981 int netgame_difficulty;
2982
2983 int network_get_game_params( char * game_name, int *mode, int *game_flags, int *level )
2984 {
2985         int i;
2986         int opt, opt_name, opt_level, opt_mode,opt_moreopts;
2987         newmenu_item m[20];
2988         char name[NETGAME_NAME_LEN+1];
2989         char slevel[5];
2990         char level_text[32];
2991         char srmaxnet[50];
2992
2993         int new_mission_num;
2994         int anarchy_only;
2995
2996         *game_flags=*game_flags;
2997
2998         SetAllAllowablesTo (1);
2999         NamesInfoSecurity=-1;
3000
3001         for (i=0;i<MAX_PLAYERS;i++)
3002                 if (i!=Player_num)
3003                         Players[i].callsign[0]=0;
3004
3005         MaxNumNetPlayers=8;
3006         Netgame.KillGoal=0;
3007         Netgame.PlayTimeAllowed=0;
3008         Netgame.Allow_marker_view=1;
3009         netgame_difficulty=Player_default_difficulty;
3010
3011         new_mission_num = multi_choose_mission(&anarchy_only);
3012
3013         if (new_mission_num < 0)
3014                 return -1;
3015
3016         if (!(FindArg ("-packets") && FindArg ("-shortpackets")))
3017                 if (!network_choose_connect ())
3018                         return -1;
3019
3020 #ifdef MACINTOSH
3021         if ( (Network_game_type == APPLETALK_GAME) && (Network_appletalk_type == LOCALTALK_TYPE) )
3022                 MaxNumNetPlayers = 3;
3023 #endif
3024
3025         strcpy(Netgame.mission_name, Mission_list[new_mission_num].filename);
3026         strcpy(Netgame.mission_title, Mission_list[new_mission_num].mission_name);
3027         Netgame.control_invul_time = control_invul_time;
3028
3029         sprintf( name, "%s%s", Players[Player_num].callsign, TXT_S_GAME );
3030         sprintf( slevel, "1" );
3031
3032         opt = 0;
3033         m[opt].type = NM_TYPE_TEXT; m[opt].text = TXT_DESCRIPTION; opt++;
3034
3035         opt_name = opt;
3036         m[opt].type = NM_TYPE_INPUT; m[opt].text = name; m[opt].text_len = NETGAME_NAME_LEN; opt++;
3037
3038         sprintf(level_text, "%s (1-%d)", TXT_LEVEL_, Last_level);
3039
3040 //      if (Last_secret_level < -1)
3041 //              sprintf(level_text+strlen(level_text)-1, ", S1-S%d)", -Last_secret_level);
3042 //      else if (Last_secret_level == -1)
3043 //              sprintf(level_text+strlen(level_text)-1, ", S1)");
3044
3045         Assert(strlen(level_text) < 32);
3046
3047         m[opt].type = NM_TYPE_TEXT; m[opt].text = level_text; opt++;
3048
3049         opt_level = opt;
3050         m[opt].type = NM_TYPE_INPUT; m[opt].text = slevel; m[opt].text_len=4; opt++;
3051 //      m[opt].type = NM_TYPE_TEXT; m[opt].text = TXT_OPTIONS; opt++;
3052         
3053         opt_mode = opt;
3054         m[opt].type = NM_TYPE_RADIO; m[opt].text = TXT_ANARCHY; m[opt].value=1; m[opt].group=0; opt++;
3055         m[opt].type = NM_TYPE_RADIO; m[opt].text = TXT_TEAM_ANARCHY; m[opt].value=0; m[opt].group=0; opt_team_anarchy=opt; opt++;
3056         m[opt].type = NM_TYPE_RADIO; m[opt].text = TXT_ANARCHY_W_ROBOTS; m[opt].value=0; m[opt].group=0; opt++;
3057         m[opt].type = NM_TYPE_RADIO; m[opt].text = TXT_COOPERATIVE; m[opt].value=0; m[opt].group=0; opt_coop=opt; opt++;
3058         m[opt].type = NM_TYPE_RADIO; m[opt].text = "Capture the flag"; m[opt].value=0; m[opt].group=0; opt_capture=opt; opt++;
3059    
3060         if (HoardEquipped())
3061         {
3062                 m[opt].type = NM_TYPE_RADIO; m[opt].text = "Hoard"; m[opt].value=0; m[opt].group=0; opt++;
3063                 m[opt].type = NM_TYPE_RADIO; m[opt].text = "Team Hoard"; m[opt].value=0; m[opt].group=0; opt_team_hoard=opt; opt++;
3064            m[opt].type = NM_TYPE_TEXT; m[opt].text = ""; opt++;
3065         } 
3066         else
3067          {  m[opt].type = NM_TYPE_TEXT; m[opt].text = ""; opt++; }
3068
3069         m[opt].type = NM_TYPE_RADIO; m[opt].text = "Open game"; m[opt].group=1; m[opt].value=0; opt++;
3070         opt_closed = opt;
3071         m[opt].type = NM_TYPE_RADIO; m[opt].text = TXT_CLOSED_GAME; m[opt].group=1; m[opt].value=0; opt++;
3072    opt_refuse = opt;
3073    m[opt].type = NM_TYPE_RADIO; m[opt].text = "Restricted Game              "; m[opt].group=1; m[opt].value=Netgame.RefusePlayers; opt++;
3074
3075 //      m[opt].type = NM_TYPE_CHECK; m[opt].text = TXT_SHOW_IDS; m[opt].value=0; opt++;
3076
3077    opt_maxnet = opt;
3078    sprintf( srmaxnet, "Maximum players: %d", MaxNumNetPlayers);
3079    m[opt].type = NM_TYPE_SLIDER; m[opt].value=MaxNumNetPlayers-2; m[opt].text= srmaxnet; m[opt].min_value=0; 
3080    m[opt].max_value=MaxNumNetPlayers-2; opt++;
3081    last_maxnet=MaxNumNetPlayers-2;
3082
3083    opt_moreopts=opt;
3084    m[opt].type = NM_TYPE_MENU;  m[opt].text = "More options..."; opt++;
3085
3086         Assert(opt <= 20);
3087
3088 menu:
3089    ExtGameStatus=GAMESTAT_NETGAME_OPTIONS; 
3090         i = newmenu_do1( NULL, NULL, opt, m, network_game_param_poll, 1 );
3091                                                                         //TXT_NETGAME_SETUP
3092    if (i==opt_moreopts)
3093     {
3094      if ( m[opt_mode+3].value )
3095       Game_mode=GM_MULTI_COOP;
3096      network_more_game_options();
3097      Game_mode=0;
3098      goto menu;
3099     }
3100   Netgame.RefusePlayers=m[opt_refuse].value;
3101
3102
3103         if ( i > -1 )   {
3104                 int j;
3105                       
3106    MaxNumNetPlayers = m[opt_maxnet].value+2;
3107    Netgame.max_numplayers=MaxNumNetPlayers;
3108                                 
3109                 for (j = 0; j < num_active_games; j++)
3110                         if (!stricmp(Active_games[j].game_name, name))
3111                         {
3112                                 nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_DUPLICATE_NAME);
3113                                 goto menu;
3114                         }
3115
3116                 strcpy( game_name, name );
3117                 
3118
3119                 *level = atoi(slevel);
3120
3121                 if ((*level < 1) || (*level > Last_level))
3122                 {
3123                         nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_LEVEL_OUT_RANGE );
3124                         sprintf(slevel, "1");
3125                         goto menu;
3126                 }
3127                 if ( m[opt_mode].value )
3128                         *mode = NETGAME_ANARCHY;
3129
3130 #ifdef SHAREWARE
3131                 else 
3132                 {
3133                         nm_messagebox(TXT_SORRY, 1, TXT_OK, TXT_REGISTERED_ONLY );
3134                         m[opt_mode+1].value = 0;
3135                         m[opt_mode+2].value = 0;
3136                         m[opt_mode+3].value = 0;
3137                    if (HoardEquipped())
3138                                 m[opt_mode+4].value = 0;
3139
3140                         m[opt_mode].value = 1;
3141                         goto menu;
3142                 }
3143 #else
3144                 else if (m[opt_mode+1].value) {
3145                         *mode = NETGAME_TEAM_ANARCHY;
3146                 }
3147                 else if (m[opt_capture].value)
3148                         *mode = NETGAME_CAPTURE_FLAG;
3149                 else if (HoardEquipped() && m[opt_capture+1].value)
3150                                 *mode = NETGAME_HOARD;
3151                 else if (HoardEquipped() && m[opt_capture+2].value)
3152                                 *mode = NETGAME_TEAM_HOARD;
3153                 else if (anarchy_only) {
3154                         nm_messagebox(NULL, 1, TXT_OK, TXT_ANARCHY_ONLY_MISSION);
3155                         m[opt_mode+2].value = 0;
3156                         m[opt_mode+3].value = 0;
3157                         m[opt_mode].value = 1;
3158                         goto menu;
3159                 }               
3160                 else if ( m[opt_mode+2].value ) 
3161                         *mode = NETGAME_ROBOT_ANARCHY;
3162                 else if ( m[opt_mode+3].value ) 
3163                         *mode = NETGAME_COOPERATIVE;
3164                 else Int3(); // Invalid mode -- see Rob
3165 #endif
3166                 if (m[opt_closed].value)
3167                         Netgame.game_flags |= NETGAME_FLAG_CLOSED;
3168       
3169         }
3170
3171         return i;
3172 }
3173
3174 void
3175 network_set_game_mode(int gamemode)
3176 {
3177         Show_kill_list = 1;
3178
3179         if ( gamemode == NETGAME_ANARCHY )
3180                 Game_mode = GM_NETWORK;
3181         else if ( gamemode == NETGAME_ROBOT_ANARCHY )
3182                 Game_mode = GM_NETWORK | GM_MULTI_ROBOTS;
3183         else if ( gamemode == NETGAME_COOPERATIVE ) 
3184                 Game_mode = GM_NETWORK | GM_MULTI_COOP | GM_MULTI_ROBOTS;
3185         else if (gamemode == NETGAME_CAPTURE_FLAG)
3186                 {
3187                  Game_mode = GM_NETWORK | GM_TEAM | GM_CAPTURE;
3188                  Show_kill_list=3;
3189                 }
3190
3191         else if (HoardEquipped() && gamemode == NETGAME_HOARD)
3192                  Game_mode = GM_NETWORK | GM_HOARD;
3193         else if (HoardEquipped() && gamemode == NETGAME_TEAM_HOARD)
3194                  {
3195                   Game_mode = GM_NETWORK | GM_HOARD | GM_TEAM;
3196                   Show_kill_list=3;
3197                  }
3198         else if ( gamemode == NETGAME_TEAM_ANARCHY )
3199         {
3200                 Game_mode = GM_NETWORK | GM_TEAM;
3201                 Show_kill_list = 3;
3202         }
3203         else
3204                 Int3();
3205
3206 #if 0
3207 #ifdef MACINTOSH                        // minimize players on localtalk games
3208         if ( (Network_game_type == APPLETALK_GAME) && (Network_appletalk_type == LOCALTALK_TYPE) ) {
3209                 if (Game_mode & GM_MULTI_ROBOTS)
3210                         MaxNumNetPlayers = 2;
3211                 else
3212                         MaxNumNetPlayers = 3;
3213         } else {
3214                 if (Game_mode & GM_MULTI_COOP)
3215                         MaxNumNetPlayers = 4;
3216                 else
3217                         MaxNumNetPlayers = 8;
3218         }
3219 #endif
3220 #endif
3221
3222 }
3223
3224 int
3225 network_find_game(void)
3226 {
3227         // Find out whether or not there is space left on this socket
3228
3229         fix t1;
3230
3231         Network_status = NETSTAT_BROWSING;
3232
3233         num_active_games = 0;
3234
3235         show_boxed_message(TXT_WAIT);
3236
3237         network_send_game_list_request();
3238         t1 = timer_get_approx_seconds() + F1_0*3;
3239
3240         while (timer_get_approx_seconds() < t1) // Wait 3 seconds for replies
3241                 network_listen();
3242
3243         clear_boxed_message();
3244
3245 //      mprintf((0, "%s %d %s\n", TXT_FOUND, num_active_games, TXT_ACTIVE_GAMES));
3246
3247         if (num_active_games < MAX_ACTIVE_NETGAMES)
3248                 return 0;
3249         return 1;
3250 }
3251
3252 void network_read_sync_packet( netgame_info * sp, int rsinit)
3253 {
3254         int i, j;
3255         char temp_callsign[CALLSIGN_LEN+1];
3256 #ifdef WORDS_BIGENDIAN
3257         netgame_info tmp_info;
3258
3259         if ( (Network_game_type == IPX_GAME) && (sp != &Netgame) ) {                            // for macintosh -- get the values unpacked to our structure format
3260                 receive_full_netgame_packet((ubyte *)sp, &tmp_info);
3261                 sp = &tmp_info;
3262         }
3263 #endif
3264
3265    if (rsinit)
3266      TempPlayersInfo=&NetPlayers;
3267         
3268         // This function is now called by all people entering the netgame.
3269
3270         // mprintf( (0, "%s %d\n", TXT_STARTING_NETGAME, sp->levelnum ));
3271
3272         if (sp != &Netgame)
3273          {
3274           memcpy( &Netgame, sp, sizeof(netgame_info) );
3275           memcpy (&NetPlayers,TempPlayersInfo,sizeof (AllNetPlayers_info));
3276          }
3277
3278         N_players = sp->numplayers;
3279         Difficulty_level = sp->difficulty;
3280         Network_status = sp->game_status;
3281
3282    //Assert(Function_mode != FMODE_GAME);
3283
3284         // New code, 11/27
3285
3286         mprintf((1, "Netgame.checksum = %d, calculated checksum = %d.\n", Netgame.segments_checksum, my_segments_checksum));
3287
3288         if (Netgame.segments_checksum != my_segments_checksum)
3289         {
3290                 Network_status = NETSTAT_MENU;
3291                 nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_NETLEVEL_NMATCH);
3292 #ifdef RELEASE
3293                 return;
3294 #endif
3295         }
3296
3297         // Discover my player number
3298
3299         memcpy(temp_callsign, Players[Player_num].callsign, CALLSIGN_LEN+1);
3300         
3301         Player_num = -1;
3302
3303         for (i=0; i<MAX_NUM_NET_PLAYERS; i++ )  {
3304                 Players[i].net_kills_total = 0;
3305 //              Players[i].net_killed_total = 0;
3306         }
3307
3308         for (i=0; i<N_players; i++ )    {
3309                 if (Network_game_type == IPX_GAME) {
3310                         if ( (!memcmp( TempPlayersInfo->players[i].network.ipx.node, My_Seq.player.network.ipx.node, 6 )) && (!stricmp( TempPlayersInfo->players[i].callsign, temp_callsign)) ) {
3311                                 if (Player_num!=-1) {
3312                                         Int3(); // Hey, we've found ourselves twice
3313                                         mprintf ((0,"Hey, we've found ourselves twice!\n"));
3314                                         Network_status = NETSTAT_MENU;
3315                                         return; 
3316                                 }
3317                                 change_playernum_to(i);
3318                         }
3319                 } else {
3320                         if ( (TempPlayersInfo->players[i].network.appletalk.node == My_Seq.player.network.appletalk.node) && (!stricmp( TempPlayersInfo->players[i].callsign, temp_callsign)) ) {
3321                                 if (Player_num!=-1) {
3322                                         Int3(); // Hey, we've found ourselves twice
3323                                         Network_status = NETSTAT_MENU;
3324                                         return; 
3325                                 }
3326                                 change_playernum_to(i);
3327                         }
3328                 }
3329                 memcpy( Players[i].callsign, TempPlayersInfo->players[i].callsign, CALLSIGN_LEN+1 );
3330
3331                 if (Network_game_type == IPX_GAME) {
3332                         if ( (*(uint *)TempPlayersInfo->players[i].network.ipx.server) != 0 )
3333                                 ipx_get_local_target( TempPlayersInfo->players[i].network.ipx.server, TempPlayersInfo->players[i].network.ipx.node, Players[i].net_address );
3334                         else
3335                                 memcpy( Players[i].net_address, TempPlayersInfo->players[i].network.ipx.node, 6 );
3336                 }
3337                                 
3338                 Players[i].n_packets_got=0;                             // How many packets we got from them
3339                 Players[i].n_packets_sent=0;                            // How many packets we sent to them
3340                 Players[i].connected = TempPlayersInfo->players[i].connected;
3341                 Players[i].net_kills_total = sp->player_kills[i];
3342                 Players[i].net_killed_total = sp->killed[i];
3343                 if ((Network_rejoined) || (i != Player_num))
3344                         Players[i].score = sp->player_score[i];
3345                 for (j = 0; j < MAX_NUM_NET_PLAYERS; j++)
3346                 {
3347                         kill_matrix[i][j] = sp->kills[i][j];
3348                 }
3349         }
3350
3351         if ( Player_num < 0 )   {
3352                 mprintf ((0,"Bad Player_num, resetting to NETSTAT_MENU!\n"));
3353                 Network_status = NETSTAT_MENU;
3354                 return;
3355         }
3356
3357         if (Network_rejoined) 
3358                 for (i=0; i<N_players;i++)
3359                         Players[i].net_killed_total = sp->killed[i];
3360
3361         if (Network_rejoined) {
3362                 network_process_monitor_vector(sp->monitor_vector);
3363                 Players[Player_num].time_level = sp->level_time;
3364         }
3365
3366         team_kills[0] = sp->team_kills[0];
3367         team_kills[1] = sp->team_kills[1];
3368         
3369         Players[Player_num].connected = 1;
3370    NetPlayers.players[Player_num].connected = 1;
3371    NetPlayers.players[Player_num].rank=GetMyNetRanking();
3372
3373         if (!Network_rejoined)
3374                 for (i=0; i<NumNetPlayerPositions; i++) {
3375                         Objects[Players[i].objnum].pos = Player_init[Netgame.locations[i]].pos;
3376                         Objects[Players[i].objnum].orient = Player_init[Netgame.locations[i]].orient;
3377                         obj_relink(Players[i].objnum,Player_init[Netgame.locations[i]].segnum);
3378                 }
3379
3380         Objects[Players[Player_num].objnum].type = OBJ_PLAYER;
3381
3382    mprintf ((0,"Changing to NETSTAT_PLAYING!\n"));
3383         Network_status = NETSTAT_PLAYING;
3384         Function_mode = FMODE_GAME;
3385         multi_sort_kill_list();
3386
3387 }
3388
3389 void
3390 network_send_sync(void)
3391 {
3392         int i, j, np;
3393
3394         // Randomize their starting locations...
3395
3396         d_srand( timer_get_fixed_seconds() );
3397         for (i=0; i<NumNetPlayerPositions; i++ )        
3398         {
3399                 if (Players[i].connected)
3400                         Players[i].connected = 1; // Get rid of endlevel connect statuses
3401                 if (Game_mode & GM_MULTI_COOP)
3402                         Netgame.locations[i] = i;
3403                 else {
3404                         do 
3405                         {
3406                                 np = d_rand() % NumNetPlayerPositions;
3407                                 for (j=0; j<i; j++ )    
3408                                 {
3409                                         if (Netgame.locations[j]==np)   
3410                                         {
3411                                                 np =-1;
3412                                                 break;
3413                                         }
3414                                 }
3415                         } while (np<0);
3416                         // np is a location that is not used anywhere else..
3417                         Netgame.locations[i]=np;
3418 //                      mprintf((0, "Player %d starting in location %d\n" ,i ,np ));
3419                 }
3420         }
3421
3422         // Push current data into the sync packet
3423
3424         network_update_netgame();
3425         Netgame.game_status = NETSTAT_PLAYING;
3426         Netgame.type = PID_SYNC;
3427         Netgame.segments_checksum = my_segments_checksum;
3428
3429         for (i=0; i<N_players; i++ )    {
3430                 if ((!Players[i].connected) || (i == Player_num))
3431                         continue;
3432
3433                 if (Network_game_type == IPX_GAME) {
3434                 // Send several times, extras will be ignored
3435                         send_internetwork_full_netgame_packet(NetPlayers.players[i].network.ipx.server, NetPlayers.players[i].network.ipx.node);
3436                         send_netplayers_packet(NetPlayers.players[i].network.ipx.server, NetPlayers.players[i].network.ipx.node);
3437                 #ifdef MACINTOSH
3438                 } else {
3439                                 appletalk_send_packet_data( (ubyte *)&Netgame, sizeof(netgame_info), NetPlayers.players[i].network.appletalk.node,
3440                                         NetPlayers.players[i].network.appletalk.net,
3441                                         NetPlayers.players[i].network.appletalk.socket);
3442                                 appletalk_send_packet_data( (ubyte *)&NetPlayers, sizeof(AllNetPlayers_info), NetPlayers.players[i].network.appletalk.node,
3443                                         NetPlayers.players[i].network.appletalk.net,
3444                                         NetPlayers.players[i].network.appletalk.socket);
3445                 #endif
3446                 }
3447
3448         }       
3449         network_read_sync_packet(&Netgame,1); // Read it myself, as if I had sent it
3450 }
3451
3452 int
3453 network_select_teams(void)
3454 {
3455 #ifndef SHAREWARE
3456         newmenu_item m[MAX_PLAYERS+4];
3457         int choice, opt, opt_team_b;
3458         ubyte team_vector = 0;
3459         char team_names[2][CALLSIGN_LEN+1];
3460         int i;
3461         int pnums[MAX_PLAYERS+2];
3462
3463         // One-time initialization
3464
3465         for (i = N_players/2; i < N_players; i++) // Put first half of players on team A
3466         {
3467                 team_vector |= (1 << i);
3468         }
3469
3470         sprintf(team_names[0], "%s", TXT_BLUE);
3471         sprintf(team_names[1], "%s", TXT_RED);
3472
3473         // Here comes da menu
3474 menu:
3475         m[0].type = NM_TYPE_INPUT; m[0].text = team_names[0]; m[0].text_len = CALLSIGN_LEN; 
3476
3477         opt = 1;
3478         for (i = 0; i < N_players; i++)
3479         {
3480                 if (!(team_vector & (1 << i)))
3481                 {
3482                         m[opt].type = NM_TYPE_MENU; m[opt].text = NetPlayers.players[i].callsign; pnums[opt] = i; opt++;
3483                 }
3484         }
3485         opt_team_b = opt;
3486         m[opt].type = NM_TYPE_INPUT; m[opt].text = team_names[1]; m[opt].text_len = CALLSIGN_LEN; opt++;
3487         for (i = 0; i < N_players; i++)
3488         {
3489                 if (team_vector & (1 << i))
3490                 {
3491                         m[opt].type = NM_TYPE_MENU; m[opt].text = NetPlayers.players[i].callsign; pnums[opt] = i; opt++;
3492                 }
3493         }
3494         m[opt].type = NM_TYPE_TEXT; m[opt].text = ""; opt++;
3495         m[opt].type = NM_TYPE_MENU; m[opt].text = TXT_ACCEPT; opt++;
3496
3497         Assert(opt <= MAX_PLAYERS+4);
3498         
3499         choice = newmenu_do(NULL, TXT_TEAM_SELECTION, opt, m, NULL);
3500
3501         if (choice == opt-1)
3502         {
3503                 if ((opt-2-opt_team_b < 2) || (opt_team_b == 1)) 
3504                 {
3505                         nm_messagebox(NULL, 1, TXT_OK, TXT_TEAM_MUST_ONE);
3506                         #ifdef RELEASE
3507                                 goto menu;
3508                         #endif
3509                 }
3510                 
3511                 Netgame.team_vector = team_vector;
3512                 strcpy(Netgame.team_name[0], team_names[0]);
3513                 strcpy(Netgame.team_name[1], team_names[1]);
3514                 return 1;
3515         }
3516
3517         else if ((choice > 0) && (choice < opt_team_b)) {
3518                 team_vector |= (1 << pnums[choice]);
3519         }
3520         else if ((choice > opt_team_b) && (choice < opt-2)) {
3521                 team_vector &= ~(1 << pnums[choice]);
3522         }
3523         else if (choice == -1)
3524                 return 0;
3525         goto menu;
3526 #else
3527         return 0;
3528 #endif
3529 }
3530
3531 int
3532 network_select_players(void)
3533 {
3534         int i, j;
3535    newmenu_item m[MAX_PLAYERS+4];
3536    char text[MAX_PLAYERS+4][45];
3537         char title[50];
3538         int save_nplayers;              //how may people would like to join
3539
3540         network_add_player( &My_Seq );
3541                 
3542         for (i=0; i< MAX_PLAYERS+4; i++ ) {
3543                 sprintf( text[i], "%d.  %-20s", i+1, "" );
3544                 m[i].type = NM_TYPE_CHECK; m[i].text = text[i]; m[i].value = 0;
3545         }
3546
3547         m[0].value = 1;                         // Assume server will play...
3548
3549    if (FindArg("-norankings"))
3550                 sprintf( text[0], "%d. %-20s", 1, Players[Player_num].callsign );
3551         else
3552                 sprintf( text[0], "%d. %s%-20s", 1, RankStrings[NetPlayers.players[Player_num].rank],Players[Player_num].callsign );
3553
3554         sprintf( title, "%s %d %s", TXT_TEAM_SELECT, MaxNumNetPlayers, TXT_TEAM_PRESS_ENTER );
3555
3556 GetPlayersAgain:
3557    ExtGameStatus=GAMESTAT_NETGAME_PLAYER_SELECT;
3558         j=newmenu_do1( NULL, title, MAX_PLAYERS+4, m, network_start_poll, 1 );
3559
3560         save_nplayers = N_players;
3561
3562         if (j<0) 
3563         {
3564                 // Aborted!                                      
3565                 // Dump all players and go back to menu mode
3566
3567 abort:
3568                 for (i=1; i<save_nplayers; i++) {
3569                         if (Network_game_type == IPX_GAME)
3570                                 network_dump_player(NetPlayers.players[i].network.ipx.server,NetPlayers.players[i].network.ipx.node, DUMP_ABORTED);
3571                         #ifdef MACINTOSH
3572                         else
3573                                 network_dump_appletalk_player(NetPlayers.players[i].network.appletalk.node,NetPlayers.players[i].network.appletalk.net, NetPlayers.players[i].network.appletalk.socket, DUMP_ABORTED);
3574                         #endif
3575                 }
3576                         
3577
3578                 Netgame.numplayers = 0;
3579                 network_send_game_info(0); // Tell everyone we're bailing
3580
3581                 Network_status = NETSTAT_MENU;
3582                 return(0);
3583         }
3584        
3585         // Count number of players chosen
3586
3587         N_players = 0;
3588         for (i=0; i<save_nplayers; i++ )
3589         {
3590                 if (m[i].value) 
3591                         N_players++;
3592         }
3593         
3594         if ( N_players > Netgame.max_numplayers) {
3595                 #ifndef MACINTOSH
3596                 nm_messagebox( TXT_ERROR, 1, TXT_OK, "%s %d %s", TXT_SORRY_ONLY, MaxNumNetPlayers, TXT_NETPLAYERS_IN );
3597                 #else
3598                 nm_messagebox( TXT_ERROR, 1, TXT_OK, "%s %d netplayers for this game.", TXT_SORRY_ONLY, MaxNumNetPlayers );
3599                 #endif
3600                 N_players = save_nplayers;
3601                 goto GetPlayersAgain;
3602         }
3603
3604 #ifdef RELEASE
3605         if ( N_players < 2 )    {
3606                 nm_messagebox( TXT_ERROR, 1, TXT_OK, TXT_TEAM_ATLEAST_TWO );
3607                 N_players = save_nplayers;
3608                 goto GetPlayersAgain;
3609         }
3610 #endif
3611
3612 #ifdef RELEASE
3613         if ( (Netgame.gamemode == NETGAME_TEAM_ANARCHY ||
3614                    Netgame.gamemode == NETGAME_CAPTURE_FLAG || Netgame.gamemode == NETGAME_TEAM_HOARD) && (N_players < 2) ) {
3615                 nm_messagebox(TXT_ERROR, 1, TXT_OK, "You must select at least two\nplayers to start a team game" );
3616                 N_players = save_nplayers;
3617                 goto GetPlayersAgain;
3618         }
3619 #endif
3620
3621         // Remove players that aren't marked.
3622         N_players = 0;
3623         for (i=0; i<save_nplayers; i++ )        {
3624                 if (m[i].value) 
3625                 {
3626                         if (i > N_players)
3627                         {
3628                                 if (Network_game_type == IPX_GAME) {
3629                                         memcpy(NetPlayers.players[N_players].network.ipx.node, NetPlayers.players[i].network.ipx.node, 6);
3630                                         memcpy(NetPlayers.players[N_players].network.ipx.server, NetPlayers.players[i].network.ipx.server, 4);
3631                                 } else {
3632                                         NetPlayers.players[N_players].network.appletalk.node = NetPlayers.players[i].network.appletalk.node;
3633                                         NetPlayers.players[N_players].network.appletalk.net = NetPlayers.players[i].network.appletalk.net;
3634                                         NetPlayers.players[N_players].network.appletalk.socket = NetPlayers.players[i].network.appletalk.socket;
3635                                 }
3636                                 memcpy(NetPlayers.players[N_players].callsign, NetPlayers.players[i].callsign, CALLSIGN_LEN+1);
3637                                 NetPlayers.players[N_players].version_major=NetPlayers.players[i].version_major;
3638                                 NetPlayers.players[N_players].version_minor=NetPlayers.players[i].version_minor;
3639                                 NetPlayers.players[N_players].rank=NetPlayers.players[i].rank;
3640                                 ClipRank (&NetPlayers.players[N_players].rank);
3641                                 network_check_for_old_version(i);
3642                         }
3643                         Players[N_players].connected = 1;
3644                         N_players++;
3645                 }
3646                 else
3647                 {
3648                         if (Network_game_type == IPX_GAME)
3649                                 network_dump_player(NetPlayers.players[i].network.ipx.server,NetPlayers.players[i].network.ipx.node, DUMP_DORK);
3650                         #ifdef MACINTOSH
3651                         else
3652                                 network_dump_appletalk_player(NetPlayers.players[i].network.appletalk.node,NetPlayers.players[i].network.appletalk.net, NetPlayers.players[i].network.appletalk.socket, DUMP_DORK);
3653                         #endif
3654                 }
3655         }
3656
3657         for (i = N_players; i < MAX_NUM_NET_PLAYERS; i++) {
3658                 if (Network_game_type == IPX_GAME) {
3659                 memset(NetPlayers.players[i].network.ipx.node, 0, 6);
3660                 memset(NetPlayers.players[i].network.ipx.server, 0, 4);
3661             } else {
3662                 NetPlayers.players[i].network.appletalk.node = 0;
3663                 NetPlayers.players[i].network.appletalk.net = 0;
3664                 NetPlayers.players[i].network.appletalk.socket = 0;
3665             }
3666         memset(NetPlayers.players[i].callsign, 0, CALLSIGN_LEN+1);
3667                 NetPlayers.players[i].version_major=0;
3668                 NetPlayers.players[i].version_minor=0;
3669                 NetPlayers.players[i].rank=0;
3670         }
3671
3672    mprintf ((0,"Select teams: Game mode is %d\n",Netgame.gamemode));
3673
3674         if (Netgame.gamemode == NETGAME_TEAM_ANARCHY ||
3675             Netgame.gamemode == NETGAME_CAPTURE_FLAG ||
3676                  Netgame.gamemode == NETGAME_TEAM_HOARD)
3677                  if (!network_select_teams())
3678                         goto abort;
3679
3680         return(1); 
3681 }
3682
3683 void 
3684 network_start_game()
3685 {
3686         int i;
3687         char game_name[NETGAME_NAME_LEN+1];
3688         int chosen_game_mode, game_flags, level;
3689
3690         if (Network_game_type == IPX_GAME) {
3691
3692                 Assert( FRAME_INFO_SIZE < IPX_MAX_DATA_SIZE );
3693                 mprintf((0, "Using frame_info len %d, max %d.\n", FRAME_INFO_SIZE, IPX_MAX_DATA_SIZE));
3694                 
3695                 if ( !Network_active )
3696                 {
3697                         nm_messagebox(NULL, 1, TXT_OK, TXT_IPX_NOT_FOUND );
3698                         return;
3699                 }
3700         #ifdef MACINTOSH
3701         } else {
3702                 int err;
3703                 char buf[256];
3704
3705                 Assert( FRAME_INFO_SIZE < APPLETALK_MAX_DATA_SIZE );            
3706                 mprintf((0, "Using frame_info len %d, max %d.\n", sizeof(frame_info), APPLETALK_MAX_DATA_SIZE));
3707                 if (Appletalk_active <= 0) {
3708                         switch (Appletalk_active) {
3709                         case APPLETALK_NOT_OPEN:
3710                                 sprintf(buf, "Appletalk is not currently active.\nPlease enable AppleTalk from the\nChooser and restart Descent.");
3711                                 break;
3712                         case APPLETALK_BAD_LISTENER:
3713                                 sprintf(buf, "The Resource Fork of Descent appears damaged.\nPlease re-install Descent or contact\nMacPlay technical support.");
3714                                 break;
3715                         case APPLETALK_NO_LOCAL_ADDR:
3716                                 sprintf(buf, "Wow! Strange!\n\nNo Local Address.");
3717                                 break;
3718                         case APPLETALK_NO_SOCKET:
3719                                 sprintf(buf, "All AppleTalk sockets are in use.\nTry shutting down other network\napplications and restarting Descent.\n");
3720                                 break;
3721                         }
3722                         nm_messagebox(NULL, 1, TXT_OK, buf);
3723                         return;
3724                 }
3725                 strcpy(Network_zone_name, DEFAULT_ZONE_NAME);
3726         #endif
3727         }
3728
3729         network_init();
3730         change_playernum_to(0);
3731
3732         if (network_find_game())
3733         {
3734                 nm_messagebox(NULL, 1, TXT_OK, TXT_NET_FULL);
3735                 return;
3736         }
3737
3738         game_flags = 0;
3739         i = network_get_game_params( game_name, &chosen_game_mode, &game_flags, &level );
3740
3741         if (i<0) return;
3742
3743         N_players = 0;
3744
3745 // LoadLevel(level); Old, no longer used.
3746
3747         Netgame.difficulty = Difficulty_level;
3748         Netgame.gamemode = chosen_game_mode;
3749         Netgame.game_status = NETSTAT_STARTING;
3750         Netgame.numplayers = 0;
3751         Netgame.max_numplayers = MaxNumNetPlayers;
3752         Netgame.levelnum = level;
3753         Netgame.protocol_version = MULTI_PROTO_VERSION;
3754
3755         strcpy(Netgame.game_name, game_name);
3756         
3757         Network_status = NETSTAT_STARTING;
3758
3759         #ifdef MACINTOSH
3760         if (Network_game_type == APPLETALK_GAME) {
3761                 OSErr err;
3762                 fix t1;
3763                 int count = 0;
3764                 
3765                 show_boxed_message("Registering Netgame");
3766                 do {
3767                         err = appletalk_register_netgame( game_name, TickCount() );
3768                         t1 = timer_get_fixed_seconds() + F1_0;
3769                         while (timer_get_fixed_seconds() < t1) ;
3770                         count++;
3771                 } while ( (err == nbpDuplicate) && (count != MAX_REGISTER_TRIES) );
3772                 clear_boxed_message();
3773                 if ( (err == tooManyReqs) || (count == MAX_REGISTER_TRIES) ) {
3774                         nm_messagebox(NULL, 1, TXT_OK, "AppleTalk Network is too busy.\nPlease try again shortly.");
3775                         Game_mode = GM_GAME_OVER;
3776                         return;
3777                 }
3778         }
3779         #endif
3780
3781         network_set_game_mode(Netgame.gamemode);
3782
3783    d_srand( timer_get_fixed_seconds() );
3784    Netgame.Security=d_rand();  // For syncing Netgames with player packets
3785
3786         if(network_select_players())
3787         {
3788                 StartNewLevel(Netgame.levelnum, 0);
3789         }
3790         else
3791                 Game_mode = GM_GAME_OVER;
3792         
3793 }
3794
3795 void restart_net_searching(newmenu_item * m)
3796 {
3797         int i;
3798         N_players = 0;
3799         num_active_games = 0;
3800
3801         memset(Active_games, 0, sizeof(netgame_info)*MAX_ACTIVE_NETGAMES);
3802
3803         for (i = 0; i < MAX_ACTIVE_NETGAMES; i++)
3804         {
3805                 sprintf(m[i+2].text, "%d.                                                     ",i+1);
3806                 m[i+2].redraw = 1;
3807         }
3808   
3809    NamesInfoSecurity=-1;
3810         Network_games_changed = 1;      
3811 }
3812
3813 char *ModeLetters[]={"ANRCHY","TEAM","ROBO","COOP","FLAG","HOARD","TMHOARD"};
3814
3815 int NumActiveNetgames=0;
3816
3817 void network_join_poll( int nitems, newmenu_item * menus, int * key, int citem )
3818 {
3819         // Polling loop for Join Game menu
3820         static fix t1 = 0;
3821         int i, osocket,join_status,temp;
3822
3823         menus = menus;
3824         citem = citem;
3825         nitems = nitems;
3826         key = key;
3827
3828         if ( (Network_game_type == IPX_GAME) && Network_allow_socket_changes ) {
3829                 osocket = Network_socket;
3830
3831                 if ( *key==KEY_PAGEDOWN )       { Network_socket--; *key = 0; }
3832                 if ( *key==KEY_PAGEUP )         { Network_socket++; *key = 0; }
3833
3834                 if (Network_socket>99)
3835                         Network_socket=99;
3836                 if (Network_socket<-99)
3837                         Network_socket=-99;
3838
3839                 if ( Network_socket+IPX_DEFAULT_SOCKET > 0x8000 )
3840                         Network_socket  = 0x8000 - IPX_DEFAULT_SOCKET;
3841
3842                 if ( Network_socket+IPX_DEFAULT_SOCKET < 0 )
3843                         Network_socket  = IPX_DEFAULT_SOCKET;
3844
3845                 if (Network_socket != osocket )         {
3846                         sprintf( menus[0].text, "\t%s %+d (PgUp/PgDn to change)", TXT_CURRENT_IPX_SOCKET, Network_socket );
3847                         menus[0].redraw = 1;
3848                         mprintf(( 0, "Changing to socket %d\n", Network_socket ));
3849                         network_listen();
3850                         mprintf ((0,"netgood 1!\n"));
3851                         ipx_change_default_socket( IPX_DEFAULT_SOCKET + Network_socket );
3852                         mprintf ((0,"netgood 2!\n"));
3853                         restart_net_searching(menus);
3854                         mprintf ((0,"netgood 3!\n"));
3855                         network_send_game_list_request();
3856                         mprintf ((0,"netgood 4!\n"));
3857                         return;
3858                 }
3859         }
3860
3861         // send a request for game info every 3 seconds
3862
3863         if (Network_game_type == IPX_GAME) {
3864                 if (timer_get_approx_seconds() > t1+F1_0*3) {
3865                         t1 = timer_get_approx_seconds();
3866                         network_send_game_list_request();
3867                 }
3868 #ifdef MACINTOSH
3869         } else if (timer_get_approx_seconds() > t1+F1_0*20) {
3870                 hide_cursor();
3871                 t1 = timer_get_approx_seconds();
3872                 restart_net_searching(menus);
3873                 show_boxed_message("Requesting list of Netgames");
3874                 network_send_game_list_request();
3875                 clear_boxed_message();
3876                 show_cursor();
3877 #endif
3878         }
3879
3880         temp=num_active_games;
3881
3882         network_listen();
3883
3884         NumActiveNetgames=num_active_games;
3885
3886         if (!Network_games_changed)
3887                 return;
3888
3889         if (temp!=num_active_games)
3890                 digi_play_sample (SOUND_HUD_MESSAGE,F1_0);
3891
3892         Network_games_changed = 0;
3893         mprintf ((0,"JOIN POLL: I'm looking at %d games!\n",num_active_games));
3894
3895         // Copy the active games data into the menu options
3896         for (i = 0; i < num_active_games; i++)
3897         {
3898                 int game_status = Active_games[i].game_status;
3899                 int j,x, k,tx,ty,ta,nplayers = 0;
3900                 char levelname[8],MissName[25],GameName[25],thold[2];
3901                 thold[1]=0;
3902
3903                 // These next two loops protect against menu skewing
3904                 // if missiontitle or gamename contain a tab
3905
3906                 for (x=0,tx=0,k=0,j=0;j<15;j++)
3907                 {
3908                         if (Active_games[i].mission_title[j]=='\t')
3909                                 continue;
3910                         thold[0]=Active_games[i].mission_title[j];
3911                         gr_get_string_size (thold,&tx,&ty,&ta);
3912
3913                         if ((x+=tx)>=LHX(55))
3914                         {
3915                                 MissName[k]=MissName[k+1]=MissName[k+2]='.';
3916                                 k+=3;
3917                                 break;
3918                         }
3919
3920                         MissName[k++]=Active_games[i].mission_title[j];
3921                 }
3922                 MissName[k]=0;
3923
3924                 for (x=0,tx=0,k=0,j=0;j<15;j++)
3925                 {
3926                         if (Active_games[i].game_name[j]=='\t')
3927                                 continue;
3928                         thold[0]=Active_games[i].game_name[j];
3929                         gr_get_string_size (thold,&tx,&ty,&ta);
3930
3931                         if ((x+=tx)>=LHX(55))
3932                         {
3933                                 GameName[k]=GameName[k+1]=GameName[k+2]='.';
3934                                 k+=3;
3935                                 break;
3936                         }
3937                         GameName[k++]=Active_games[i].game_name[j];
3938                 }
3939                 GameName[k]=0;
3940
3941
3942                 nplayers=Active_games[i].numconnected;
3943
3944                 if (Active_games[i].levelnum < 0)
3945                         sprintf(levelname, "S%d", -Active_games[i].levelnum);
3946                 else
3947                         sprintf(levelname, "%d", Active_games[i].levelnum);
3948
3949                 if (game_status == NETSTAT_STARTING)
3950                 {
3951                         sprintf (menus[i+2].text,"%d.\t%s \t%s \t  %d/%d \t%s \t %s \t%s",
3952                                          i+1,GameName,ModeLetters[Active_games[i].gamemode],nplayers,
3953                                          Active_games[i].max_numplayers,MissName,levelname,"Forming");
3954                 }
3955                 else if (game_status == NETSTAT_PLAYING)
3956                 {
3957                         join_status=can_join_netgame(&Active_games[i],NULL);
3958                         //               mprintf ((0,"Joinstatus=%d\n",join_status));
3959
3960                         if (join_status==1)
3961                                 sprintf (menus[i+2].text,"%d.\t%s \t%s \t  %d/%d \t%s \t %s \t%s",
3962                                                  i+1,GameName,ModeLetters[Active_games[i].gamemode],nplayers,
3963                                                  Active_games[i].max_numplayers,MissName,levelname,"Open");
3964                         else if (join_status==2)
3965                                 sprintf (menus[i+2].text,"%d.\t%s \t%s \t  %d/%d \t%s \t %s \t%s",
3966                                                  i+1,GameName,ModeLetters[Active_games[i].gamemode],nplayers,
3967                                                  Active_games[i].max_numplayers,MissName,levelname,"Full");
3968                         else if (join_status==3)
3969                                 sprintf (menus[i+2].text,"%d.\t%s \t%s \t  %d/%d \t%s \t %s \t%s",
3970                                                  i+1,GameName,ModeLetters[Active_games[i].gamemode],nplayers,
3971                                                  Active_games[i].max_numplayers,MissName,levelname,"Restrict");
3972                         else
3973                                 sprintf (menus[i+2].text,"%d.\t%s \t%s \t  %d/%d \t%s \t %s \t%s",
3974                                                  i+1,GameName,ModeLetters[Active_games[i].gamemode],nplayers,
3975                                                  Active_games[i].max_numplayers,MissName,levelname,"Closed");
3976
3977                 }
3978                 else
3979                         sprintf (menus[i+2].text,"%d.\t%s \t%s \t  %d/%d \t%s \t %s \t%s",
3980                                          i+1,GameName,ModeLetters[Active_games[i].gamemode],nplayers,
3981                                          Active_games[i].max_numplayers,MissName,levelname,"Between");
3982
3983
3984                 Assert(strlen(menus[i+2].text) < 100);
3985                 menus[i+2].redraw = 1;
3986         }
3987
3988         for (i = num_active_games; i < MAX_ACTIVE_NETGAMES; i++)
3989         {
3990                 sprintf(menus[i+2].text, "%d.                                                     ",i+1);
3991                 menus[i+2].redraw = 1;
3992         }
3993 }
3994
3995 int
3996 network_wait_for_sync(void)
3997 {
3998         char text[60];
3999         newmenu_item m[2];
4000         int i, choice;
4001         
4002         Network_status = NETSTAT_WAITING;
4003
4004         m[0].type=NM_TYPE_TEXT; m[0].text = text;
4005         m[1].type=NM_TYPE_TEXT; m[1].text = TXT_NET_LEAVE;
4006         
4007         i = network_send_request();
4008
4009         if (i < 0)
4010                 return(-1);
4011
4012         sprintf( m[0].text, "%s\n'%s' %s", TXT_NET_WAITING, NetPlayers.players[i].callsign, TXT_NET_TO_ENTER );
4013
4014 menu:   
4015         choice=newmenu_do( NULL, TXT_WAIT, 2, m, network_sync_poll );
4016
4017         if (choice > -1)
4018                 goto menu;
4019
4020         if (Network_status != NETSTAT_PLAYING)  
4021         {
4022                 sequence_packet me;
4023
4024 //              if (Network_status == NETSTAT_ENDLEVEL)
4025 //              {
4026 //                      network_send_endlevel_packet(0);
4027 //                      longjmp(LeaveGame, 0);
4028 //              }               
4029
4030                 mprintf((0, "Aborting join.\n"));
4031                 me.type = PID_QUIT_JOINING;
4032                 memcpy( me.player.callsign, Players[Player_num].callsign, CALLSIGN_LEN+1 );
4033                 if (Network_game_type == IPX_GAME) {
4034                         memcpy( me.player.network.ipx.node, ipx_get_my_local_address(), 6 );
4035                         memcpy( me.player.network.ipx.server, ipx_get_my_server_address(), 4 );
4036                         send_internetwork_sequence_packet(me, NetPlayers.players[0].network.ipx.server, NetPlayers.players[0].network.ipx.node);
4037                 #ifdef MACINTOSH
4038                 } else {
4039                         me.player.network.appletalk.node = appletalk_get_my_node();
4040                         me.player.network.appletalk.net = appletalk_get_my_net();
4041                         me.player.network.appletalk.socket = appletalk_get_my_socket();
4042                 #endif
4043                 }
4044                 N_players = 0;
4045                 Function_mode = FMODE_MENU;
4046                 Game_mode = GM_GAME_OVER;
4047                 return(-1);     // they cancelled               
4048         }
4049         return(0);
4050 }
4051
4052 void 
4053 network_request_poll( int nitems, newmenu_item * menus, int * key, int citem )
4054 {
4055         // Polling loop for waiting-for-requests menu
4056
4057         int i = 0;
4058         int num_ready = 0;
4059
4060         menus = menus;
4061         citem = citem;
4062         nitems = nitems;
4063         key = key;
4064
4065         // Send our endlevel packet at regular intervals
4066
4067 //      if (timer_get_approx_seconds() > t1+ENDLEVEL_SEND_INTERVAL)
4068 //      {
4069 //              network_send_endlevel_packet();
4070 //              t1 = timer_get_approx_seconds();
4071 //      }
4072
4073         network_listen();
4074
4075         for (i = 0; i < N_players; i++)
4076         {
4077                 if ((Players[i].connected == 1) || (Players[i].connected == 0))
4078                         num_ready++;
4079         }
4080
4081         if (num_ready == N_players) // All players have checked in or are disconnected
4082         {
4083                 *key = -2;
4084         }
4085 }
4086
4087 void
4088 network_wait_for_requests(void)
4089 {
4090         // Wait for other players to load the level before we send the sync
4091         int choice, i;
4092         newmenu_item m[1];
4093         
4094         Network_status = NETSTAT_WAITING;
4095
4096         m[0].type=NM_TYPE_TEXT; m[0].text = TXT_NET_LEAVE;
4097
4098         mprintf((0, "Entered wait_for_requests : N_players = %d.\n", N_players));
4099
4100         for (choice = 0; choice < N_players; choice++)
4101                 mprintf((0, "Players[%d].connected = %d.\n", choice, Players[choice].connected));
4102
4103         Network_status = NETSTAT_WAITING;
4104         network_flush();
4105
4106         Players[Player_num].connected = 1;
4107
4108 menu:
4109         choice = newmenu_do(NULL, TXT_WAIT, 1, m, network_request_poll);        
4110
4111         if (choice == -1)
4112         {
4113                 // User aborted
4114                 choice = nm_messagebox(NULL, 3, TXT_YES, TXT_NO, TXT_START_NOWAIT, TXT_QUITTING_NOW);
4115                 if (choice == 2)
4116                         return;
4117                 if (choice != 0)
4118                         goto menu;
4119                 
4120                 // User confirmed abort
4121                 
4122                 for (i=0; i < N_players; i++) {
4123                         if ((Players[i].connected != 0) && (i != Player_num)) {
4124                                 if (Network_game_type == IPX_GAME)
4125                                         network_dump_player(NetPlayers.players[i].network.ipx.server, NetPlayers.players[i].network.ipx.node, DUMP_ABORTED);
4126                                 #ifdef MACINTOSH
4127                                 else
4128                                         network_dump_appletalk_player(NetPlayers.players[i].network.appletalk.node, NetPlayers.players[i].network.appletalk.net, NetPlayers.players[i].network.appletalk.socket, DUMP_ABORTED);
4129                                 #endif
4130                         }
4131                 }
4132
4133                 #ifdef MACINTOSH
4134                 if (Network_game_type == APPLETALK_GAME)
4135                         network_release_registered_game();
4136                 #endif
4137                 longjmp(LeaveGame, 0);  
4138         }
4139         else if (choice != -2)
4140                 goto menu;
4141 }
4142
4143 /* Do required syncing after each level, before starting new one */
4144 int
4145 network_level_sync(void)
4146 {
4147         int result;
4148
4149         mprintf((0, "Player %d entering network_level_sync.\n", Player_num));
4150         
4151         MySyncPackInitialized = 0;
4152
4153 //      my_segments_checksum = netmisc_calc_checksum(Segments, sizeof(segment)*(Highest_segment_index+1));
4154
4155         network_flush(); // Flush any old packets
4156
4157         if (N_players == 0)
4158                 result = network_wait_for_sync();
4159         else if (network_i_am_master())
4160         {
4161                 network_wait_for_requests();
4162                 network_send_sync();
4163                 result = 0;
4164         }
4165         else
4166                 result = network_wait_for_sync();
4167
4168    network_count_powerups_in_mine();
4169
4170         if (result)
4171         {
4172                 Players[Player_num].connected = 0;
4173                 network_send_endlevel_packet();
4174                 #ifdef MACINTOSH
4175                 if (Network_game_type == APPLETALK_GAME)
4176                         network_release_registered_game();
4177                 #endif
4178                 longjmp(LeaveGame, 0);
4179         }
4180         return(0);
4181 }
4182
4183 void network_count_powerups_in_mine(void)
4184  {
4185   int i;
4186
4187   for (i=0;i<MAX_POWERUP_TYPES;i++)
4188         PowerupsInMine[i]=0;
4189         
4190   for (i=0;i<=Highest_object_index;i++) 
4191         {
4192          if (Objects[i].type==OBJ_POWERUP)
4193           {
4194                 PowerupsInMine[Objects[i].id]++;
4195                 if (multi_powerup_is_4pack(Objects[i].id))
4196                    PowerupsInMine[Objects[i].id-1]+=4;
4197           }
4198         }
4199                   
4200  }
4201
4202 #ifdef MACINTOSH
4203
4204 // code to release the NBP binding of an appletalk game
4205
4206 void network_release_registered_game()
4207 {
4208         if (Network_game_type == APPLETALK_GAME)
4209                 appletalk_remove_netgame();
4210 }
4211
4212 // code to sort zone lists....
4213
4214 int zone_sort_func(const char **e0, const char **e1)
4215 {
4216         return strcmp(*e0, *e1);
4217 }
4218
4219 void network_get_appletalk_zone()
4220 {
4221         int num_zones, i, item, default_item;
4222         char **zone_list;
4223         char default_zone[MAX_ZONE_LENGTH];                     // my zone
4224
4225         Network_zone_name[0] = '\0';
4226         
4227         show_boxed_message("Looking for AppleTalk Zones");
4228         num_zones = appletalk_get_zone_names(&zone_list);
4229         clear_boxed_message();
4230
4231         if (num_zones < 0)      {               // error in getting zone list...maybe no router available....
4232                 if ( (num_zones == tooManyReqs) || (num_zones == noDataArea) ){
4233                         nm_messagebox(NULL, 1, TXT_OK, "AppleTalk Network is too busy.\nPlease try again shortly.");
4234                         longjmp(LeaveGame,0);
4235                 }
4236                 num_zones = 0;
4237         }
4238         
4239         if (num_zones == 0) {
4240                 strcpy(Network_zone_name, DEFAULT_ZONE_NAME);
4241                 return;
4242         }
4243         
4244         if (num_zones == 1) {
4245                 Network_zone_name[0] = (char)(strlen(zone_list[0]));
4246                 memcpy( &(Network_zone_name[1]), zone_list[0], strlen(zone_list[0]) );
4247                 goto zone_done;
4248         }
4249         
4250 // sort the zone names
4251
4252         for (i = 0; i < num_zones; i++)
4253                 strlwr(zone_list[i]);
4254
4255         qsort(zone_list, num_zones, sizeof(char *), zone_sort_func);
4256
4257 // get my current zone so we can highlight that one first
4258
4259         if (appletalk_get_my_zone(default_zone))
4260                 default_item = 0;
4261         else {
4262                 for (i = 0; i < num_zones; i++) {
4263                         if ( !stricmp(zone_list[i], default_zone) ) {
4264                                 default_item = i;
4265                                 break;
4266                         }
4267                 }
4268         }
4269
4270 rezone:         
4271         item = newmenu_listbox1("AppleTalk Zones", num_zones, zone_list, 0, default_item, NULL);
4272         
4273         if (item == -1)
4274                 goto rezone;
4275                 
4276         Network_zone_name[0] = (char)(strlen(zone_list[item]));
4277         memcpy( &(Network_zone_name[1]), zone_list[item], strlen(zone_list[item]) );
4278         
4279 zone_done:
4280         for (i = 0; i < num_zones; i++)
4281                 d_free(zone_list[i]);
4282         d_free(zone_list);
4283 }
4284 #endif
4285
4286 void nm_draw_background1(char * filename);
4287
4288 void network_join_game()
4289 {
4290         int choice, i;
4291         char menu_text[MAX_ACTIVE_NETGAMES+2][200];
4292         
4293         newmenu_item m[MAX_ACTIVE_NETGAMES+2];
4294
4295         if (Network_game_type == IPX_GAME) {
4296                 if ( !Network_active )
4297                 {
4298                         nm_messagebox(NULL, 1, TXT_OK, TXT_IPX_NOT_FOUND);
4299                         return;
4300                 }
4301         #ifdef MACINTOSH
4302         } else if (Appletalk_active <= 0) {
4303                 char buf[256];
4304                 
4305                 switch (Appletalk_active) {
4306                         case APPLETALK_NOT_OPEN:
4307                                 sprintf(buf, "Appletalk is not currently active.\nPlease enable AppleTalk from the\nChooser and restart Descent.");
4308                                 break;
4309                         case APPLETALK_BAD_LISTENER:
4310                                 sprintf(buf, "The Resource Fork of Descent appears damaged.\nPlease re-install Descent or contact\nMacPlay technical support.");
4311                                 break;
4312                         case APPLETALK_NO_LOCAL_ADDR:
4313                                 sprintf(buf, "Wow! Strange!\n\nNo Local Address.");
4314                                 break;
4315                         case APPLETALK_NO_SOCKET:
4316                                 sprintf(buf, "All AppleTalk sockets are in use.\nTry shutting down other network\napplications and restarting Descent.\n");
4317                                 break;
4318                 }
4319                 nm_messagebox(NULL, 1, TXT_OK, buf);
4320                 return;
4321         #endif
4322         }
4323
4324         network_init();
4325
4326         N_players = 0;
4327
4328         setjmp(LeaveGame);
4329         
4330         #ifdef MACINTOSH
4331         if (Network_game_type == APPLETALK_GAME)
4332                 network_get_appletalk_zone();
4333         #endif
4334         
4335         Network_send_objects = 0; 
4336         Network_sending_extras=0;
4337         Network_rejoined=0;
4338           
4339         Network_status = NETSTAT_BROWSING; // We are looking at a game menu
4340         
4341    network_flush();
4342         network_listen();  // Throw out old info
4343
4344         #ifdef MACINTOSH
4345         if (Network_game_type == IPX_GAME)
4346         #endif          // note link to if
4347                 network_send_game_list_request(); // broadcast a request for lists
4348
4349         num_active_games = 0;
4350
4351    memset(m, 0, sizeof(newmenu_item)*MAX_ACTIVE_NETGAMES);
4352    memset(Active_games, 0, sizeof(netgame_info)*MAX_ACTIVE_NETGAMES);
4353    memset(ActiveNetPlayers,0,sizeof(AllNetPlayers_info)*MAX_ACTIVE_NETGAMES);
4354         
4355         gr_set_fontcolor(BM_XRGB(15,15,23),-1);
4356
4357         m[0].text = menu_text[0];
4358         m[0].type = NM_TYPE_TEXT;
4359         if (Network_game_type == IPX_GAME) {
4360                 if (Network_allow_socket_changes)
4361                         sprintf( m[0].text, "\tCurrent IPX Socket is default %+d (PgUp/PgDn to change)", Network_socket );
4362                 else
4363                         strcpy( m[0].text, "" ); //sprintf( m[0].text, "" );
4364         #ifdef MACINTOSH
4365         } else {
4366                 p2cstr(Network_zone_name);
4367                 if (strcmp(Network_zone_name, "*"))             // only print if there is a zone name
4368                         sprintf(m[0].text, "\tCurrent Zone is %s", Network_zone_name);          // is Network_zone_name a pascal string????
4369                 c2pstr(Network_zone_name);
4370         #endif
4371         }
4372
4373         m[1].text=menu_text[1];
4374         m[1].type=NM_TYPE_TEXT;
4375         sprintf (m[1].text,"\tGAME \tMODE \t#PLYRS \tMISSION \tLEV \tSTATUS");
4376
4377         for (i = 0; i < MAX_ACTIVE_NETGAMES; i++) {
4378                 m[i+2].text = menu_text[i+2];
4379                 m[i+2].type = NM_TYPE_MENU;
4380                 sprintf(m[i+2].text, "%d.                                                               ", i+1);
4381                 m[i+2].redraw = 1;
4382         }
4383
4384         Network_games_changed = 1;      
4385 remenu:
4386         SurfingNet=1;
4387         nm_draw_background1(Menu_pcx_name);             //load this here so if we abort after loading level, we restore the palette
4388         gr_palette_load(gr_palette);
4389    ExtGameStatus=GAMESTAT_JOIN_NETGAME;
4390         choice=newmenu_dotiny("NETGAMES", NULL,MAX_ACTIVE_NETGAMES+2, m, network_join_poll);
4391         SurfingNet=0;
4392
4393         if (choice==-1) {
4394                 Network_status = NETSTAT_MENU;
4395                 return; // they cancelled               
4396         }               
4397    choice-=2;
4398
4399         if (choice >=num_active_games)
4400         {
4401                 nm_messagebox(TXT_SORRY, 1, TXT_OK, TXT_INVALID_CHOICE);
4402                 goto remenu;
4403         }
4404
4405         // Choice has been made and looks legit
4406         if (Active_games[choice].game_status == NETSTAT_ENDLEVEL)
4407         {
4408                 nm_messagebox(TXT_SORRY, 1, TXT_OK, TXT_NET_GAME_BETWEEN2);
4409                 goto remenu;
4410         }
4411
4412         if (Active_games[choice].protocol_version != MULTI_PROTO_VERSION)
4413         {
4414                 if (Active_games[choice].protocol_version == 3) {
4415                         #ifndef SHAREWARE
4416                                 nm_messagebox(TXT_SORRY, 1, TXT_OK, "Your version of Descent 2\nis incompatible with the\nDemo version");
4417                         #endif
4418                 }
4419                 else if (Active_games[choice].protocol_version == 4) {
4420                         #ifdef SHAREWARE
4421                                 nm_messagebox(TXT_SORRY, 1, TXT_OK, "This Demo version of\nDescent 2 is incompatible\nwith the full commercial version");
4422                         #endif
4423                 }
4424                 else
4425                         nm_messagebox(TXT_SORRY, 1, TXT_OK, TXT_VERSION_MISMATCH);
4426
4427                 goto remenu;
4428         }
4429
4430 #ifndef SHAREWARE
4431         {       
4432                 // Check for valid mission name
4433                         mprintf((0, "Loading mission:%s.\n", Active_games[choice].mission_name));
4434                         if (!load_mission_by_name(Active_games[choice].mission_name))
4435                         {
4436                                 nm_messagebox(NULL, 1, TXT_OK, TXT_MISSION_NOT_FOUND);
4437                                 goto remenu;
4438                         }
4439         }
4440 #endif
4441
4442 #if defined (D2_OEM)
4443         {
4444                 if (Active_games[choice].levelnum>8)
4445                  {
4446                                 nm_messagebox(NULL, 1, TXT_OK, "This OEM version only supports\nthe first 8 levels!");
4447                                 goto remenu;
4448                  }
4449         }
4450 #endif                  
4451
4452      if (!network_wait_for_all_info (choice))
4453                 {
4454                   nm_messagebox (TXT_SORRY,1,TXT_OK,"There was a join error!");
4455                   Network_status = NETSTAT_BROWSING; // We are looking at a game menu
4456                   goto remenu;
4457                 }       
4458           
4459           Network_status = NETSTAT_BROWSING; // We are looking at a game menu
4460  
4461      if (!can_join_netgame(&Active_games[choice],&ActiveNetPlayers[choice]))
4462                         {
4463                                 if (Active_games[choice].numplayers == Active_games[choice].max_numplayers)
4464                                         nm_messagebox(TXT_SORRY, 1, TXT_OK, TXT_GAME_FULL);
4465                                 else
4466                                         nm_messagebox(TXT_SORRY, 1, TXT_OK, TXT_IN_PROGRESS);
4467                                 goto remenu;
4468                         }
4469
4470         // Choice is valid, prepare to join in
4471
4472         memcpy(&Netgame, &Active_games[choice], sizeof(netgame_info));
4473    memcpy (&NetPlayers,&ActiveNetPlayers[choice],sizeof(AllNetPlayers_info));
4474
4475         Difficulty_level = Netgame.difficulty;
4476         MaxNumNetPlayers = Netgame.max_numplayers;
4477         change_playernum_to(1);
4478
4479         #ifdef MACINTOSH
4480
4481 // register the joining player with NBP.  This will have the nice effect of a player wanting to
4482 // join to be able to see the netgame if this player were to become the master.
4483
4484         if (Network_game_type == APPLETALK_GAME) {
4485                 OSErr err;
4486                 int count = 0;
4487                 fix t1;
4488                 
4489                 show_boxed_message("Registering Netgame");
4490                 do {
4491                         err = appletalk_register_netgame( Active_games[choice].game_name, TickCount() );
4492                         t1 = timer_get_fixed_seconds() + F1_0;
4493                         while ( timer_get_fixed_seconds() < t1 ) ;
4494                         count++;
4495                 } while ( (err == nbpDuplicate) && (count != MAX_REGISTER_TRIES) );
4496                 clear_boxed_message();
4497                 if ( (err == tooManyReqs) || (count == MAX_REGISTER_TRIES) ) {
4498                         nm_messagebox(NULL, 1, TXT_OK, "AppleTalk Network is too busy.\nPlease try again shortly.");
4499                         goto remenu;
4500                 }
4501         }
4502         #endif
4503
4504         network_set_game_mode(Netgame.gamemode);
4505
4506         network_AdjustMaxDataSize ();
4507
4508         StartNewLevel(Netgame.levelnum, 0);
4509
4510         return;         // look ma, we're in a game!!!
4511 }
4512
4513 void network_AdjustMaxDataSize ()
4514  {
4515           
4516    if (Netgame.ShortPackets)
4517         MaxXDataSize=NET_XDATA_SIZE;
4518    else
4519       MaxXDataSize=NET_XDATA_SIZE;
4520  }
4521   
4522
4523 fix StartWaitAllTime=0;
4524 int WaitAllChoice=0;
4525 #define ALL_INFO_REQUEST_INTERVAL F1_0*3
4526
4527 void network_wait_all_poll( int nitems, newmenu_item * menus, int * key, int citem )
4528  {
4529   static fix t1=0;
4530
4531   menus=menus;
4532   nitems=nitems;
4533   citem=citem;
4534
4535   if (timer_get_approx_seconds() > t1+ALL_INFO_REQUEST_INTERVAL)
4536         {
4537                 network_send_all_info_request(PID_SEND_ALL_GAMEINFO,SecurityCheck);
4538                 t1 = timer_get_approx_seconds();
4539         }
4540
4541   network_do_big_wait(WaitAllChoice);  
4542    
4543   if(SecurityCheck==-1)
4544    *key=-2;
4545  }
4546  
4547 int network_wait_for_all_info (int choice)
4548  {
4549   int pick;
4550   
4551   newmenu_item m[2];
4552
4553   m[0].type=NM_TYPE_TEXT; m[0].text = "Press Escape to cancel";
4554
4555   WaitAllChoice=choice;
4556   StartWaitAllTime=timer_get_approx_seconds();
4557   SecurityCheck=Active_games[choice].Security;
4558   NetSecurityFlag=0;
4559
4560   GetMenu:
4561   pick=newmenu_do( NULL, "Connecting...", 1, m, network_wait_all_poll );
4562
4563   if (pick>-1 && SecurityCheck!=-1)
4564         goto GetMenu;
4565
4566   if (SecurityCheck==-1)
4567     {   
4568            SecurityCheck=0;     
4569            return (1);
4570          }
4571   SecurityCheck=0;      
4572   return (0);
4573  }
4574
4575 void network_do_big_wait(int choice)
4576 {
4577         int size;
4578         ubyte packet[IPX_MAX_DATA_SIZE],*data;
4579         AllNetPlayers_info *temp_info;
4580 #ifdef WORDS_BIGENDIAN
4581         AllNetPlayers_info info_struct;
4582 #endif
4583 #ifdef MACINTOSH
4584         ubyte apacket[APPLETALK_MAX_DATA_SIZE];
4585 #endif
4586   
4587         #ifdef MACINTOSH
4588         if (Network_game_type == APPLETALK_GAME)
4589                 size=appletalk_get_packet_data( apacket );
4590         else
4591         #endif
4592                 size=ipx_get_packet_data( packet );
4593         
4594   if (size>0)
4595         {
4596                 #ifdef MACINTOSH
4597                 if (Network_game_type == APPLETALK_GAME)
4598                         data = apacket;
4599                 else
4600                 #endif
4601                         data = packet;
4602   
4603          switch (data[0])
4604      {  
4605                 case PID_GAME_INFO:
4606
4607                 if (Network_game_type == IPX_GAME) {
4608                         receive_full_netgame_packet(data, &TempNetInfo);
4609                 } else {
4610                         memcpy((ubyte *)&TempNetInfo, data, sizeof(netgame_info));
4611                 }
4612                 mprintf ((0,"This is %s game with a security of %d\n",TempNetInfo.game_name,TempNetInfo.Security));
4613
4614       if (TempNetInfo.Security !=SecurityCheck)
4615                  {
4616                   mprintf ((0,"Bad security on big_wait...rejecting.\n"));      
4617                   break;
4618                  }
4619                       
4620       if (NetSecurityFlag==NETSECURITY_WAIT_FOR_GAMEINFO)
4621            {
4622                    if (TempPlayersInfo->Security==TempNetInfo.Security)
4623                         {
4624                           mprintf ((0,"EQUAL !: Game=%d Players=%d ",TempPlayersInfo->Security,TempNetInfo.Security));
4625                 if (TempPlayersInfo->Security==SecurityCheck)
4626                           {
4627                                         memcpy (&Active_games[choice],(ubyte *)&TempNetInfo,sizeof(netgame_info));
4628                                         memcpy (&ActiveNetPlayers[choice],TempPlayersInfo,sizeof(AllNetPlayers_info));
4629                                         SecurityCheck=-1;
4630                           }
4631                         }
4632            }
4633                 else
4634            {
4635               NetSecurityFlag=NETSECURITY_WAIT_FOR_PLAYERS;
4636               NetSecurityNum=TempNetInfo.Security;
4637                                     
4638               if (network_wait_for_playerinfo())
4639                         {
4640                            mprintf ((0,"HUH? Game=%d Player=%d\n",NetSecurityNum,TempPlayersInfo->Security));
4641                                 memcpy (&Active_games[choice],(ubyte *)&TempNetInfo,sizeof(netgame_info));
4642                                 memcpy (&ActiveNetPlayers[choice],TempPlayersInfo,sizeof(AllNetPlayers_info));
4643                                 SecurityCheck=-1;
4644                         }
4645               NetSecurityFlag=0;
4646               NetSecurityNum=0;
4647            }
4648         break;
4649       case PID_PLAYERSINFO:
4650                mprintf ((0,"Got a PID_PLAYERSINFO!\n"));
4651
4652                         if (Network_game_type == IPX_GAME) {
4653 #ifndef WORDS_BIGENDIAN
4654                                 temp_info=(AllNetPlayers_info *)data;
4655 #else
4656                                 receive_netplayers_packet(data, &info_struct);
4657                                 temp_info = &info_struct;
4658 #endif
4659                         } else {
4660                                 temp_info = (AllNetPlayers_info *)data;
4661                         }
4662                         if (temp_info->Security!=SecurityCheck) 
4663                                 break;     // If this isn't the guy we're looking for, move on
4664
4665                         memcpy (&TempPlayersBase,(ubyte *)&temp_info,sizeof(AllNetPlayers_info));
4666                         TempPlayersInfo=&TempPlayersBase;
4667                         WaitingForPlayerInfo=0;
4668                         NetSecurityNum=TempPlayersInfo->Security;
4669                         NetSecurityFlag=NETSECURITY_WAIT_FOR_GAMEINFO;
4670                         break;
4671            }
4672         }
4673 }
4674
4675 void network_leave_game()
4676
4677    int nsave;
4678                 
4679         network_do_frame(1, 1);
4680    
4681    #ifdef NETPROFILING
4682         fclose (SendLogFile);
4683            fclose (RecieveLogFile);
4684         #endif
4685
4686         if ((network_i_am_master()))
4687         {
4688                 while (Network_sending_extras>1 && Player_joining_extras!=-1)
4689                   network_send_extras();
4690    
4691                 Netgame.numplayers = 0;
4692            nsave=N_players;
4693            N_players=0;
4694                 network_send_game_info(NULL);
4695                 N_players=nsave;
4696                 
4697                 mprintf ((0,"HEY! I'm master and I've left.\n"));
4698         }
4699         
4700         Players[Player_num].connected = 0;
4701         network_send_endlevel_packet();
4702         change_playernum_to(0);
4703         Game_mode = GM_GAME_OVER;
4704    write_player_file();
4705
4706 //      WIN(ipx_destroy_read_thread());
4707
4708         network_flush();
4709 }
4710
4711 void network_flush()
4712 {
4713         #ifdef MACINTOSH
4714         ubyte apacket[APPLETALK_MAX_DATA_SIZE];
4715         #endif
4716         ubyte packet[IPX_MAX_DATA_SIZE];
4717
4718         #ifdef MACINTOSH
4719         if ( (Network_game_type == APPLETALK_GAME) && (Appletalk_active <= 0) )
4720                 return;
4721         #endif
4722         
4723         if ( Network_game_type == IPX_GAME )
4724                 if (!Network_active) return;
4725
4726         #ifdef MACINTOSH
4727         if (Network_game_type == APPLETALK_GAME)
4728                 while (appletalk_get_packet_data(apacket) > 0) ;
4729         else
4730         #endif
4731                 while (ipx_get_packet_data(packet) > 0) ;
4732 }
4733
4734 void network_listen()
4735 {
4736         #ifdef MACINTOSH
4737         ubyte apacket[APPLETALK_MAX_DATA_SIZE];
4738         #endif
4739         int size;
4740         ubyte packet[IPX_MAX_DATA_SIZE];
4741         int i,loopmax=999;
4742
4743         if (Network_status==NETSTAT_PLAYING && Netgame.ShortPackets && !Network_send_objects)
4744          {
4745                 loopmax=N_players*Netgame.PacketsPerSec;
4746          }
4747         
4748         #ifdef MACINTOSH
4749         if ( (Network_game_type == APPLETALK_GAME) && (Appletalk_active <= 0) )
4750                 return;
4751         #endif
4752
4753         if (Network_game_type == IPX_GAME)      
4754                 if (!Network_active) return;
4755
4756         if (!(Game_mode & GM_NETWORK) && (Function_mode == FMODE_GAME))
4757                 mprintf((0, "Calling network_listen() when not in net game.\n"));
4758
4759         WaitingForPlayerInfo=1;
4760         NetSecurityFlag=NETSECURITY_OFF;
4761
4762         i=1;
4763         if (Network_game_type == IPX_GAME) {
4764                 size = ipx_get_packet_data( packet );
4765                 while ( size > 0)       {
4766                         network_process_packet( packet, size );
4767                         if (++i>loopmax)
4768                          break;
4769                         size = ipx_get_packet_data( packet );
4770                 }
4771         #ifdef MACINTOSH
4772         } else {
4773                 size = appletalk_get_packet_data( apacket );
4774                 while ( size > 0)       {
4775                         network_process_packet( apacket, size );
4776                         if (++i>loopmax)
4777                          break;
4778                         size = appletalk_get_packet_data( apacket );
4779                 }
4780         #endif
4781         }
4782 }
4783
4784 int network_wait_for_playerinfo()
4785 {
4786         int size,retries=0;
4787         ubyte packet[IPX_MAX_DATA_SIZE];
4788         struct AllNetPlayers_info *TempInfo;
4789         fix basetime;
4790         ubyte id;
4791 #ifdef WORDS_BIGENDIAN
4792         AllNetPlayers_info info_struct;
4793 #endif
4794 #ifdef MACINTOSH
4795         ubyte apacket[APPLETALK_MAX_DATA_SIZE];
4796 #endif
4797
4798         #ifdef MACINTOSH
4799         if ( (Network_game_type == APPLETALK_GAME) && (Appletalk_active <= 0) )
4800                 return;
4801         #endif
4802
4803         if ( Network_game_type == IPX_GAME)
4804                 if (!Network_active) return(0);
4805                 
4806       //  if (!WaitingForPlayerInfo)
4807         // return (1);
4808
4809         if (!(Game_mode & GM_NETWORK) && (Function_mode == FMODE_GAME))
4810                 {
4811         mprintf((0, "Calling network_wait_for_playerinfo() when not in net game.\n"));
4812                 }       
4813         if (Network_status==NETSTAT_PLAYING)
4814          {
4815                   Int3(); //MY GOD! Get Jason...this is the source of many problems
4816              return (0);
4817          }
4818    basetime=timer_get_approx_seconds();
4819
4820    while (WaitingForPlayerInfo && retries<50 && (timer_get_approx_seconds()<(basetime+(F1_0*5))))
4821       {
4822                 if (Network_game_type == IPX_GAME) {
4823                         size = ipx_get_packet_data( packet );
4824                         id = packet[0];
4825                 } else {
4826                         #ifdef MACINTOSH
4827                         size = appletalk_get_packet_data( apacket );
4828                         id = apacket[0];
4829                         #endif
4830                 }
4831
4832         if (size>0 && id==PID_PLAYERSINFO)
4833         {
4834 #ifdef WORDS_BIGENDIAN
4835 #ifdef MACINTOSH
4836                 if (Network_game_type == APPLETALK_GAME) {
4837                         TempInfo = (AllNetPlayers_info *)apacket;
4838                 } else
4839 #endif
4840                 {
4841                         receive_netplayers_packet(packet, &info_struct);
4842                         TempInfo = &info_struct;
4843                 }
4844 #else
4845                 TempInfo=(AllNetPlayers_info *)packet;
4846 #endif
4847
4848                 retries++;
4849
4850             if (NetSecurityFlag==NETSECURITY_WAIT_FOR_PLAYERS)
4851              {
4852               if (NetSecurityNum==TempInfo->Security)
4853                {
4854                 mprintf ((0,"HEYEQUAL: Player=%d Game=%d\n",TempInfo->Security,NetSecurityNum));
4855                 memcpy (&TempPlayersBase,(ubyte *)TempInfo,sizeof(AllNetPlayers_info));
4856                 TempPlayersInfo=&TempPlayersBase;
4857                 NetSecurityFlag=NETSECURITY_OFF;
4858                 NetSecurityNum=0;
4859                 WaitingForPlayerInfo=0;
4860                 return (1);
4861                }
4862               else
4863                continue;
4864              }
4865             else
4866              {
4867               mprintf ((0,"I'm original!\n"));
4868
4869               NetSecurityNum=TempInfo->Security;
4870               NetSecurityFlag=NETSECURITY_WAIT_FOR_GAMEINFO;
4871            
4872               memcpy (&TempPlayersBase,(ubyte *)TempInfo,sizeof(AllNetPlayers_info));
4873               TempPlayersInfo=&TempPlayersBase;
4874               WaitingForPlayerInfo=0;
4875               return (1);
4876              }
4877            }
4878          }
4879    return (0);
4880   }
4881
4882
4883 void network_send_data( ubyte * ptr, int len, int urgent )
4884 {
4885         char check;
4886
4887    #ifdef NETPROFILING
4888            TTSent[ptr[0]]++;  
4889            fprintf (SendLogFile,"Packet type: %d Len:%d Urgent:%d TT=%d\n",ptr[0],len,urgent,TTSent[ptr[0]]);
4890            fflush (SendLogFile);
4891         #endif
4892     
4893         if (Endlevel_sequence)
4894                 return;
4895
4896         if (!MySyncPackInitialized)     {
4897                 MySyncPackInitialized = 1;
4898                 memset( &MySyncPack, 0, sizeof(frame_info) );
4899         }
4900         
4901         if (urgent)
4902                 PacketUrgent = 1;
4903
4904         if ((MySyncPack.data_size+len) > MaxXDataSize ) {
4905                 check = ptr[0];
4906                 network_do_frame(1, 0);
4907                 if (MySyncPack.data_size != 0) {
4908                         mprintf((0, "%d bytes were added to data by network_do_frame!\n", MySyncPack.data_size));
4909                         Int3();
4910                 }
4911 //              Int3();         // Trying to send too much!
4912 //              return;
4913                 mprintf((0, "Packet overflow, sending additional packet, type %d len %d.\n", ptr[0], len));
4914                 Assert(check == ptr[0]);
4915         }
4916
4917         Assert(MySyncPack.data_size+len <= MaxXDataSize);
4918
4919         memcpy( &MySyncPack.data[MySyncPack.data_size], ptr, len );
4920         MySyncPack.data_size += len;
4921 }
4922
4923 void network_timeout_player(int playernum)
4924 {
4925         // Remove a player from the game if we haven't heard from them in 
4926         // a long time.
4927         int i, n = 0;
4928
4929         Assert(playernum < N_players);
4930         Assert(playernum > -1);
4931
4932         network_disconnect_player(playernum);
4933         create_player_appearance_effect(&Objects[Players[playernum].objnum]);
4934
4935         digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
4936
4937         HUD_init_message("%s %s", Players[playernum].callsign, TXT_DISCONNECTING);
4938         for (i = 0; i < N_players; i++)
4939                 if (Players[i].connected) 
4940                         n++;
4941
4942         if (n == 1)
4943         {
4944                 nm_messagebox(NULL, 1, TXT_OK, TXT_YOU_ARE_ONLY);
4945         }
4946 }
4947
4948 fix last_send_time = 0;
4949 fix last_timeout_check = 0;
4950
4951 #ifdef WORDS_BIGENDIAN
4952 void squish_short_frame_info(short_frame_info old_info, ubyte *data)
4953 {
4954         int loc = 0;
4955         int tmpi;
4956         short tmps;
4957         
4958         data[0] = old_info.type;                                            loc++;
4959         /* skip three for pad byte */                                       loc += 3;
4960         tmpi = INTEL_INT(old_info.numpackets);
4961         memcpy(&(data[loc]), &tmpi, 4);                                     loc += 4;
4962
4963         memcpy(&(data[loc]), old_info.thepos.bytemat, 9);                   loc += 9;
4964         tmps = INTEL_SHORT(old_info.thepos.xo);
4965         memcpy(&(data[loc]), &tmps, 2);                                     loc += 2;
4966         tmps = INTEL_SHORT(old_info.thepos.yo);
4967         memcpy(&(data[loc]), &tmps, 2);                                     loc += 2;
4968         tmps = INTEL_SHORT(old_info.thepos.zo);
4969         memcpy(&(data[loc]), &tmps, 2);                                     loc += 2;
4970         tmps = INTEL_SHORT(old_info.thepos.segment);
4971         memcpy(&(data[loc]), &tmps, 2);                                     loc += 2;
4972         tmps = INTEL_SHORT(old_info.thepos.velx);
4973         memcpy(&(data[loc]), &tmps, 2);                                     loc += 2;
4974         tmps = INTEL_SHORT(old_info.thepos.vely);
4975         memcpy(&(data[loc]), &tmps, 2);                                     loc += 2;
4976         tmps = INTEL_SHORT(old_info.thepos.velz);
4977         memcpy(&(data[loc]), &tmps, 2);                                     loc += 2;
4978
4979         tmps = INTEL_SHORT(old_info.data_size);
4980         memcpy(&(data[loc]), &tmps, 2);                                     loc += 2;
4981
4982         data[loc] = old_info.playernum;                                     loc++;
4983         data[loc] = old_info.obj_render_type;                               loc++;
4984         data[loc] = old_info.level_num;                                     loc++;
4985         memcpy(&(data[loc]), old_info.data, old_info.data_size);
4986 }
4987 #endif
4988
4989 char NakedBuf[NET_XDATA_SIZE+4];
4990 int NakedPacketLen=0;
4991 int NakedPacketDestPlayer=-1;
4992
4993 void network_do_frame(int force, int listen)
4994 {
4995         int i;
4996         short_frame_info ShortSyncPack;
4997         static fix LastEndlevel=0;
4998
4999         if (!(Game_mode&GM_NETWORK)) return;
5000
5001         if ((Network_status != NETSTAT_PLAYING) || (Endlevel_sequence)) // Don't send postion during escape sequence...
5002                 goto listen;
5003
5004   if (NakedPacketLen)
5005         {
5006                 Assert (NakedPacketDestPlayer>-1);
5007 //         mprintf ((0,"Sending a naked packet to %s (%d bytes)!\n",Players[NakedPacketDestPlayer].callsign,NakedPacketLen));
5008                 if (Network_game_type == IPX_GAME) 
5009                         ipx_send_packet_data( (ubyte *)NakedBuf, NakedPacketLen, NetPlayers.players[NakedPacketDestPlayer].network.ipx.server, NetPlayers.players[NakedPacketDestPlayer].network.ipx.node,Players[NakedPacketDestPlayer].net_address );
5010                 #ifdef MACINTOSH
5011                 else
5012                         appletalk_send_packet_data( (ubyte *)NakedBuf, NakedPacketLen, NetPlayers.players[NakedPacketDestPlayer].network.appletalk.node, NetPlayers.players[NakedPacketDestPlayer].network.appletalk.net,NetPlayers.players[NakedPacketDestPlayer].network.appletalk.socket );
5013                 #endif
5014                 NakedPacketLen=0;
5015                 NakedPacketDestPlayer=-1;
5016    }
5017   
5018    if (WaitForRefuseAnswer && timer_get_approx_seconds()>(RefuseTimeLimit+(F1_0*12)))
5019                 WaitForRefuseAnswer=0;
5020                         
5021         last_send_time += FrameTime;
5022         last_timeout_check += FrameTime;
5023
5024    // Send out packet PacksPerSec times per second maximum... unless they fire, then send more often...
5025    if ( (last_send_time>F1_0/Netgame.PacketsPerSec) || (Network_laser_fired) || force || PacketUrgent )       {        
5026                 if ( Players[Player_num].connected )    {
5027                         int objnum = Players[Player_num].objnum;
5028                         PacketUrgent = 0;
5029
5030                         if (listen) {
5031                                 multi_send_robot_frame(0);
5032                                 multi_send_fire();              // Do firing if needed..
5033                         }
5034
5035                         last_send_time = 0;
5036
5037                         if (Netgame.ShortPackets)
5038                         {
5039 #ifdef WORDS_BIGENDIAN
5040                                 ubyte send_data[IPX_MAX_DATA_SIZE];
5041                                 //int squished_size;
5042 #endif
5043                                 create_shortpos(&ShortSyncPack.thepos, Objects+objnum, 0);
5044                                 ShortSyncPack.type                                      = PID_PDATA;
5045                                 ShortSyncPack.playernum                         = Player_num;
5046                                 ShortSyncPack.obj_render_type           = Objects[objnum].render_type;
5047                                 ShortSyncPack.level_num                         = Current_level_num;
5048                                 ShortSyncPack.data_size                         = MySyncPack.data_size;
5049                                 memcpy (&ShortSyncPack.data[0],&MySyncPack.data[0],MySyncPack.data_size);
5050
5051                                 for (i=0; i<N_players; i++ )    {
5052                                         if ( (Players[i].connected) && (i!=Player_num ) )       {
5053                                                 MySyncPack.numpackets = Players[i].n_packets_sent++;
5054                                                 ShortSyncPack.numpackets=MySyncPack.numpackets;
5055                                                 if (Network_game_type == IPX_GAME) {
5056 #ifndef WORDS_BIGENDIAN
5057                                                         ipx_send_packet_data( (ubyte *)&ShortSyncPack, sizeof(short_frame_info)-MaxXDataSize+MySyncPack.data_size, NetPlayers.players[i].network.ipx.server, NetPlayers.players[i].network.ipx.node,Players[i].net_address );
5058 #else
5059                                                         squish_short_frame_info(ShortSyncPack, send_data);
5060                                                         ipx_send_packet_data( (ubyte *)send_data, IPX_SHORT_INFO_SIZE-MaxXDataSize+MySyncPack.data_size, NetPlayers.players[i].network.ipx.server, NetPlayers.players[i].network.ipx.node,Players[i].net_address );
5061 #endif
5062                                                 #ifdef MACINTOSH
5063                                                 } else {
5064                                                         appletalk_send_packet_data( (ubyte *)&ShortSyncPack, sizeof(short_frame_info)-MaxXDataSize+MySyncPack.data_size, NetPlayers.players[i].network.appletalk.node, NetPlayers.players[i].network.appletalk.net, NetPlayers.players[i].network.appletalk.socket );
5065                                                 #endif
5066                                                 }
5067                                         }
5068                                 }
5069                         }
5070                         else  // If long packets
5071                         {
5072                                 int send_data_size;
5073                                 
5074                                 MySyncPack.type                                 = PID_PDATA;
5075                                 MySyncPack.playernum                    = Player_num;
5076                                 MySyncPack.obj_render_type              = Objects[objnum].render_type;
5077                                 MySyncPack.level_num                    = Current_level_num;
5078                                 MySyncPack.obj_segnum                   = Objects[objnum].segnum;
5079                                 MySyncPack.obj_pos                              = Objects[objnum].pos;
5080                                 MySyncPack.obj_orient                   = Objects[objnum].orient;
5081                                 MySyncPack.phys_velocity                = Objects[objnum].mtype.phys_info.velocity;
5082                                 MySyncPack.phys_rotvel                  = Objects[objnum].mtype.phys_info.rotvel;
5083                                 
5084                                 send_data_size = MySyncPack.data_size;                  // do this so correct size data is sent
5085
5086 #ifdef WORDS_BIGENDIAN                        // do the swap stuff
5087                                 if (Network_game_type == IPX_GAME) {
5088                                         MySyncPack.obj_segnum = INTEL_SHORT(MySyncPack.obj_segnum);
5089                                         MySyncPack.obj_pos.x = INTEL_INT((int)MySyncPack.obj_pos.x);
5090                                         MySyncPack.obj_pos.y = INTEL_INT((int)MySyncPack.obj_pos.y);
5091                                         MySyncPack.obj_pos.z = INTEL_INT((int)MySyncPack.obj_pos.z);
5092                                         
5093                                         MySyncPack.obj_orient.rvec.x = INTEL_INT((int)MySyncPack.obj_orient.rvec.x);
5094                                         MySyncPack.obj_orient.rvec.y = INTEL_INT((int)MySyncPack.obj_orient.rvec.y);
5095                                         MySyncPack.obj_orient.rvec.z = INTEL_INT((int)MySyncPack.obj_orient.rvec.z);
5096                                         MySyncPack.obj_orient.uvec.x = INTEL_INT((int)MySyncPack.obj_orient.uvec.x);
5097                                         MySyncPack.obj_orient.uvec.y = INTEL_INT((int)MySyncPack.obj_orient.uvec.y);
5098                                         MySyncPack.obj_orient.uvec.z = INTEL_INT((int)MySyncPack.obj_orient.uvec.z);
5099                                         MySyncPack.obj_orient.fvec.x = INTEL_INT((int)MySyncPack.obj_orient.fvec.x);
5100                                         MySyncPack.obj_orient.fvec.y = INTEL_INT((int)MySyncPack.obj_orient.fvec.y);
5101                                         MySyncPack.obj_orient.fvec.z = INTEL_INT((int)MySyncPack.obj_orient.fvec.z);
5102                                         
5103                                         MySyncPack.phys_velocity.x = INTEL_INT((int)MySyncPack.phys_velocity.x);
5104                                         MySyncPack.phys_velocity.y = INTEL_INT((int)MySyncPack.phys_velocity.y);
5105                                         MySyncPack.phys_velocity.z = INTEL_INT((int)MySyncPack.phys_velocity.z);
5106                                 
5107                                         MySyncPack.phys_rotvel.x = INTEL_INT((int)MySyncPack.phys_rotvel.x);
5108                                         MySyncPack.phys_rotvel.y = INTEL_INT((int)MySyncPack.phys_rotvel.y);
5109                                         MySyncPack.phys_rotvel.z = INTEL_INT((int)MySyncPack.phys_rotvel.z);
5110                                         
5111                                         MySyncPack.data_size = INTEL_SHORT(MySyncPack.data_size);
5112                                 }
5113 #endif
5114
5115                                 for (i=0; i<N_players; i++ )    {
5116                                         if ( (Players[i].connected) && (i!=Player_num ) )       {
5117                                                 if (Network_game_type == IPX_GAME)
5118                                                         MySyncPack.numpackets = INTEL_INT(Players[i].n_packets_sent);
5119                                                 else
5120                                                         MySyncPack.numpackets = Players[i].n_packets_sent;
5121
5122                                                 Players[i].n_packets_sent++;
5123                                                 if (Network_game_type == IPX_GAME)
5124                                                         ipx_send_packet_data( (ubyte *)&MySyncPack, sizeof(frame_info)-MaxXDataSize+send_data_size, NetPlayers.players[i].network.ipx.server, NetPlayers.players[i].network.ipx.node,Players[i].net_address );
5125                                                 #ifdef MACINTOSH
5126                                                 else
5127                                                         appletalk_send_packet_data( (ubyte *)&MySyncPack, sizeof(frame_info)-MaxXDataSize+send_data_size, NetPlayers.players[i].network.appletalk.node, NetPlayers.players[i].network.appletalk.net, NetPlayers.players[i].network.appletalk.socket);
5128                                                 #endif
5129                                         }
5130                                 }
5131                         }
5132                                 
5133                         MySyncPack.data_size = 0;               // Start data over at 0 length.
5134                         if (Control_center_destroyed)
5135                         {
5136                                 if (Player_is_dead)
5137                                         Players[Player_num].connected=3;
5138                                 if (timer_get_approx_seconds() > (LastEndlevel+(F1_0/2)))
5139                                 {
5140                                         network_send_endlevel_packet();
5141                                         LastEndlevel=timer_get_approx_seconds();
5142                                 }
5143                         }
5144                         //mprintf( (0, "Packet has %d bytes appended (TS=%d)\n", MySyncPack.data_size, sizeof(frame_info)-MaxXDataSize+MySyncPack.data_size ));
5145                 }
5146         }
5147    
5148         if (!listen)
5149                 return;
5150
5151         if ((last_timeout_check > F1_0) && !(Control_center_destroyed))
5152         {
5153                 fix approx_time = timer_get_approx_seconds();
5154                 // Check for player timeouts
5155                 for (i = 0; i < N_players; i++)
5156                 {
5157                         if ((i != Player_num) && (Players[i].connected == 1))
5158                         {
5159                                 if ((LastPacketTime[i] == 0) || (LastPacketTime[i] > approx_time))
5160                                 {
5161                                         LastPacketTime[i] = approx_time;
5162                                         continue;
5163                                 }
5164                                 if ((approx_time - LastPacketTime[i]) > (15*F1_0))
5165                                         network_timeout_player(i);
5166                         }
5167                 }
5168                 last_timeout_check = 0;
5169         }
5170
5171 listen:
5172         if (!listen)
5173         {
5174                 MySyncPack.data_size = 0;
5175                 return;
5176         }
5177         network_listen();
5178
5179    if (VerifyPlayerJoined!=-1 && !(FrameCount & 63))
5180           resend_sync_due_to_packet_loss_for_allender(); // This will resend to network_player_rejoining
5181  
5182         if (Network_send_objects)
5183                 network_send_objects();
5184
5185         if (Network_sending_extras && VerifyPlayerJoined==-1)
5186         {
5187           network_send_extras();
5188           return;
5189     }
5190 }
5191
5192 int missed_packets = 0;
5193
5194 int ConsistencyCount = 0;
5195
5196 void network_consistency_error(void)
5197 {
5198         if (++ConsistencyCount < 10)
5199                 return;
5200
5201         Function_mode = FMODE_MENU;
5202         #ifndef MACINTOSH
5203         nm_messagebox(NULL, 1, TXT_OK, TXT_CONSISTENCY_ERROR);
5204         #else
5205         nm_messagebox(NULL, 1, TXT_OK, "Failed to join the netgame.\nYou are missing packets.  Check\nyour network connection and\ntry again.");
5206         #endif
5207         Function_mode = FMODE_GAME;
5208         ConsistencyCount = 0;
5209         multi_quit_game = 1;
5210         multi_leave_menu = 1;
5211         multi_reset_stuff();
5212         Function_mode = FMODE_MENU;
5213 }
5214
5215 void network_process_pdata (char *data)
5216  {
5217   Assert (Game_mode & GM_NETWORK);
5218  
5219   if (Netgame.ShortPackets)
5220         network_read_pdata_short_packet ((short_frame_info *)data);
5221   else
5222         network_read_pdata_packet ((frame_info *)data);
5223  }
5224
5225 void network_read_pdata_packet(frame_info *pd )
5226 {
5227         int TheirPlayernum;
5228         int TheirObjnum;
5229         object * TheirObj = NULL;
5230         
5231 // frame_info should be aligned...for mac, make the necessary adjustments
5232 #ifdef WORDS_BIGENDIAN
5233         if (Network_game_type == IPX_GAME) {
5234                 pd->numpackets = INTEL_INT(pd->numpackets);
5235                 pd->obj_pos.x = INTEL_INT(pd->obj_pos.x);
5236                 pd->obj_pos.y = INTEL_INT(pd->obj_pos.y);
5237                 pd->obj_pos.z = INTEL_INT(pd->obj_pos.z);
5238         
5239                 pd->obj_orient.rvec.x = (fix)INTEL_INT((int)pd->obj_orient.rvec.x);
5240                 pd->obj_orient.rvec.y = (fix)INTEL_INT((int)pd->obj_orient.rvec.y);
5241                 pd->obj_orient.rvec.z = (fix)INTEL_INT((int)pd->obj_orient.rvec.z);
5242                 pd->obj_orient.uvec.x = (fix)INTEL_INT((int)pd->obj_orient.uvec.x);
5243                 pd->obj_orient.uvec.y = (fix)INTEL_INT((int)pd->obj_orient.uvec.y);
5244                 pd->obj_orient.uvec.z = (fix)INTEL_INT((int)pd->obj_orient.uvec.z);
5245                 pd->obj_orient.fvec.x = (fix)INTEL_INT((int)pd->obj_orient.fvec.x);
5246                 pd->obj_orient.fvec.y = (fix)INTEL_INT((int)pd->obj_orient.fvec.y);
5247                 pd->obj_orient.fvec.z = (fix)INTEL_INT((int)pd->obj_orient.fvec.z);
5248         
5249                 pd->phys_velocity.x = (fix)INTEL_INT((int)pd->phys_velocity.x);
5250                 pd->phys_velocity.y = (fix)INTEL_INT((int)pd->phys_velocity.y);
5251                 pd->phys_velocity.z = (fix)INTEL_INT((int)pd->phys_velocity.z);
5252         
5253                 pd->phys_rotvel.x = (fix)INTEL_INT((int)pd->phys_rotvel.x);
5254                 pd->phys_rotvel.y = (fix)INTEL_INT((int)pd->phys_rotvel.y);
5255                 pd->phys_rotvel.z = (fix)INTEL_INT((int)pd->phys_rotvel.z);
5256                 
5257                 pd->obj_segnum = INTEL_SHORT(pd->obj_segnum);
5258                 pd->data_size = INTEL_SHORT(pd->data_size);
5259         }
5260 #endif
5261
5262         TheirPlayernum = pd->playernum;
5263         TheirObjnum = Players[pd->playernum].objnum;
5264         
5265         if (TheirPlayernum < 0) {
5266                 Int3(); // This packet is bogus!!
5267                 return;
5268         }
5269
5270    if (VerifyPlayerJoined!=-1 && TheirPlayernum==VerifyPlayerJoined)
5271          {
5272           // Hurray! Someone really really got in the game (I think).
5273      mprintf ((0,"Hurray! VPJ (%d) reset!\n",VerifyPlayerJoined));
5274      VerifyPlayerJoined=-1;
5275          }
5276  
5277         if (!multi_quit_game && (TheirPlayernum >= N_players)) {
5278                 if (Network_status!=NETSTAT_WAITING)
5279                  {
5280                         Int3(); // We missed an important packet!
5281                         network_consistency_error();
5282                         return;
5283                  }
5284                 else
5285                  return;
5286         }
5287         if (Endlevel_sequence || (Network_status == NETSTAT_ENDLEVEL) ) {
5288                 int old_Endlevel_sequence = Endlevel_sequence;
5289                 Endlevel_sequence = 1;
5290                 if ( pd->data_size>0 )  {
5291                         // pass pd->data to some parser function....
5292                         multi_process_bigdata( pd->data, pd->data_size );
5293                 }
5294                 Endlevel_sequence = old_Endlevel_sequence;
5295                 return;
5296         }
5297 //      mprintf((0, "Gametime = %d, Frametime = %d.\n", GameTime, FrameTime));
5298
5299         if ((byte)pd->level_num != Current_level_num)
5300         {
5301                 mprintf((0, "Got frame packet from player %d wrong level %d!\n", pd->playernum, pd->level_num));
5302                 return;
5303         }
5304
5305         TheirObj = &Objects[TheirObjnum];
5306
5307         //------------- Keep track of missed packets -----------------
5308         Players[TheirPlayernum].n_packets_got++;
5309         TotalPacketsGot++;
5310         LastPacketTime[TheirPlayernum] = timer_get_approx_seconds();
5311
5312         if  ( pd->numpackets != Players[TheirPlayernum].n_packets_got ) {
5313                 int missed_packets;
5314                 
5315                 missed_packets = pd->numpackets-Players[TheirPlayernum].n_packets_got;
5316                 if ((pd->numpackets-Players[TheirPlayernum].n_packets_got)>0)
5317                         TotalMissedPackets += pd->numpackets-Players[TheirPlayernum].n_packets_got;
5318
5319                         if ( missed_packets > 0 )       
5320                                 mprintf(( 0, "Missed %d packets from player #%d (%d total)\n", pd->numpackets-Players[TheirPlayernum].n_packets_got, TheirPlayernum, missed_packets ));
5321                         else
5322                                 mprintf( (0, "Got %d late packets from player #%d (%d total)\n", Players[TheirPlayernum].n_packets_got-pd->numpackets, TheirPlayernum, missed_packets ));
5323
5324                 #ifdef MACINTOSH
5325                 #ifdef APPLETALK_DEBUG
5326                 if (Network_game_type == APPLETALK_GAME) {
5327                         if ( missed_packets > 0 )       
5328                                 fprintf( at_fp, "Missed %d packets from player #%d (%d total)\n", pd->numpackets-Players[TheirPlayernum].n_packets_got, TheirPlayernum, missed_packets );
5329                         else
5330                                 fprintf( at_fp, "Got %d late packets from player #%d (%d total)\n", Players[TheirPlayernum].n_packets_got-pd->numpackets, TheirPlayernum, missed_packets );
5331                 }
5332                 #endif
5333                 
5334                 #ifdef IPX_DEBUG
5335                 if (Network_game_type == IPX_GAME) {
5336                         if ( missed_packets > 0 )       
5337                                 fprintf( ipx_fp, "Missed %d packets from player #%d (%d total)\n", pd->numpackets-Players[TheirPlayernum].n_packets_got, TheirPlayernum, missed_packets );
5338                         else
5339                                 fprintf( ipx_fp, "Got %d late packets from player #%d (%d total)\n", Players[TheirPlayernum].n_packets_got-pd->numpackets, TheirPlayernum, missed_packets );
5340                 }
5341                 #endif
5342                 #endif
5343
5344                 Players[TheirPlayernum].n_packets_got = pd->numpackets;
5345         }
5346
5347         //------------ Read the player's ship's object info ----------------------
5348         TheirObj->pos                           = pd->obj_pos;
5349         TheirObj->orient                        = pd->obj_orient;
5350         TheirObj->mtype.phys_info.velocity = pd->phys_velocity;
5351         TheirObj->mtype.phys_info.rotvel = pd->phys_rotvel;
5352
5353         if ((TheirObj->render_type != pd->obj_render_type) && (pd->obj_render_type == RT_POLYOBJ))
5354                 multi_make_ghost_player(TheirPlayernum);
5355
5356         obj_relink(TheirObjnum,pd->obj_segnum);
5357
5358         if (TheirObj->movement_type == MT_PHYSICS)
5359                 set_thrust_from_velocity(TheirObj);
5360
5361         //------------ Welcome them back if reconnecting --------------
5362         if (!Players[TheirPlayernum].connected) {
5363                 Players[TheirPlayernum].connected = 1;
5364
5365                 if (Newdemo_state == ND_STATE_RECORDING)
5366                         newdemo_record_multi_reconnect(TheirPlayernum);
5367
5368                 multi_make_ghost_player(TheirPlayernum);
5369
5370                 create_player_appearance_effect(&Objects[TheirObjnum]);
5371
5372                 digi_play_sample( SOUND_HUD_MESSAGE, F1_0);
5373                 
5374                 ClipRank (&NetPlayers.players[TheirPlayernum].rank);
5375
5376                 if (FindArg("-norankings"))      
5377                         HUD_init_message( "'%s' %s", Players[TheirPlayernum].callsign, TXT_REJOIN );
5378                 else
5379                         HUD_init_message( "%s'%s' %s", RankStrings[NetPlayers.players[TheirPlayernum].rank],Players[TheirPlayernum].callsign, TXT_REJOIN );
5380
5381
5382                 multi_send_score();
5383         }
5384
5385         //------------ Parse the extra data at the end ---------------
5386
5387         if ( pd->data_size>0 )  {
5388                 // pass pd->data to some parser function....
5389                 multi_process_bigdata( pd->data, pd->data_size );
5390         }
5391
5392 }
5393
5394 #ifdef WORDS_BIGENDIAN
5395 void get_short_frame_info(ubyte *old_info, short_frame_info *new_info)
5396 {
5397         int loc = 0;
5398         
5399         new_info->type = old_info[loc];                                     loc++;
5400         /* skip three for pad byte */                                       loc += 3;
5401         memcpy(&(new_info->numpackets), &(old_info[loc]), 4);               loc += 4;
5402         new_info->numpackets = INTEL_INT(new_info->numpackets);
5403         memcpy(new_info->thepos.bytemat, &(old_info[loc]), 9);              loc += 9;
5404         memcpy(&(new_info->thepos.xo), &(old_info[loc]), 2);                loc += 2;
5405         memcpy(&(new_info->thepos.yo), &(old_info[loc]), 2);                loc += 2;
5406         memcpy(&(new_info->thepos.zo), &(old_info[loc]), 2);                loc += 2;
5407         memcpy(&(new_info->thepos.segment), &(old_info[loc]), 2);           loc += 2;
5408         memcpy(&(new_info->thepos.velx), &(old_info[loc]), 2);              loc += 2;
5409         memcpy(&(new_info->thepos.vely), &(old_info[loc]), 2);              loc += 2;
5410         memcpy(&(new_info->thepos.velz), &(old_info[loc]), 2);              loc += 2;
5411         new_info->thepos.xo = INTEL_SHORT(new_info->thepos.xo);
5412         new_info->thepos.yo = INTEL_SHORT(new_info->thepos.yo);
5413         new_info->thepos.zo = INTEL_SHORT(new_info->thepos.zo);
5414         new_info->thepos.segment = INTEL_SHORT(new_info->thepos.segment);
5415         new_info->thepos.velx = INTEL_SHORT(new_info->thepos.velx);
5416         new_info->thepos.vely = INTEL_SHORT(new_info->thepos.vely);
5417         new_info->thepos.velz = INTEL_SHORT(new_info->thepos.velz);
5418
5419         memcpy(&(new_info->data_size), &(old_info[loc]), 2);                loc += 2;
5420         new_info->data_size = INTEL_SHORT(new_info->data_size);
5421         new_info->playernum = old_info[loc];                                loc++;
5422         new_info->obj_render_type = old_info[loc];                          loc++;
5423         new_info->level_num = old_info[loc];                                loc++;
5424         memcpy(new_info->data, &(old_info[loc]), new_info->data_size);
5425 }
5426 #else
5427 #define get_short_frame_info(old_info, new_info) \
5428         memcpy(new_info, old_info, sizeof(short_frame_info))
5429 #endif
5430
5431 void network_read_pdata_short_packet(short_frame_info *pd )
5432 {
5433         int TheirPlayernum;
5434         int TheirObjnum;
5435         object * TheirObj = NULL;
5436         short_frame_info new_pd;
5437
5438 // short frame info is not aligned because of shortpos.  The mac
5439 // will call totally hacked and gross function to fix this up.
5440
5441         if (Network_game_type == IPX_GAME) {
5442                 get_short_frame_info((ubyte *)pd, &new_pd);
5443         } else {
5444                 memcpy(&new_pd, (ubyte *)pd, sizeof(short_frame_info));
5445         }
5446
5447         TheirPlayernum = new_pd.playernum;
5448         TheirObjnum = Players[new_pd.playernum].objnum;
5449
5450         if (TheirPlayernum < 0) {
5451                 Int3(); // This packet is bogus!!
5452                 return;
5453         }
5454         if (!multi_quit_game && (TheirPlayernum >= N_players)) {
5455                 if (Network_status!=NETSTAT_WAITING)
5456                  {
5457                         Int3(); // We missed an important packet!
5458                         network_consistency_error();
5459                         return;
5460                  }
5461                 else
5462                  return;
5463         }
5464
5465    if (VerifyPlayerJoined!=-1 && TheirPlayernum==VerifyPlayerJoined)
5466          {
5467           // Hurray! Someone really really got in the game (I think).
5468       mprintf ((0,"Hurray! VPJ (%d) reset!\n",VerifyPlayerJoined));
5469       VerifyPlayerJoined=-1;
5470          }
5471
5472         if (Endlevel_sequence || (Network_status == NETSTAT_ENDLEVEL) ) {
5473                 int old_Endlevel_sequence = Endlevel_sequence;
5474                 Endlevel_sequence = 1;
5475                 if ( new_pd.data_size>0 )       {
5476                         // pass pd->data to some parser function....
5477                         multi_process_bigdata( new_pd.data, new_pd.data_size );
5478                 }
5479                 Endlevel_sequence = old_Endlevel_sequence;
5480                 return;
5481         }
5482 //      mprintf((0, "Gametime = %d, Frametime = %d.\n", GameTime, FrameTime));
5483
5484         if ((byte)new_pd.level_num != Current_level_num)
5485         {
5486                 mprintf((0, "Got frame packet from player %d wrong level %d!\n", new_pd.playernum, new_pd.level_num));
5487                 return;
5488         }
5489
5490         TheirObj = &Objects[TheirObjnum];
5491
5492         //------------- Keep track of missed packets -----------------
5493         Players[TheirPlayernum].n_packets_got++;
5494         TotalPacketsGot++;
5495         LastPacketTime[TheirPlayernum] = timer_get_approx_seconds();
5496
5497         if  ( new_pd.numpackets != Players[TheirPlayernum].n_packets_got )      {
5498                 int missed_packets;
5499         
5500                 missed_packets = new_pd.numpackets-Players[TheirPlayernum].n_packets_got;
5501                 if ((new_pd.numpackets-Players[TheirPlayernum].n_packets_got)>0)
5502                         TotalMissedPackets += new_pd.numpackets-Players[TheirPlayernum].n_packets_got;
5503
5504                 if ( missed_packets > 0 )       
5505                         mprintf( (0, "Missed %d packets from player #%d (%d total)\n", new_pd.numpackets-Players[TheirPlayernum].n_packets_got, TheirPlayernum, missed_packets ));
5506                 else
5507                         mprintf( (0, "Got %d late packets from player #%d (%d total)\n", Players[TheirPlayernum].n_packets_got-new_pd.numpackets, TheirPlayernum, missed_packets ));
5508
5509                 Players[TheirPlayernum].n_packets_got = new_pd.numpackets;
5510         }
5511
5512         //------------ Read the player's ship's object info ----------------------
5513
5514         extract_shortpos(TheirObj, &new_pd.thepos, 0);
5515
5516         if ((TheirObj->render_type != new_pd.obj_render_type) && (new_pd.obj_render_type == RT_POLYOBJ))
5517                 multi_make_ghost_player(TheirPlayernum);
5518
5519         if (TheirObj->movement_type == MT_PHYSICS)
5520                 set_thrust_from_velocity(TheirObj);
5521
5522         //------------ Welcome them back if reconnecting --------------
5523         if (!Players[TheirPlayernum].connected) {
5524                 Players[TheirPlayernum].connected = 1;
5525
5526                 if (Newdemo_state == ND_STATE_RECORDING)
5527                         newdemo_record_multi_reconnect(TheirPlayernum);
5528
5529                 multi_make_ghost_player(TheirPlayernum);
5530
5531                 create_player_appearance_effect(&Objects[TheirObjnum]);
5532
5533                 digi_play_sample( SOUND_HUD_MESSAGE, F1_0);
5534                 ClipRank (&NetPlayers.players[TheirPlayernum].rank);
5535                 
5536                 if (FindArg("-norankings"))
5537                         HUD_init_message( "'%s' %s", Players[TheirPlayernum].callsign, TXT_REJOIN );
5538                 else
5539                         HUD_init_message( "%s'%s' %s", RankStrings[NetPlayers.players[TheirPlayernum].rank],Players[TheirPlayernum].callsign, TXT_REJOIN );
5540
5541
5542                 multi_send_score();
5543         }
5544
5545         //------------ Parse the extra data at the end ---------------
5546
5547         if ( new_pd.data_size>0 )       {
5548                 // pass pd->data to some parser function....
5549                 multi_process_bigdata( new_pd.data, new_pd.data_size );
5550         }
5551
5552 }
5553
5554   
5555 void network_set_power (void)
5556  {
5557   int opt=0,choice,opt_primary,opt_second,opt_power;
5558   newmenu_item m[40];
5559   
5560   opt_primary=opt;
5561   m[opt].type = NM_TYPE_CHECK; m[opt].text = "Laser upgrade"; m[opt].value=Netgame.DoLaserUpgrade; opt++;
5562   m[opt].type = NM_TYPE_CHECK; m[opt].text = "Super lasers"; m[opt].value=Netgame.DoSuperLaser; opt++;
5563   m[opt].type = NM_TYPE_CHECK; m[opt].text = "Quad Lasers"; m[opt].value=Netgame.DoQuadLasers; opt++;
5564   m[opt].type = NM_TYPE_CHECK; m[opt].text = "Vulcan cannon"; m[opt].value=Netgame.DoVulcan; opt++;
5565   m[opt].type = NM_TYPE_CHECK; m[opt].text = "Spreadfire cannon"; m[opt].value=Netgame.DoSpread; opt++;
5566   m[opt].type = NM_TYPE_CHECK; m[opt].text = "Plasma cannon"; m[opt].value=Netgame.DoPlasma; opt++;
5567   m[opt].type = NM_TYPE_CHECK; m[opt].text = "Fusion cannon"; m[opt].value=Netgame.DoFusions; opt++;
5568   m[opt].type = NM_TYPE_CHECK; m[opt].text = "Gauss cannon"; m[opt].value=Netgame.DoGauss; opt++;
5569   m[opt].type = NM_TYPE_CHECK; m[opt].text = "Helix cannon"; m[opt].value=Netgame.DoHelix; opt++;
5570   m[opt].type = NM_TYPE_CHECK; m[opt].text = "Phoenix cannon"; m[opt].value=Netgame.DoPhoenix; opt++;
5571   m[opt].type = NM_TYPE_CHECK; m[opt].text = "Omega cannon"; m[opt].value=Netgame.DoOmega; opt++;
5572   
5573   opt_second=opt;   
5574   m[opt].type = NM_TYPE_CHECK; m[opt].text = "Homing Missiles"; m[opt].value=Netgame.DoHoming; opt++;
5575   m[opt].type = NM_TYPE_CHECK; m[opt].text = "Proximity Bombs"; m[opt].value=Netgame.DoProximity; opt++;
5576   m[opt].type = NM_TYPE_CHECK; m[opt].text = "Smart Missiles"; m[opt].value=Netgame.DoSmarts; opt++;
5577   m[opt].type = NM_TYPE_CHECK; m[opt].text = "Mega Missiles"; m[opt].value=Netgame.DoMegas; opt++;
5578   m[opt].type = NM_TYPE_CHECK; m[opt].text = "Flash Missiles"; m[opt].value=Netgame.DoFlash; opt++;
5579   m[opt].type = NM_TYPE_CHECK; m[opt].text = "Guided Missiles"; m[opt].value=Netgame.DoGuided; opt++;
5580   m[opt].type = NM_TYPE_CHECK; m[opt].text = "Smart Mines"; m[opt].value=Netgame.DoSmartMine; opt++;
5581   m[opt].type = NM_TYPE_CHECK; m[opt].text = "Mercury Missiles"; m[opt].value=Netgame.DoMercury; opt++;
5582   m[opt].type = NM_TYPE_CHECK; m[opt].text = "EarthShaker Missiles"; m[opt].value=Netgame.DoEarthShaker; opt++;
5583
5584   opt_power=opt;
5585   m[opt].type = NM_TYPE_CHECK; m[opt].text = "Invulnerability"; m[opt].value=Netgame.DoInvulnerability; opt++;
5586   m[opt].type = NM_TYPE_CHECK; m[opt].text = "Cloaking"; m[opt].value=Netgame.DoCloak; opt++;
5587   m[opt].type = NM_TYPE_CHECK; m[opt].text = "Afterburners"; m[opt].value=Netgame.DoAfterburner; opt++;
5588   m[opt].type = NM_TYPE_CHECK; m[opt].text = "Ammo rack"; m[opt].value=Netgame.DoAmmoRack; opt++;
5589   m[opt].type = NM_TYPE_CHECK; m[opt].text = "Energy Converter"; m[opt].value=Netgame.DoConverter; opt++;
5590   m[opt].type = NM_TYPE_CHECK; m[opt].text = "Headlight"; m[opt].value=Netgame.DoHeadlight; opt++;
5591   
5592   choice = newmenu_do(NULL, "Objects to allow", opt, m, NULL);
5593
5594   Netgame.DoLaserUpgrade=m[opt_primary].value; 
5595   Netgame.DoSuperLaser=m[opt_primary+1].value;
5596   Netgame.DoQuadLasers=m[opt_primary+2].value;  
5597   Netgame.DoVulcan=m[opt_primary+3].value;
5598   Netgame.DoSpread=m[opt_primary+4].value;
5599   Netgame.DoPlasma=m[opt_primary+5].value;
5600   Netgame.DoFusions=m[opt_primary+6].value;
5601   Netgame.DoGauss=m[opt_primary+7].value;
5602   Netgame.DoHelix=m[opt_primary+8].value;
5603   Netgame.DoPhoenix=m[opt_primary+9].value;
5604   Netgame.DoOmega=m[opt_primary+10].value;
5605   
5606   Netgame.DoHoming=m[opt_second].value;
5607   Netgame.DoProximity=m[opt_second+1].value;
5608   Netgame.DoSmarts=m[opt_second+2].value;
5609   Netgame.DoMegas=m[opt_second+3].value;
5610   Netgame.DoFlash=m[opt_second+4].value;
5611   Netgame.DoGuided=m[opt_second+5].value;
5612   Netgame.DoSmartMine=m[opt_second+6].value;
5613   Netgame.DoMercury=m[opt_second+7].value;
5614   Netgame.DoEarthShaker=m[opt_second+8].value;
5615
5616   Netgame.DoInvulnerability=m[opt_power].value;
5617   Netgame.DoCloak=m[opt_power+1].value;
5618   Netgame.DoAfterburner=m[opt_power+2].value;
5619   Netgame.DoAmmoRack=m[opt_power+3].value;
5620   Netgame.DoConverter=m[opt_power+4].value;     
5621   Netgame.DoHeadlight=m[opt_power+5].value;     
5622   
5623  }
5624
5625 void SetAllAllowablesTo (int on)
5626  {
5627   last_cinvul = control_invul_time = 0;   //default to zero
5628    
5629   Netgame.DoMegas=Netgame.DoSmarts=Netgame.DoFusions=Netgame.DoHelix=\
5630   Netgame.DoPhoenix=Netgame.DoCloak=Netgame.DoInvulnerability=\
5631   Netgame.DoAfterburner=Netgame.DoGauss=Netgame.DoVulcan=Netgame.DoPlasma=on;
5632
5633   Netgame.DoOmega=Netgame.DoSuperLaser=Netgame.DoProximity=\
5634   Netgame.DoSpread=Netgame.DoMercury=Netgame.DoSmartMine=Netgame.DoFlash=\
5635   Netgame.DoGuided=Netgame.DoEarthShaker=on;
5636   
5637   Netgame.DoConverter=Netgame.DoAmmoRack=Netgame.DoHeadlight=on;
5638   Netgame.DoHoming=Netgame.DoLaserUpgrade=Netgame.DoQuadLasers=on;
5639   Netgame.BrightPlayers=Netgame.invul=on;
5640  }
5641
5642 fix LastPTA;
5643 int LastKillGoal;
5644
5645 // Jeez -- mac compiler can't handle all of these on the same decl line.
5646 int opt_setpower,opt_playtime,opt_killgoal,opt_socket,opt_marker_view,opt_light,opt_show_on_map;
5647 int opt_difficulty,opt_packets,opt_short_packets, opt_bright,opt_start_invul;
5648 int opt_show_names;
5649
5650 void network_more_options_poll( int nitems, newmenu_item * menus, int * key, int citem );
5651   
5652 void network_more_game_options ()
5653  {
5654   int opt=0,i;
5655   char PlayText[80],KillText[80],srinvul[50],socket_string[5],packstring[5];
5656   newmenu_item m[21];
5657
5658   sprintf (socket_string,"%d",Network_socket);
5659   sprintf (packstring,"%d",Netgame.PacketsPerSec);
5660
5661   opt_difficulty = opt;
5662   m[opt].type = NM_TYPE_SLIDER; m[opt].value=netgame_difficulty; m[opt].text=TXT_DIFFICULTY; m[opt].min_value=0; m[opt].max_value=(NDL-1); opt++;
5663
5664   opt_cinvul = opt;
5665   sprintf( srinvul, "%s: %d %s", TXT_REACTOR_LIFE, control_invul_time*5, TXT_MINUTES_ABBREV );
5666   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=10; opt++;
5667
5668   opt_playtime=opt;
5669   sprintf( PlayText, "Max time: %d %s", Netgame.PlayTimeAllowed*5, TXT_MINUTES_ABBREV );
5670   m[opt].type = NM_TYPE_SLIDER; m[opt].value=Netgame.PlayTimeAllowed; m[opt].text= PlayText; m[opt].min_value=0; m[opt].max_value=10; opt++;
5671
5672   opt_killgoal=opt;
5673   sprintf( KillText, "Kill Goal: %d kills", Netgame.KillGoal*5);
5674   m[opt].type = NM_TYPE_SLIDER; m[opt].value=Netgame.KillGoal; m[opt].text= KillText; m[opt].min_value=0; m[opt].max_value=10; opt++;
5675
5676   opt_start_invul=opt;
5677   m[opt].type = NM_TYPE_CHECK; m[opt].text = "Invulnerable when reappearing"; m[opt].value=Netgame.invul; opt++;
5678         
5679   opt_marker_view = opt;
5680   m[opt].type = NM_TYPE_CHECK; m[opt].text = "Allow camera views from Markers"; m[opt].value=Netgame.Allow_marker_view; opt++;
5681   opt_light = opt;
5682   m[opt].type = NM_TYPE_CHECK; m[opt].text = "Indestructible lights"; m[opt].value=Netgame.AlwaysLighting; opt++;
5683
5684   opt_bright = opt;
5685   m[opt].type = NM_TYPE_CHECK; m[opt].text = "Bright player ships"; m[opt].value=Netgame.BrightPlayers; opt++;
5686   
5687   opt_show_names=opt;
5688   m[opt].type = NM_TYPE_CHECK; m[opt].text = "Show enemy names on HUD"; m[opt].value=Netgame.ShowAllNames; opt++;
5689
5690   opt_show_on_map=opt;
5691   m[opt].type = NM_TYPE_CHECK; m[opt].text = TXT_SHOW_ON_MAP; m[opt].value=(Netgame.game_flags & NETGAME_FLAG_SHOW_MAP); opt_show_on_map=opt; opt++;
5692
5693   opt_short_packets=opt;
5694   m[opt].type = NM_TYPE_CHECK; m[opt].text = "Short packets"; m[opt].value=Netgame.ShortPackets; opt++;
5695
5696   opt_setpower = opt;
5697   m[opt].type = NM_TYPE_MENU;  m[opt].text = "Set Objects allowed..."; opt++;
5698
5699   if (Network_game_type == IPX_GAME) {
5700           m[opt].type = NM_TYPE_TEXT; m[opt].text = "Network socket"; opt++;
5701           opt_socket = opt;
5702           m[opt].type = NM_TYPE_INPUT; m[opt].text = socket_string; m[opt].text_len=4; opt++;
5703   }
5704
5705   m[opt].type = NM_TYPE_TEXT; m[opt].text = "Packets per second (2 - 20)"; opt++;
5706   opt_packets=opt;
5707   m[opt].type = NM_TYPE_INPUT; m[opt].text=packstring; m[opt].text_len=2; opt++;
5708
5709   LastKillGoal=Netgame.KillGoal;
5710   LastPTA=Netgame.PlayTimeAllowed;
5711
5712   menu:
5713
5714   ExtGameStatus=GAMESTAT_MORE_NETGAME_OPTIONS; 
5715   i = newmenu_do1( NULL, "Additional netgame options", opt, m, network_more_options_poll, 0 );
5716
5717    //control_invul_time = atoi( srinvul )*60*F1_0;
5718     control_invul_time = m[opt_cinvul].value;
5719     Netgame.control_invul_time = control_invul_time*5*F1_0*60;
5720
5721   if (i==opt_setpower)
5722    {
5723     network_set_power ();
5724     goto menu;
5725    }
5726
5727   Netgame.PacketsPerSec=atoi(packstring);
5728
5729   if (Netgame.PacketsPerSec>20)
5730         {
5731          Netgame.PacketsPerSec=20;
5732          nm_messagebox(TXT_ERROR, 1, TXT_OK, "Packet value out of range\nSetting value to 20");
5733         }
5734   if (Netgame.PacketsPerSec<2)
5735         {
5736           nm_messagebox(TXT_ERROR, 1, TXT_OK, "Packet value out of range\nSetting value to 2");
5737           Netgame.PacketsPerSec=2;      
5738         }
5739
5740   mprintf ((0,"Hey! Sending out %d packets per second\n",Netgame.PacketsPerSec));
5741
5742         if (Network_game_type == IPX_GAME) { 
5743                 if ((atoi(socket_string))!=Network_socket) {
5744                         Network_socket=atoi(socket_string);
5745                         ipx_change_default_socket( IPX_DEFAULT_SOCKET + Network_socket );
5746                 }
5747         }
5748
5749   Netgame.invul=m[opt_start_invul].value;       
5750   Netgame.BrightPlayers=m[opt_bright].value;
5751   Netgame.ShortPackets=m[opt_short_packets].value;
5752   Netgame.ShowAllNames=m[opt_show_names].value;
5753   network_AdjustMaxDataSize();
5754
5755   Netgame.Allow_marker_view=m[opt_marker_view].value;   
5756   Netgame.AlwaysLighting=m[opt_light].value;     
5757   netgame_difficulty=Difficulty_level = m[opt_difficulty].value;
5758   if (m[opt_show_on_map].value)
5759         Netgame.game_flags |= NETGAME_FLAG_SHOW_MAP;
5760         
5761         
5762  }
5763
5764 void network_more_options_poll( int nitems, newmenu_item * menus, int * key, int citem )
5765  {
5766    menus = menus;
5767    citem = citem;      // kills compile warnings
5768    nitems = nitems;
5769    key = key;
5770
5771    if ( last_cinvul != menus[opt_cinvul].value )   {
5772         sprintf( menus[opt_cinvul].text, "%s: %d %s", TXT_REACTOR_LIFE, menus[opt_cinvul].value*5, TXT_MINUTES_ABBREV );
5773         last_cinvul = menus[opt_cinvul].value;
5774         menus[opt_cinvul].redraw = 1;
5775    }
5776   
5777   if (menus[opt_playtime].value!=LastPTA)
5778    {
5779     #ifdef SHAREWARE
5780       LastPTA=0;
5781       nm_messagebox ("Sorry",1,TXT_OK,"Registered version only!");
5782       menus[opt_playtime].value=0;
5783       menus[opt_playtime].redraw=1;
5784       return;
5785     #endif  
5786
5787     if (Game_mode & GM_MULTI_COOP)
5788      {
5789       LastPTA=0;
5790       nm_messagebox ("Sorry",1,TXT_OK,"You can't change those for coop!");
5791       menus[opt_playtime].value=0;
5792       menus[opt_playtime].redraw=1;
5793       return;
5794      }
5795
5796     Netgame.PlayTimeAllowed=menus[opt_playtime].value;
5797     sprintf( menus[opt_playtime].text, "Max Time: %d %s", Netgame.PlayTimeAllowed*5, TXT_MINUTES_ABBREV );
5798     LastPTA=Netgame.PlayTimeAllowed;
5799     menus[opt_playtime].redraw=1;
5800    }
5801   if (menus[opt_killgoal].value!=LastKillGoal)
5802    {
5803     #ifdef SHAREWARE
5804       nm_messagebox ("Sorry",1,TXT_OK,"Registered version only!");
5805       menus[opt_killgoal].value=0;
5806       menus[opt_killgoal].redraw=1;
5807       LastKillGoal=0;
5808       return;
5809          #endif         
5810
5811
5812     if (Game_mode & GM_MULTI_COOP)
5813      {
5814       nm_messagebox ("Sorry",1,TXT_OK,"You can't change those for coop!");
5815       menus[opt_killgoal].value=0;
5816       menus[opt_killgoal].redraw=1;
5817       LastKillGoal=0;
5818       return;
5819      }
5820
5821     
5822     Netgame.KillGoal=menus[opt_killgoal].value;
5823     sprintf( menus[opt_killgoal].text, "Kill Goal: %d kills", Netgame.KillGoal*5);
5824     LastKillGoal=Netgame.KillGoal;
5825     menus[opt_killgoal].redraw=1;
5826    }
5827  }
5828
5829 extern void multi_send_light (int,ubyte);
5830 extern void multi_send_light_specific (int,int,ubyte);
5831
5832 void network_send_smash_lights (int pnum) 
5833  {
5834   // send the lights that have been blown out
5835
5836   int i;
5837
5838   pnum=pnum;
5839   
5840   for (i=0;i<=Highest_segment_index;i++)
5841    if (Light_subtracted[i])
5842     multi_send_light_specific(pnum,i,Light_subtracted[i]);
5843  }
5844
5845 extern int Num_triggers;
5846 extern void multi_send_trigger_specific (char pnum,char);
5847
5848 void network_send_fly_thru_triggers (int pnum) 
5849  {
5850   // send the fly thru triggers that have been disabled
5851
5852   int i;
5853
5854   for (i=0;i<Num_triggers;i++)
5855    if (Triggers[i].flags & TF_DISABLED)
5856     multi_send_trigger_specific(pnum,i);
5857  }
5858
5859 void network_send_player_flags()
5860  {
5861   int i;
5862
5863   for (i=0;i<N_players;i++)
5864         multi_send_flags(i);
5865  }
5866
5867 void network_ping (ubyte flag,int pnum)
5868 {
5869         ubyte mybuf[2];
5870
5871         mybuf[0]=flag;
5872         mybuf[1]=Player_num;
5873
5874         if (Network_game_type == IPX_GAME)
5875                 ipx_send_packet_data( (ubyte *)mybuf, 2, NetPlayers.players[pnum].network.ipx.server, NetPlayers.players[pnum].network.ipx.node,Players[pnum].net_address );
5876         #ifdef MACINTOSH
5877         else
5878                 appletalk_send_packet_data( (ubyte *)mybuf, 2, NetPlayers.players[pnum].network.appletalk.node, NetPlayers.players[pnum].network.appletalk.net, NetPlayers.players[pnum].network.appletalk.socket);
5879         #endif
5880 }
5881
5882 extern fix PingLaunchTime,PingReturnTime;
5883
5884 void network_handle_ping_return (ubyte pnum)
5885  {
5886   if (PingLaunchTime==0 || pnum>=N_players)
5887         {
5888          mprintf ((0,"Got invalid PING RETURN from %s!\n",Players[pnum].callsign));
5889     return;
5890    }
5891   
5892   PingReturnTime=timer_get_fixed_seconds();
5893
5894   HUD_init_message ("Ping time for %s is %d ms!",Players[pnum].callsign,
5895         f2i(fixmul(PingReturnTime-PingLaunchTime,i2f(1000))));
5896   PingLaunchTime=0;
5897  }
5898         
5899 void network_send_ping (ubyte pnum)
5900  {
5901   network_ping (PID_PING_SEND,pnum);
5902  }  
5903
5904 void DoRefuseStuff (sequence_packet *their)
5905  {
5906   int i,new_player_num;
5907
5908   ClipRank (&their->player.rank);
5909         
5910   for (i=0;i<MAX_PLAYERS;i++)
5911          if (!strcmp (their->player.callsign,Players[i].callsign))
5912         {
5913                 network_welcome_player(their);
5914                         return;
5915                 }
5916   
5917    if (!WaitForRefuseAnswer)    
5918     {
5919         for (i=0;i<MAX_PLAYERS;i++)
5920                  if (!strcmp (their->player.callsign,Players[i].callsign))
5921                 {
5922                         network_welcome_player(their);
5923                                 return;
5924                         }
5925    
5926       digi_play_sample (SOUND_HUD_JOIN_REQUEST,F1_0*2);           
5927   
5928       if (Game_mode & GM_TEAM)
5929                  {
5930         
5931                                         
5932       if (!FindArg("-norankings"))
5933               HUD_init_message ("%s %s wants to join",RankStrings[their->player.rank],their->player.callsign);
5934         #ifndef MACINTOSH
5935                 HUD_init_message ("%s joining. Alt-1 assigns to team %s. Alt-2 to team %s",their->player.callsign,Netgame.team_name[0],Netgame.team_name[1]);
5936                 #else
5937                 HUD_init_message ("%s joining. Opt-1 assigns to team %s. Opt-2 to team %s",their->player.callsign,Netgame.team_name[0],Netgame.team_name[1]);
5938                 #endif
5939 //                      HUD_init_message ("Alt-1 to place on team %s!",Netgame.team_name[0]);
5940 //                      HUD_init_message ("Alt-2 to place on team %s!",Netgame.team_name[1]);
5941                  }               
5942                 else    
5943                 HUD_init_message ("%s wants to join...press F6 to connect",their->player.callsign);
5944
5945            strcpy (RefusePlayerName,their->player.callsign);
5946            RefuseTimeLimit=timer_get_approx_seconds();   
5947                 RefuseThisPlayer=0;
5948                 WaitForRefuseAnswer=1;
5949          }
5950         else
5951          {      
5952         for (i=0;i<MAX_PLAYERS;i++)
5953                  if (!strcmp (their->player.callsign,Players[i].callsign))
5954                 {
5955                         network_welcome_player(their);
5956                                 return;
5957                         }
5958       
5959                 if (strcmp(their->player.callsign,RefusePlayerName))
5960                         return;
5961
5962                 if (RefuseThisPlayer)
5963                  {
5964                                 RefuseTimeLimit=0;
5965                                 RefuseThisPlayer=0;
5966                                 WaitForRefuseAnswer=0;
5967                                 if (Game_mode & GM_TEAM)
5968                                  {
5969                                         new_player_num=GetNewPlayerNumber (their);
5970                                         mprintf ((0,"Newplayernum=%d\n",new_player_num));
5971                 
5972                                         Assert (RefuseTeam==1 || RefuseTeam==2);        
5973                                 
5974                                         if (RefuseTeam==1)      
5975                                         Netgame.team_vector &=(~(1<<new_player_num));
5976                                         else
5977                                         Netgame.team_vector |=(1<<new_player_num);
5978                                         network_welcome_player(their);
5979                                         network_send_netgame_update (); 
5980                                  }
5981                                 else
5982                                         network_welcome_player(their);
5983                            return;
5984                  }
5985                                         
5986           if ((timer_get_approx_seconds()) > RefuseTimeLimit+REFUSE_INTERVAL)
5987                 {
5988                         RefuseTimeLimit=0;
5989                         RefuseThisPlayer=0;
5990                         WaitForRefuseAnswer=0;
5991                         if (!strcmp (their->player.callsign,RefusePlayerName)) {
5992                                 if (Network_game_type == IPX_GAME)
5993                                         network_dump_player(their->player.network.ipx.server,their->player.network.ipx.node, DUMP_DORK);
5994                                 #ifdef MACINTOSH
5995                                 else
5996                                         network_dump_appletalk_player(their->player.network.appletalk.node,their->player.network.appletalk.net, their->player.network.appletalk.socket, DUMP_DORK);
5997                                 #endif
5998                         }
5999                         return;
6000                 }
6001                                                         
6002     }
6003  }
6004
6005 int GetNewPlayerNumber (sequence_packet *their)
6006   {
6007          int i;
6008         
6009          their=their;
6010         
6011                 if ( N_players < MaxNumNetPlayers)
6012                         return (N_players);
6013                 
6014                 else
6015                 {
6016                         // Slots are full but game is open, see if anyone is
6017                         // disconnected and replace the oldest player with this new one
6018                 
6019                         int oldest_player = -1;
6020                         fix oldest_time = timer_get_approx_seconds();
6021
6022                         Assert(N_players == MaxNumNetPlayers);
6023
6024                         for (i = 0; i < N_players; i++)
6025                         {
6026                                 if ( (!Players[i].connected) && (LastPacketTime[i] < oldest_time))
6027                                 {
6028                                         oldest_time = LastPacketTime[i];
6029                                         oldest_player = i;
6030                                 }
6031                         }
6032             return (oldest_player);
6033           }
6034   }
6035
6036 void network_send_extras ()
6037  {
6038         Assert (Player_joining_extras>-1);
6039
6040    if (!network_i_am_master())
6041          {
6042           mprintf ((0,"Hey! I'm not the master and I was gonna send info!\n"));
6043          // Int3();     
6044          // Network_sending_extras=0;
6045          // return;
6046     }
6047
6048
6049    if (Network_sending_extras==40)
6050                 network_send_fly_thru_triggers(Player_joining_extras);
6051    if (Network_sending_extras==38)
6052         network_send_door_updates(Player_joining_extras);
6053    if (Network_sending_extras==35)
6054                 network_send_markers();
6055    if (Network_sending_extras==30 && (Game_mode & GM_MULTI_ROBOTS))
6056                 multi_send_stolen_items();
6057         if (Network_sending_extras==25 && (Netgame.PlayTimeAllowed || Netgame.KillGoal))
6058                 multi_send_kill_goal_counts();
6059    if (Network_sending_extras==20)
6060                 network_send_smash_lights(Player_joining_extras);
6061    if (Network_sending_extras==15)
6062                 network_send_player_flags();    
6063    if (Network_sending_extras==10)
6064                 multi_send_powerup_update();  
6065  //  if (Network_sending_extras==5)
6066 //              network_send_door_updates(Player_joining_extras); // twice!
6067
6068         Network_sending_extras--;
6069    if (!Network_sending_extras)
6070          Player_joining_extras=-1;
6071  }
6072
6073
6074 void network_send_naked_packet(char *buf, short len, int who)
6075 {
6076         if (!(Game_mode&GM_NETWORK)) return;
6077
6078    if (NakedPacketLen==0)
6079          {
6080            NakedBuf[0]=PID_NAKED_PDATA;
6081            NakedBuf[1]=Player_num;
6082                 NakedPacketLen=2;
6083          }
6084
6085    if (len+NakedPacketLen>MaxXDataSize)
6086     {
6087 //         mprintf ((0,"Sending a naked packet of %d bytes.\n",NakedPacketLen));
6088                 if (Network_game_type == IPX_GAME)
6089                         ipx_send_packet_data( (ubyte *)NakedBuf, NakedPacketLen, NetPlayers.players[who].network.ipx.server, NetPlayers.players[who].network.ipx.node,Players[who].net_address );
6090                 #ifdef MACINTOSH
6091                 else
6092                         appletalk_send_packet_data( (ubyte *)NakedBuf, NakedPacketLen, NetPlayers.players[who].network.appletalk.node, NetPlayers.players[who].network.appletalk.net, NetPlayers.players[who].network.appletalk.socket );
6093                 #endif
6094                 NakedPacketLen=2;
6095                 memcpy (&NakedBuf[NakedPacketLen],buf,len);     
6096                 NakedPacketLen+=len;
6097                 NakedPacketDestPlayer=who;
6098          }
6099    else
6100          {
6101                 memcpy (&NakedBuf[NakedPacketLen],buf,len);     
6102                 NakedPacketLen+=len;
6103                 NakedPacketDestPlayer=who;
6104          }
6105  }
6106
6107
6108 void network_process_naked_pdata (char *data,int len)
6109  {
6110    int pnum=data[1]; 
6111    Assert (data[0]=PID_NAKED_PDATA);
6112
6113 //   mprintf ((0,"Processing a naked packet of %d length.\n",len));
6114
6115         if (pnum < 0) {
6116            mprintf ((0,"Naked packet is bad!\n"));
6117                 Int3(); // This packet is bogus!!
6118                 return;
6119         }
6120
6121         if (!multi_quit_game && (pnum >= N_players)) {
6122                 if (Network_status!=NETSTAT_WAITING)
6123                  {
6124                         Int3(); // We missed an important packet!
6125                         network_consistency_error();
6126                         return;
6127                  }
6128                 else
6129                  return;
6130         }
6131
6132         if (Endlevel_sequence || (Network_status == NETSTAT_ENDLEVEL) ) {
6133                 int old_Endlevel_sequence = Endlevel_sequence;
6134                 Endlevel_sequence = 1;
6135                 multi_process_bigdata( data+2, len-2);
6136                 Endlevel_sequence = old_Endlevel_sequence;
6137                 return;
6138         }
6139
6140         multi_process_bigdata( data+2, len-2 );
6141  }
6142
6143 int ConnectionPacketLevel[]={0,1,1,1};
6144 int ConnectionSecLevel[]={12,3,5,7};
6145
6146 int AppletalkConnectionPacketLevel[]={0,1,0};
6147 int AppletalkConnectionSecLevel[]={10,3,8};
6148
6149 #if defined(RELEASE) && !defined(MACINTOSH)             // use the code below for mac appletalk games
6150 int network_choose_connect ()
6151  {
6152   return (1);
6153  }
6154 #else
6155 int network_choose_connect ()
6156  {
6157 #if 0
6158         newmenu_item m[16];
6159         int choice,opt=0;
6160 #endif
6161
6162         if (Network_game_type == IPX_GAME) {  
6163                 #if 0
6164            m[opt].type = NM_TYPE_MENU;  m[opt].text = "Local Subnet"; opt++;
6165            m[opt].type = NM_TYPE_MENU;  m[opt].text = "14.4 modem over Internet"; opt++;
6166            m[opt].type = NM_TYPE_MENU;  m[opt].text = "28.8 modem over Internet"; opt++;
6167            m[opt].type = NM_TYPE_MENU;  m[opt].text = "ISDN or T1 over Internet"; opt++;
6168
6169            choice = newmenu_do1( NULL, "Choose connection type", opt, m, NULL, 0 );
6170
6171                 if (choice<0)
6172                  return (NULL);
6173
6174            Assert (choice>=0 && choice<=3);
6175    
6176                 Netgame.ShortPackets=ConnectionPacketLevel[choice];
6177                 Netgame.PacketsPerSec=ConnectionSecLevel[choice];
6178                 #endif
6179            return (1);
6180         #ifdef MACINTOSH
6181         } else {
6182            m[opt].type = NM_TYPE_MENU;  m[opt].text = "EtherTalk"; opt++;
6183            m[opt].type = NM_TYPE_MENU;  m[opt].text = "LocalTalk"; opt++;
6184            m[opt].type = NM_TYPE_MENU;  m[opt].text = "Other"; opt++;
6185         
6186            choice = newmenu_do1( NULL, "Choose connection type", opt, m, NULL, 0 );
6187
6188                 if (choice<0)
6189                  return (NULL);
6190
6191                 Network_appletalk_type = choice;
6192                 
6193                 Assert (choice>=0 && choice<=2);
6194    
6195                 Netgame.ShortPackets=AppletalkConnectionPacketLevel[choice];
6196                 Netgame.PacketsPerSec=AppletalkConnectionSecLevel[choice];
6197            return (1);
6198         #endif
6199         }
6200   return (0);   
6201  }
6202 #endif
6203
6204
6205
6206 #ifdef NETPROFILING
6207 void OpenSendLog ()
6208  {
6209   int i;
6210   SendLogFile=(FILE *)fopen ("sendlog.net","w");
6211   for (i=0;i<100;i++)
6212         TTSent[i]=0;
6213  }
6214 void OpenRecieveLog ()
6215  {
6216   int i;
6217   RecieveLogFile=(FILE *)fopen ("recvlog.net","w");
6218   for (i=0;i<100;i++)
6219         TTRecv[i]=0;
6220  }
6221 #endif
6222
6223 int GetMyNetRanking ()
6224  {
6225   int rank;
6226   int eff;
6227
6228   if (Netlife_kills+Netlife_killed==0)
6229          return (1);
6230  
6231   rank=(int) (((float)Netlife_kills/3000.0)*8.0);
6232  
6233   eff=(int)((float)((float)Netlife_kills/((float)Netlife_killed+(float)Netlife_kills))*100.0);
6234
6235   if (rank>8)
6236    rank=8;
6237   
6238   if (eff<0)
6239          eff=0;
6240  
6241   if (eff<60)
6242         rank-=((59-eff)/10);
6243         
6244   if (rank<0)
6245         rank=0;
6246   if (rank>8)
6247         rank=8;
6248  
6249   mprintf ((0,"Rank is %d (%s)\n",rank+1,RankStrings[rank+1]));
6250   return (rank+1);
6251  }
6252
6253 void ClipRank (signed char *rank)
6254  {
6255   // This function insures no crashes when dealing with D2 1.0
6256
6257  
6258   if (*rank<0 || *rank>9)
6259         *rank=0;
6260  }
6261 void network_check_for_old_version (char pnum)
6262  {  
6263   if (NetPlayers.players[(int)pnum].version_major==1 && (NetPlayers.players[(int)pnum].version_minor & 0x0F)==0)
6264         NetPlayers.players[(int)pnum].rank=0;
6265  }
6266
6267 void network_request_player_names (int n)
6268  {
6269   network_send_all_info_request (PID_GAME_PLAYERS,Active_games[n].Security);
6270   NamesInfoSecurity=Active_games[n].Security;
6271  }
6272
6273 extern char already_showing_info;
6274 extern int newmenu_dotiny2( char * title, char * subtitle, int nitems, newmenu_item * item, void (*subfunction)(int nitems,newmenu_item * items, int * last_key, int citem));
6275
6276 void network_process_names_return (char *data)
6277  {
6278         newmenu_item m[15];
6279    char mtext[15][50],temp[50];
6280         int i,t,l,gnum,num=0,count=5,numplayers;
6281    
6282    if (NamesInfoSecurity!=(*(int *)(data+1)))
6283          {
6284           mprintf ((0,"Bad security on names return!\n"));
6285           mprintf ((0,"NIS=%d data=%d\n",NamesInfoSecurity,(*(int *)(data+1))));
6286           return;
6287          }
6288
6289    numplayers=data[count]; count++;
6290
6291    if (numplayers==255)
6292          {
6293                  SurfingNet=0;  
6294                  NamesInfoSecurity=-1;
6295                  nm_messagebox(NULL, 1, "OK", "That game is refusing\nname requests.\n");
6296                  SurfingNet=1;
6297                  return;
6298          }
6299
6300    Assert (numplayers>0 && numplayers<MAX_NUM_NET_PLAYERS);
6301         
6302    for (i=0;i<12;i++)
6303         {
6304          m[i].text=(char *)&mtext[i];
6305     m[i].type=NM_TYPE_TEXT;             
6306         }
6307
6308    for (gnum=-1,i=0;i<num_active_games;i++)
6309          {
6310           if (NamesInfoSecurity==Active_games[i].Security)
6311                 {
6312                  gnum=i;
6313                  break;
6314                 }
6315          }
6316         
6317         if (gnum==-1)
6318     {
6319        SurfingNet=0;
6320                  NamesInfoSecurity=-1;
6321                  nm_messagebox(NULL, 1, "OK", "The game you have requested\nnames from is gone.\n");
6322                  SurfingNet=1;
6323                  return;
6324          }
6325  
6326    sprintf (mtext[num],"Players of game '%s':",Active_games[gnum].game_name); num++;
6327    for (i=0;i<numplayers;i++)
6328          {
6329           l=data[count++];
6330
6331      mprintf ((0,"%s\n",data+count));
6332
6333           for (t=0;t<CALLSIGN_LEN+1;t++)
6334                  temp[t]=data[count++];   
6335      if (FindArg("-norankings"))        
6336              sprintf (mtext[num],"%s",temp);
6337           else
6338              sprintf (mtext[num],"%s%s",RankStrings[l],temp);
6339         
6340           num++;        
6341          }
6342
6343         if (data[count]==99)
6344         {
6345          sprintf (mtext[num++]," ");
6346          sprintf (mtext[num++],"Short packets: %s",data[count+1]?"On":"Off");
6347          sprintf (mtext[num++],"Packets Per Second: %d",data[count+2]);
6348    }
6349
6350         already_showing_info=1; 
6351         newmenu_dotiny2( NULL, NULL, num, m, NULL);
6352         already_showing_info=0; 
6353  }
6354
6355 char NameReturning=1;
6356
6357 void network_send_player_names (sequence_packet *their)
6358  {
6359   int numconnected=0,count=0,i,t;
6360   char buf[80];
6361
6362   if (!their)
6363    {
6364     mprintf ((0,"Got a player name without a return address! Get Jason\n"));
6365          return;
6366         }
6367
6368    buf[0]=PID_NAMES_RETURN; count++;
6369    (*(int *)(buf+1))=Netgame.Security; count+=4;
6370
6371    if (!NameReturning)
6372          {
6373      buf[count]=255; count++; 
6374           goto sendit;
6375          }
6376  
6377    mprintf ((0,"RealSec=%d DS=%d\n",Netgame.Security,*(int *)(buf+1)));
6378   
6379    for (i=0;i<N_players;i++)
6380          if (Players[i].connected)
6381                 numconnected++;
6382
6383    buf[count]=numconnected; count++; 
6384    for (i=0;i<N_players;i++)
6385          if (Players[i].connected)
6386           {
6387                 buf[count++]=NetPlayers.players[i].rank; 
6388                 
6389                 for (t=0;t<CALLSIGN_LEN+1;t++)
6390                  {
6391                   buf[count]=NetPlayers.players[i].callsign[t];
6392                   count++;
6393                  }
6394           }
6395
6396    buf[count++]=99;
6397         buf[count++]=Netgame.ShortPackets;              
6398         buf[count++]=Netgame.PacketsPerSec;
6399   
6400    sendit:
6401                                 ;               // empty statement allows compilation on mac which does not have the
6402                                                 // statement below and kills the compiler when there is no
6403                                                 // statement following the label "sendit"
6404            
6405    #ifndef MACINTOSH
6406            ipx_send_internetwork_packet_data((ubyte *)buf, count, their->player.network.ipx.server, their->player.network.ipx.node);
6407         #endif
6408  }
6409
6410
6411 int HoardEquipped()
6412 {
6413         static int checked=-1;
6414
6415 #ifdef WINDOWS
6416         return 0;
6417 #endif
6418
6419         if (checked==-1)
6420         {
6421                 if (cfexist("hoard.ham"))
6422                         checked=1;
6423                 else
6424                         checked=0;
6425         }
6426         return (checked);
6427 }