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