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