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