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