]> icculus.org git repositories - taylor/freespace2.git/blob - src/network/multiutil.cpp
Freespace 1 support
[taylor/freespace2.git] / src / network / multiutil.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell 
5  * or otherwise commercially exploit the source or things you created based on
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/Network/MultiUtil.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * C file that contains misc. functions to support multiplayer
16  *
17  * $Log$
18  * Revision 1.9  2003/05/25 02:30:43  taylor
19  * Freespace 1 support
20  *
21  * Revision 1.8  2002/06/17 06:33:10  relnev
22  * ryan's struct patch for gcc 2.95
23  *
24  * Revision 1.7  2002/06/09 04:41:24  relnev
25  * added copyright header
26  *
27  * Revision 1.6  2002/06/05 08:05:29  relnev
28  * stub/warning removal.
29  *
30  * reworked the sound code.
31  *
32  * Revision 1.5  2002/06/02 04:26:34  relnev
33  * warning cleanup
34  *
35  * Revision 1.4  2002/05/27 00:40:47  theoddone33
36  * Fix net_addr vs net_addr_t
37  *
38  * Revision 1.3  2002/05/26 20:49:54  theoddone33
39  * More progress
40  *
41  * Revision 1.2  2002/05/07 03:16:47  theoddone33
42  * The Great Newline Fix
43  *
44  * Revision 1.1.1.1  2002/05/03 03:28:10  root
45  * Initial import.
46  *
47  * 
48  * 47    9/15/99 1:45a Dave
49  * Don't init joystick on standalone. Fixed campaign mode on standalone.
50  * Fixed no-score-report problem in TvT
51  * 
52  * 46    9/09/99 8:53p Dave
53  * Fixed multiplayer degenerate orientation case problem. Make sure warp
54  * effect never goes lower than LOD 1. 
55  * 
56  * 45    8/24/99 1:50a Dave
57  * Fixed client-side afterburner stuttering. Added checkbox for no version
58  * checking on PXO join. Made button info passing more friendly between
59  * client and server.
60  * 
61  * 44    8/23/99 10:30a Dave
62  * Make sure a kill limit of 0 really means "no kill limit"
63  * 
64  * 43    8/22/99 5:53p Dave
65  * Scoring fixes. Added self destruct key. Put callsigns in the logfile
66  * instead of ship designations for multiplayer players.
67  * 
68  * 42    8/22/99 1:19p Dave
69  * Fixed up http proxy code. Cleaned up scoring code. Reverse the order in
70  * which d3d cards are detected.
71  * 
72  * 41    8/19/99 10:59a Dave
73  * Packet loss detection.
74  * 
75  * 40    8/06/99 2:44a Dave
76  * Make sure dead players who leave respawn AI.
77  * 
78  * 39    8/06/99 12:29a Dave
79  * Multiple bug fixes.
80  * 
81  * 38    8/03/99 11:02p Dave
82  * Maybe fixed sync problems in multiplayer.
83  * 
84  * 37    7/30/99 7:01p Dave
85  * Dogfight escort gauge. Fixed up laser rendering in Glide.
86  * 
87  * 36    7/26/99 5:50p Dave
88  * Revised ingame join. Better? We'll see....
89  * 
90  * 35    7/15/99 9:20a Andsager
91  * FS2_DEMO initial checkin
92  * 
93  * 34    7/08/99 10:53a Dave
94  * New multiplayer interpolation scheme. Not 100% done yet, but still
95  * better than the old way.
96  * 
97  * 33    6/25/99 5:04p Jasenw
98  * Added some debug line stuff.
99  * 
100  * 32    6/16/99 4:06p Dave
101  * New pilot info popup. Added new draw-bitmap-as-poly function.
102  * 
103  * 31    5/18/99 11:50a Andsager
104  * Remove unused object type OBJ_GHOST_SAVE
105  * 
106  * 30    5/04/99 5:20p Dave
107  * Fixed up multiplayer join screen and host options screen. Should both
108  * be at 100% now.
109  * 
110  * 29    5/03/99 8:33p Dave
111  * New version of multi host options screen.
112  * 
113  * 28    4/27/99 12:16a Dave
114  * Fixed beam weapon muzzle glow problem. Fixed premature timeout on the
115  * pxo server list screen. Fixed secondary firing for hosts on a
116  * standalone. Fixed wacky multiplayer weapon "shuddering" problem.
117  * 
118  * 27    4/25/99 7:43p Dave
119  * Misc small bug fixes. Made sun draw properly.
120  * 
121  * 26    4/25/99 3:02p Dave
122  * Build defines for the E3 build.
123  * 
124  * 25    4/09/99 2:21p Dave
125  * Multiplayer beta stuff. CD checking.
126  * 
127  * 24    4/08/99 2:10a Dave
128  * Numerous bug fixes for the beta. Added builtin mission info for the
129  * beta.
130  * 
131  * 23    3/10/99 6:50p Dave
132  * Changed the way we buffer packets for all clients. Optimized turret
133  * fired packets. Did some weapon firing optimizations.
134  * 
135  * 22    3/09/99 6:24p Dave
136  * More work on object update revamping. Identified several sources of
137  * unnecessary bandwidth.
138  * 
139  * 21    3/08/99 7:03p Dave
140  * First run of new object update system. Looks very promising.
141  * 
142  * 20    3/01/99 10:00a Dave
143  * Fxied several dogfight related stats bugs.
144  * 
145  * 19    2/24/99 2:25p Dave
146  * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn
147  * bug for dogfight more.
148  * 
149  * 18    2/23/99 2:29p Dave
150  * First run of oldschool dogfight mode. 
151  * 
152  * 17    2/12/99 6:16p Dave
153  * Pre-mission Squad War code is 95% done.
154  * 
155  * 16    2/11/99 3:08p Dave
156  * PXO refresh button. Very preliminary squad war support.
157  * 
158  * 15    2/08/99 5:07p Dave
159  * FS2 chat server support. FS2 specific validated missions.
160  * 
161  * 14    1/30/99 1:29a Dave
162  * Fixed nebula thumbnail problem. Full support for 1024x768 choose pilot
163  * screen.  Fixed beam weapon death messages.
164  * 
165  * 13    1/14/99 6:06p Dave
166  * 100% full squad logo support for single player and multiplayer.
167  * 
168  * 12    1/12/99 4:07a Dave
169  * Put in barracks code support for selecting squad logos. Properly
170  * distribute squad logos in a multiplayer game.
171  * 
172  * 11    12/14/98 4:01p Dave
173  * Got multi_data stuff working well with new xfer stuff. 
174  * 
175  * 10    12/14/98 12:13p Dave
176  * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
177  * Need to test now.
178  * 
179  * 9     11/19/98 4:19p Dave
180  * Put IPX sockets back in psnet. Consolidated all multiplayer config
181  * files into one.
182  * 
183  * 8     11/19/98 8:04a Dave
184  * Full support for D3-style reliable sockets. Revamped packet lag/loss
185  * system, made it receiver side and at the lowest possible level.
186  * 
187  * 7     11/17/98 11:12a Dave
188  * Removed player identification by address. Now assign explicit id #'s.
189  * 
190  * 6     11/05/98 5:55p Dave
191  * Big pass at reducing #includes
192  * 
193  * 5     10/13/98 9:29a Dave
194  * Started neatening up freespace.h. Many variables renamed and
195  * reorganized. Added AlphaColors.[h,cpp]
196  * 
197  * 4     10/09/98 2:57p Dave
198  * Starting splitting up OS stuff.
199  * 
200  * 3     10/07/98 6:27p Dave
201  * Globalized mission and campaign file extensions. Removed Silent Threat
202  * special code. Moved \cache \players and \multidata into the \data
203  * directory.
204  * 
205  * 2     10/07/98 10:53a Dave
206  * Initial checkin.
207  * 
208  * 1     10/07/98 10:50a Dave
209  * 
210  * 283   9/28/98 1:54p Dave
211  * Make sure French and German don't xfer builtin files they don't have
212  * (to themselves)
213  * 
214  * 282   9/28/98 1:41p Dave
215  * Properly detect "builtin" missions.
216  * 
217  * 281   9/21/98 11:46p Dave
218  * Flag mission disk players in the right spot.
219  * 
220  * 280   9/15/98 7:24p Dave
221  * Minor UI changes. Localized bunch of new text.
222  * 
223  * $NoKeywords: $
224  */
225
226 #ifndef PLAT_UNIX
227 #include <winsock.h>
228 #else
229 #include <sys/types.h>
230 #include <sys/socket.h>
231 #include <netinet/in.h>
232 #include <arpa/inet.h>
233 #include <netdb.h>
234 #include <errno.h>
235 #endif
236 #include <ctype.h>
237 #include "multiutil.h"
238 #include "multimsgs.h"
239 #include "multi.h"
240 #include "linklist.h"
241 #include "gamesequence.h"
242 #include "hudmessage.h"
243 #include "freespace.h"
244 #include "key.h"
245 #include "2d.h"
246 #include "weapon.h"
247 #include "timer.h"
248 #include "ship.h"
249 #include "psnet.h"
250 #include "player.h"
251 #include "missionparse.h"
252 #include "missionshipchoice.h"
253 #include "missionscreencommon.h"
254 #include "missionweaponchoice.h"
255 #include "multi_xfer.h"
256 #include "stand_gui.h"
257 #include "shipfx.h"
258 #include "object.h"
259 #include "managepilot.h"
260 #include "multiteamselect.h"
261 #include "shiphit.h"
262 #include "missiondebrief.h"
263 #include "observer.h"
264 #include "missionmessage.h"
265 #include "multilag.h"
266 #include "popup.h"
267 #include "popupdead.h"
268 #include "hudconfig.h"
269 #include "multiui.h"
270 #include "optionsmenu.h"
271 #include "missionhotkey.h"
272 #include "missiongoals.h"
273 #include "afterburner.h"
274 #include "chatbox.h"
275 #include "multi_kick.h"
276 #include "multi_data.h"
277 #include "multi_voice.h"
278 #include "multi_ping.h"
279 #include "multi_team.h"
280 #include "multi_respawn.h"
281 #include "multi_ingame.h"
282 #include "multi_observer.h"
283 #include "multi_pinfo.h"
284 #include "multi_endgame.h"
285 #include "multi_pmsg.h"
286 #include "multi_pause.h"
287 #include "multi_obj.h"
288 #include "multi_log.h"
289 #include "multi_rate.h"
290 #include "osregistry.h"
291 #include "hudescort.h"
292
293 extern int MSG_WINDOW_X_START;  // used to position multiplayer text messages
294 extern int MSG_WINDOW_Y_START;
295 extern int MSG_WINDOW_HEIGHT;
296
297 extern int ascii_table[];
298 extern int shifted_ascii_table[];
299
300 // if a client doesn't receive an update for an object after this many seconds, query server
301 // as to the objects status.
302 #define MULTI_CLIENT_OBJ_TIMEOUT                10
303 #define MAX_SHIPS_PER_QUERY                     10
304
305
306 // this function assignes the given object with the given signature.  If the signature is 0, then we choose
307 // the next signature number from the correct pool.  I thought that it might be desireable
308 // to not always have to take the next signature on the list.  what_kind is used to assign either a
309 // permanent or non-permanent signature to an object.  permanent signatures are used for ships, non_permanent
310 // signatures are used for everything else.
311 ushort multi_assign_network_signature( int what_kind )
312 {
313         ushort sig;     
314
315         // do limit checking on the permanent and non_permanent signatures.  Ships are considered "permanent"
316         // as are debris and asteroids since they don't die very often.  It would be vary rare for this
317         // value (the permanent signature) to wrap.  For now, this condition is an error condition
318         if ( what_kind == MULTI_SIG_SHIP ) {
319                 if ( Next_ship_signature == 0 ){
320                         Next_ship_signature = SHIP_SIG_MIN;
321                 }
322
323                 sig = Next_ship_signature++;
324
325                 if ( Next_ship_signature == SHIP_SIG_MAX ) {
326                         Int3();                 // get Allender -- signature stuff wrapped.
327                         Next_ship_signature = SHIP_SIG_MIN;
328                 }
329
330                 // signature stuff for asteroids.
331         } else if ( what_kind == MULTI_SIG_ASTEROID ) {
332                 if ( Next_asteroid_signature == 0 ){
333                         Next_asteroid_signature = ASTEROID_SIG_MIN;
334                 }
335
336                 sig = Next_asteroid_signature++;
337                 if ( Next_asteroid_signature == ASTEROID_SIG_MAX ) {
338                         Int3();                 // get Allender -- signature stuff wrapped.
339                         Next_asteroid_signature = ASTEROID_SIG_MIN;
340                 }
341
342                 // signatures for debris
343         } else if ( what_kind == MULTI_SIG_DEBRIS ) {
344                 if ( Next_debris_signature == 0 ){
345                         Next_debris_signature = DEBRIS_SIG_MIN;
346                 }
347
348                 sig = Next_debris_signature++;
349                 if ( Next_debris_signature == DEBRIS_SIG_MAX ) {
350                         Int3();                 // get Allender -- signature stuff wrapped.
351                         Next_debris_signature = DEBRIS_SIG_MIN;
352                 }
353
354                 // signature stuff for weapons and other expendable things.
355         } else if ( what_kind == MULTI_SIG_NON_PERMANENT ) {
356                 if ( Next_non_perm_signature == 0 ){
357                         Next_non_perm_signature = NPERM_SIG_MIN;
358                 }
359
360                 sig = Next_non_perm_signature++;
361                 if ( Next_non_perm_signature == NPERM_SIG_MAX ){
362                         Next_non_perm_signature = NPERM_SIG_MIN;
363                 }
364         } else {
365                 Int3();         // get allender - Illegal signature type requested
366                 sig = 0;
367         }
368
369         return sig;
370 }
371
372 // this function returns the next network signature that will be used for a newly created object
373 // what_kind parameter tells us what kind of signature to get -- permanent or non-permanent
374 ushort multi_get_next_network_signature( int what_kind )
375 {
376         if ( what_kind == MULTI_SIG_SHIP ) {
377                 if ( Next_ship_signature == 0 )
378                         Next_ship_signature = SHIP_SIG_MIN;
379                 return Next_ship_signature;
380
381         } else if ( what_kind == MULTI_SIG_DEBRIS ) {
382                 if ( Next_debris_signature == 0 )
383                         Next_debris_signature = DEBRIS_SIG_MIN;
384                 return Next_debris_signature;
385
386         } else if ( what_kind == MULTI_SIG_ASTEROID ) {
387                 if ( Next_asteroid_signature == 0 )
388                         Next_asteroid_signature = ASTEROID_SIG_MIN;
389                 return Next_asteroid_signature;
390
391         } else if ( what_kind == MULTI_SIG_NON_PERMANENT ) {
392                 if ( Next_non_perm_signature == 0 )
393                         Next_non_perm_signature = NPERM_SIG_MIN;
394                 return Next_non_perm_signature;
395
396         } else {
397                 Int3();                 // get allender
398                 return 0;
399         }
400 }
401
402 // this routine sets the network signature to the given value.  Should be called from client only
403 // and is used mainly for firing weapons.  what_kind tells us permanent or non-permanent signature
404 void multi_set_network_signature( ushort signature, int what_kind )
405 {
406         Assert( signature != 0 );
407
408         if ( what_kind == MULTI_SIG_SHIP ) {
409                 Assert( (signature >= SHIP_SIG_MIN) && (signature <= SHIP_SIG_MAX) );
410                 Next_ship_signature = signature;
411         } else if ( what_kind == MULTI_SIG_DEBRIS ) {
412                 Assert( (signature >= DEBRIS_SIG_MIN) && (signature <= DEBRIS_SIG_MAX) );
413                 Next_debris_signature = signature;
414         } else if ( what_kind == MULTI_SIG_ASTEROID ) {
415                 Assert( (signature >= ASTEROID_SIG_MIN) && (signature <= ASTEROID_SIG_MAX) );
416                 Next_asteroid_signature = signature;
417         } else if ( what_kind == MULTI_SIG_NON_PERMANENT ) {
418                 Assert( (signature >= NPERM_SIG_MIN) && (signature <= NPERM_SIG_MAX) );
419                 Next_non_perm_signature = signature;
420         } else
421                 Int3();                 // get Allender
422 }
423
424 // multi_get_network_object() takes a net_signature and tries to locate the object in the object list
425 // with that network signature.  Returns NULL if the object cannot be found
426 object *multi_get_network_object( ushort net_signature )
427 {
428         object *objp;
429
430         if ( net_signature == 0 )
431                 return NULL;
432
433         if(GET_FIRST(&obj_used_list) == NULL)
434                 return NULL;
435
436         for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) )
437                 if ( objp->net_signature == net_signature )
438                         break;
439
440         // if not found on used list, check create list
441         if ( objp == END_OF_LIST(&obj_used_list) ) {
442                 for ( objp = GET_FIRST(&obj_create_list); objp != END_OF_LIST(&obj_create_list); objp = GET_NEXT(objp) )
443                         if ( objp->net_signature == net_signature )
444                                 break;
445
446                 if ( objp == END_OF_LIST(&obj_create_list) )
447                         objp = NULL;
448         }
449
450         return objp;
451 }
452
453
454 // -------------------------------------------------------------------------------------------------
455 //      multi_random_death_word() will return a word from the below list at random.
456 //
457 //      Note:  Keep words grouped into sections of 10
458
459 #define NUM_DEATH_WORDS 40
460
461 char *multi_random_death_word()
462 {
463         int index;
464
465         index = rand() % NUM_DEATH_WORDS;
466         switch (index) {
467                 case 0:
468                         return XSTR("zapped",853);
469                 case 1:
470                         return XSTR("caulked",854);
471                 case 2:
472                         return XSTR("killed",855);
473                 case 3:
474                         return XSTR("waxed",856);
475                 case 4:
476                         return XSTR("popped",857);
477                 case 5:
478                         return XSTR("murdered",858);
479                 case 6:
480                         return XSTR("bludgeoned",859);
481                 case 7:
482                         return XSTR("destroyed",860);
483                 case 8:
484                         return XSTR("iced",861);
485                 case 9:
486                         return XSTR("obliterated",862);
487                 case 10:
488                         return XSTR("toasted",863);
489                 case 11:
490                         return XSTR("roasted",864);
491                 case 12:
492                         return XSTR("turned into anti-matter",865);
493                 case 13:
494                         return XSTR("killed like a pig",866);
495                 case 14:
496                         return XSTR("taught a lesson",867);
497                 case 15:
498                         return XSTR("slaughtered with impunity",868);
499                 case 16:
500                         return XSTR("spanked like a naughty boy",869);
501                 case 17:
502                         return XSTR("skunked",870);
503                 case 18:
504                         return XSTR("beat senseless",871);
505                 case 19:
506                         return XSTR("shot up",872);
507                 case 20:
508                         return XSTR("spaced",873);
509                 case 21:
510                         return XSTR("hosed",874);
511                 case 22:
512                         return XSTR("capped",875);
513                 case 23:
514                         return XSTR("beat down",876);
515                 case 24:
516                         return XSTR("hit wit da shizzo",877);
517                 case 25:
518                         return XSTR("sk00led",878);
519                 case 26:
520                         return XSTR("whooped up",879);
521                 case 27:
522                         return XSTR("brought to the all-you-can-take whoop ass buffet",880);
523                 case 28:
524                         return XSTR("served up a whoop ass sandwich...hold the mercy",881);
525                 case 29:
526                         return XSTR("gibbed by Kayser Sozay's rocket",882);
527                 case 30:
528                         return XSTR("shot down",883);
529                 case 31:
530                         return XSTR("given early retirement",884);
531                 case 32:
532                         return XSTR("instructed",885);
533                 case 33:
534                         return XSTR("eviscerated",886);
535                 case 34:
536                         return XSTR("pummelled",887);
537                 case 35:
538                         return XSTR("eradicated",888);
539                 case 36:
540                         return XSTR("cleansed",889);
541                 case 37:
542                         return XSTR("perforated",890);
543                 case 38:
544                         return XSTR("canned",891);
545                 case 39:
546                         return XSTR("decompressed",892);
547         }
548
549         return NOX("Error");
550 }
551
552 // -------------------------------------------------------------------------------------------------
553 //      multi_random_chat_start() will return a word from the below list at random.
554 //
555 //
556
557 #define NUM_CHAT_START_WORDS    8
558 #define MAX_CHAT_PHRASE_LEN     25              // be careful not to exceed (or update if exceeded)
559
560 char *multi_random_chat_start()
561 {
562         int index;
563
564         index = rand() % NUM_CHAT_START_WORDS;
565         switch (index) {
566                 case 0:
567                         return XSTR("says",893);
568                 case 1:
569                         return XSTR("bleats",894);
570                 case 2:
571                         return XSTR("opines",895);
572                 case 3:
573                         return XSTR("postulates",896);
574                 case 4:
575                         return XSTR("declares",897);
576                 case 5:
577                         return XSTR("vomits out",898);
578                 case 6:
579                         return XSTR("whines",899);
580                 case 7:
581                         return XSTR("barks",900);
582         }
583
584         return NOX("Error");
585 }
586
587 // -------------------------------------------------------------------------------------------------
588 //      multi_ship_class_lookup() will return the Ship_info[] index for the ship specified as a 
589 // parameter.
590 //
591 //
592
593 int multi_ship_class_lookup(char* ship_name)
594 {
595         int i, player_ship_class;
596
597         // find the ship_info index for the ship_name
598
599         player_ship_class = -1;
600         for (i = 0; i < Num_ship_types; i++) {
601                 if ( !stricmp(Ship_info[i].name, ship_name) ) {
602                         player_ship_class = i;
603                         break;
604                 }
605         } // end for 
606
607         if (i == Num_ship_types){
608                 return -1;
609         }
610
611         return player_ship_class;
612 }
613
614 // -------------------------------------------------------------------------------------------------
615 //      find_player() is called when a packet arrives, and we need to know which net player to update.
616 // The matching is done based on the address and port.  Port checking is done in case multiple
617 // instances of FreeSpace are running on the same box.
618 //
619 //
620
621 int find_player( net_addr_t* addr )
622 {
623         int i;
624
625         for (i = 0; i < MAX_PLAYERS; i++ ) {
626                 if ( !MULTI_CONNECTED(Net_players[i])){
627                         continue;
628                 }
629                 if ( psnet_same( addr, &(Net_players[i].p_info.addr)) ){
630                         return i;
631                 }
632         }
633
634         return -1;
635 }
636
637 // so that we can lookup on the admin port transparently
638 int find_player_no_port(net_addr_t *addr)
639 {
640         int i;
641         int len;
642
643         for (i = 0; i < MAX_PLAYERS; i++ ) {
644                 if ( !MULTI_CONNECTED(Net_players[i])){
645                         continue;
646                 }
647                 if(addr->type == NET_IPX){
648                         len = 6;
649                 } else { 
650                         len = 4;
651                 }
652                 if ( memcmp(&addr->addr,&Net_players[i].p_info.addr.addr,len)== 0){
653                         return i;
654                 }
655         }
656
657         return -1;
658 }
659
660 int find_player_id(short player_id)
661 {
662         int i;
663         for (i = 0; i < MAX_PLAYERS; i++ ) {
664                 if ( !MULTI_CONNECTED(Net_players[i])){
665                         continue;
666                 }
667                 if(player_id == Net_players[i].player_id){
668                         return i;
669                 }
670         }       
671
672         // couldn't find the player
673         return -1;
674 }
675
676 // note this is only valid to do on a server!
677 int find_player_socket(PSNET_SOCKET_RELIABLE sock)
678 {
679         int i;
680         for (i = 0; i < MAX_PLAYERS; i++ ) {
681                 if ( !MULTI_CONNECTED(Net_players[i])){
682                         continue;
683                 }
684                 if(sock == Net_players[i].reliable_socket){
685                         return i;
686                 }
687         }       
688
689         // couldn't find the player
690         return -1;
691 }
692
693 // multi_find_player_by_object returns a player num (reference by Net_players[x]) when given a object *.
694 // used to find netplayers in game when only the object is known
695 int multi_find_player_by_object( object *objp )
696 {
697         int i, objnum;
698
699         objnum = OBJ_INDEX(objp);
700         for (i = 0; i < MAX_PLAYERS; i++ ) {
701                 if ( !MULTI_CONNECTED(Net_players[i])){
702                         continue;
703                 }
704                 if ( objnum == Net_players[i].player->objnum ){
705                         return i;
706                 }
707         }
708
709         // return -1 if the object is not found -- this is a bad situation, but we will handle it in higher
710         // level code
711         return -1;
712 }
713
714 // returns a player num based upon player object signature
715 int multi_find_player_by_signature( int signature )
716 {
717         int idx;
718
719         for(idx=0;idx<MAX_PLAYERS;idx++){
720                 // compare against each player's object signature
721                 if(MULTI_CONNECTED(Net_players[idx]) && (Objects[Net_players[idx].player->objnum].signature == signature)){
722                         // found the player
723                         return idx;
724                 }
725         }
726
727         // didn't find the player
728         return -1;
729 }
730
731 // returns a player num based upon object net signature
732 int multi_find_player_by_net_signature(ushort net_signature)
733 {
734         int idx;
735
736         for(idx=0;idx<MAX_PLAYERS;idx++){
737                 // compare against each player's object signature
738                 if(MULTI_CONNECTED(Net_players[idx]) && (Objects[Net_players[idx].player->objnum].net_signature == net_signature)){
739                         // found the player
740                         return idx;
741                 }
742         }
743
744         // didn't find the player
745         return -1;
746 }
747
748 int multi_find_player_by_ship_name(char *ship_name)
749 {
750         int idx;
751
752         // bogus
753         if(ship_name == NULL){
754                 return -1;
755         }
756
757         for(idx=0; idx<MAX_PLAYERS; idx++){
758                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_OBSERVER(Net_players[idx]) && (Net_players[idx].player != NULL) && (Net_players[idx].player->objnum >= 0) && (Net_players[idx].player->objnum < MAX_OBJECTS) && (Objects[Net_players[idx].player->objnum].type == OBJ_SHIP) && 
759                         (Objects[Net_players[idx].player->objnum].instance >= 0) && (Objects[Net_players[idx].player->objnum].instance < MAX_SHIPS) && !stricmp(ship_name, Ships[Objects[Net_players[idx].player->objnum].instance].ship_name) ){
760                         return idx;
761                 }
762         }
763
764         // didn't find the player
765         return -1;
766 }
767
768 int multi_get_player_ship(int np_index)
769 {
770         // bogus
771         if((np_index < 0) || (np_index >= MAX_PLAYERS)){
772                 return -1;
773         }
774
775         // cool?
776         if(MULTI_CONNECTED(Net_players[np_index]) && !MULTI_OBSERVER(Net_players[np_index]) && !MULTI_STANDALONE(Net_players[np_index]) && 
777                 (Net_players[np_index].player != NULL) && (Net_players[np_index].player->objnum >= 0) && (Net_players[np_index].player->objnum < MAX_OBJECTS) && (Objects[Net_players[np_index].player->objnum].type == OBJ_SHIP) && 
778                 (Objects[Net_players[np_index].player->objnum].instance >= 0) && (Objects[Net_players[np_index].player->objnum].instance < MAX_SHIPS) ){
779
780                 return Objects[Net_players[np_index].player->objnum].instance;
781         }
782
783         // nope
784         return -1;
785 }
786
787 // find_open_netplayer_slot() attempts to find a free slot in the Net_players structure for a new player.
788 // it returns -1 if there is no new slot, and the slot number if one is found
789 // NOTE : this attempts to preserve a player object as long as possible for him to come back to
790 int multi_find_open_netplayer_slot()
791 {
792         int i;
793         int found_first;
794
795         found_first = -1;
796         for (i = 0; i < MAX_PLAYERS; i++)
797                 if ( !MULTI_CONNECTED(Net_players[i]) ){
798                    if(found_first == -1) {
799                                 found_first = i;
800                                 break;
801                         }
802                 }
803
804         if(i == MAX_PLAYERS){
805                 if(found_first == -1)
806                         return -1;
807                 else
808                         return found_first;
809         }
810
811         return i;
812 }
813
814 // find_open_player_slot() attempts to find an open player array slot.  The 0th element of the array
815 // should always be taken by the console player's pilot.  All other slots are up for grab though.
816 int multi_find_open_player_slot()
817 {
818         int i;
819
820         for (i = 0; i < MAX_PLAYERS; i++)
821                 if ( !(Players[i].flags & PLAYER_FLAGS_STRUCTURE_IN_USE) )
822                         break;
823         
824         if ( i == MAX_PLAYERS )
825                 return -1;
826
827         return i;
828 }
829
830 // stuff_netplayer_info stuffs information into the given Net_player structure.  It is called when
831 // a new person is entering the game.  The state of the Net_player is set to it's basic starting
832 // state
833 void stuff_netplayer_info( net_player *nplayer, net_addr_t *addr, int ship_class, player *pplayer )
834 {
835         nplayer->p_info.addr = *addr;
836         nplayer->flags |= NETINFO_FLAG_CONNECTED;
837         nplayer->state = NETPLAYER_STATE_JOINING;
838         nplayer->p_info.ship_class = ship_class;
839         nplayer->player = pplayer;
840         nplayer->p_info.options.obj_update_level = OBJ_UPDATE_HIGH;
841
842         // if setting up my net flags, then set the flag to say I can do networking.
843         if ( nplayer == Net_player ){
844                 nplayer->flags |= NETINFO_FLAG_DO_NETWORKING;
845         }
846 }
847
848 // multi_assign_player_ship takes a Net_player index and an object * and assigned that player to
849 // that object
850 void multi_assign_player_ship( int net_player, object *objp,int ship_class )
851 {
852         ship *shipp;
853         int idx;
854
855         Assert ( MULTI_CONNECTED(Net_players[net_player]) );
856
857         shipp = &Ships[objp->instance];
858
859         Net_players[net_player].player->objnum = OBJ_INDEX(objp);
860         Net_players[net_player].p_info.ship_class = ship_class;
861
862         // check to see if we are assigning my player -- if so, then set Player_ship and Player_ai
863         if ( Net_player == &Net_players[net_player] ) {
864                 Player_obj = objp;
865                 Player_ship = shipp;
866                 Player_ai = &Ai_info[Player_ship->ai_index];
867         }
868
869         // find the parse object for this ship.  Also, set the wingman status stuff so wingman status gauge
870         // works properly.
871         Net_players[net_player].p_info.p_objp = mission_parse_get_arrival_ship( shipp->ship_name );
872         Assert( Net_players[net_player].p_info.p_objp != NULL );                // get allender -- ship should be on list
873         Net_players[net_player].p_info.p_objp->ship_class = ship_class;         // be sure this gets set so respawns work
874
875         // game server and this client need to initialize this information so object updating
876         // works properly.
877         if ( MULTIPLAYER_MASTER || (Net_player == &Net_players[net_player]) ) {
878                 Net_players[net_player].s_info.eye_pos = objp->pos;
879                 Net_players[net_player].s_info.eye_orient = objp->orient;
880         }
881
882         // zero update info     
883         for(idx=0; idx<MAX_PLAYERS; idx++){
884                 shipp->np_updates[idx].orient_chksum = 0;
885                 shipp->np_updates[idx].pos_chksum = 0;
886                 shipp->np_updates[idx].seq = 0;
887                 shipp->np_updates[idx].status_update_stamp = -1;
888                 shipp->np_updates[idx].subsys_update_stamp = -1;
889                 shipp->np_updates[idx].update_stamp = -1;
890         }
891 }
892
893 // -------------------------------------------------------------------------------------------------
894 //      create_player() is called when a net player needs to be instantiated.  The ship that is created
895 // depends on the parameter ship_class.  Note that if ship_class is invalid, the ship default_player_ship
896 // is used.  Returns 1 on success, 0 otherwise
897
898 int multi_create_player( int net_player_num, player *pl, char* name, net_addr_t* addr, int ship_class, short id)
899 {
900         int player_ship_class = ship_class;
901         int i,current_player_count;
902
903         Assert ( net_player_num < MAX_PLAYERS );                                // probably shoudln't be able to even get into this routine if no room  
904         
905         // blast _any_ old data
906         memset(&Net_players[net_player_num],0,sizeof(net_player));
907
908         // get the current # of players
909         current_player_count = multi_num_players();
910
911         // DOH!!! The lack of this caused many bugs. 
912         Net_players[net_player_num].flags = (NETINFO_FLAG_DO_NETWORKING);
913                 
914         if ( ship_class == -1 ) {
915                 nprintf(("Network","Network ==> ship class is -1, creating a default ship for multiplayer\n"));
916
917                 // find the ship that matches the string stored in default_player_ship
918
919                 for (i = 0; i < Num_ship_types; i++) {
920                         if ( !stricmp(Ship_info[i].name, default_player_ship) ) {
921                                 player_ship_class = i;
922                                 break;
923                         }
924                 }
925
926                 if (i == Num_ship_types)
927                         Assert(0);
928         }
929         
930         if ( player_ship_class >= Num_ship_types ) {
931                 player_ship_class = multi_ship_class_lookup(default_player_ship);
932                 nprintf(("Network","Network ==> Ship class was %d Creating a default ship for multiplayer\n", player_ship_class));
933         }
934
935         // blast the old player data
936         memset(pl,0,sizeof(player));
937
938         // set up the net_player structure
939         stuff_netplayer_info( &Net_players[net_player_num], addr, player_ship_class, pl );
940         Net_players[net_player_num].s_info.num_last_buttons = 0;
941         // Net_players[net_player_num].respawn_count = 0;
942         Net_players[net_player_num].last_heard_time = timer_get_fixed_seconds();
943         Net_players[net_player_num].reliable_socket = INVALID_SOCKET;
944         Net_players[net_player_num].s_info.kick_timestamp = -1;
945         Net_players[net_player_num].s_info.voice_token_timestamp = -1;
946         Net_players[net_player_num].s_info.tracker_security_last = -1;
947         Net_players[net_player_num].s_info.target_objnum = -1;
948         Net_players[net_player_num].s_info.accum_buttons = 0;
949
950         // zero out this players ping times data
951         multi_ping_reset(&Net_players[net_player_num].s_info.ping);
952
953         // zero out his object update and control info sequencing data
954         Net_players[net_player_num].client_cinfo_seq = 0;
955         Net_players[net_player_num].client_server_seq = 0;              
956         
957         // timestamp his last_full_update_time
958         Net_players[net_player_num].s_info.last_full_update_time = timestamp(0);
959
960         // nil his file xfer handle
961         Net_players[net_player_num].s_info.xfer_handle = -1;
962
963         // nil his data rate timestamp stuff
964         Net_players[net_player_num].s_info.rate_stamp = -1;
965         Net_players[net_player_num].s_info.rate_bytes = 0;
966
967         // nil packet buffer stuff
968         Net_players[net_player_num].s_info.unreliable_buffer_size = 0;
969         Net_players[net_player_num].s_info.reliable_buffer_size = 0;
970         
971         // various ack handles  
972         strcpy(pl->callsign, name);
973         pilot_set_short_callsign(pl, SHORT_CALLSIGN_PIXEL_W);   // calculate the short callsign 
974         pl->flags |= PLAYER_FLAGS_STRUCTURE_IN_USE;
975         pl->objnum = -1;
976
977         // if we're the standalone server and this is the first guy to join, mark him as the host
978         // and give him all host priveleges
979         if((Game_mode & GM_STANDALONE_SERVER) && (current_player_count == 0)){
980                 Net_players[net_player_num].flags |= NETINFO_FLAG_GAME_HOST;
981         }
982         
983         Net_players[net_player_num].player_id = id;
984
985         Net_player->sv_bytes_sent = 0;
986         Net_player->sv_last_pl = -1;
987         Net_player->cl_bytes_recvd = 0;
988         Net_player->cl_last_pl = -1;
989
990         // add to the escort list
991         if(MULTI_IN_MISSION){
992                 hud_escort_add_player(id);
993         }
994
995         return 1;
996 }
997
998 // next function makes a player object an ai object (also decrementing the num_players in the
999 // netgame).  It appropiately sets the object flags and other interesting AI information which
1000 // ought to get set in order to make this new ai object behave correctly.
1001 void multi_make_player_ai( object *pobj )
1002 {
1003
1004         Assert ( pobj != NULL );
1005
1006         if ( pobj->type != OBJ_SHIP )
1007                 return;
1008
1009         pobj->flags &= ~(OF_PLAYER_SHIP);
1010         obj_set_flags( pobj, pobj->flags | OF_COULD_BE_PLAYER );
1011
1012         // target_objnum must be -1 or else new AI ship will fire on whatever this player
1013         // had targeted.
1014         set_target_objnum( &(Ai_info[Ships[pobj->instance].ai_index]), -1 );
1015
1016 }
1017
1018 // delete_player takes an index into the Net_players array to delete.  Deletion of a player might happen
1019 // because of the player leaving the game on his own, or no net activity from the player after X seconds
1020 void delete_player(int player_num,int kicked_reason)
1021 {                       
1022         char notify_string[256] = "";
1023         int idx;
1024         
1025         if(!MULTI_CONNECTED(Net_players[player_num])){
1026                 return;
1027         }
1028
1029         // NETLOG
1030         ml_printf(NOX("Deleting player %s"), Net_players[player_num].player->callsign);
1031         
1032         psnet_rel_close_socket( &(Net_players[player_num].reliable_socket) );                                   // close out the reliable socket        
1033
1034         // if this guy was ingame joining, the remove my netgame flag so others may join
1035         if ( Net_players[player_num].flags & NETINFO_FLAG_INGAME_JOIN ) {
1036                 Netgame.flags &= ~NG_FLAG_INGAME_JOINING;
1037                 Netgame.flags &= ~NG_FLAG_INGAME_JOINING_CRITICAL;
1038         }
1039         
1040         Net_players[player_num].flags &= ~NETINFO_FLAG_CONNECTED;                                                       // person not connected anymore
1041         Net_players[player_num].player->flags &= ~(PLAYER_FLAGS_STRUCTURE_IN_USE);    // free up his player structure
1042
1043         Net_players[player_num].s_info.reliable_connect_time = -1;
1044
1045         // add to the escort list
1046         hud_escort_remove_player(Net_players[player_num].player_id);
1047
1048         // is this guy the server
1049         if(Net_players[player_num].flags & NETINFO_FLAG_AM_MASTER){
1050                 // if the server leaves in the debriefing state, we should still wait until the player selects accept before we quit
1051                 if ((gameseq_get_state() != GS_STATE_DEBRIEF) && (gameseq_get_state() != GS_STATE_MULTI_DOGFIGHT_DEBRIEF)){
1052                         multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_SERVER_LEFT);
1053                 }               
1054         }
1055         // if he was just the host      
1056         else if(Net_players[player_num].flags & NETINFO_FLAG_GAME_HOST){
1057                 Net_players[player_num].flags &= ~(NETINFO_FLAG_GAME_HOST);
1058         
1059                 // am I the server
1060                 if((Net_player != NULL) && (Net_player->flags & NETINFO_FLAG_AM_MASTER)){
1061                         // are we a standalone server and in a mission?
1062                         if((Game_mode & GM_STANDALONE_SERVER) && MULTI_IN_MISSION){                     
1063                                 // choose a new host                    
1064                                 int found_new_host = 0;
1065                                 for(idx=0; idx<MAX_PLAYERS; idx++){
1066                                         if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx])){
1067                                                 // make this guy the host
1068                                                 Net_players[idx].flags |= NETINFO_FLAG_GAME_HOST;
1069
1070                                                 // send a packet
1071                                                 send_host_captain_change_packet(Net_players[idx].player_id, 0);
1072
1073                                                 found_new_host = 1;
1074                                                 break;
1075                                         }
1076                                 }
1077                                 // end the game         
1078                                 if(!found_new_host){
1079                                         multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_HOST_LEFT);
1080                                 }                        
1081                         } else {
1082                                 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_HOST_LEFT);
1083                         }
1084                 } 
1085         }
1086
1087         // if we're the server of the game, notify everyone else that this guy has left
1088         // if he's marked as being kicked, then other players know about it already, so don't send again
1089         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
1090                 if(Net_players[player_num].flags & NETINFO_FLAG_KICKED){
1091                         char str[512];
1092                         memset(str, 0, 512);
1093                         multi_kick_get_text(&Net_players[player_num], Net_players[player_num].s_info.kick_reason, str);
1094                         multi_display_chat_msg(str, player_num, 0);                                                      
1095                 } else {
1096                         send_leave_game_packet(Net_players[player_num].player_id, kicked_reason);
1097                 }
1098         }
1099         
1100         // if this guy is an observer, we have to make sure we delete his observer object (only if we're the server however)
1101         if( (Net_player->flags & NETINFO_FLAG_AM_MASTER) && (Net_players[player_num].flags & NETINFO_FLAG_OBSERVER) ) {
1102                 if ( Net_players[player_num].player->objnum != -1 ){
1103                         obj_delete(Net_players[player_num].player->objnum);                     // maybe change this to set flag instead                
1104                 }
1105         } else {
1106                 // otherwise mark it so that he can return to it later if possible
1107                 if ( (Net_players[player_num].player->objnum >= 0) && (Net_players[player_num].player->objnum < MAX_OBJECTS) && (Objects[Net_players[player_num].player->objnum].type == OBJ_SHIP) && (Objects[Net_players[player_num].player->objnum].instance >= 0) && (Objects[Net_players[player_num].player->objnum].instance < MAX_SHIPS)) {
1108                         multi_make_player_ai( &Objects[Net_players[player_num].player->objnum] );
1109                 } else {
1110                         multi_respawn_player_leave(&Net_players[player_num]);
1111                 }
1112         }
1113
1114         // if we're in the team select, and we're the host, we have to make sure all team select data is correctly updated                      
1115         // MWA 7/28/98.  Don't do this for a standalone server.  Since he doesn't go through the team selection
1116         // screens, his data structures are not 100% accurate.  Doing so on a standalone resulted in the wrong
1117         // ships getting marked as COULD_BE_PLAYER.  On the standalone, these flags will get properly set
1118         // in the multi_make_player_ai code above.
1119         if( (Netgame.game_state == NETGAME_STATE_BRIEFING) && !(Game_mode & GM_STANDALONE_SERVER) ) {
1120                 multi_ts_handle_player_drop();                  
1121         }                                                               
1122
1123         if (gameseq_get_state() == GS_STATE_DEBRIEF){
1124                 debrief_handle_player_drop();
1125         }
1126
1127         // handle any specific dropping conditions
1128         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
1129                 multi_team_handle_drop();
1130         }       
1131         multi_data_handle_drop(player_num);     
1132
1133         // tell the pinfo popup that a player has left
1134         multi_pinfo_notify_drop(&Net_players[player_num]);
1135
1136         // tell the datarate stuff that the player has dropped
1137         multi_rate_reset(player_num);
1138
1139         // display a message that this guy has left
1140         if(Net_players[player_num].player->callsign){
1141                 sprintf(notify_string,XSTR("<%s has left>",901),Net_players[player_num].player->callsign);
1142                 multi_display_chat_msg(notify_string,0,0);
1143         }
1144         
1145         // standalone gui type stuff
1146         if (Game_mode & GM_STANDALONE_SERVER) {
1147                 std_remove_player(&Net_players[player_num]);
1148                 std_connect_set_connect_count();
1149         }
1150
1151         // blast this memory clean
1152         memset(&Net_players[player_num], 0, sizeof(net_player));
1153         Net_players[player_num].reliable_socket = INVALID_SOCKET;
1154
1155         extern int Multi_client_update_times[MAX_PLAYERS];
1156         Multi_client_update_times[player_num] = -1;
1157 }
1158
1159 #define INACTIVE_LIMIT_NORMAL (15 * F1_0)
1160 #define INACTIVE_LIMIT_WAIT   (20 * F1_0)
1161
1162 // -------------------------------------------------------------------------------------------------
1163 //      multi_cull_zombies() will check if there has been any net players or observers that have been inactive for
1164 // INACTIVE_LIMIT milliseconds since last check.  If so, they are taken out of the net game.
1165 //
1166 //
1167
1168 void multi_cull_zombies()
1169 {
1170 #if 0
1171         fix current_time;
1172         int inactive_limit;
1173         int i;
1174
1175         if(gameseq_get_state() == GS_STATE_MULTI_WAIT)
1176                 inactive_limit = INACTIVE_LIMIT_WAIT;
1177         else
1178                 inactive_limit = INACTIVE_LIMIT_NORMAL;
1179
1180         current_time = timer_get_fixed_seconds();
1181
1182         for (i = 0; i < MAX_PLAYERS; i++) {
1183                 if ( !MULTI_CONNECTED(&Net_players[i])){
1184                         continue;
1185                 }
1186
1187                 // server will(should) cull out players based on their sockets dying.
1188                 if ( Net_players[i].flags & NETINFO_FLAG_MASTER ){
1189                         continue;
1190                 }
1191
1192                 if ( (current_time - Net_players[i].last_heard_time) > inactive_limit) {
1193                         HUD_printf(XSTR("Dumping %s after prolonged inactivity",902),Net_players[i].player->callsign);
1194                         nprintf(("Network", "Assuming %s is a zombie, removing from game\n", Net_players[i].player->callsign));
1195
1196                         multi_kick_player(i,0);                 
1197                 }
1198         }
1199 #endif
1200 }
1201
1202 // -------------------------------------------------------------------------------------------------
1203 // netmisc_calc_checksum() calculates the checksum of a block of memory.
1204 //
1205 //
1206 ushort netmisc_calc_checksum( void * vptr, int len )
1207 {
1208         ubyte * ptr = (ubyte *)vptr;
1209         unsigned int sum1,sum2;
1210
1211         sum1 = sum2 = 0;
1212
1213         while(len--)    {
1214                 sum1 += *ptr++;
1215                 if (sum1 >= 255 ) sum1 -= 255;
1216                 sum2 += sum1;
1217         }
1218         sum2 %= 255;
1219         
1220         return (unsigned short)((sum1<<8)+ sum2);
1221 }
1222
1223
1224 // -------------------------------------------------------------------------------------------------
1225 // fill_net_addr() calculates the checksum of a block of memory.
1226 //
1227 //
1228
1229 void fill_net_addr(net_addr_t* addr, ubyte* address, ubyte* net_id, ushort port)
1230 {
1231         Assert(addr != NULL);
1232         Assert(address != NULL);
1233         Assert(net_id != NULL);
1234
1235         addr->type = Multi_options_g.protocol;
1236         memset( addr->addr, 0x00, 6);
1237         memcpy( addr->addr, address, ADDRESS_LENGTH);
1238         memcpy( addr->net_id, net_id, 4);
1239         addr->port = port;
1240 }
1241
1242
1243
1244 // -------------------------------------------------------------------------------------------------
1245 // get_text_address()
1246 //
1247 //
1248
1249 char* get_text_address( char * text, ubyte * address )
1250 {
1251
1252         in_addr temp_addr;
1253
1254         switch ( Multi_options_g.protocol ) {
1255                 case NET_IPX:
1256                         strcpy( text, XSTR("[ipx address here]",903) ); // TODO: find equiv to inet_ntoa() for IPX
1257                         sprintf(text, "%x %x %x %x %x %x",      address[0],
1258                                                                                                                         address[1],
1259                                                                                                                         address[2],
1260                                                                                                                         address[3],
1261                                                                                                                         address[4],
1262                                                                                                                         address[5]);
1263
1264                         break;
1265
1266                 case NET_TCP:
1267                         memcpy(&temp_addr.s_addr, address, 4);
1268                         strcpy( text, inet_ntoa(temp_addr) );
1269                         break;
1270
1271                 default:
1272                         Assert(0);
1273                         break;
1274
1275         } // end switch
1276
1277         return text;
1278 }
1279
1280 // non-16byte version of matrix packing
1281 // return size of packed matrix
1282 void multi_pack_orient_matrix(ubyte *data,matrix *m)
1283 {       
1284    data[16]=0;
1285
1286         if(m->v.rvec.xyz.z < 0) data[16] |= (1<<0);     // X
1287         if(m->v.uvec.xyz.z < 0) data[16] |= (1<<1);     // Y
1288         if(m->v.fvec.xyz.z < 0) data[16] |= (1<<2);     // V
1289         if(m->v.fvec.xyz.x < 0) data[16] |= (1<<3);     // Z
1290         if(m->v.fvec.xyz.y < 0) data[16] |= (1<<4);     // W
1291         memcpy(&data[0],&m->v.rvec.xyz.x,4);                    // a
1292         memcpy(&data[4],&m->v.rvec.xyz.y,4);                    // b
1293         memcpy(&data[8],&m->v.uvec.xyz.x,4);                    // c
1294         memcpy(&data[12],&m->v.uvec.xyz.y,4);                   // d
1295 }
1296
1297 // return bytes processed
1298 // non-16 byte version of unpack matrix code
1299 void multi_unpack_orient_matrix(ubyte *data,matrix *m)
1300 {       
1301         memcpy(&m->v.rvec.xyz.x,&data[0],4); 
1302         memcpy(&m->v.rvec.xyz.y,&data[4],4); 
1303         memcpy(&m->v.uvec.xyz.x,&data[8],4);  
1304    memcpy(&m->v.uvec.xyz.y,&data[12],4);     
1305         
1306         m->v.rvec.xyz.z = fl_sqrt(fl_abs(1 - (m->v.rvec.xyz.x * m->v.rvec.xyz.x) - (m->v.rvec.xyz.y * m->v.rvec.xyz.y))); // X
1307    m->v.uvec.xyz.z = fl_sqrt(fl_abs(1 - (m->v.uvec.xyz.x * m->v.uvec.xyz.x) - (m->v.uvec.xyz.y * m->v.uvec.xyz.y))); // Y
1308         m->v.fvec.xyz.z = fl_sqrt(fl_abs(1 - (m->v.rvec.xyz.z * m->v.rvec.xyz.z) - (m->v.uvec.xyz.z * m->v.uvec.xyz.z))); // V
1309         m->v.fvec.xyz.x = fl_sqrt(fl_abs(1 - (m->v.rvec.xyz.x * m->v.rvec.xyz.x) - (m->v.uvec.xyz.x * m->v.uvec.xyz.x))); // Z
1310         m->v.fvec.xyz.y = fl_sqrt(fl_abs(1 - (m->v.rvec.xyz.y * m->v.rvec.xyz.y) - (m->v.uvec.xyz.y * m->v.uvec.xyz.y))); // W
1311
1312         m->v.rvec.xyz.z *= (data[16] & (1<<0)) ? -1.0f : 1.0f;
1313         m->v.uvec.xyz.z *= (data[16] & (1<<1)) ? -1.0f : 1.0f;
1314         m->v.fvec.xyz.z *= (data[16] & (1<<2)) ? -1.0f : 1.0f;
1315         m->v.fvec.xyz.x *= (data[16] & (1<<3)) ? -1.0f : 1.0f;
1316         m->v.fvec.xyz.y *= (data[16] & (1<<4)) ? -1.0f : 1.0f;
1317 }
1318                               
1319 void multi_do_client_warp(float flFrametime)
1320 {
1321    ship_obj *moveup;
1322         
1323    moveup = GET_FIRST(&Ship_obj_list);
1324         while(moveup!=END_OF_LIST(&Ship_obj_list)){
1325                 // do all _necessary_ ship warp in (arrival) processing
1326                 if ( Ships[Objects[moveup->objnum].instance].flags & SF_ARRIVING )      
1327                         shipfx_warpin_frame( &Objects[moveup->objnum], flFrametime );
1328                 moveup = GET_NEXT(moveup);
1329         }       
1330 }       
1331
1332 // ------------------------------------------------------------------------------------
1333 // ship status change stuff
1334
1335 int lookup_ship_status(net_player *p,int unique_id,int remove)
1336 {
1337         int idx;
1338         
1339         for(idx=0;idx<p->s_info.num_last_buttons;idx++){
1340                 if(p->s_info.last_buttons_id[idx] == unique_id){
1341                         if(remove){
1342                                 remove_ship_status_item(p,idx);
1343                                 p->s_info.num_last_buttons--;
1344                         }
1345                         return 1;
1346                 }
1347         }
1348         return 0;
1349 }
1350
1351 void remove_ship_status_item(net_player *p,int id)
1352 {
1353         int idx;
1354         for(idx=id;idx<BUTTON_INFO_SAVE_COUNT-1;idx++){
1355                 p->s_info.last_buttons[idx] = p->s_info.last_buttons[idx+1];
1356                 p->s_info.last_buttons_id[idx] = p->s_info.last_buttons_id[idx+1];
1357                 p->s_info.last_buttons_time[idx] = p->s_info.last_buttons_time[idx+1];
1358         }
1359 }
1360
1361 // 
1362 void add_net_button_info(net_player *p,button_info *bi,int unique_id)
1363 {
1364    int lookup,idx;
1365         fix earliest;
1366
1367         // if the list is full, put it in the oldest slot since it's probably a lost packet anyway
1368    if(p->s_info.num_last_buttons < BUTTON_INFO_SAVE_COUNT-1){
1369                 p->s_info.last_buttons[p->s_info.num_last_buttons] = *bi;
1370                 p->s_info.last_buttons_id[p->s_info.num_last_buttons] = unique_id;
1371                 p->s_info.last_buttons_time[p->s_info.num_last_buttons] = timer_get_fixed_seconds();
1372                 p->s_info.num_last_buttons++;
1373         } else {
1374                 earliest = 0;
1375       lookup = -1;
1376                 for(idx=0;idx<BUTTON_INFO_SAVE_COUNT;idx++){
1377                         if((p->s_info.last_buttons_time[idx] < earliest) || (earliest == 0)){
1378                                 earliest = p->s_info.last_buttons_time[idx];
1379                                 lookup = idx;
1380                         }
1381                 }
1382                 if(lookup != -1){
1383                         p->s_info.last_buttons[lookup] = *bi;
1384                         p->s_info.last_buttons_id[lookup] = unique_id;
1385                         p->s_info.last_buttons_time[lookup] = timer_get_fixed_seconds();
1386                 }
1387         }               
1388 }
1389
1390 extern int button_function_critical(int n,net_player *p = NULL);
1391 void multi_apply_ship_status(net_player *p,button_info *bi,int locally)
1392 {
1393         int i, j;
1394         Multi_button_info_ok=1;
1395         for ( i = 0; i < NUM_BUTTON_FIELDS; i++ ) {
1396                 if ( bi->status[i] == 0 )
1397                         continue;
1398                 // at least one bit is set in the status integer
1399                 for ( j = 0; j < 32; j++ ) {
1400
1401                         // check if the bit is set. If button_function returns 1 (implying the action was taken), then unset the bit
1402                         if ( bi->status[i] & (1<<j) ) {
1403             if(locally){
1404                                         if(button_function_critical(32*i + j,NULL))   // will apply to this console
1405                                                 bi->status[i] &= ~(1<<j);
1406                                 } else {
1407                                         if(button_function_critical(32*i + j,p))      // will only apply to a net-player
1408                                                 bi->status[i] &= ~(1<<j);
1409                                 }
1410                         }
1411                 }
1412         }
1413         Multi_button_info_ok=0;
1414 }
1415
1416 // send 10x a second MAX
1417 #define MULTI_SHIP_STATUS_TIME                  350
1418 int Multi_ship_status_stamp = -1;
1419 button_info Multi_ship_status_bi;
1420
1421 void multi_maybe_send_ship_status()
1422 {
1423         int idx;
1424         button_info *bi = &Player->bi;
1425
1426         // strip out noncritical button presses
1427         button_strip_noncritical_keys(bi);
1428
1429         // xor all fields into the accum button info
1430         for(idx=0; idx<NUM_BUTTON_FIELDS; idx++){               
1431                 Multi_ship_status_bi.status[idx] |= bi->status[idx];            
1432         }
1433
1434         // check timestamp
1435         if((Multi_ship_status_stamp < 0) || timestamp_elapsed_safe(Multi_ship_status_stamp, MULTI_SHIP_STATUS_TIME*2)){
1436                 int should_send = 0;
1437                 for(idx=0; idx<NUM_BUTTON_FIELDS; idx++){                       
1438                         // we have at least something to send
1439                         if(Multi_ship_status_bi.status[idx] != 0){
1440                                 should_send = 1;                        
1441                         }               
1442                 }
1443
1444                 // do we have something to send
1445                 if(should_send){
1446                         // add_net_button_info(Net_player, &Multi_ship_status_bi, Multi_button_info_id);
1447                         send_ship_status_packet(Net_player, &Multi_ship_status_bi, Multi_button_info_id++);
1448                 }
1449
1450                 // zero it out
1451                 memset(&Multi_ship_status_bi, 0, sizeof(button_info));
1452
1453                 // reset timestamp
1454                 Multi_ship_status_stamp = timestamp(MULTI_SHIP_STATUS_TIME);
1455         }
1456 }
1457
1458 void multi_subsys_update_all()
1459 {
1460         /*
1461         int idx;
1462         Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
1463         for(idx=0;idx<MAX_PLAYERS;idx++){
1464                 if((Net_players[idx].flags & NETINFO_FLAG_CONNECTED) && !psnet_same(&My_addr,&Net_players[idx].addr) && !(Net_players[idx].flags & NETINFO_FLAG_OBSERVER))
1465                         send_subsys_update_packet(&Net_players[idx]);
1466         }
1467         */
1468 }
1469
1470 int multi_find_player_by_callsign(char *callsign)
1471 {
1472         int idx;
1473         for(idx=0;idx<MAX_PLAYERS;idx++){
1474                 if(MULTI_CONNECTED(Net_players[idx]) && (strcmp(callsign,Net_players[idx].player->callsign)==0)){
1475                         return idx;
1476                 }
1477         }
1478
1479         return -1;
1480 }
1481
1482 // if Game_current_mission_filename is a builtin multiplayer mission
1483 int multi_is_builtin_mission()
1484 {
1485         // int idx;
1486         char name[512];
1487
1488         // get the full filename
1489         memset(name,0,512);
1490         strcpy(name,Game_current_mission_filename);
1491         cf_add_ext(name, FS_MISSION_FILE_EXT);
1492
1493         // if this mission is builtin   
1494         if(game_find_builtin_mission(name) != NULL){
1495                 return 1;
1496         }
1497
1498         // not builtin
1499         return 0;
1500 }
1501
1502 // verify that the player has a valid mission file and do 1 of 3 things
1503 void server_verify_filesig(short player_id, ushort sum_sig, int length_sig)
1504 {       
1505 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
1506         net_player *pl;
1507    int player;
1508
1509         player = find_player_id(player_id);
1510         Assert(player >= 0);
1511         if(player < 0){
1512                 return;
1513         }
1514         pl = &Net_players[player];
1515
1516         // all missions should be builtin, so if we don't have a match, kick the player
1517         if((sum_sig != Multi_current_file_checksum) || (length_sig != Multi_current_file_length)){
1518                 multi_kick_player(player, 0, KICK_REASON_CANT_XFER);
1519         } else {
1520                 pl->flags |= NETINFO_FLAG_MISSION_OK;
1521         }
1522 #else
1523         net_player *pl;
1524    int player;
1525         int ok; 
1526         int is_builtin;
1527    
1528         player = find_player_id(player_id);
1529         Assert(player >= 0);
1530         if(player < 0){
1531                 return;
1532         }
1533         pl = &Net_players[player];
1534
1535         // if the current mission is a builtin mission, check stuff out
1536         is_builtin = multi_is_builtin_mission();
1537                 
1538         if(is_builtin){
1539                 // if the player doesn't have it, kick him
1540                 if((sum_sig == 0xffff) && (length_sig == -1)){
1541                         multi_kick_player(player, 0, KICK_REASON_CANT_XFER);
1542                 } else {
1543                         pl->flags |= NETINFO_FLAG_MISSION_OK;
1544                 }
1545
1546                 // done
1547                 return;
1548         }
1549
1550         if( (length_sig != Multi_current_file_length) || (sum_sig != Multi_current_file_checksum)){
1551                 ok = 0;
1552         } else {
1553                 ok = 1; 
1554         }
1555
1556    // in an ingame join situation
1557         if(pl->flags & NETINFO_FLAG_INGAME_JOIN){               
1558                 if(!ok){
1559                         // if the netgame settings allow in-mission file xfers
1560                         if(Netgame.options.flags & MSO_FLAG_INGAME_XFER){
1561                                 pl->s_info.ingame_join_flags |= INGAME_JOIN_FLAG_FILE_XFER;
1562                                 pl->s_info.xfer_handle = multi_xfer_send_file(pl->reliable_socket, Netgame.mission_name, CF_TYPE_MISSIONS);
1563                         }
1564                         // otherwise send him a nak and tell him to get the hell away 
1565                         else {
1566                                 send_ingame_nak(ACK_FILE_ACCEPTED,pl);
1567                         }
1568                 } else {
1569                         pl->flags |= NETINFO_FLAG_MISSION_OK;
1570                 }
1571         }
1572         // in a normal join situation
1573         else {
1574                 // if the file does not check out, send it to him
1575                 if(!ok){                        
1576                         pl->s_info.xfer_handle = multi_xfer_send_file(pl->reliable_socket, Netgame.mission_name, CF_TYPE_MISSIONS);
1577                 }
1578                 // otherwise mark him as having a valid mission
1579                 else {
1580                         pl->flags |= NETINFO_FLAG_MISSION_OK;                   
1581                 }
1582         }
1583 #endif
1584 }
1585
1586 // check to see if every client has NETINFO_FLAG_MISSION_OK
1587 int server_all_filesigs_ok()
1588 {
1589         int idx;
1590         int ok = 1;
1591         
1592         for(idx=0;idx<MAX_PLAYERS;idx++){
1593                 if(MULTI_CONNECTED(Net_players[idx]) && !(Net_players[idx].flags & NETINFO_FLAG_MISSION_OK)){
1594                         ok = 0;
1595                         break;
1596                 }
1597         }
1598
1599         return ok;
1600 }
1601
1602 void multi_untag_player_ships()
1603 {
1604         ship_obj *moveup;
1605
1606         moveup = GET_FIRST(&Ship_obj_list);
1607         while(moveup != END_OF_LIST(&Ship_obj_list)){
1608                 if(Objects[moveup->objnum].flags & OF_PLAYER_SHIP){
1609                         Objects[moveup->objnum].flags &= ~(OF_PLAYER_SHIP);
1610                         obj_set_flags( &Objects[moveup->objnum], Objects[moveup->objnum].flags | OF_COULD_BE_PLAYER );
1611                 }
1612                 moveup = GET_NEXT(moveup);
1613         }
1614 }
1615
1616 // broadcast alltime stats to everyone in the game
1617 void multi_broadcast_stats(int stats_code)
1618 {
1619         int idx;
1620
1621         // broadcast everyone's stats to everyone else
1622         for(idx=0;idx<MAX_PLAYERS;idx++){
1623                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){                 
1624                         send_player_stats_block_packet(&Net_players[idx], stats_code);
1625                 }
1626         }                       
1627 }
1628
1629 // check to see if all players other than the local player are in a given NETPLAYER_ state
1630 // this is _EXTREMELY_ useful when doing network sequencing with the reliable sockets
1631 int multi_netplayer_state_check(int state,int ignore_standalone)
1632 {
1633         int idx;        
1634         for(idx=0;idx<MAX_PLAYERS;idx++){
1635                 if(MULTI_CONNECTED(Net_players[idx]) && (Net_player->player_id != Net_players[idx].player_id)){
1636                         if(ignore_standalone && ((Net_players[idx].flags & NETINFO_FLAG_AM_MASTER) && !(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST)) ){
1637                                 continue;
1638                         }
1639
1640                         if(Net_players[idx].state != state){
1641                                 return 0;
1642                         }
1643                 }
1644         }
1645         return 1;
1646 }
1647
1648 int multi_netplayer_state_check2(int state, int state2, int ignore_standalone)
1649 {
1650         int idx;        
1651         for(idx=0;idx<MAX_PLAYERS;idx++){
1652                 if(MULTI_CONNECTED(Net_players[idx]) && (Net_player->player_id != Net_players[idx].player_id)){
1653                         if(ignore_standalone && ((Net_players[idx].flags & NETINFO_FLAG_AM_MASTER) && !(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST)) ){
1654                                 continue;
1655                         }
1656
1657                         if((Net_players[idx].state != state) && (Net_players[idx].state != state2)){
1658                                 return 0;
1659                         }
1660                 }
1661         }
1662         return 1;
1663 }
1664
1665 int multi_netplayer_state_check3(int state, int state2, int state3, int ignore_standalone)
1666 {
1667         int idx;        
1668         for(idx=0;idx<MAX_PLAYERS;idx++){
1669                 if(MULTI_CONNECTED(Net_players[idx]) && (Net_player->player_id != Net_players[idx].player_id)){
1670                         if(ignore_standalone && ((Net_players[idx].flags & NETINFO_FLAG_AM_MASTER) && !(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST)) ){
1671                                 continue;
1672                         }
1673
1674                         if((Net_players[idx].state != state) && (Net_players[idx].state != state2) && (Net_players[idx].state != state3)){
1675                                 return 0;
1676                         }
1677                 }
1678         }
1679         return 1;
1680 }
1681
1682 // check to see if all players other than the local player are in a given NETPLAYER_ state
1683 // this is _EXTREMELY_ useful when doing network sequencing with the reliable sockets
1684 int multi_netplayer_flag_check(int flags,int ignore_standalone)
1685 {
1686         int idx;        
1687         for(idx=0;idx<MAX_PLAYERS;idx++){
1688                 if(MULTI_CONNECTED(Net_players[idx]) && (Net_player->player_id != Net_players[idx].player_id)){
1689                         if(ignore_standalone && ((Net_players[idx].flags & NETINFO_FLAG_AM_MASTER) && !(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST)) ){
1690                                 continue;
1691                         }
1692
1693                         if(!(Net_players[idx].flags & flags)){
1694                                 return 0;
1695                         }
1696                 }
1697         }
1698         return 1;
1699 }
1700
1701 // function which gets called from psnet_* code to evaluate an error received on a reliable
1702 // socket.  In general, we only want to do drastic measures if the error indicates that the client
1703 // is no longer there.
1704 void multi_eval_socket_error(PSNET_SOCKET sock, int error)
1705 {
1706         if ( error == WSAENOTSOCK ){
1707                 nprintf(("Network","Socket connection terminated and/or nonexistent, bailing..\n"));
1708
1709                 // mwa -- don't go back to main menu.  You don't want host to do this.  Maybe we can ignore it
1710                 // because of a leaving player.
1711                 return;
1712                 //gameseq_post_event(GS_EVENT_MAIN_MENU);
1713                 // Int3();                                                      // get allender -- something happened to socket connection!!!
1714         }
1715
1716         if ( (error != WSAECONNRESET) && (error != WSAECONNABORTED) && (error != WSAESHUTDOWN) ) {
1717                 nprintf(("Network", "Error %d received on reliable socket -- ignoring\n", error));
1718                 return;
1719         }
1720
1721         if(error == WSAESHUTDOWN){
1722                 nprintf(("Network","Received WSAESHUTDOWN on client socket. Cool.\n"));
1723         }
1724
1725         // mwa -- always return for now because debugging with the stuff below is a real pain.
1726         // in essence, you can't do it!
1727         //return;
1728
1729         if( Net_player->flags & NETINFO_FLAG_AM_MASTER ) {
1730                 int idx;
1731
1732                 nprintf(("Network", "pitching player because drop on reliable socket\n"));
1733                 // find the netplayer whose socket we have an error on.  Dump the player when we find him.
1734                 // NOTE : make sure not to ban him
1735                 for(idx=0;idx<MAX_PLAYERS;idx++){
1736                         if(Net_players[idx].reliable_socket == sock){
1737                                 delete_player(idx);
1738                                 return;
1739                         }
1740                 }
1741         } else {
1742                 nprintf(("Network", "Communications to server lost -- quitting game\n"));
1743                 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_CONTACT_LOST);
1744         }
1745 }
1746
1747 // send a repair info packet with code == code to a player if his object is the one being acted upon
1748 // dest_objp is the player object (or possible player object).  source_objp is the object pointer
1749 // of the repair ship that is the source of the action.  code means what is happening -- queueing up
1750 // for repair, getting repaired, aborted, complete, etc. 
1751 void multi_maybe_send_repair_info(object *dest_objp, object *source_objp, int code )
1752 {
1753         // only send information on player objects
1754         if ( !MULTIPLAYER_MASTER )
1755                 return;
1756         
1757         Assert( dest_objp->type == OBJ_SHIP );
1758         Assert( dest_objp != source_objp );
1759
1760         send_repair_info_packet( dest_objp, source_objp, code );
1761 }
1762
1763 int multi_is_valid_unknown_packet(ubyte type){
1764         return (type == GAME_QUERY) || (type == JOIN) || (type == PING) || (type == PONG) || (type == GAME_INFO)
1765                 || (type == ACCEPT) || (type == GAME_ACTIVE) || (type == INGAME_NAK) || (type == DENY)
1766                 || (type == UPDATE_DESCRIPT) || (type == ACCEPT_PLAYER_DATA) ? 1 : 0;   
1767 }
1768
1769 void multi_create_standalone_object()
1770 {
1771         // now create a bullshit ship for the standalone
1772         matrix m = IDENTITY_MATRIX;
1773         vector v;
1774         int objnum, pobj_num;
1775
1776         vm_vec_zero(&v);
1777         objnum = observer_create(&m,&v);
1778         Player_obj = &Objects[objnum];
1779         obj_set_flags(Player_obj, Player_obj->flags & (~OF_COLLIDES) );
1780         //obj_set_flags(Player_obj, Player_obj->flags | OF_SHOULD_BE_DEAD);
1781         obj_set_flags(Player_obj, Player_obj->flags & (~OF_PLAYER_SHIP));
1782         Net_player->player->objnum = objnum;
1783
1784         // create the default player ship object and use that as my default virtual "ship", and make it "invisible"
1785         pobj_num = parse_create_object(&Player_start_pobject);
1786         Assert(pobj_num != -1);
1787         obj_set_flags(&Objects[pobj_num],OF_PLAYER_SHIP);
1788         Objects[pobj_num].net_signature = STANDALONE_SHIP_SIG;
1789         Player_ship = &Ships[Objects[pobj_num].instance];
1790
1791         // make ship hidden from sensors so that this observer cannot target it.  Observers really have two ships
1792         // one observer, and one "Player_ship".  Observer needs to ignore the Player_ship.
1793         Player_ship->flags |= SF_HIDDEN_FROM_SENSORS;
1794         strcpy(Player_ship->ship_name, XSTR("Standalone Ship",904));
1795         Player_ai = &Ai_info[Ships[Objects[pobj_num].instance].ai_index];               
1796
1797 }
1798
1799 int multi_message_should_broadcast(int type)
1800 {
1801         return (type == MESSAGE_ARRIVE_ENEMY) || (type == MESSAGE_BETA_ARRIVED) || 
1802                     (type == MESSAGE_GAMMA_ARRIVED) || (type == MESSAGE_HELP) || (type == MESSAGE_REINFORCEMENTS) ? 1 : 0;
1803 }
1804
1805 // active game list handling functions
1806 active_game *multi_new_active_game( void )
1807 {
1808         active_game *new_game;
1809
1810         new_game = (active_game *)malloc(sizeof(active_game));
1811         if ( new_game == NULL ) {
1812                 nprintf(("Network", "Cannot allocate space for new active game structure\n"));
1813                 return NULL;
1814         }       
1815
1816         if ( Active_game_head != NULL ) {
1817                 new_game->next = Active_game_head->next;
1818                 new_game->next->prev = new_game;
1819                 Active_game_head->next = new_game;
1820                 new_game->prev = Active_game_head;
1821         } else {
1822                 Active_game_head = new_game;
1823                 Active_game_head->next = Active_game_head->prev = Active_game_head;
1824         }
1825
1826         Active_game_count++;
1827
1828         // notify the join game screen of this new item
1829         multi_join_notify_new_game();
1830                 
1831         return new_game;
1832 }
1833
1834 active_game *multi_update_active_games(active_game *ag)
1835 {
1836         active_game *gp = NULL;
1837         active_game *stop = NULL;               
1838
1839         // see if we have a game from this address already -- if not, create one.  In either case, get a pointer
1840         // to an active_game structure
1841         if ( Active_game_head != NULL ) {       // no games on list at all
1842                 int on_list;
1843
1844                 gp = Active_game_head;
1845                 stop = Active_game_head;
1846
1847                 on_list = 0;
1848                 do {
1849                         if ( psnet_same(&gp->server_addr, &ag->server_addr) /*&& (gp->game.security == game->security)*/ ) {
1850                                 on_list = 1;
1851                                 break;
1852                         }
1853                         gp = gp->next;
1854                 } while (gp != stop);
1855
1856                 // insert in the list
1857                 if (!on_list){
1858                         gp = multi_new_active_game();
1859                         // gp->ping_time = -1.0f;                       
1860
1861                         // copy in the game information
1862                         memcpy(&gp->server_addr,&ag->server_addr,sizeof(net_addr_t));
1863                         strcpy(gp->name,ag->name);
1864                         strcpy(gp->mission_name,ag->mission_name);
1865                         strcpy(gp->title,ag->title);                    
1866                         gp->num_players = ag->num_players;
1867                         gp->flags = ag->flags;
1868                         
1869                         // ping him
1870                         /*
1871                         gp->ping_start = timer_get_fixed_seconds();
1872                         gp->ping_end = -1;
1873                         send_ping(&gp->server_addr);                    
1874                         */
1875                         multi_ping_reset(&gp->ping);
1876                         multi_ping_send(&gp->server_addr,&gp->ping);
1877                 }
1878                 // otherwise update the netgame info we have for this guy
1879                 else {                          
1880                         memset(gp->name,0,MAX_GAMENAME_LEN+1);
1881                         strcpy(gp->name,ag->name);
1882                         memset(gp->mission_name,0,NAME_LENGTH+1);
1883                         strcpy(gp->mission_name,ag->mission_name);
1884                         memset(gp->title,0,NAME_LENGTH+1);
1885                         strcpy(gp->title,ag->title);                    
1886                         gp->num_players = ag->num_players;
1887                         gp->flags = ag->flags;                  
1888                 }
1889         } else {
1890                 gp = multi_new_active_game();
1891                 // gp->ping_time = -1.0f;               
1892
1893                 // copy in the game information 
1894                 memcpy(&gp->server_addr,&ag->server_addr,sizeof(net_addr_t));
1895                 strcpy(gp->name,ag->name);
1896                 strcpy(gp->mission_name,ag->mission_name);
1897                 strcpy(gp->title,ag->title);            
1898                 gp->num_players = ag->num_players;
1899                 gp->flags = ag->flags;
1900                 
1901                 // ping him
1902                 // gp->ping_start = timer_get_fixed_seconds();
1903                 // gp->ping_end = -1;
1904                 // send_ping(&gp->server_addr);
1905                 multi_ping_reset(&gp->ping);
1906                 multi_ping_send(&gp->server_addr,&gp->ping);
1907         }               
1908
1909         // don't do anything if we don't have a game entry
1910         if(gp == NULL){
1911                 return NULL;
1912         }
1913         
1914         // update the last time we heard from him
1915         if((Multi_options_g.protocol == NET_TCP) && (Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST)){
1916                 gp->heard_from_timer = timestamp(MULTI_JOIN_SERVER_TIMEOUT_LOCAL);
1917         } else {
1918                 gp->heard_from_timer = timestamp(MULTI_JOIN_SERVER_TIMEOUT);
1919         }
1920
1921         return gp;
1922 }
1923
1924 void multi_free_active_games()
1925 {
1926         active_game *moveup,*backup;    
1927
1928         moveup = Active_game_head;
1929         backup = NULL;
1930         if(moveup != NULL){
1931                 do {                    
1932                         backup = moveup;
1933                         moveup = moveup->next;
1934                         
1935                         free(backup);
1936                         backup = NULL;
1937                 } while(moveup != Active_game_head);
1938                 Active_game_head = NULL;
1939         }       
1940         Active_game_count = 0;
1941 }
1942
1943 server_item *multi_new_server_item( void )
1944 {
1945         server_item *new_game;
1946
1947         new_game = (server_item *)malloc(sizeof(server_item));
1948         if ( new_game == NULL ) {
1949                 nprintf(("Network", "Cannot allocate space for new server_item structure\n"));
1950                 return NULL;
1951         }
1952
1953         if ( Game_server_head != NULL ) {
1954                 new_game->next = Game_server_head->next;
1955                 new_game->next->prev = new_game;
1956                 Game_server_head->next = new_game;
1957                 new_game->prev = Game_server_head;
1958         } else {
1959                 Game_server_head = new_game;
1960                 Game_server_head->next = Game_server_head->prev = Game_server_head;
1961         }
1962                 
1963         return new_game;
1964 }
1965
1966 void multi_free_server_list()
1967 {
1968         server_item *moveup,*backup;    
1969
1970         moveup = Game_server_head;
1971         backup = NULL;
1972         if(moveup != NULL){
1973                 do {                    
1974                         backup = moveup;
1975                         moveup = moveup->next;
1976                         
1977                         free(backup);
1978                         backup = NULL;
1979                 } while(moveup != Game_server_head);
1980                 Game_server_head = NULL;
1981         }       
1982 }
1983
1984 int multi_num_players()
1985 {
1986         int idx,count;
1987
1988         // count the players who are actively connected
1989         count = 0;
1990         for(idx=0;idx<MAX_PLAYERS;idx++){
1991                 // count all connected players except the standalone server (if any)
1992                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx])){                 
1993                         count++;
1994                 }
1995         }
1996
1997         return count;
1998 }
1999
2000 int multi_num_observers()
2001 {
2002         int idx,count;
2003
2004         // count the players who are actively connected
2005         count = 0;
2006         for(idx=0;idx<MAX_PLAYERS;idx++){
2007                 // count all connected players except the standalone server (if any)
2008                 if(MULTI_CONNECTED(Net_players[idx]) && MULTI_PERM_OBSERVER(Net_players[idx])){
2009                         count++;
2010                 }
2011         }
2012
2013         return count;
2014
2015 }
2016
2017 int multi_num_connections()
2018 {
2019         int idx,count;
2020
2021         // count the players who are actively connected
2022         count = 0;
2023         for(idx=0;idx<MAX_PLAYERS;idx++){
2024                 // count all connected players except the standalone server (if any)
2025                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
2026                         count++;
2027                 }
2028         }
2029
2030         return count;
2031 }
2032
2033 int multi_can_message(net_player *p)
2034 {
2035         int max_rank;
2036         ship *sp;
2037
2038         // if the player is an observer of any kind, he cannot message
2039         if(p->flags & NETINFO_FLAG_OBSERVER){
2040                 return 0;
2041         }
2042
2043         switch(Netgame.options.squad_set){
2044         // only those of the highest rank can message
2045         case MSO_SQUAD_RANK:
2046                 max_rank = multi_get_highest_rank();
2047                 if(p->player->stats.rank < max_rank){
2048                         return 0;
2049                 }
2050                 break;
2051
2052         // only wing/team leaders can message
2053         case MSO_SQUAD_LEADER:
2054                 // if the player has an invalid object #
2055                 if(p->player->objnum < 0){
2056                         return 0;
2057                 }
2058
2059                 // check to see if he's a wingleader
2060                 sp = &Ships[Objects[p->player->objnum].instance];               
2061                 if(stricmp(sp->ship_name,NOX("alpha 1")) && stricmp(sp->ship_name,NOX("beta 1")) && stricmp(sp->ship_name,NOX("gamma 1")) && stricmp(sp->ship_name,NOX("zeta 1")) ){
2062                         return 0;
2063                 }       
2064                 break;
2065
2066         // anyone can end message
2067         case MSO_SQUAD_ANY:
2068                 break;
2069
2070         // only the host can message
2071         case MSO_SQUAD_HOST:
2072                 if(!(p->flags & NETINFO_FLAG_GAME_HOST)){
2073                         return 0;
2074                 }
2075                 break;
2076         }       
2077         
2078         return 1;
2079 }
2080
2081 int multi_can_end_mission(net_player *p)
2082 {
2083         int max_rank;
2084         ship *sp;       
2085
2086         // the host can _always_ unpause a game
2087         if(p->flags & NETINFO_FLAG_GAME_HOST){
2088                 return 1;
2089         }
2090
2091         switch(Netgame.options.endgame_set){
2092         // only those of the highest rank can end the mission
2093         case MSO_END_RANK:
2094                 max_rank = multi_get_highest_rank();
2095                 if(p->player->stats.rank < max_rank){
2096                         return 0;
2097                 }
2098                 break;
2099
2100         // only wing/team leaders can end the mission
2101         case MSO_END_LEADER:
2102                 // if the player has an invalid object #
2103                 if(p->player->objnum < 0){
2104                         return 0;
2105                 }
2106
2107                 // check to see if he's a wingleader
2108                 sp = &Ships[Objects[p->player->objnum].instance];
2109                 if(stricmp(sp->ship_name,NOX("alpha 1")) && stricmp(sp->ship_name,NOX("beta 1")) && stricmp(sp->ship_name,NOX("gamma 1")) && stricmp(sp->ship_name,NOX("zeta 1")) ){
2110                         return 0;
2111                 }       
2112                 break;
2113
2114         // anyone can end the mission
2115         case MSO_END_ANY:
2116                 break;
2117
2118         // only the host can end the mission
2119         case MSO_END_HOST:
2120                 if(!(p->flags & NETINFO_FLAG_GAME_HOST)){
2121                         return 0;
2122                 }
2123                 break;
2124         }       
2125         
2126         return 1;
2127 }
2128
2129 int multi_eval_join_request(join_request *jr,net_addr_t *addr)
2130 {       
2131         int team0_avail,team1_avail;
2132
2133         // if the server versions are incompatible
2134         if(jr->version < MULTI_FS_SERVER_COMPATIBLE_VERSION){
2135                 return JOIN_DENY_JR_BAD_VERSION;
2136         }
2137
2138         // check to make sure we are otherwise in a state to accept
2139         if(Netgame.game_state != NETGAME_STATE_FORMING){
2140                 return JOIN_DENY_JR_STATE;
2141         }
2142         
2143         // the standalone has some oddball situations which we must handle seperately
2144         if(Game_mode & GM_STANDALONE_SERVER){           
2145                 // if this is the first connection, he will be the host so we must always accept him
2146                 if(multi_num_players() == 0){
2147                         // check to see if this is a tracker game, and if so make sure this is a valid MT player        
2148                         // we probably eventually want to make sure he's not passing us a fake tracker id#
2149                         if (MULTI_IS_TRACKER_GAME) {
2150                                 if(jr->tracker_id < 0){
2151                                         return JOIN_DENY_JR_TRACKER_INVAL;
2152                                 }                       
2153                         }                       
2154
2155                         // if we're password protected          
2156                         if(std_is_host_passwd() && strcmp(jr->passwd, Multi_options_g.std_passwd)){
2157                                 return JOIN_DENY_JR_PASSWD;
2158                         }
2159                                 
2160                         // don't allow the host to join as an observer
2161                         if(jr->flags & JOIN_FLAG_AS_OBSERVER){
2162                                 return JOIN_DENY_JR_NOOBS;
2163                         } else {
2164                                 return -1;
2165                         }
2166                 }               
2167         }
2168
2169         // first off check to see if we're violating any of our max players/observers/connections boundaries    
2170                 // if we've already got the full 16 (MAX_PLAYERS) connections - yow
2171         if( (multi_num_connections() >= MULTI_MAX_CONNECTIONS) ||                       
2172                 // if we're full of observers and this guy wants to be an observer
2173                 ((multi_num_observers() >= MAX_OBSERVERS) && (jr->flags & JOIN_FLAG_AS_OBSERVER)) ||
2174                 // if we're up to MULTI_MAX_PLAYERS-1 and we're on the standalone
2175                 ((multi_num_players() >= (MULTI_MAX_PLAYERS - 1)) && (Game_mode & GM_STANDALONE_SERVER)) ||
2176                 // if we're up to MULTI_MAX_PLAYERS
2177                 (multi_num_players() >= MULTI_MAX_PLAYERS) ||
2178                 // if the max players for a standalone was set
2179                 ((Multi_options_g.std_max_players != -1) && (multi_num_players() >= Multi_options_g.std_max_players)) ){
2180
2181                 // we're full buddy - sorry
2182                 return JOIN_DENY_JR_FULL;
2183         }
2184         
2185         // check to see if this is a tracker game, and if so make sure this is a valid MT player        
2186         // we probably eventually want to make sure he's not passing us a fake tracker id#
2187         if (MULTI_IS_TRACKER_GAME) {
2188                 if(jr->tracker_id < 0){
2189                         return JOIN_DENY_JR_TRACKER_INVAL;
2190                 }                       
2191         }
2192
2193         // check to see if the player is trying to ingame join in a closed game
2194         if(MULTI_IN_MISSION && (Netgame.mode == NG_MODE_CLOSED)){
2195                 return JOIN_DENY_JR_CLOSED;
2196         }       
2197
2198         // check to see if the player has passed a valid password in a password protected game
2199         if((Netgame.mode == NG_MODE_PASSWORD) && strcmp(Netgame.passwd,jr->passwd)){
2200                 return JOIN_DENY_JR_PASSWD;
2201         }
2202
2203         // check to see if the netgame is forming and is temporarily marked as closed
2204         if((Netgame.game_state == NETGAME_STATE_FORMING) && (Netgame.flags & NG_FLAG_TEMP_CLOSED)){
2205                 return JOIN_DENY_JR_TEMP_CLOSED;
2206         }       
2207
2208         // check to make sure he meets the rank requirement
2209         if((Netgame.mode == NG_MODE_RANK_ABOVE) && (jr->player_rank < Netgame.rank_base)){
2210                 return JOIN_DENY_JR_RANK_LOW;
2211         }
2212
2213         // check to make sure he meets the rank requirement
2214         if((Netgame.mode == NG_MODE_RANK_BELOW) && (jr->player_rank > Netgame.rank_base)){
2215                 return JOIN_DENY_JR_RANK_HIGH;
2216         }       
2217
2218         // can't ingame join a non-dogfight game
2219         if((Netgame.game_state != NETGAME_STATE_FORMING) && !(Netgame.type_flags & NG_TYPE_DOGFIGHT)){
2220                 return JOIN_DENY_JR_TYPE;
2221         }       
2222
2223         // if the player was banned by the standalone
2224         if((Game_mode & GM_STANDALONE_SERVER) && std_player_is_banned(jr->callsign)){
2225                 return JOIN_DENY_JR_BANNED;
2226         }
2227
2228         // if the game is in-mission, make sure there are ships available
2229         if(MULTI_IN_MISSION && !(jr->flags & JOIN_FLAG_AS_OBSERVER)){
2230                 team0_avail = 0;
2231                 team1_avail = 0;
2232                 multi_player_ships_available(&team0_avail,&team1_avail);
2233
2234                 // if there are no ships available on either team
2235                 if((team0_avail == 0) && (team1_avail == 0)){
2236                         return JOIN_DENY_JR_FULL;
2237                 }
2238         }
2239
2240         // if my ingame joining flag is set, then deny since we only allow one ingame joiner at a time
2241         if ( Netgame.flags & NG_FLAG_INGAME_JOINING ){
2242                 return JOIN_DENY_JR_INGAME_JOIN;
2243         }
2244
2245         // check to make sure the game is not full (of observers, or players, as appropriate)
2246         if((jr->flags & JOIN_FLAG_AS_OBSERVER)){
2247                 if((multi_num_observers() + 1) > Netgame.options.max_observers){
2248                         return JOIN_DENY_JR_FULL;
2249                 }
2250         } 
2251
2252         // if the netgame is restricted or is team vs. team
2253         if((Netgame.mode == NG_MODE_RESTRICTED) || (Netgame.type_flags & NG_TYPE_TEAM)){
2254                 // ingame, we must query the host to see if this guy is accepted
2255                 if(MULTI_IN_MISSION){
2256                         return JOIN_QUERY_RESTRICTED;
2257                 }               
2258         }               
2259         
2260         // check to make sure this player hasn't been kick/banned
2261         if(multi_kick_is_banned(addr)){
2262                 return JOIN_DENY_JR_BANNED;
2263         }
2264
2265         // check to make sure this player doesn't already exist
2266         if ( find_player(addr) >= 0 ) {
2267                 return JOIN_DENY_JR_DUP;
2268         }
2269
2270         return -1;
2271 }
2272
2273 // called by any machine (client, host, server, standalone, etc), to begin warping out all player objects
2274 void multi_warpout_all_players()
2275 {
2276         int idx;
2277
2278         // i'f i'm already marked as warping out, don't do this again
2279         if(Net_player->flags & NETINFO_FLAG_WARPING_OUT){
2280                 return;
2281         }
2282
2283         // stop my afterburners
2284         if((Player_obj != NULL) && (Player_obj->type == OBJ_SHIP) && !(Game_mode & GM_STANDALONE_SERVER)){
2285                 afterburners_stop( Player_obj, 1 );
2286         }
2287
2288         // traverse through each player
2289         for(idx=0;idx<MAX_PLAYERS;idx++) {
2290                 object *objp;
2291
2292                 // only warpout player _ships_ which are not mine
2293                 if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx]) && (Objects[Net_players[idx].player->objnum].type == OBJ_SHIP)){
2294
2295                         objp = &Objects[Net_players[idx].player->objnum];
2296
2297                         obj_set_flags( objp, objp->flags & (~OF_COLLIDES) );
2298                         shipfx_warpout_start( objp );
2299                 }
2300         }
2301
2302         // now, mark ourselves as needing to warp out
2303         Net_player->flags |= NETINFO_FLAG_WARPING_OUT;
2304         
2305         // if we're an observer, or we're respawning, or we can't warp out. so just jump into the debrief state
2306         if((Net_player->flags & NETINFO_FLAG_OBSERVER) || (Net_player->flags & NETINFO_FLAG_RESPAWNING) ||
2307                 (Net_player->flags & NETINFO_FLAG_OBSERVER) || ((Player_obj->type == OBJ_SHIP) && (Player_ship->flags & SF_CANNOT_WARP)) ){             
2308
2309                 if(Netgame.type_flags & NG_TYPE_DOGFIGHT){
2310                         gameseq_post_event(GS_EVENT_MULTI_DOGFIGHT_DEBRIEF);
2311                 } else {
2312                         gameseq_post_event(GS_EVENT_DEBRIEF);           
2313                 }
2314         }
2315         // if we're a ship, then begin the warpout process
2316         else {
2317                 // turn off collision detection for my ship
2318                 obj_set_flags(Player_obj, Player_obj->flags & (~OF_COLLIDES) );
2319                 gameseq_post_event(GS_EVENT_PLAYER_WARPOUT_START_FORCED);                               
2320         }
2321 }
2322
2323 // determine the highest rank of any of the players in the game
2324 int multi_get_highest_rank()
2325 {
2326         int idx;
2327         int max_rank = -1;
2328         
2329         // go through all the players
2330         for(idx=0;idx<MAX_PLAYERS;idx++){
2331                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player->stats.rank > max_rank)){
2332                         max_rank = Net_players[idx].player->stats.rank;
2333                 }
2334         }
2335
2336         // return what we found
2337         return max_rank;                        
2338 }
2339
2340 // called on the machine of the player who hit alt+j
2341 void multi_handle_end_mission_request()
2342 {
2343         int idx;
2344         
2345         // all clients should send the request to the server. no exceptions
2346         if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
2347                 send_endgame_packet();
2348         }
2349         // the server of the game does some further processing
2350         else {
2351                 ml_string("Server received endgame request, proceeding...");                    
2352
2353                 // first we should toss all ingame joiners
2354                 for(idx=0;idx<MAX_PLAYERS;idx++){
2355                         if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_INGAME_JOIN)){
2356                                 multi_kick_player(idx,0,KICK_REASON_INGAME_ENDED);
2357                         }
2358                 }               
2359                                 
2360                 // send the endgame packet to all clients, who will act on it immediately
2361                 send_endgame_packet();                  
2362                 Netgame.game_state = NETGAME_STATE_ENDGAME;             
2363                 send_netgame_update_packet();                                   
2364                 
2365                 if(Game_mode & GM_STANDALONE_SERVER){                                   
2366                         // move to the standalone postgame (which is where we'll handle stats packets, etc)
2367                         gameseq_post_event(GS_EVENT_STANDALONE_POSTGAME);
2368                 }
2369
2370                 // begin the warpout process for all players and myself
2371                 multi_warpout_all_players();
2372         } 
2373 }
2374
2375 // called to handle any special cases where a player is in some submemu when the game is ended 
2376 void multi_handle_state_special()
2377 {
2378         int stack_depth,current_depth;  
2379         
2380         // first off - kill any active popups
2381         popup_kill_any_active();
2382
2383         // kill any popupdeads
2384         if(popupdead_is_active()){
2385                 popupdead_close();
2386         }
2387
2388         // kill off the pilot info popup if its active
2389         if(multi_pinfo_popup_active()){
2390                 multi_pinfo_popup_kill();
2391         }
2392
2393         // now do any special processing for being in states other then the gameplay states
2394         stack_depth = gameseq_get_depth();
2395         
2396         // if we're not pushed on top of any states, do any special state case handling here
2397         if(stack_depth == 0){           
2398                 // currently there are no special cases, so return
2399                 return;
2400         } 
2401         // if we are pushed on any states, post events to pop them off one by one
2402         else {
2403                 current_depth = stack_depth;
2404                 do {            
2405                         switch(gameseq_get_state(stack_depth - current_depth)){                         
2406                         // the hotkey screen
2407                         case GS_STATE_HOTKEY_SCREEN :           
2408                                 mission_hotkey_exit();
2409                                 Game_do_state_should_skip = 1;
2410                                 break;
2411                         // the options menu
2412                         case GS_STATE_OPTIONS_MENU:             
2413                                 options_cancel_exit();
2414                                 Game_do_state_should_skip = 1;
2415                                 break;
2416                         // the hud config (1 deeper in the options menu)
2417                         case GS_STATE_HUD_CONFIG:
2418                                 hud_config_cancel();                    
2419                                 Game_do_state_should_skip = 1;
2420                                 break;
2421                         // controls config (1 deeper than the options menu)
2422                         case GS_STATE_CONTROL_CONFIG:
2423                                 control_config_cancel_exit();   
2424                                 Game_do_state_should_skip = 1;
2425                                 break;
2426                         // mission goals screen
2427                         case GS_STATE_SHOW_GOALS:
2428                                 mission_goal_exit();
2429                                 Game_do_state_should_skip = 1;
2430                                 break;                  
2431                         // mission log scrollback
2432                         case GS_STATE_MISSION_LOG_SCROLLBACK:
2433                                 hud_scrollback_exit();
2434                                 Game_do_state_should_skip = 1;
2435                                 break;
2436                         // pause screen
2437                         case GS_STATE_MULTI_PAUSED:
2438                                 gameseq_pop_state();
2439                                 Game_do_state_should_skip = 1;
2440                                 break;
2441                         }
2442
2443                         // next pushed state
2444                         current_depth--;
2445                 } while(current_depth > 0);     
2446         }
2447 }
2448
2449 // called by the file xfer subsytem when we start receiving a file
2450 void multi_file_xfer_notify(int handle)
2451 {
2452         char *filename;
2453         int len,idx;
2454         int force_dir;
2455         int cf_type;
2456         int is_mission = 0;     
2457
2458         // get the filename of the file we are receiving
2459         filename = NULL;
2460         filename = multi_xfer_get_filename(handle);
2461
2462         // get the directory the file is forced into
2463         force_dir = multi_xfer_get_force_dir(handle);
2464                 
2465         // something is messed up
2466         if(filename == NULL){
2467                 return;
2468         }
2469
2470         // convert the filename to all lowercase
2471         len = strlen(filename);
2472         for(idx=0;idx<len;idx++){
2473                 filename[idx] = (char)tolower(filename[idx]);
2474         }               
2475
2476         // if this is a mission file
2477         is_mission = (strstr(filename, FS_MISSION_FILE_EXT) != NULL);
2478         
2479         // determine where its going to go
2480         if(is_mission){
2481                 cf_type = Net_player->p_info.options.flags & MLO_FLAG_XFER_MULTIDATA ? CF_TYPE_MULTI_CACHE : CF_TYPE_MISSIONS;
2482         } else {
2483                 cf_type = CF_TYPE_MULTI_CACHE;
2484         }
2485
2486         // QUICK FIX
2487         // check to see if the file is read-only                        
2488         if((strlen(filename) > 0) && !cf_access(filename, cf_type, 00) && (cf_access(filename, cf_type, 02) == -1)){    
2489                 multi_xfer_xor_flags(handle, MULTI_XFER_FLAG_REJECT);
2490
2491                 Net_player->flags &= ~(NETINFO_FLAG_DO_NETWORKING);
2492                 popup(PF_USE_AFFIRMATIVE_ICON, 1, XSTR("&Ok", 713), XSTR("An outdated copy of this file exists, but it cannot be overwritten by the server because it is set to be read-only. Change the permissions on this file next time.", 714));           
2493                 multi_quit_game(PROMPT_NONE);           
2494                 return;
2495         }       
2496
2497         // if the incoming filename is a freespace file, set my netplayer state to be "file xfer"
2498         if(is_mission){
2499                 // we'd better not be xferring a file right now
2500                 Assert(Net_player->s_info.xfer_handle == -1);
2501
2502                 // force into the multidata directory
2503                 multi_xfer_handle_force_dir(handle, cf_type);           
2504         
2505                 // set my xfer handle
2506                 Net_player->s_info.xfer_handle = handle;
2507                 
2508                 Net_player->state = NETPLAYER_STATE_MISSION_XFER;
2509                 send_netplayer_update_packet();
2510         }
2511         // otherwise always hand it off to the multi_data system
2512         else {
2513                 multi_data_handle_incoming(handle);             
2514         }
2515 }
2516
2517 // return the lag/disconnected status of the game
2518 #define MULTI_LAG_VAL           400
2519 int multi_query_lag_status()
2520 {
2521         // -1 == not lagged, 0 == lagged, 1 == disconnected
2522
2523         // if I'm the server of the game, I can't be lagged
2524         if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2525                 return -1;
2526         }
2527         
2528         // if we've been disconnected for some reason or another
2529         if(multi_client_server_dead()){
2530                 return 1;
2531         }
2532
2533         // if our ping time to the server is over a certain time
2534         if(Netgame.server->s_info.ping.ping_avg >= MULTI_LAG_VAL){
2535                 return 0;
2536         } 
2537
2538         // not lagged
2539         return -1;
2540 }
2541
2542 // process a valid join request
2543 void multi_process_valid_join_request(join_request *jr, net_addr_t *who_from, int ingame_join_team)
2544 {
2545         int net_player_num,player_num;
2546         short id_num;
2547         
2548         // create netplayer and player objects for this guy
2549         net_player_num = multi_find_open_netplayer_slot();
2550         player_num = multi_find_open_player_slot();
2551         id_num = multi_get_new_id();
2552         Assert((net_player_num != -1) && (player_num != -1));                   
2553
2554         // if he is requesting to join as an observer
2555         if(jr->flags & JOIN_FLAG_AS_OBSERVER){                  
2556                 // create the (permanently) observer player
2557                 if(!multi_obs_create_player(net_player_num, jr->callsign, who_from, &Players[player_num])){
2558                         Int3();
2559                 }
2560
2561                 // copy his pilot image filename
2562                 if(strlen(jr->image_filename) > 0){
2563                         strcpy(Net_players[net_player_num].player->image_filename, jr->image_filename);
2564                 } else {
2565                         strcpy(Net_players[net_player_num].player->image_filename, "");
2566                 }
2567
2568                 // copy his pilot squad filename
2569 #ifndef MAKE_FS1
2570                 Net_players[net_player_num].player->insignia_texture = -1;
2571                 player_set_squad_bitmap(Net_players[net_player_num].player, jr->squad_filename);                
2572 #endif
2573
2574                 // clear his multi_data info
2575                 multi_data_handle_join(net_player_num);
2576
2577                 // set some extra flags for him as appropriate
2578                 if(MULTI_IN_MISSION){
2579                         Net_players[net_player_num].flags |= NETINFO_FLAG_INGAME_JOIN;                  
2580                 }
2581
2582                 Net_players[net_player_num].flags |= NETINFO_FLAG_CONNECTED;            
2583                 Net_players[net_player_num].player_id = id_num;
2584                 Net_players[net_player_num].tracker_player_id = jr->tracker_id;
2585
2586                 // store pxo info
2587 #ifndef MAKE_FS1
2588                 if(strlen(jr->pxo_squad_name) > 0){
2589                         strcpy(Net_players[net_player_num].p_info.pxo_squad_name, jr->pxo_squad_name);
2590                 } else {
2591                         strcpy(Net_players[net_player_num].p_info.pxo_squad_name, "");
2592                 }               
2593 #endif
2594
2595                 // if he's using hacked data
2596                 if(jr->flags & JOIN_FLAG_HAXOR){
2597                         Net_players[net_player_num].flags |= NETINFO_FLAG_HAXOR;
2598                 }
2599
2600                 // set his reliable connect time
2601                 Net_players[net_player_num].s_info.reliable_connect_time = time(NULL);
2602
2603                 // send the accept packet here
2604                 send_accept_packet(net_player_num, (Net_players[net_player_num].flags & NETINFO_FLAG_INGAME_JOIN) ? ACCEPT_OBSERVER | ACCEPT_INGAME : ACCEPT_OBSERVER);
2605         } else {
2606                 // create the player object     
2607                 if(!multi_create_player( net_player_num, &Players[player_num], jr->callsign, who_from, -1, id_num )){
2608                         Int3();
2609                 }
2610                 
2611                 // copy his pilot image filename
2612                 if(strlen(jr->image_filename) > 0){
2613                         strcpy(Net_players[net_player_num].player->image_filename, jr->image_filename);
2614                 } else {
2615                         strcpy(Net_players[net_player_num].player->image_filename, "");
2616                 }
2617
2618                 // copy his pilot squad filename                
2619 #ifndef MAKE_FS1
2620                 Net_players[net_player_num].player->insignia_texture = -1;
2621                 player_set_squad_bitmap(Net_players[net_player_num].player, jr->squad_filename);                                
2622 #endif
2623
2624                 // clear his multi_data info
2625                 multi_data_handle_join(net_player_num);
2626
2627                 // mark him as being connected
2628                 Net_players[net_player_num].flags |= NETINFO_FLAG_CONNECTED;
2629                                                 
2630                 // set his tracker id correctly
2631                 Net_players[net_player_num].tracker_player_id = jr->tracker_id;                         
2632
2633                 // set his player id#
2634                 Net_players[net_player_num].player_id = id_num;
2635
2636                 // store pxo info
2637 #ifndef MAKE_FS1
2638                 if(strlen(jr->pxo_squad_name) > 0){
2639                         strcpy(Net_players[net_player_num].p_info.pxo_squad_name, jr->pxo_squad_name);
2640                 } else {
2641                         strcpy(Net_players[net_player_num].p_info.pxo_squad_name, "");
2642                 }               
2643 #endif
2644
2645                 // if he's using hacked data
2646                 if(jr->flags & JOIN_FLAG_HAXOR){
2647                         Net_players[net_player_num].flags |= NETINFO_FLAG_HAXOR;
2648                 }
2649                 
2650                 // flag him appropriately if he's doing an ingame join
2651                 if(MULTI_IN_MISSION){
2652                         Int3();
2653                         Net_players[net_player_num].flags |= NETINFO_FLAG_INGAME_JOIN;
2654                         Net_players[net_player_num].s_info.ingame_join_flags = 0;
2655                 }               
2656
2657                 // set his reliable connect time
2658                 Net_players[net_player_num].s_info.reliable_connect_time = time(NULL);
2659
2660                 // if he's joining as a host (on the standalone)
2661                 if(Net_players[net_player_num].flags & NETINFO_FLAG_GAME_HOST){
2662                         send_accept_packet(net_player_num, ACCEPT_HOST);
2663
2664                         Netgame.host = &Net_players[net_player_num];
2665
2666                         // set the game and player states appropriately
2667                         Netgame.game_state = NETGAME_STATE_STD_HOST_SETUP;                      
2668                 }
2669                 // if he's joining ingame
2670                 else if(Net_players[net_player_num].flags & NETINFO_FLAG_INGAME_JOIN){
2671                         // if we're in team vs. team mode
2672                         if(Netgame.type_flags & NG_TYPE_TEAM){
2673                                 Assert(ingame_join_team != -1);
2674
2675                                 Net_players[net_player_num].p_info.team = ingame_join_team;
2676                         }
2677
2678                         send_accept_packet(net_player_num, ACCEPT_INGAME, ingame_join_team);
2679
2680                         // set his last full update time for updating him on ingame join ships
2681                         Net_players[net_player_num].s_info.last_full_update_time = timestamp(INGAME_SHIP_UPDATE_TIME);
2682                 } 
2683                 // if he's joining as an otherwise ordinary client
2684                 else {
2685                         send_accept_packet(net_player_num, ACCEPT_CLIENT);
2686                 }
2687         }
2688
2689         // set my ingame joining flag if the new guy is joining ingame
2690         if ( Net_players[net_player_num].flags & NETINFO_FLAG_INGAME_JOIN ){
2691                 Int3();
2692                 Netgame.flags |= NG_FLAG_INGAME_JOINING;
2693         }       
2694         
2695         // copy in his options
2696         memcpy(&Net_players[net_player_num].p_info.options, &jr->player_options, sizeof(multi_local_options));
2697
2698         // if on the standalone, then do any necessary gui updating
2699         if(Game_mode & GM_STANDALONE_SERVER) {          
2700                 std_add_player(&Net_players[net_player_num]);
2701                 std_connect_set_connect_count();
2702                 std_connect_set_host_connect_status();
2703         } else {
2704                 // let the create game screen know someone has joined
2705                 if(gameseq_get_state() == GS_STATE_MULTI_HOST_SETUP){
2706                         multi_create_handle_join(&Net_players[net_player_num]);
2707                 }
2708         }
2709
2710         extern int Multi_client_update_times[MAX_PLAYERS];
2711         Multi_client_update_times[net_player_num] = -1;
2712
2713         // notify datarate
2714         multi_rate_reset(net_player_num);
2715 }
2716
2717 // if a player is trying to join a restricted game, evaluate the keypress (accept or not, etc)
2718 int multi_process_restricted_keys(int k)
2719 {       
2720         int key1=-1,key2=-1;    //JAS: Get rid of optimized warning
2721         int team_val;
2722         
2723         // if the query timestamp is not set, don't do anything
2724         if(Multi_restr_query_timestamp == -1){
2725                 return 0;
2726         }
2727
2728         // determine what keys to look for based upon the mode we're in
2729         switch(Multi_join_restr_mode){
2730         // normal restricted join, Y or N
2731         case MULTI_JOIN_RESTR_MODE_1:
2732                 key1 = KEY_Y;
2733                 key2 = KEY_N;
2734                 break;
2735
2736         // team vs team, team 0 only has ships
2737         case MULTI_JOIN_RESTR_MODE_2:
2738                 key1 = KEY_Y;
2739                 key2 = KEY_N;
2740                 break;
2741
2742         // team vs team, team 1 only has ships          
2743         case MULTI_JOIN_RESTR_MODE_3:
2744                 key1 = KEY_Y;
2745                 key2 = KEY_N;
2746                 break;
2747
2748         // team vs team, both teams have ships
2749         case MULTI_JOIN_RESTR_MODE_4:
2750                 key1 = KEY_1;
2751                 key2 = KEY_2;
2752                 break;
2753         
2754         // illegal mode
2755         default :
2756                 Int3();
2757         }       
2758
2759         // check the keypress
2760         if((k == key1) || (k == key2)){
2761                 // unset the timestamp
2762                 Multi_restr_query_timestamp = -1;                       
2763
2764                 // MWA -- 5/26/98.  Next line commented out.  It should be cleared when the ingame joiner
2765                 // actually gets into the mission
2766                 //Netgame.flags &= ~(NG_FLAG_INGAME_JOINING);
2767
2768                 // determine which team to put him on (if any)
2769                 switch(Multi_join_restr_mode){
2770                 // normal restricted join, Y or N
2771                 case MULTI_JOIN_RESTR_MODE_1:
2772                         team_val = (k == key1) ? 0 : -1;
2773                         break;
2774
2775                 // team vs team, team 0 only has ships
2776                 case MULTI_JOIN_RESTR_MODE_2:
2777                         team_val = (k == key1) ? 0 : -1;                        
2778                         break;
2779
2780                 // team vs team, team 1 only has ships          
2781                 case MULTI_JOIN_RESTR_MODE_3:
2782                         team_val = (k == key1) ? 1 : -1;                        
2783                         break;
2784
2785                 // team vs team, both teams have ships
2786                 case MULTI_JOIN_RESTR_MODE_4:
2787                         team_val = (k == key1) ? 0 : 1;                 
2788                         break;
2789         
2790                 // illegal mode
2791                 default :
2792                         team_val = -1;  // JAS: Get rid of optimized warning
2793                         Int3();
2794                 }                               
2795
2796                 // perform the proper response  
2797                 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2798                         if(team_val >= 0){                      
2799                                 multi_process_valid_join_request(&Multi_restr_join_request,&Multi_restr_addr,team_val);                 
2800                         }
2801                 }
2802                 // otherwise tell the standalone to accept him
2803                 else {
2804                         if(team_val >= 0){
2805                                 send_host_restr_packet("null",1,team_val);
2806                         } else {
2807                                 send_host_restr_packet("null",2,-1);
2808                         }
2809                 }
2810
2811                 // processed a key
2812                 return 1;
2813         }
2814         
2815         // didn't process any keys
2816         return 0;
2817 }
2818
2819 // determine the status of available player ships (use team_0 for non team vs. team situations)
2820 void multi_player_ships_available(int *team_0,int *team_1)
2821 {
2822         ship_obj *moveup;
2823         int mp_team_num;
2824         
2825         *team_0 = 0;
2826         *team_1 = 0;
2827         
2828         moveup = GET_FIRST(&Ship_obj_list);
2829         while(moveup!=END_OF_LIST(&Ship_obj_list)){
2830                 // if this ship is flagged as OF_COULD_BE_PLAYER
2831                 if(Objects[moveup->objnum].flags & OF_COULD_BE_PLAYER){
2832                         // get the team # for this ship
2833                         mp_team_num = multi_ts_get_team(Ships[Objects[moveup->objnum].instance].ship_name);
2834                         if(mp_team_num == 0){
2835                                 (*team_0)++;
2836                         } else if(mp_team_num == 1){
2837                                 (*team_1)++;
2838                         }
2839                 }
2840                 
2841                 moveup = GET_NEXT(moveup);
2842         }       
2843 }
2844
2845 // server should update the player's bank/link status with the data in the passed ship
2846 void multi_server_update_player_weapons(net_player *pl,ship *shipp)
2847 {
2848         // don't process when the ship is dying.
2849         if ( (shipp->flags & SF_DYING) || NETPLAYER_IS_DEAD(pl) )
2850                 return;
2851
2852         // primary bank status
2853         pl->s_info.cur_primary_bank = (char)shipp->weapons.current_primary_bank;
2854
2855         // primary link status
2856         pl->s_info.cur_link_status &= ~(1<<0);
2857         if(shipp->flags & SF_PRIMARY_LINKED){
2858                 pl->s_info.cur_link_status |= (1<<0);
2859         }
2860
2861         // secondary bank status
2862         if ( shipp->weapons.current_secondary_bank < 0 ) {
2863                 nprintf(("Network", "bashing %s's current sbank to 0\n", shipp->ship_name));
2864                 shipp->weapons.current_secondary_bank = 0;
2865         }
2866         pl->s_info.cur_secondary_bank = (char)shipp->weapons.current_secondary_bank;
2867
2868         // secondary link status
2869         pl->s_info.cur_link_status &= ~(1<<1);
2870         if(shipp->flags & SF_SECONDARY_DUAL_FIRE){
2871                 pl->s_info.cur_link_status |= (1<<1);
2872         }
2873
2874         // ets values
2875         pl->s_info.ship_ets = 0x0000;
2876         // shield ets           
2877         pl->s_info.ship_ets |= ((ushort)shipp->shield_recharge_index << 8);
2878         // weapon ets
2879         pl->s_info.ship_ets |= ((ushort)shipp->weapon_recharge_index << 4);
2880         // engine ets
2881         pl->s_info.ship_ets |= ((ushort)shipp->engine_recharge_index);
2882
2883         Assert( pl->s_info.ship_ets != 0 );
2884 }
2885
2886 // flush the multidata cache directory
2887 void multi_flush_multidata_cache()
2888 {
2889         nprintf(("Network","FLUSHING MULTIDATA CACHE\n"));
2890         
2891         // call the cfile function to flush the directory
2892         cfile_flush_dir(CF_TYPE_MULTI_CACHE);
2893 }
2894
2895 // flush all data from a previous mission before starting the next
2896 void multi_flush_mission_stuff()
2897 {
2898         int idx;
2899         
2900         for(idx=0;idx<MAX_PLAYERS;idx++){
2901                 if(MULTI_CONNECTED(Net_players[idx])){                  
2902                         // unset all unneeded status bits
2903                         Net_players[idx].flags &= ~(NETINFO_FLAG_MISSION_OK | NETINFO_FLAG_RESPAWNING | NETINFO_FLAG_WARPING_OUT);
2904                         
2905                         // server is always "mission ok"
2906                         if(Net_players[idx].flags & NETINFO_FLAG_AM_MASTER){
2907                                 Net_players[idx].flags |= NETINFO_FLAG_MISSION_OK;
2908                         }
2909
2910                         // if this guy is a non-permanent observer, unset the appropriate flags
2911                         if(MULTI_TEMP_OBSERVER(Net_players[idx])){
2912                                 Net_players[idx].flags &= ~(NETINFO_FLAG_OBSERVER | NETINFO_FLAG_OBS_PLAYER);
2913                         }
2914
2915                         // misc
2916                         multi_ping_reset(&Net_players[idx].s_info.ping);                                
2917                         Net_players[idx].s_info.num_last_buttons = 0;
2918                         Net_players[idx].s_info.wing_index_backup = 0;
2919                         Net_players[idx].s_info.wing_index = 0;
2920                         Net_players[idx].p_info.ship_class = -1;
2921                         Net_players[idx].p_info.ship_index = -1;
2922                         Net_players[idx].s_info.xfer_handle = -1;
2923
2924                         // ack handles
2925                         Net_players[idx].s_info.wing_index_backup = 0;
2926                                 
2927                         // objnum 
2928                         Players[idx].objnum = -1;
2929                 }
2930         }       
2931
2932         // reset netgame stuff
2933         Netgame.flags &= ~(NG_FLAG_TEMP_CLOSED);                
2934
2935         multi_xfer_reset();
2936
2937         // standalone servers should clear their goal trees now
2938         if(Game_mode & GM_STANDALONE_SERVER){
2939                 std_multi_setup_goal_tree();
2940         }
2941         
2942         // object signatures
2943         // this will eventually get reset to Netgame.security the next time an object gets its signature assigned.
2944         // We do this to resynchronize the host/server and all clients
2945         Next_ship_signature = SHIP_SIG_MIN;
2946         Next_asteroid_signature = ASTEROID_SIG_MIN;
2947         Next_non_perm_signature = NPERM_SIG_MIN;
2948
2949         // everyone will need to either reload the current mission, leave, or load the next mission, so in any case
2950         Multi_mission_loaded = 0;
2951 }
2952
2953 // should we ignore all controls and keypresses because of some multiplayer 
2954 int multi_ignore_controls(int key)
2955 {               
2956         // if the multiplayer text messaging system is active, don't return any keys    
2957         if((key > 0) && multi_msg_text_process(key)){
2958                 return 1;
2959         }
2960
2961         // if the host of the game is being prompted to accept or deny a player in a restricted game
2962         if((key > 0) && multi_process_restricted_keys(key)){
2963                 return 1;
2964         }
2965         
2966         // if we're in text messaging mode, ignore controls
2967         if(multi_msg_text_mode()){
2968                 return 1;
2969         }       
2970
2971         // if the pause system wants to eat keys for a while
2972         if(multi_pause_eat_keys()){
2973                 return 1;
2974         }
2975
2976         // multiplayer didn't eat the key
2977         return 0;
2978 }
2979
2980 // if the kill limit has been reached by any given player
2981 int multi_kill_limit_reached()
2982 {
2983         int idx;
2984
2985         // is the kill limit <= 0 ?
2986         // if so, consider it as _no_ kill limit
2987         if(Netgame.options.kill_limit <= 0){
2988                 return 0;
2989         }
2990         
2991         // look through all active, non-observer players
2992         for(idx=0;idx<MAX_PLAYERS;idx++){
2993                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_OBSERVER(Net_players[idx]) && (Net_players[idx].player->stats.m_kill_count_ok >= Netgame.options.kill_limit)){
2994                         // someone reached the limit
2995                         return 1;
2996                 }
2997         }
2998
2999         // limit has not been reached yet
3000         return 0;
3001 }
3002
3003 // display a chat message (write to the correct spot - hud, standalone gui, chatbox, etc)
3004 void multi_display_chat_msg(char *msg, int player_index, int add_id)
3005 {
3006         // if i'm a standalone, always add to the gui
3007         if(Game_mode & GM_STANDALONE_SERVER){
3008                 std_add_chat_text(msg,player_index,add_id);
3009                 return;
3010         }
3011         
3012         // in gameplay
3013         if(Game_mode & GM_IN_MISSION){                                  
3014                 // if we're paused, send it to the chatbox
3015                 if(Multi_pause_status){
3016                         chatbox_add_line(msg, player_index, add_id);
3017                 }
3018                 // otherwise print to the HUD
3019                 else {
3020                         multi_msg_display_mission_text(msg, player_index);
3021                 }
3022         }
3023         // otherwise add it to the chatbox
3024         else {
3025                 chatbox_add_line(msg, player_index, add_id);
3026         }               
3027 }
3028
3029 // fill in Current_file_checksum and Current_file_length
3030 void multi_get_mission_checksum(char *filename)
3031 {
3032         CFILE *in;
3033
3034         Multi_current_file_checksum = 0xffff;
3035         Multi_current_file_length = -1;
3036
3037         // get the filename
3038         in = cfopen(filename,"rb");
3039         if(in != NULL){
3040                 // get the length of the file
3041                 Multi_current_file_length = cfilelength(in);
3042                 cfclose(in);
3043
3044                 in = cfopen(filename,"rb");
3045                 if(in != NULL){
3046                         // get the checksum of the file
3047                         cf_chksum_short(in,&Multi_current_file_checksum);
3048
3049                         // close the file
3050                         cfclose(in);
3051                         in = NULL;
3052                 }
3053                 // if the file doesn't exist, setup some special values, so the server recognizes this
3054                 else {
3055                         Multi_current_file_checksum = 0xffff;
3056                         Multi_current_file_length = -1;
3057                 }
3058         } else {
3059                 // don't transfew builtin missions
3060                 if(multi_is_builtin_mission()){ 
3061                         multi_quit_game(PROMPT_ALL, MULTI_END_NOTIFY_KICKED_CANT_XFER);
3062                 }
3063         }
3064         nprintf(("Network","NET FILE CHECKSUM : %d %d\n",Multi_current_file_checksum,Multi_current_file_length));
3065 }
3066
3067 char multi_unit_to_char(float unit)
3068 {
3069         char ret;
3070         
3071         if(unit > 1.0f){
3072                 Int3();
3073                 unit = 1.0f;
3074         }
3075         if(unit < -1.0f){
3076                 Int3();
3077                 unit = -1.0f;
3078         }
3079
3080         ret = (char)(unit * 127.0f);
3081         return ret;
3082 }
3083
3084 float multi_char_to_unit(float val)
3085 {
3086         float ret;
3087
3088         ret = (float)val / 127.0f;
3089         if(ret > 1.0f){
3090                 Int3();
3091                 ret = 1.0f;
3092         }
3093         if(ret < -1.0f){
3094                 Int3();
3095                 ret = -1.0f;
3096         }
3097
3098         return ret;
3099 }
3100
3101 // if we should render our ping time to the server in a multiplayer game
3102 int multi_show_ingame_ping()
3103 {
3104         // always show it for now
3105         return 1;
3106 }
3107
3108 int multi_get_connection_speed()
3109 {
3110         int cspeed;
3111         char *connection_speed;
3112         
3113         connection_speed = os_config_read_string(NULL, "ConnectionSpeed", "");  
3114
3115         if ( !stricmp(connection_speed, NOX("Slow")) ) {
3116                 cspeed = CONNECTION_SPEED_288;
3117         } else if ( !stricmp(connection_speed, NOX("56K")) ) {
3118                 cspeed = CONNECTION_SPEED_56K;
3119         } else if ( !stricmp(connection_speed, NOX("ISDN")) ) {
3120                 cspeed = CONNECTION_SPEED_SISDN;
3121         } else if ( !stricmp(connection_speed, NOX("Cable")) ) {
3122                 cspeed = CONNECTION_SPEED_CABLE;
3123         } else if ( !stricmp(connection_speed, NOX("Fast")) ) {
3124                 cspeed = CONNECTION_SPEED_T1;
3125         } else {
3126                 cspeed = CONNECTION_SPEED_NONE;
3127         }
3128
3129         return cspeed;
3130 }
3131
3132 // return a MVALID_STATUS_* define based upon the passed string
3133 int multi_string_to_status(char *valid_string)
3134 {
3135         return MVALID_STATUS_UNKNOWN;   
3136 }
3137
3138 // if we're in tracker mode, do a validation update on all known missions
3139 void multi_update_valid_missions()
3140 {
3141         char next_filename[MAX_FILENAME_LEN+1]; 
3142         char next_line[512];
3143         char status_string[50];
3144         char temp[256];
3145         char *tok;
3146         CFILE *in;
3147         int idx, file_index;    
3148         int was_cancelled;
3149
3150         // if we're a standalone, show a dialog saying "validating missions"
3151         if(Game_mode & GM_STANDALONE_SERVER){
3152                 std_create_gen_dialog("Validating missions");
3153                 std_gen_set_text("Querying:",1);
3154         }
3155
3156         // mark all missions on our list as being MVALID_STATUS_UNKNOWN
3157         for(idx=0; idx<Multi_create_mission_count; idx++){
3158                 Multi_create_mission_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
3159         }
3160         
3161         // attempt to open the valid mission config file
3162         in = cfopen(MULTI_VALID_MISSION_FILE, "rt", CFILE_NORMAL, CF_TYPE_DATA);
3163         if(in != NULL){         
3164                 // read in all listed missions
3165                 while(!cfeof(in)){
3166                         // read in a line
3167                         memset(next_line, 0, 512);
3168                         cfgets(next_line, 512, in);
3169                         drop_trailing_white_space(next_line);
3170                         drop_leading_white_space(next_line);
3171
3172                         // read in a filename
3173                         memset(next_filename, 0, MAX_FILENAME_LEN+1);
3174                         memset(temp, 0, 256);
3175                         tok = strtok(next_line, " ");
3176                         if(tok == NULL){
3177                                 continue;
3178                         }                       
3179                         strcpy(temp, tok);
3180                         drop_trailing_white_space(temp);
3181                         drop_leading_white_space(temp);
3182                         strcpy(next_filename, temp);
3183                         
3184                         // read in the status string
3185                         memset(status_string, 0, 50);
3186                         memset(temp, 0, 256);
3187                         tok = strtok(NULL," \n");
3188                         if(tok == NULL){
3189                                 continue;
3190                         }
3191                         strcpy(temp, tok);
3192                         drop_trailing_white_space(temp);
3193                         drop_leading_white_space(temp);
3194                         strcpy(status_string, temp);
3195
3196                         // try and find the file
3197                         file_index = multi_create_lookup_mission(next_filename);
3198                         if(file_index >= 0){
3199                                 Multi_create_mission_list[file_index].valid_status = (char)multi_string_to_status(status_string);
3200                         }
3201                 }
3202
3203                 // close the infile
3204                 cfclose(in);
3205                 in = NULL;      
3206         }       
3207
3208         // now poll for all unknown missions
3209         was_cancelled = 0;
3210
3211         // if the operation was cancelled, don't write anything new
3212         if(was_cancelled){
3213                 // if we're a standalone, kill the validate dialog
3214                 if(Game_mode & GM_STANDALONE_SERVER){
3215                         std_destroy_gen_dialog();
3216                 }
3217
3218                 return;
3219         }
3220
3221         // now rewrite the outfile with the new mission info
3222         in = cfopen(MULTI_VALID_MISSION_FILE, "wt", CFILE_NORMAL, CF_TYPE_DATA);
3223         if(in == NULL){
3224                 // if we're a standalone, kill the validate dialog
3225                 if(Game_mode & GM_STANDALONE_SERVER){
3226                         std_destroy_gen_dialog();
3227                 }
3228
3229                 return;
3230         }
3231         for(idx=0; idx<Multi_create_mission_count; idx++){
3232                 switch(Multi_create_mission_list[idx].valid_status){
3233                 case MVALID_STATUS_VALID:
3234                         cfputs(Multi_create_mission_list[idx].filename, in);
3235                         cfputs(NOX("   valid"), in);
3236                         cfputs(NOX("\n"), in);
3237                         break;
3238
3239                 case MVALID_STATUS_INVALID:
3240                         cfputs(Multi_create_mission_list[idx].filename, in);
3241                         cfputs(NOX("   invalid"), in);
3242                         cfputs(NOX("\n"), in);
3243                         break;
3244                 }
3245         }
3246
3247         // close the outfile
3248         cfclose(in);
3249         in = NULL;
3250
3251         // if we're a standalone, kill the validate dialog
3252         if(Game_mode & GM_STANDALONE_SERVER){
3253                 std_destroy_gen_dialog();
3254         }
3255 }
3256
3257 // get a new id# for a player
3258 short multi_get_new_id()
3259 {
3260         if(Multi_id_num > 20000){
3261                 Multi_id_num = 0;
3262         } 
3263
3264         return Multi_id_num++;
3265 }
3266
3267
3268 // ------------------------------------
3269
3270 //XSTR:OFF
3271 DCF(multi,"changes multiplayer settings")
3272 {
3273         if(Dc_command){
3274                 dc_get_arg(ARG_STRING);
3275                 
3276                 if(strcmp(Dc_arg, "kick")==0){                          // kick a player
3277                         multi_dcf_kick();
3278 #ifndef NDEBUG
3279                 } else if(strcmp(Dc_arg, "stats")==0) {
3280                         // multi_toggle_stats();
3281                 } else if(strcmp(Dc_arg, "show_stats")==0) {
3282                         // multi_show_basic_stats(0);
3283                 } else if(strcmp(Dc_arg, "dump_stats")==0) {
3284                         // multi_show_basic_stats(1);
3285 #endif
3286                 } else if(strcmp(Dc_arg, "voice")==0){                          // settings for multiplayer voice
3287                         multi_voice_dcf();
3288                 } else if(strcmp(Dc_arg, "respawn_chump")==0){  // set a really large # of respawns
3289                         if((Net_player != NULL) && (Net_player->flags & NETINFO_FLAG_GAME_HOST)){                       
3290                                 Netgame.respawn = 9999;
3291                                 Netgame.options.respawn = 9999;                         
3292
3293                                 // if i'm the server, send a netgame update
3294                                 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3295                                         send_netgame_update_packet();
3296                                 } 
3297                         }
3298                 } else if(strcmp(Dc_arg, "ss_leaders")==0){             // only host or team captains can modify ships
3299                         if((Net_player != NULL) && (Net_player->flags & NETINFO_FLAG_GAME_HOST)){                       
3300                                 Netgame.options.flags |= MSO_FLAG_SS_LEADERS;
3301                                 multi_options_update_netgame();
3302                         }
3303                 } else if(strcmp(Dc_arg, "make_players")==0){
3304 #ifndef NDEBUG
3305                         multi_make_fake_players(MAX_PLAYERS);
3306 #endif
3307                 } else if(strcmp(Dc_arg, "givecd")==0){
3308                         extern int Multi_has_cd;
3309                         Multi_has_cd = 1;
3310                 } else if(strcmp(Dc_arg, "oo")==0){                                             
3311                         int new_flags = -1;
3312
3313                         dc_get_arg(ARG_INT);
3314                         if(Dc_arg_type & ARG_INT){
3315                                 new_flags = Dc_arg_int;
3316                         }
3317
3318                         dc_printf("Interesting flags\nPos : %d\nVelocity : %d\nDesired vel : %d\nOrient : %d\nRotvel : %d\nDesired rotvel %d\n",
3319                                                  1<<0, 1<<7, 1<<8, 1<<1, 1<<9, 1<<10);                                          
3320                 } else if(strcmp(Dc_arg, "oo_sort")==0){                        
3321                         extern int OO_sort;
3322
3323                         OO_sort = !OO_sort;
3324                         if(OO_sort){
3325                                 dc_printf("Network object sorting ENABLED\n");
3326                         } else {
3327                                 dc_printf("Network object sorting DISABLED\n");
3328                         }
3329                 }
3330         }
3331 }
3332
3333 //XSTR:ON
3334
3335 // PXO crc checking stuff
3336 #ifndef NDEBUG
3337
3338 void multi_spew_pxo_checksums(int max_files, char *outfile)
3339 {
3340         char **file_names;
3341         char full_name[MAX_FILENAME_LEN+1];
3342         char wild_card[256];
3343         int count, idx;
3344         uint checksum;
3345         FILE *out;
3346
3347         // allocate filename space      
3348         file_names = (char**)malloc(sizeof(char*) * max_files);
3349         if(file_names != NULL){
3350                 memset(wild_card, 0, 256);
3351                 strcpy(wild_card, NOX("*"));
3352                 strcat(wild_card, FS_MISSION_FILE_EXT);
3353                 count = cf_get_file_list(max_files, file_names, CF_TYPE_MISSIONS, wild_card);   
3354         
3355                 // open the outfile
3356                 out = fopen(outfile, "wt");
3357                 if(out == NULL){
3358                         return;
3359                 }
3360                 
3361                 // do all the checksums
3362                 for(idx=0; idx<count; idx++){
3363                         memset(full_name, 0, MAX_FILENAME_LEN+1);                       
3364                         strcpy(full_name, cf_add_ext(file_names[idx], FS_MISSION_FILE_EXT));
3365
3366                         if(cf_chksum_long(full_name, &checksum)){
3367                                 fprintf(out, "%s        :       %d\n", full_name, (int)checksum);
3368                         }
3369                 }
3370
3371                 fclose(out);
3372                 free(file_names);
3373         }
3374 }
3375
3376 DCF(pxospew,"spew PXO 32 bit checksums for all visible mission files")
3377 {
3378         int max_files;
3379
3380         dc_get_arg(ARG_INT);
3381         if(Dc_arg_type & ARG_INT){
3382                 max_files = Dc_arg_int; 
3383
3384                 dc_get_arg(ARG_STRING);
3385                 if(Dc_arg_type & ARG_STRING){
3386                         multi_spew_pxo_checksums(max_files, Dc_arg);
3387                 }
3388         }
3389 }
3390
3391 #endif
3392
3393 // make a bunch of fake players - don't rely on this to be very safe - its mostly used for interface testing
3394 #ifndef NDEBUG
3395 void multi_make_fake_players(int count)
3396 {
3397         int idx;
3398         
3399         for(idx=0;idx<count;idx++){
3400                 if(!MULTI_CONNECTED(Net_players[idx])){
3401                         Net_players[idx].player = &Players[idx];
3402                         sprintf(Net_players[idx].player->callsign,"Player %d",idx);
3403                         Net_players[idx].flags |= NETINFO_FLAG_CONNECTED;
3404                 }
3405         }
3406 }
3407 #endif
3408
3409 // ---------------------------------------------------------------------------------------------------------------------
3410 // PACK UNPACK STUFF
3411 //
3412
3413 #ifndef PLAT_UNIX
3414 #pragma optimize("", off)
3415 #endif
3416
3417 typedef struct bitbuffer {
3418         ubyte           mask;
3419    int          rack;
3420         ubyte           *data;
3421         ubyte           *org_data;
3422 } bitbuffer;
3423
3424 void bitbuffer_init( bitbuffer *bitbuf, ubyte *data )
3425 {
3426         bitbuf->rack = 0;       
3427         bitbuf->mask = 0x80;
3428         bitbuf->data = data;
3429         bitbuf->org_data = data;
3430 }
3431
3432 int bitbuffer_write_flush( bitbuffer *bitbuf )
3433 {
3434         // Flush to next byte
3435         if ( bitbuf->mask != 0x80 )     {
3436         *bitbuf->data++ = (ubyte)bitbuf->rack;
3437         }
3438         return bitbuf->data-bitbuf->org_data;
3439 }
3440
3441 int bitbuffer_read_flush( bitbuffer *bitbuf )
3442 {
3443         return bitbuf->data-bitbuf->org_data;
3444 }
3445
3446 void bitbuffer_put( bitbuffer *bitbuf, uint data, int bit_count ) 
3447 {
3448         uint mask;
3449
3450         mask = 1L << ( bit_count - 1 );
3451         while ( mask != 0) {
3452                 if ( mask & data )      {
3453                         bitbuf->rack |= bitbuf->mask;
3454                 }
3455                 bitbuf->mask >>= 1;
3456                 if ( bitbuf->mask == 0 ) {
3457                         *bitbuf->data++=(ubyte)bitbuf->rack;
3458                         bitbuf->rack = 0;
3459                         bitbuf->mask = 0x80;
3460                 }
3461                 mask >>= 1;
3462         }
3463 }
3464
3465 uint bitbuffer_get_unsigned( bitbuffer *bitbuf, int bit_count ) 
3466 {
3467         uint mask;
3468         uint return_value;
3469
3470         mask = 1L << ( bit_count - 1 );
3471         return_value = 0;
3472
3473         while ( mask != 0)      {
3474                 if ( bitbuf->mask == 0x80 ) {
3475                         bitbuf->rack = *bitbuf->data++;
3476                 }
3477                 if ( bitbuf->rack & bitbuf->mask )      {
3478                         return_value |= mask;
3479                 }
3480                 mask >>= 1;
3481                 bitbuf->mask >>= 1;
3482                 if ( bitbuf->mask == 0 )        {
3483                         bitbuf->mask = 0x80;
3484                 }
3485         }
3486
3487         return return_value;
3488 }
3489
3490 int bitbuffer_get_signed( bitbuffer *bitbuf, int bit_count ) 
3491 {
3492         uint mask;
3493         uint return_value;
3494
3495         mask = 1L << ( bit_count - 1 );
3496         return_value = 0;
3497
3498         while ( mask != 0)      {
3499                 if ( bitbuf->mask == 0x80 ) {
3500                         bitbuf->rack = *bitbuf->data++;
3501                 }
3502                 if ( bitbuf->rack & bitbuf->mask )      {
3503                         return_value |= mask;
3504                 }
3505                 mask >>= 1;
3506                 bitbuf->mask >>= 1;
3507                 if ( bitbuf->mask == 0 )        {
3508                         bitbuf->mask = 0x80;
3509                 }
3510         }
3511
3512         // sign extend return value
3513         return_value <<= (32-bit_count);
3514         
3515         return ((int)return_value)>>(32-bit_count);
3516 }
3517         
3518
3519
3520 // Packs/unpacks an object position.
3521 // Returns number of bytes read or written.
3522 // #define OO_POS_RET_SIZE                                                      9
3523 int multi_pack_unpack_position( int write, ubyte *data, vector *pos)
3524 {
3525         bitbuffer buf;
3526
3527         bitbuffer_init(&buf,data);
3528
3529         int a, b, c;
3530
3531         if ( write )    {
3532                 // Output pos
3533
3534                 a = fl2i(pos->xyz.x*105.0f+0.5f); 
3535                 b = fl2i(pos->xyz.y*105.0f+0.5f);
3536                 c = fl2i(pos->xyz.z*105.0f+0.5f);
3537                 CAP(a,-8388608,8388607);
3538                 CAP(b,-8388608,8388607);
3539                 CAP(c,-8388608,8388607);
3540                 
3541                 bitbuffer_put( &buf, (uint)a, 24 );
3542                 bitbuffer_put( &buf, (uint)b, 24 );
3543                 bitbuffer_put( &buf, (uint)c, 24 );
3544
3545
3546                 return bitbuffer_write_flush(&buf);
3547
3548         } else {
3549
3550                 // unpack pos
3551                 a = bitbuffer_get_signed(&buf,24);
3552                 b = bitbuffer_get_signed(&buf,24);
3553                 c = bitbuffer_get_signed(&buf,24);
3554
3555                 pos->xyz.x = i2fl(a)/105.0f;
3556                 pos->xyz.y = i2fl(b)/105.0f;
3557                 pos->xyz.z = i2fl(c)/105.0f;
3558
3559                 return bitbuffer_read_flush(&buf);
3560         }
3561 }
3562
3563 int degenerate_count = 0;
3564 int non_degenerate_count = 0;
3565
3566 /*
3567 hack = ((ushort)orient->v.fvec.xyz.x * 32767);
3568                         memcpy(&hack, &orient->v.fvec.xyz.x, 4);
3569                         bitbuffer_put( &buf, hack, 32  );
3570                         memcpy(&hack, &orient->v.fvec.xyz.y, 4);
3571                         bitbuffer_put( &buf, hack, 32  );
3572                         memcpy(&hack, &orient->v.fvec.xyz.z, 4);
3573                         bitbuffer_put( &buf, hack, 32  );
3574
3575                         memcpy(&hack, &orient->v.uvec.xyz.x, 4);
3576                         bitbuffer_put( &buf, hack, 32  );
3577                         memcpy(&hack, &orient->v.uvec.xyz.y, 4);
3578                         bitbuffer_put( &buf, hack, 32  );
3579                         memcpy(&hack, &orient->v.uvec.xyz.z, 4);
3580                         bitbuffer_put( &buf, hack, 32  );
3581
3582                         memcpy(&hack, &orient->v.rvec.xyz.x, 4);
3583                         bitbuffer_put( &buf, hack, 32  );
3584                         memcpy(&hack, &orient->v.rvec.xyz.y, 4);
3585                         bitbuffer_put( &buf, hack, 32  );
3586                         memcpy(&hack, &orient->v.rvec.xyz.z, 4);
3587                         bitbuffer_put( &buf, hack, 32  );*/
3588
3589 /*
3590 hack = bitbuffer_get_unsigned(&buf, 32);
3591                         memcpy(&orient->v.fvec.xyz.x, &hack, 4);
3592                         hack = bitbuffer_get_unsigned(&buf, 32);
3593                         memcpy(&orient->v.fvec.xyz.y, &hack, 4);
3594                         hack = bitbuffer_get_unsigned(&buf, 32);
3595                         memcpy(&orient->v.fvec.xyz.z, &hack, 4);
3596
3597                         hack = bitbuffer_get_unsigned(&buf, 32);
3598                         memcpy(&orient->v.uvec.xyz.x, &hack, 4);
3599                         hack = bitbuffer_get_unsigned(&buf, 32);
3600                         memcpy(&orient->v.uvec.xyz.y, &hack, 4);
3601                         hack = bitbuffer_get_unsigned(&buf, 32);
3602                         memcpy(&orient->v.uvec.xyz.z, &hack, 4);
3603
3604                         hack = bitbuffer_get_unsigned(&buf, 32);
3605                         memcpy(&orient->v.rvec.xyz.x, &hack, 4);
3606                         hack = bitbuffer_get_unsigned(&buf, 32);
3607                         memcpy(&orient->v.rvec.xyz.y, &hack, 4);
3608                         hack = bitbuffer_get_unsigned(&buf, 32);
3609                         memcpy(&orient->v.rvec.xyz.z, &hack, 4);*/
3610
3611 // Packs/unpacks an orientation matrix.
3612 // Returns number of bytes read or written.
3613 // #define OO_ORIENT_RET_SIZE                                           6
3614 int multi_pack_unpack_orient( int write, ubyte *data, matrix *orient)
3615 {
3616         bitbuffer buf;
3617
3618         bitbuffer_init(&buf, data + 1);
3619
3620         vector rot_axis;        
3621         float theta;
3622         int a, b, c, d;
3623         angles ang;     
3624         ubyte flag = 0x00;      
3625
3626         #define D_SCALE 32768.0f
3627         #define D_MAX_RANGE 32767
3628         #define D_MIN_RANGE -32768
3629
3630         #define N_SCALE 2048.0f
3631         #define N_MAX_RANGE 2047
3632         #define N_MIN_RANGE -2048
3633
3634         if ( write )    {                       
3635                 // degenerate case - send the whole orient matrix
3636                 vm_extract_angles_matrix(&ang, orient); 
3637                 if((ang.h > 3.130) && (ang.h < 3.150)){
3638                         degenerate_count++;
3639
3640                         flag = 0xff;
3641                         
3642                         // stuff it     
3643                         a = fl2i(orient->v.fvec.xyz.x * D_SCALE);
3644                         CAP(a, D_MIN_RANGE, D_MAX_RANGE);                       
3645                         bitbuffer_put( &buf, a, 16  );
3646                         a = fl2i(orient->v.fvec.xyz.y * D_SCALE);
3647                         CAP(a, D_MIN_RANGE, D_MAX_RANGE);                       
3648                         bitbuffer_put( &buf, a, 16  );
3649                         a = fl2i(orient->v.fvec.xyz.z * D_SCALE);
3650                         CAP(a, D_MIN_RANGE, D_MAX_RANGE);                       
3651                         bitbuffer_put( &buf, a, 16  );
3652
3653                         a = fl2i(orient->v.uvec.xyz.x * D_SCALE);
3654                         CAP(a, D_MIN_RANGE, D_MAX_RANGE);                       
3655                         bitbuffer_put( &buf, a, 16  );
3656                         a = fl2i(orient->v.uvec.xyz.y * D_SCALE);
3657                         CAP(a, D_MIN_RANGE, D_MAX_RANGE);                       
3658                         bitbuffer_put( &buf, a, 16  );
3659                         a = fl2i(orient->v.uvec.xyz.z * D_SCALE);
3660                         CAP(a, D_MIN_RANGE, D_MAX_RANGE);                       
3661                         bitbuffer_put( &buf, a, 16  );
3662
3663                         a = fl2i(orient->v.rvec.xyz.x * D_SCALE);
3664                         CAP(a, D_MIN_RANGE, D_MAX_RANGE);                       
3665                         bitbuffer_put( &buf, a, 16  );
3666                         a = fl2i(orient->v.rvec.xyz.y * D_SCALE);
3667                         CAP(a, D_MIN_RANGE, D_MAX_RANGE);                       
3668                         bitbuffer_put( &buf, a, 16  );
3669                         a = fl2i(orient->v.rvec.xyz.z * D_SCALE);
3670                         CAP(a, D_MIN_RANGE, D_MAX_RANGE);                       
3671                         bitbuffer_put( &buf, a, 16  );
3672                 } else {
3673                         non_degenerate_count++;
3674                                 
3675                         vm_matrix_to_rot_axis_and_angle(orient, &theta, &rot_axis);             
3676                         // Have theta, which is an angle between 0 and PI.
3677                         // Convert it to be between -1.0f and 1.0f
3678                         theta = theta*2.0f/PI-1.0f;                     
3679
3680                         // -1 to 1
3681                         a = fl2i(rot_axis.xyz.x*N_SCALE); 
3682                         b = fl2i(rot_axis.xyz.y*N_SCALE);
3683                         c = fl2i(rot_axis.xyz.z*N_SCALE);
3684                         d = fl2i(theta*N_SCALE);
3685
3686                         CAP(a, N_MIN_RANGE, N_MAX_RANGE);
3687                         CAP(b, N_MIN_RANGE, N_MAX_RANGE);
3688                         CAP(c, N_MIN_RANGE, N_MAX_RANGE);
3689                         CAP(d, N_MIN_RANGE, N_MAX_RANGE);
3690                                         
3691                         bitbuffer_put( &buf, (uint)a, 12 );
3692                         bitbuffer_put( &buf, (uint)b, 12 );
3693                         bitbuffer_put( &buf, (uint)c, 12 );
3694                         bitbuffer_put( &buf, (uint)d, 12 );
3695                 }
3696
3697                 // flag for degenerate case
3698                 data[0] = flag;
3699
3700                 return bitbuffer_write_flush(&buf) + 1;
3701         } else {
3702                 flag = data[0];
3703
3704                 // degenerate
3705                 if(flag){
3706                         a = bitbuffer_get_signed(&buf, 16);
3707                         orient->v.fvec.xyz.x = i2fl(a) / D_SCALE;                       
3708                         a = bitbuffer_get_signed(&buf, 16);
3709                         orient->v.fvec.xyz.y = i2fl(a) / D_SCALE;                       
3710                         a = bitbuffer_get_signed(&buf, 16);
3711                         orient->v.fvec.xyz.z = i2fl(a) / D_SCALE;                       
3712
3713                         a = bitbuffer_get_signed(&buf, 16);
3714                         orient->v.uvec.xyz.x = i2fl(a) / D_SCALE;                       
3715                         a = bitbuffer_get_signed(&buf, 16);
3716                         orient->v.uvec.xyz.y = i2fl(a) / D_SCALE;                       
3717                         a = bitbuffer_get_signed(&buf, 16);
3718                         orient->v.uvec.xyz.z = i2fl(a) / D_SCALE;                       
3719
3720                         a = bitbuffer_get_signed(&buf, 16);
3721                         orient->v.rvec.xyz.x = i2fl(a) / D_SCALE;                       
3722                         a = bitbuffer_get_signed(&buf, 16);
3723                         orient->v.rvec.xyz.y = i2fl(a) / D_SCALE;                       
3724                         a = bitbuffer_get_signed(&buf, 16);
3725                         orient->v.rvec.xyz.z = i2fl(a) / D_SCALE;                       
3726                 } else {
3727                         a = bitbuffer_get_signed(&buf,12);
3728                         b = bitbuffer_get_signed(&buf,12);
3729                         c = bitbuffer_get_signed(&buf,12);
3730                         d = bitbuffer_get_signed(&buf,12);
3731
3732                         // special case         
3733                         rot_axis.xyz.x = i2fl(a)/N_SCALE;
3734                         rot_axis.xyz.y = i2fl(b)/N_SCALE;
3735                         rot_axis.xyz.z = i2fl(c)/N_SCALE;
3736                         theta = i2fl(d)/N_SCALE;
3737                                 
3738                         // Convert theta back to range 0-PI
3739                         theta = (theta+1.0f)*PI/2.0f;
3740                                 
3741                         vm_quaternion_rotate(orient, theta, &rot_axis);         
3742
3743                         vm_orthogonalize_matrix(orient);                
3744                 }
3745
3746                 return bitbuffer_read_flush(&buf) + 1;
3747         }
3748 }
3749
3750
3751 // Packs/unpacks an orientation matrix.
3752 // Returns number of bytes read or written.
3753 // #define OO_ORIENT_RET_SIZE                                           6
3754 /*
3755 int multi_pack_unpack_orient( int write, ubyte *data, matrix *orient)
3756 {
3757         bitbuffer buf;
3758
3759         bitbuffer_init(&buf,data);
3760
3761         vector rot_axis;        
3762         float theta;
3763         int a, b, c, d;
3764
3765         if ( write )    {
3766
3767                 // if our heading is 3.14 radians
3768                 //angles ang;
3769                 //vm_extract_angles_matrix(&a, orient);
3770                 //if((ang.h > 3.1300) && (ang.h < 3.1500)){                     
3771                 //} else {
3772
3773                         util_matrix_to_rot_axis_and_angle(orient, &theta, &rot_axis);           
3774                         // Have theta, which is an angle between 0 and PI.
3775                         // Convert it to be between -1.0f and 1.0f
3776                         theta = theta*2.0f/PI-1.0f;
3777
3778                         #define SCALE 2048.0f
3779
3780                         #define MAX_RANGE 2047
3781                         #define MIN_RANGE -2048
3782
3783                         // -1 to 1
3784                         a = fl2i(rot_axis.xyz.x*SCALE); 
3785                         b = fl2i(rot_axis.xyz.y*SCALE);
3786                         c = fl2i(rot_axis.xyz.z*SCALE);
3787                         d = fl2i(theta*SCALE);
3788
3789                         CAP(a,MIN_RANGE,MAX_RANGE);
3790                         CAP(b,MIN_RANGE,MAX_RANGE);
3791                         CAP(c,MIN_RANGE,MAX_RANGE);
3792                         CAP(d,MIN_RANGE,MAX_RANGE);
3793                 //}
3794                 
3795                 bitbuffer_put( &buf, (uint)a, 12 );
3796                 bitbuffer_put( &buf, (uint)b, 12 );
3797                 bitbuffer_put( &buf, (uint)c, 12 );
3798                 bitbuffer_put( &buf, (uint)d, 12 );
3799
3800                 return bitbuffer_write_flush(&buf);
3801
3802         } else {
3803
3804                 a = bitbuffer_get_signed(&buf,12);
3805                 b = bitbuffer_get_signed(&buf,12);
3806                 c = bitbuffer_get_signed(&buf,12);
3807                 d = bitbuffer_get_signed(&buf,12);
3808
3809                 // special case         
3810                 rot_axis.xyz.x = i2fl(a)/SCALE;
3811                 rot_axis.xyz.y = i2fl(b)/SCALE;
3812                 rot_axis.xyz.z = i2fl(c)/SCALE;
3813                 theta = i2fl(d)/SCALE;
3814                         
3815                 // Convert theta back to range 0-PI
3816                 theta = (theta+1.0f)*PI/2.0f;
3817                         
3818                 vm_quaternion_rotate(orient, theta, &rot_axis);         
3819
3820                 vm_orthogonalize_matrix(orient);                
3821
3822                 return bitbuffer_read_flush(&buf);
3823         }
3824 }
3825 */
3826
3827 // Packs/unpacks velocity
3828 // Returns number of bytes read or written.
3829 // #define OO_VEL_RET_SIZE                                                      4
3830 int multi_pack_unpack_vel( int write, ubyte *data, matrix *orient, vector *pos, physics_info *pi)
3831 {
3832         bitbuffer buf;
3833
3834         bitbuffer_init(&buf,data);
3835
3836         int a, b, c;
3837         float r, u, f;
3838
3839         if ( write )    {
3840                 // output velocity
3841                 r = vm_vec_dot( &orient->v.rvec, &pi->vel );
3842                 u = vm_vec_dot( &orient->v.uvec, &pi->vel );
3843                 f = vm_vec_dot( &orient->v.fvec, &pi->vel );
3844
3845                 a = fl2i(r * 0.5f); 
3846                 b = fl2i(u * 0.5f);
3847                 c = fl2i(f * 0.5f);
3848                 CAP(a,-512,511);
3849                 CAP(b,-512,511);
3850                 CAP(c,-512,511);
3851                 bitbuffer_put( &buf, (uint)a, 10 );
3852                 bitbuffer_put( &buf, (uint)b, 10 );
3853                 bitbuffer_put( &buf, (uint)c, 10 );
3854
3855                 return bitbuffer_write_flush(&buf);
3856         } else {
3857                 // unpack velocity
3858                 float r, u, f;
3859                 a = bitbuffer_get_signed(&buf,10);
3860                 b = bitbuffer_get_signed(&buf,10);
3861                 c = bitbuffer_get_signed(&buf,10);
3862                 r = i2fl(a)/0.5f;
3863                 u = i2fl(b)/0.5f;
3864                 f = i2fl(c)/0.5f;
3865
3866                 // Convert into world coordinates
3867                 vm_vec_zero(&pi->vel);
3868                 vm_vec_scale_add2( &pi->vel, &orient->v.rvec, r );
3869                 vm_vec_scale_add2( &pi->vel, &orient->v.uvec, u );
3870                 vm_vec_scale_add2( &pi->vel, &orient->v.fvec, f );
3871
3872                 return bitbuffer_read_flush(&buf);
3873         }
3874 }
3875
3876 // Packs/unpacks desired_velocity
3877 // Returns number of bytes read or written.
3878 // #define OO_DESIRED_VEL_RET_SIZE                              3
3879 int multi_pack_unpack_desired_vel( int write, ubyte *data, matrix *orient, vector *pos, physics_info *pi, ship_info *sip)
3880 {
3881         bitbuffer buf;
3882
3883         bitbuffer_init(&buf,data);
3884
3885         int a;
3886         vector  max_vel;
3887         float r,u,f;
3888         int fields = 0;
3889
3890         max_vel.xyz.x = max( sip->max_vel.xyz.x, sip->afterburner_max_vel.xyz.x );
3891         max_vel.xyz.y = max( sip->max_vel.xyz.y, sip->afterburner_max_vel.xyz.y );
3892         max_vel.xyz.z = max( sip->max_vel.xyz.z, sip->afterburner_max_vel.xyz.z );      
3893
3894         if ( write )    {
3895                 // Find desired vel in local coordinates
3896                 // Velocity can be from -1024 to 1024
3897
3898                 // bitfields for each value             
3899                 if(max_vel.xyz.x > 0.0f){
3900                         fields |= (1<<0);
3901                 }
3902                 if(max_vel.xyz.y > 0.0f){
3903                         fields |= (1<<1);
3904                 }
3905                 if(max_vel.xyz.z > 0.0f){
3906                         fields |= (1<<2);
3907                 }               
3908                 // fields = sip - Ship_info;
3909                 bitbuffer_put(&buf, (uint)fields, 8);
3910
3911                 r = vm_vec_dot( &orient->v.rvec, &pi->desired_vel );
3912                 u = vm_vec_dot( &orient->v.uvec, &pi->desired_vel );
3913                 f = vm_vec_dot( &orient->v.fvec, &pi->desired_vel );
3914
3915                 if ( max_vel.xyz.x > 0.0f )     {
3916                         r = r / max_vel.xyz.x;
3917                         a = fl2i( r * 128.0f );
3918                         CAP(a,-128, 127 );                      
3919                         bitbuffer_put( &buf, (uint)a, 8 );                      
3920                 } 
3921
3922                 if ( max_vel.xyz.y > 0.0f )     {
3923                         u = u / max_vel.xyz.y;
3924                         a = fl2i( u * 128.0f );
3925                         CAP(a,-128, 127 );
3926                         bitbuffer_put( &buf, (uint)a, 8 );
3927                 } 
3928
3929                 if ( max_vel.xyz.z > 0.0f )     {
3930                         f = f / max_vel.xyz.z;
3931                         a = fl2i( f * 128.0f );
3932                         CAP(a,-128, 127 );
3933                         bitbuffer_put( &buf, (uint)a, 8 );
3934                 }
3935
3936                 return bitbuffer_write_flush(&buf);
3937         } else {
3938
3939                 // Find desired vel in local coordinates
3940                 // Velocity can be from -1024 to 1024
3941
3942                 // get the fields bitbuffer
3943                 fields = bitbuffer_get_signed(&buf, 8);
3944                 
3945                 if ( fields & (1<<0) )  {
3946                         a = bitbuffer_get_signed(&buf,8);
3947                         r = i2fl(a)/128.0f;
3948                 } else {
3949                         r = 0.0f;
3950                 }
3951                 
3952                 if ( fields & (1<<1) )  {
3953                         a = bitbuffer_get_signed(&buf,8);
3954                         u = i2fl(a)/128.0f;
3955                 } else {
3956                         u = 0.0f;
3957                 }
3958                 
3959                 if ( fields & (1<<2) )  {
3960                         a = bitbuffer_get_signed(&buf,8);
3961                         f = i2fl(a)/128.0f;
3962                 } else {
3963                         f = 0.0f;
3964                 }               
3965                 
3966                 // Convert into world coordinates
3967                 vm_vec_zero(&pi->vel);
3968                 vm_vec_scale_add2( &pi->desired_vel, &orient->v.rvec, r*max_vel.xyz.x );
3969                 vm_vec_scale_add2( &pi->desired_vel, &orient->v.uvec, u*max_vel.xyz.y );
3970                 vm_vec_scale_add2( &pi->desired_vel, &orient->v.fvec, f*max_vel.xyz.z );
3971
3972                 return bitbuffer_read_flush(&buf);
3973         }
3974 }
3975
3976 // Packs/unpacks rotational velocity
3977 // Returns number of bytes read or written.
3978 // #define OO_ROTVEL_RET_SIZE                                           4
3979 int multi_pack_unpack_rotvel( int write, ubyte *data, matrix *orient, vector *pos, physics_info *pi)
3980 {
3981         bitbuffer buf;
3982
3983         bitbuffer_init(&buf,data);
3984
3985         int a, b, c;
3986
3987         if ( write )    {
3988                 // output rotational velocity
3989                 a = fl2i(pi->rotvel.xyz.x*32.0f); 
3990                 b = fl2i(pi->rotvel.xyz.y*32.0f);
3991                 c = fl2i(pi->rotvel.xyz.z*32.0f);
3992                 CAP(a,-512,511);
3993                 CAP(b,-512,511);
3994                 CAP(c,-512,511);
3995                 bitbuffer_put( &buf, (uint)a, 10 );
3996                 bitbuffer_put( &buf, (uint)b, 10 );
3997                 bitbuffer_put( &buf, (uint)c, 10 );
3998
3999
4000                 return bitbuffer_write_flush(&buf);
4001
4002         } else {
4003
4004                 // unpack rotational velocity
4005                 a = bitbuffer_get_signed(&buf,10);
4006                 b = bitbuffer_get_signed(&buf,10);
4007                 c = bitbuffer_get_signed(&buf,10);
4008                 pi->rotvel.xyz.x = i2fl(a)/32.0f;
4009                 pi->rotvel.xyz.y = i2fl(b)/32.0f;
4010                 pi->rotvel.xyz.z = i2fl(c)/32.0f;
4011
4012                 return bitbuffer_read_flush(&buf);
4013         }
4014 }
4015
4016 // Packs/unpacks desired rotvel
4017 // Returns number of bytes read or written.
4018 // #define OO_DESIRED_ROTVEL_RET_SIZE                   3
4019 int multi_pack_unpack_desired_rotvel( int write, ubyte *data, matrix *orient, vector *pos, physics_info *pi, ship_info *sip)
4020 {
4021         bitbuffer buf;
4022         int fields = 0;
4023
4024         bitbuffer_init(&buf,data);
4025
4026         int a;
4027         float r,u,f;
4028
4029         if ( write )    {
4030                 // use ship_info values for max_rotvel instead of taking it from physics info
4031
4032                 // bitfields for each value
4033                 if(sip->max_rotvel.xyz.x > 0.0f){
4034                         fields |= (1<<0);
4035                 }
4036                 if(sip->max_rotvel.xyz.y > 0.0f){
4037                         fields |= (1<<1);
4038                 }
4039                 if(sip->max_rotvel.xyz.z > 0.0f){
4040                         fields |= (1<<2);
4041
4042                 }
4043                 bitbuffer_put(&buf, (uint)fields, 8);
4044
4045                 // output desired rotational velocity as a percent of max               
4046                 if ( sip->max_rotvel.xyz.x > 0.0f )     {               
4047                         a = fl2i( pi->desired_rotvel.xyz.x*128.0f / sip->max_rotvel.xyz.x );
4048                         CAP(a,-128, 127 );
4049                         bitbuffer_put( &buf, (uint)a, 8 );
4050                 } 
4051
4052                 if ( sip->max_rotvel.xyz.y > 0.0f )     {               
4053                         a = fl2i( pi->desired_rotvel.xyz.y*128.0f / sip->max_rotvel.xyz.y );
4054                         CAP(a,-128, 127 );
4055                         bitbuffer_put( &buf, (uint)a, 8 );
4056                 } 
4057
4058                 if ( sip->max_rotvel.xyz.z > 0.0f )     {               
4059                         a = fl2i( pi->desired_rotvel.xyz.z*128.0f / sip->max_rotvel.xyz.z );
4060                         CAP(a,-128, 127 );
4061                         bitbuffer_put( &buf, (uint)a, 8 );
4062                 } 
4063
4064                 return bitbuffer_write_flush(&buf);
4065         } else {
4066                 fields = bitbuffer_get_signed(&buf, 8);
4067
4068                 // unpack desired rotational velocity
4069                 if ( fields & (1<<0) )  {               
4070                         a = bitbuffer_get_signed(&buf,8);
4071                         r = i2fl(a)/128.0f;
4072                 } else {
4073                         r = 0.0f;
4074                 }
4075                 if ( fields & (1<<1) )  {               
4076                         a = bitbuffer_get_signed(&buf,8);
4077                         u = i2fl(a)/128.0f;
4078                 } else {
4079                         u = 0.0f;
4080                 }
4081                 if ( fields & (1<<2) )  {               
4082                         a = bitbuffer_get_signed(&buf,8);
4083                         f = i2fl(a)/128.0f;
4084                 } else {
4085                         f = 0.0f;
4086                 }
4087                 pi->desired_rotvel.xyz.x = r*sip->max_rotvel.xyz.x;
4088                 pi->desired_rotvel.xyz.y = u*sip->max_rotvel.xyz.y;
4089                 pi->desired_rotvel.xyz.z = f*sip->max_rotvel.xyz.z;
4090
4091                 return bitbuffer_read_flush(&buf);
4092         }
4093 }
4094
4095 #ifndef PLAT_UNIX
4096 #pragma optimize("", on)
4097 #endif