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