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