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