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