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