2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
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
10 * $Logfile: /Freespace2/code/Network/MultiUtil.cpp $
15 * C file that contains misc. functions to support multiplayer
18 * Revision 1.10 2006/04/26 19:48:58 taylor
19 * various big-endian fixes, mainly networking support related
21 * Revision 1.9 2003/05/25 02:30:43 taylor
24 * Revision 1.8 2002/06/17 06:33:10 relnev
25 * ryan's struct patch for gcc 2.95
27 * Revision 1.7 2002/06/09 04:41:24 relnev
28 * added copyright header
30 * Revision 1.6 2002/06/05 08:05:29 relnev
31 * stub/warning removal.
33 * reworked the sound code.
35 * Revision 1.5 2002/06/02 04:26:34 relnev
38 * Revision 1.4 2002/05/27 00:40:47 theoddone33
39 * Fix net_addr vs net_addr_t
41 * Revision 1.3 2002/05/26 20:49:54 theoddone33
44 * Revision 1.2 2002/05/07 03:16:47 theoddone33
45 * The Great Newline Fix
47 * Revision 1.1.1.1 2002/05/03 03:28:10 root
51 * 47 9/15/99 1:45a Dave
52 * Don't init joystick on standalone. Fixed campaign mode on standalone.
53 * Fixed no-score-report problem in TvT
55 * 46 9/09/99 8:53p Dave
56 * Fixed multiplayer degenerate orientation case problem. Make sure warp
57 * effect never goes lower than LOD 1.
59 * 45 8/24/99 1:50a Dave
60 * Fixed client-side afterburner stuttering. Added checkbox for no version
61 * checking on PXO join. Made button info passing more friendly between
64 * 44 8/23/99 10:30a Dave
65 * Make sure a kill limit of 0 really means "no kill limit"
67 * 43 8/22/99 5:53p Dave
68 * Scoring fixes. Added self destruct key. Put callsigns in the logfile
69 * instead of ship designations for multiplayer players.
71 * 42 8/22/99 1:19p Dave
72 * Fixed up http proxy code. Cleaned up scoring code. Reverse the order in
73 * which d3d cards are detected.
75 * 41 8/19/99 10:59a Dave
76 * Packet loss detection.
78 * 40 8/06/99 2:44a Dave
79 * Make sure dead players who leave respawn AI.
81 * 39 8/06/99 12:29a Dave
84 * 38 8/03/99 11:02p Dave
85 * Maybe fixed sync problems in multiplayer.
87 * 37 7/30/99 7:01p Dave
88 * Dogfight escort gauge. Fixed up laser rendering in Glide.
90 * 36 7/26/99 5:50p Dave
91 * Revised ingame join. Better? We'll see....
93 * 35 7/15/99 9:20a Andsager
94 * FS2_DEMO initial checkin
96 * 34 7/08/99 10:53a Dave
97 * New multiplayer interpolation scheme. Not 100% done yet, but still
98 * better than the old way.
100 * 33 6/25/99 5:04p Jasenw
101 * Added some debug line stuff.
103 * 32 6/16/99 4:06p Dave
104 * New pilot info popup. Added new draw-bitmap-as-poly function.
106 * 31 5/18/99 11:50a Andsager
107 * Remove unused object type OBJ_GHOST_SAVE
109 * 30 5/04/99 5:20p Dave
110 * Fixed up multiplayer join screen and host options screen. Should both
113 * 29 5/03/99 8:33p Dave
114 * New version of multi host options screen.
116 * 28 4/27/99 12:16a Dave
117 * Fixed beam weapon muzzle glow problem. Fixed premature timeout on the
118 * pxo server list screen. Fixed secondary firing for hosts on a
119 * standalone. Fixed wacky multiplayer weapon "shuddering" problem.
121 * 27 4/25/99 7:43p Dave
122 * Misc small bug fixes. Made sun draw properly.
124 * 26 4/25/99 3:02p Dave
125 * Build defines for the E3 build.
127 * 25 4/09/99 2:21p Dave
128 * Multiplayer beta stuff. CD checking.
130 * 24 4/08/99 2:10a Dave
131 * Numerous bug fixes for the beta. Added builtin mission info for the
134 * 23 3/10/99 6:50p Dave
135 * Changed the way we buffer packets for all clients. Optimized turret
136 * fired packets. Did some weapon firing optimizations.
138 * 22 3/09/99 6:24p Dave
139 * More work on object update revamping. Identified several sources of
140 * unnecessary bandwidth.
142 * 21 3/08/99 7:03p Dave
143 * First run of new object update system. Looks very promising.
145 * 20 3/01/99 10:00a Dave
146 * Fxied several dogfight related stats bugs.
148 * 19 2/24/99 2:25p Dave
149 * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn
150 * bug for dogfight more.
152 * 18 2/23/99 2:29p Dave
153 * First run of oldschool dogfight mode.
155 * 17 2/12/99 6:16p Dave
156 * Pre-mission Squad War code is 95% done.
158 * 16 2/11/99 3:08p Dave
159 * PXO refresh button. Very preliminary squad war support.
161 * 15 2/08/99 5:07p Dave
162 * FS2 chat server support. FS2 specific validated missions.
164 * 14 1/30/99 1:29a Dave
165 * Fixed nebula thumbnail problem. Full support for 1024x768 choose pilot
166 * screen. Fixed beam weapon death messages.
168 * 13 1/14/99 6:06p Dave
169 * 100% full squad logo support for single player and multiplayer.
171 * 12 1/12/99 4:07a Dave
172 * Put in barracks code support for selecting squad logos. Properly
173 * distribute squad logos in a multiplayer game.
175 * 11 12/14/98 4:01p Dave
176 * Got multi_data stuff working well with new xfer stuff.
178 * 10 12/14/98 12:13p Dave
179 * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
182 * 9 11/19/98 4:19p Dave
183 * Put IPX sockets back in psnet. Consolidated all multiplayer config
186 * 8 11/19/98 8:04a Dave
187 * Full support for D3-style reliable sockets. Revamped packet lag/loss
188 * system, made it receiver side and at the lowest possible level.
190 * 7 11/17/98 11:12a Dave
191 * Removed player identification by address. Now assign explicit id #'s.
193 * 6 11/05/98 5:55p Dave
194 * Big pass at reducing #includes
196 * 5 10/13/98 9:29a Dave
197 * Started neatening up freespace.h. Many variables renamed and
198 * reorganized. Added AlphaColors.[h,cpp]
200 * 4 10/09/98 2:57p Dave
201 * Starting splitting up OS stuff.
203 * 3 10/07/98 6:27p Dave
204 * Globalized mission and campaign file extensions. Removed Silent Threat
205 * special code. Moved \cache \players and \multidata into the \data
208 * 2 10/07/98 10:53a Dave
211 * 1 10/07/98 10:50a Dave
213 * 283 9/28/98 1:54p Dave
214 * Make sure French and German don't xfer builtin files they don't have
217 * 282 9/28/98 1:41p Dave
218 * Properly detect "builtin" missions.
220 * 281 9/21/98 11:46p Dave
221 * Flag mission disk players in the right spot.
223 * 280 9/15/98 7:24p Dave
224 * Minor UI changes. Localized bunch of new text.
230 #include <winsock2.h>
232 #include <sys/types.h>
233 #include <sys/socket.h>
234 #include <netinet/in.h>
235 #include <arpa/inet.h>
240 #include "multiutil.h"
241 #include "multimsgs.h"
243 #include "linklist.h"
244 #include "gamesequence.h"
245 #include "hudmessage.h"
246 #include "freespace.h"
254 #include "missionparse.h"
255 #include "missionshipchoice.h"
256 #include "missionscreencommon.h"
257 #include "missionweaponchoice.h"
258 #include "multi_xfer.h"
259 #include "stand_server.h"
262 #include "managepilot.h"
263 #include "multiteamselect.h"
265 #include "missiondebrief.h"
266 #include "observer.h"
267 #include "missionmessage.h"
268 #include "multilag.h"
270 #include "popupdead.h"
271 #include "hudconfig.h"
273 #include "optionsmenu.h"
274 #include "missionhotkey.h"
275 #include "missiongoals.h"
276 #include "afterburner.h"
278 #include "multi_kick.h"
279 #include "multi_data.h"
280 #include "multi_voice.h"
281 #include "multi_ping.h"
282 #include "multi_team.h"
283 #include "multi_respawn.h"
284 #include "multi_ingame.h"
285 #include "multi_observer.h"
286 #include "multi_pinfo.h"
287 #include "multi_endgame.h"
288 #include "multi_pmsg.h"
289 #include "multi_pause.h"
290 #include "multi_obj.h"
291 #include "multi_log.h"
292 #include "multi_rate.h"
293 #include "osregistry.h"
294 #include "hudescort.h"
295 #include "multi_fstracker.h"
297 extern int MSG_WINDOW_X_START; // used to position multiplayer text messages
298 extern int MSG_WINDOW_Y_START;
299 extern int MSG_WINDOW_HEIGHT;
301 // if a client doesn't receive an update for an object after this many seconds, query server
302 // as to the objects status.
303 #define MULTI_CLIENT_OBJ_TIMEOUT 10
304 #define MAX_SHIPS_PER_QUERY 10
307 // this function assignes the given object with the given signature. If the signature is 0, then we choose
308 // the next signature number from the correct pool. I thought that it might be desireable
309 // to not always have to take the next signature on the list. what_kind is used to assign either a
310 // permanent or non-permanent signature to an object. permanent signatures are used for ships, non_permanent
311 // signatures are used for everything else.
312 ushort multi_assign_network_signature( int what_kind )
316 // do limit checking on the permanent and non_permanent signatures. Ships are considered "permanent"
317 // as are debris and asteroids since they don't die very often. It would be vary rare for this
318 // value (the permanent signature) to wrap. For now, this condition is an error condition
319 if ( what_kind == MULTI_SIG_SHIP ) {
320 if ( Next_ship_signature == 0 ){
321 Next_ship_signature = SHIP_SIG_MIN;
324 sig = Next_ship_signature++;
326 if ( Next_ship_signature == SHIP_SIG_MAX ) {
327 Int3(); // get Allender -- signature stuff wrapped.
328 Next_ship_signature = SHIP_SIG_MIN;
331 // signature stuff for asteroids.
332 } else if ( what_kind == MULTI_SIG_ASTEROID ) {
333 if ( Next_asteroid_signature == 0 ){
334 Next_asteroid_signature = ASTEROID_SIG_MIN;
337 sig = Next_asteroid_signature++;
338 if ( Next_asteroid_signature == ASTEROID_SIG_MAX ) {
339 Int3(); // get Allender -- signature stuff wrapped.
340 Next_asteroid_signature = ASTEROID_SIG_MIN;
343 // signatures for debris
344 } else if ( what_kind == MULTI_SIG_DEBRIS ) {
345 if ( Next_debris_signature == 0 ){
346 Next_debris_signature = DEBRIS_SIG_MIN;
349 sig = Next_debris_signature++;
350 if ( Next_debris_signature == DEBRIS_SIG_MAX ) {
351 Int3(); // get Allender -- signature stuff wrapped.
352 Next_debris_signature = DEBRIS_SIG_MIN;
355 // signature stuff for weapons and other expendable things.
356 } else if ( what_kind == MULTI_SIG_NON_PERMANENT ) {
357 if ( Next_non_perm_signature == 0 ){
358 Next_non_perm_signature = NPERM_SIG_MIN;
361 sig = Next_non_perm_signature++;
362 if ( Next_non_perm_signature == NPERM_SIG_MAX ){
363 Next_non_perm_signature = NPERM_SIG_MIN;
366 Int3(); // get allender - Illegal signature type requested
373 // this function returns the next network signature that will be used for a newly created object
374 // what_kind parameter tells us what kind of signature to get -- permanent or non-permanent
375 ushort multi_get_next_network_signature( int what_kind )
377 if ( what_kind == MULTI_SIG_SHIP ) {
378 if ( Next_ship_signature == 0 )
379 Next_ship_signature = SHIP_SIG_MIN;
380 return Next_ship_signature;
382 } else if ( what_kind == MULTI_SIG_DEBRIS ) {
383 if ( Next_debris_signature == 0 )
384 Next_debris_signature = DEBRIS_SIG_MIN;
385 return Next_debris_signature;
387 } else if ( what_kind == MULTI_SIG_ASTEROID ) {
388 if ( Next_asteroid_signature == 0 )
389 Next_asteroid_signature = ASTEROID_SIG_MIN;
390 return Next_asteroid_signature;
392 } else if ( what_kind == MULTI_SIG_NON_PERMANENT ) {
393 if ( Next_non_perm_signature == 0 )
394 Next_non_perm_signature = NPERM_SIG_MIN;
395 return Next_non_perm_signature;
398 Int3(); // get allender
403 // this routine sets the network signature to the given value. Should be called from client only
404 // and is used mainly for firing weapons. what_kind tells us permanent or non-permanent signature
405 void multi_set_network_signature( ushort signature, int what_kind )
407 SDL_assert( signature != 0 );
409 if ( what_kind == MULTI_SIG_SHIP ) {
410 SDL_assert( (signature >= SHIP_SIG_MIN) && (signature <= SHIP_SIG_MAX) );
411 Next_ship_signature = signature;
412 } else if ( what_kind == MULTI_SIG_DEBRIS ) {
413 SDL_assert( (signature >= DEBRIS_SIG_MIN) && (signature <= DEBRIS_SIG_MAX) );
414 Next_debris_signature = signature;
415 } else if ( what_kind == MULTI_SIG_ASTEROID ) {
416 SDL_assert( (signature >= ASTEROID_SIG_MIN) && (signature <= ASTEROID_SIG_MAX) );
417 Next_asteroid_signature = signature;
418 } else if ( what_kind == MULTI_SIG_NON_PERMANENT ) {
419 SDL_assert( (signature >= NPERM_SIG_MIN) && (signature <= NPERM_SIG_MAX) );
420 Next_non_perm_signature = signature;
422 Int3(); // get Allender
425 // multi_get_network_object() takes a net_signature and tries to locate the object in the object list
426 // with that network signature. Returns NULL if the object cannot be found
427 object *multi_get_network_object( ushort net_signature )
431 if ( net_signature == 0 )
434 if(GET_FIRST(&obj_used_list) == NULL)
437 for ( objp = GET_FIRST(&obj_used_list); objp != END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) )
438 if ( objp->net_signature == net_signature )
441 // if not found on used list, check create list
442 if ( objp == END_OF_LIST(&obj_used_list) ) {
443 for ( objp = GET_FIRST(&obj_create_list); objp != END_OF_LIST(&obj_create_list); objp = GET_NEXT(objp) )
444 if ( objp->net_signature == net_signature )
447 if ( objp == END_OF_LIST(&obj_create_list) )
455 // -------------------------------------------------------------------------------------------------
456 // multi_random_death_word() will return a word from the below list at random.
458 // Note: Keep words grouped into sections of 10
460 #define NUM_DEATH_WORDS 40
462 const char *multi_random_death_word()
466 index = rand() % NUM_DEATH_WORDS;
469 return XSTR("zapped",853);
471 return XSTR("caulked",854);
473 return XSTR("killed",855);
475 return XSTR("waxed",856);
477 return XSTR("popped",857);
479 return XSTR("murdered",858);
481 return XSTR("bludgeoned",859);
483 return XSTR("destroyed",860);
485 return XSTR("iced",861);
487 return XSTR("obliterated",862);
489 return XSTR("toasted",863);
491 return XSTR("roasted",864);
493 return XSTR("turned into anti-matter",865);
495 return XSTR("killed like a pig",866);
497 return XSTR("taught a lesson",867);
499 return XSTR("slaughtered with impunity",868);
501 return XSTR("spanked like a naughty boy",869);
503 return XSTR("skunked",870);
505 return XSTR("beat senseless",871);
507 return XSTR("shot up",872);
509 return XSTR("spaced",873);
511 return XSTR("hosed",874);
513 return XSTR("capped",875);
515 return XSTR("beat down",876);
517 return XSTR("hit wit da shizzo",877);
519 return XSTR("sk00led",878);
521 return XSTR("whooped up",879);
523 return XSTR("brought to the all-you-can-take whoop ass buffet",880);
525 return XSTR("served up a whoop ass sandwich...hold the mercy",881);
527 return XSTR("gibbed by Kayser Sozay's rocket",882);
529 return XSTR("shot down",883);
531 return XSTR("given early retirement",884);
533 return XSTR("instructed",885);
535 return XSTR("eviscerated",886);
537 return XSTR("pummelled",887);
539 return XSTR("eradicated",888);
541 return XSTR("cleansed",889);
543 return XSTR("perforated",890);
545 return XSTR("canned",891);
547 return XSTR("decompressed",892);
553 // -------------------------------------------------------------------------------------------------
554 // multi_random_chat_start() will return a word from the below list at random.
558 #define NUM_CHAT_START_WORDS 8
559 #define MAX_CHAT_PHRASE_LEN 25 // be careful not to exceed (or update if exceeded)
561 const char *multi_random_chat_start()
565 index = rand() % NUM_CHAT_START_WORDS;
568 return XSTR("says",893);
570 return XSTR("bleats",894);
572 return XSTR("opines",895);
574 return XSTR("postulates",896);
576 return XSTR("declares",897);
578 return XSTR("vomits out",898);
580 return XSTR("whines",899);
582 return XSTR("barks",900);
588 // -------------------------------------------------------------------------------------------------
589 // multi_ship_class_lookup() will return the Ship_info[] index for the ship specified as a
594 int multi_ship_class_lookup(const char* ship_name)
596 int i, player_ship_class;
598 // find the ship_info index for the ship_name
600 player_ship_class = -1;
601 for (i = 0; i < Num_ship_types; i++) {
602 if ( !SDL_strcasecmp(Ship_info[i].name, ship_name) ) {
603 player_ship_class = i;
608 if (i == Num_ship_types){
612 return player_ship_class;
615 // -------------------------------------------------------------------------------------------------
616 // find_player() is called when a packet arrives, and we need to know which net player to update.
617 // The matching is done based on the address and port. Port checking is done in case multiple
618 // instances of FreeSpace are running on the same box.
622 int find_player( net_addr_t* addr )
626 for (i = 0; i < MAX_PLAYERS; i++ ) {
627 if ( !MULTI_CONNECTED(Net_players[i])){
630 if ( psnet_same( addr, &(Net_players[i].p_info.addr)) ){
638 // so that we can lookup on the admin port transparently
639 int find_player_no_port(net_addr_t *addr)
643 for (i = 0; i < MAX_PLAYERS; i++ ) {
644 if ( !MULTI_CONNECTED(Net_players[i])){
648 if ( memcmp(&addr->addr, &Net_players[i].p_info.addr.addr, IP_ADDRESS_LENGTH) == 0 ) {
656 int find_player_id(short player_id)
659 for (i = 0; i < MAX_PLAYERS; i++ ) {
660 if ( !MULTI_CONNECTED(Net_players[i])){
663 if(player_id == Net_players[i].player_id){
668 // couldn't find the player
672 // note this is only valid to do on a server!
673 int find_player_socket(PSNET_SOCKET_RELIABLE sock)
676 for (i = 0; i < MAX_PLAYERS; i++ ) {
677 if ( !MULTI_CONNECTED(Net_players[i])){
680 if(sock == Net_players[i].reliable_socket){
685 // couldn't find the player
689 // multi_find_player_by_object returns a player num (reference by Net_players[x]) when given a object *.
690 // used to find netplayers in game when only the object is known
691 int multi_find_player_by_object( object *objp )
695 objnum = OBJ_INDEX(objp);
696 for (i = 0; i < MAX_PLAYERS; i++ ) {
697 if ( !MULTI_CONNECTED(Net_players[i])){
700 if ( objnum == Net_players[i].player->objnum ){
705 // return -1 if the object is not found -- this is a bad situation, but we will handle it in higher
710 // returns a player num based upon player object signature
711 int multi_find_player_by_signature( int signature )
715 for(idx=0;idx<MAX_PLAYERS;idx++){
716 // compare against each player's object signature
717 if(MULTI_CONNECTED(Net_players[idx]) && (Objects[Net_players[idx].player->objnum].signature == signature)){
723 // didn't find the player
727 // returns a player num based upon object net signature
728 int multi_find_player_by_net_signature(ushort net_signature)
732 for(idx=0;idx<MAX_PLAYERS;idx++){
733 // compare against each player's object signature
734 if(MULTI_CONNECTED(Net_players[idx]) && (Objects[Net_players[idx].player->objnum].net_signature == net_signature)){
740 // didn't find the player
744 int multi_find_player_by_ship_name(const char *ship_name)
749 if(ship_name == NULL){
753 for(idx=0; idx<MAX_PLAYERS; idx++){
754 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) &&
755 (Objects[Net_players[idx].player->objnum].instance >= 0) && (Objects[Net_players[idx].player->objnum].instance < MAX_SHIPS) && !SDL_strcasecmp(ship_name, Ships[Objects[Net_players[idx].player->objnum].instance].ship_name) ){
760 // didn't find the player
764 int multi_get_player_ship(int np_index)
767 if((np_index < 0) || (np_index >= MAX_PLAYERS)){
772 if(MULTI_CONNECTED(Net_players[np_index]) && !MULTI_OBSERVER(Net_players[np_index]) && !MULTI_STANDALONE(Net_players[np_index]) &&
773 (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) &&
774 (Objects[Net_players[np_index].player->objnum].instance >= 0) && (Objects[Net_players[np_index].player->objnum].instance < MAX_SHIPS) ){
776 return Objects[Net_players[np_index].player->objnum].instance;
783 // find_open_netplayer_slot() attempts to find a free slot in the Net_players structure for a new player.
784 // it returns -1 if there is no new slot, and the slot number if one is found
785 // NOTE : this attempts to preserve a player object as long as possible for him to come back to
786 int multi_find_open_netplayer_slot()
792 for (i = 0; i < MAX_PLAYERS; i++)
793 if ( !MULTI_CONNECTED(Net_players[i]) ){
794 if(found_first == -1) {
800 if(i == MAX_PLAYERS){
801 if(found_first == -1)
810 // find_open_player_slot() attempts to find an open player array slot. The 0th element of the array
811 // should always be taken by the console player's pilot. All other slots are up for grab though.
812 int multi_find_open_player_slot()
816 for (i = 0; i < MAX_PLAYERS; i++)
817 if ( !(Players[i].flags & PLAYER_FLAGS_STRUCTURE_IN_USE) )
820 if ( i == MAX_PLAYERS )
826 // stuff_netplayer_info stuffs information into the given Net_player structure. It is called when
827 // a new person is entering the game. The state of the Net_player is set to it's basic starting
829 void stuff_netplayer_info( net_player *nplayer, net_addr_t *addr, int ship_class, player *pplayer )
831 nplayer->p_info.addr = *addr;
832 nplayer->flags |= NETINFO_FLAG_CONNECTED;
833 nplayer->state = NETPLAYER_STATE_JOINING;
834 nplayer->p_info.ship_class = ship_class;
835 nplayer->player = pplayer;
836 nplayer->p_info.options.obj_update_level = OBJ_UPDATE_HIGH;
838 // if setting up my net flags, then set the flag to say I can do networking.
839 if ( nplayer == Net_player ){
840 nplayer->flags |= NETINFO_FLAG_DO_NETWORKING;
844 // multi_assign_player_ship takes a Net_player index and an object * and assigned that player to
846 void multi_assign_player_ship( int net_player, object *objp,int ship_class )
851 SDL_assert ( MULTI_CONNECTED(Net_players[net_player]) );
853 shipp = &Ships[objp->instance];
855 Net_players[net_player].player->objnum = OBJ_INDEX(objp);
856 Net_players[net_player].p_info.ship_class = ship_class;
858 // check to see if we are assigning my player -- if so, then set Player_ship and Player_ai
859 if ( Net_player == &Net_players[net_player] ) {
862 Player_ai = &Ai_info[Player_ship->ai_index];
865 // find the parse object for this ship. Also, set the wingman status stuff so wingman status gauge
867 Net_players[net_player].p_info.p_objp = mission_parse_get_arrival_ship( shipp->ship_name );
868 SDL_assert( Net_players[net_player].p_info.p_objp != NULL ); // get allender -- ship should be on list
869 Net_players[net_player].p_info.p_objp->ship_class = ship_class; // be sure this gets set so respawns work
871 // game server and this client need to initialize this information so object updating
873 if ( MULTIPLAYER_MASTER || (Net_player == &Net_players[net_player]) ) {
874 Net_players[net_player].s_info.eye_pos = objp->pos;
875 Net_players[net_player].s_info.eye_orient = objp->orient;
879 for(idx=0; idx<MAX_PLAYERS; idx++){
880 shipp->np_updates[idx].orient_chksum = 0;
881 shipp->np_updates[idx].pos_chksum = 0;
882 shipp->np_updates[idx].seq = 0;
883 shipp->np_updates[idx].status_update_stamp = -1;
884 shipp->np_updates[idx].subsys_update_stamp = -1;
885 shipp->np_updates[idx].update_stamp = -1;
889 // -------------------------------------------------------------------------------------------------
890 // create_player() is called when a net player needs to be instantiated. The ship that is created
891 // depends on the parameter ship_class. Note that if ship_class is invalid, the ship default_player_ship
892 // is used. Returns 1 on success, 0 otherwise
894 int multi_create_player( int net_player_num, player *pl, char* name, net_addr_t* addr, int ship_class, short id)
896 int player_ship_class = ship_class;
897 int i,current_player_count;
899 SDL_assert ( net_player_num < MAX_PLAYERS ); // probably shoudln't be able to even get into this routine if no room
901 // blast _any_ old data
902 memset(&Net_players[net_player_num],0,sizeof(net_player));
904 // get the current # of players
905 current_player_count = multi_num_players();
907 // DOH!!! The lack of this caused many bugs.
908 Net_players[net_player_num].flags = (NETINFO_FLAG_DO_NETWORKING);
910 if ( ship_class == -1 ) {
911 nprintf(("Network","Network ==> ship class is -1, creating a default ship for multiplayer\n"));
913 // find the ship that matches the string stored in default_player_ship
915 for (i = 0; i < Num_ship_types; i++) {
916 if ( !SDL_strcasecmp(Ship_info[i].name, default_player_ship) ) {
917 player_ship_class = i;
922 if (i == Num_ship_types)
926 if ( player_ship_class >= Num_ship_types ) {
927 player_ship_class = multi_ship_class_lookup(default_player_ship);
928 nprintf(("Network","Network ==> Ship class was %d Creating a default ship for multiplayer\n", player_ship_class));
931 // blast the old player data
932 memset(pl,0,sizeof(player));
934 // set up the net_player structure
935 stuff_netplayer_info( &Net_players[net_player_num], addr, player_ship_class, pl );
936 Net_players[net_player_num].s_info.num_last_buttons = 0;
937 // Net_players[net_player_num].respawn_count = 0;
938 Net_players[net_player_num].last_heard_time = timer_get_fixed_seconds();
939 Net_players[net_player_num].reliable_socket = INVALID_SOCKET;
940 Net_players[net_player_num].s_info.kick_timestamp = -1;
941 Net_players[net_player_num].s_info.voice_token_timestamp = -1;
942 Net_players[net_player_num].s_info.tracker_security_last = -1;
943 Net_players[net_player_num].s_info.target_objnum = -1;
944 Net_players[net_player_num].s_info.accum_buttons = 0;
946 // zero out this players ping times data
947 multi_ping_reset(&Net_players[net_player_num].s_info.ping);
949 // zero out his object update and control info sequencing data
950 Net_players[net_player_num].client_cinfo_seq = 0;
951 Net_players[net_player_num].client_server_seq = 0;
953 // timestamp his last_full_update_time
954 Net_players[net_player_num].s_info.last_full_update_time = timestamp(0);
956 // nil his file xfer handle
957 Net_players[net_player_num].s_info.xfer_handle = -1;
959 // nil his data rate timestamp stuff
960 Net_players[net_player_num].s_info.rate_stamp = -1;
961 Net_players[net_player_num].s_info.rate_bytes = 0;
963 // nil packet buffer stuff
964 Net_players[net_player_num].s_info.unreliable_buffer_size = 0;
965 Net_players[net_player_num].s_info.reliable_buffer_size = 0;
967 // various ack handles
968 SDL_strlcpy(pl->callsign, name, SDL_arraysize(pl->callsign));
969 pilot_set_short_callsign(pl, SHORT_CALLSIGN_PIXEL_W); // calculate the short callsign
970 pl->flags |= PLAYER_FLAGS_STRUCTURE_IN_USE;
973 // if we're the standalone server and this is the first guy to join, mark him as the host
974 // and give him all host priveleges
975 if((Game_mode & GM_STANDALONE_SERVER) && (current_player_count == 0)){
976 Net_players[net_player_num].flags |= NETINFO_FLAG_GAME_HOST;
979 Net_players[net_player_num].player_id = id;
981 Net_player->sv_bytes_sent = 0;
982 Net_player->sv_last_pl = -1;
983 Net_player->cl_bytes_recvd = 0;
984 Net_player->cl_last_pl = -1;
986 // add to the escort list
987 if(MULTI_IN_MISSION){
988 hud_escort_add_player(id);
994 // next function makes a player object an ai object (also decrementing the num_players in the
995 // netgame). It appropiately sets the object flags and other interesting AI information which
996 // ought to get set in order to make this new ai object behave correctly.
997 void multi_make_player_ai( object *pobj )
1000 SDL_assert ( pobj != NULL );
1002 if ( pobj->type != OBJ_SHIP )
1005 pobj->flags &= ~(OF_PLAYER_SHIP);
1006 obj_set_flags( pobj, pobj->flags | OF_COULD_BE_PLAYER );
1008 // target_objnum must be -1 or else new AI ship will fire on whatever this player
1010 set_target_objnum( &(Ai_info[Ships[pobj->instance].ai_index]), -1 );
1014 // delete_player takes an index into the Net_players array to delete. Deletion of a player might happen
1015 // because of the player leaving the game on his own, or no net activity from the player after X seconds
1016 void delete_player(int player_num,int kicked_reason)
1018 char notify_string[256] = "";
1021 if(!MULTI_CONNECTED(Net_players[player_num])){
1026 ml_printf(NOX("Deleting player %s"), Net_players[player_num].player->callsign);
1028 psnet_rel_close_socket( &(Net_players[player_num].reliable_socket) ); // close out the reliable socket
1030 // if this guy was ingame joining, the remove my netgame flag so others may join
1031 if ( Net_players[player_num].flags & NETINFO_FLAG_INGAME_JOIN ) {
1032 Netgame.flags &= ~NG_FLAG_INGAME_JOINING;
1033 Netgame.flags &= ~NG_FLAG_INGAME_JOINING_CRITICAL;
1036 Net_players[player_num].flags &= ~NETINFO_FLAG_CONNECTED; // person not connected anymore
1037 Net_players[player_num].player->flags &= ~(PLAYER_FLAGS_STRUCTURE_IN_USE); // free up his player structure
1039 Net_players[player_num].s_info.reliable_connect_time = -1;
1041 // add to the escort list
1042 hud_escort_remove_player(Net_players[player_num].player_id);
1044 // is this guy the server
1045 if(Net_players[player_num].flags & NETINFO_FLAG_AM_MASTER){
1046 // if the server leaves in the debriefing state, we should still wait until the player selects accept before we quit
1047 if ((gameseq_get_state() != GS_STATE_DEBRIEF) && (gameseq_get_state() != GS_STATE_MULTI_DOGFIGHT_DEBRIEF)){
1048 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_SERVER_LEFT);
1051 // if he was just the host
1052 else if(Net_players[player_num].flags & NETINFO_FLAG_GAME_HOST){
1053 Net_players[player_num].flags &= ~(NETINFO_FLAG_GAME_HOST);
1056 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
1057 // are we a standalone server and in a mission?
1058 if((Game_mode & GM_STANDALONE_SERVER) && MULTI_IN_MISSION){
1059 // choose a new host
1060 int found_new_host = 0;
1061 for(idx=0; idx<MAX_PLAYERS; idx++){
1062 if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx])){
1063 // make this guy the host
1064 Net_players[idx].flags |= NETINFO_FLAG_GAME_HOST;
1067 send_host_captain_change_packet(Net_players[idx].player_id, 0);
1074 if(!found_new_host){
1075 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_HOST_LEFT);
1078 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_HOST_LEFT);
1083 // if we're the server of the game, notify everyone else that this guy has left
1084 // if he's marked as being kicked, then other players know about it already, so don't send again
1085 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
1086 if(Net_players[player_num].flags & NETINFO_FLAG_KICKED){
1088 memset(str, 0, 512);
1089 multi_kick_get_text(&Net_players[player_num], Net_players[player_num].s_info.kick_reason, str, SDL_arraysize(str));
1090 multi_display_chat_msg(str, player_num, 0);
1092 send_leave_game_packet(Net_players[player_num].player_id, kicked_reason);
1096 // if this guy is an observer, we have to make sure we delete his observer object (only if we're the server however)
1097 if( (Net_player->flags & NETINFO_FLAG_AM_MASTER) && (Net_players[player_num].flags & NETINFO_FLAG_OBSERVER) ) {
1098 if ( Net_players[player_num].player->objnum != -1 ){
1099 obj_delete(Net_players[player_num].player->objnum); // maybe change this to set flag instead
1102 // otherwise mark it so that he can return to it later if possible
1103 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)) {
1104 multi_make_player_ai( &Objects[Net_players[player_num].player->objnum] );
1106 multi_respawn_player_leave(&Net_players[player_num]);
1110 // if we're in the team select, and we're the host, we have to make sure all team select data is correctly updated
1111 // MWA 7/28/98. Don't do this for a standalone server. Since he doesn't go through the team selection
1112 // screens, his data structures are not 100% accurate. Doing so on a standalone resulted in the wrong
1113 // ships getting marked as COULD_BE_PLAYER. On the standalone, these flags will get properly set
1114 // in the multi_make_player_ai code above.
1115 if( (Netgame.game_state == NETGAME_STATE_BRIEFING) && !(Game_mode & GM_STANDALONE_SERVER) ) {
1116 multi_ts_handle_player_drop();
1119 if (gameseq_get_state() == GS_STATE_DEBRIEF){
1120 debrief_handle_player_drop();
1123 // handle any specific dropping conditions
1124 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
1125 multi_team_handle_drop();
1127 multi_data_handle_drop(player_num);
1129 // tell the pinfo popup that a player has left
1130 multi_pinfo_notify_drop(&Net_players[player_num]);
1132 // tell the datarate stuff that the player has dropped
1133 multi_rate_reset(player_num);
1135 // display a message that this guy has left
1136 if(Net_players[player_num].player->callsign[0]){
1137 SDL_snprintf(notify_string,SDL_arraysize(notify_string),XSTR("<%s has left>",901),Net_players[player_num].player->callsign);
1138 multi_display_chat_msg(notify_string,0,0);
1141 // standalone gui type stuff
1142 if (Game_mode & GM_STANDALONE_SERVER) {
1143 std_remove_player(&Net_players[player_num]);
1144 std_connect_set_connect_count();
1147 // blast this memory clean
1148 memset(&Net_players[player_num], 0, sizeof(net_player));
1149 Net_players[player_num].reliable_socket = INVALID_SOCKET;
1151 extern int Multi_client_update_times[MAX_PLAYERS];
1152 Multi_client_update_times[player_num] = -1;
1155 #define INACTIVE_LIMIT_NORMAL (15 * F1_0)
1156 #define INACTIVE_LIMIT_WAIT (20 * F1_0)
1158 // -------------------------------------------------------------------------------------------------
1159 // multi_cull_zombies() will check if there has been any net players or observers that have been inactive for
1160 // INACTIVE_LIMIT milliseconds since last check. If so, they are taken out of the net game.
1164 void multi_cull_zombies()
1171 if(gameseq_get_state() == GS_STATE_MULTI_WAIT)
1172 inactive_limit = INACTIVE_LIMIT_WAIT;
1174 inactive_limit = INACTIVE_LIMIT_NORMAL;
1176 current_time = timer_get_fixed_seconds();
1178 for (i = 0; i < MAX_PLAYERS; i++) {
1179 if ( !MULTI_CONNECTED(&Net_players[i])){
1183 // server will(should) cull out players based on their sockets dying.
1184 if ( Net_players[i].flags & NETINFO_FLAG_MASTER ){
1188 if ( (current_time - Net_players[i].last_heard_time) > inactive_limit) {
1189 HUD_printf(XSTR("Dumping %s after prolonged inactivity",902),Net_players[i].player->callsign);
1190 nprintf(("Network", "Assuming %s is a zombie, removing from game\n", Net_players[i].player->callsign));
1192 multi_kick_player(i,0);
1198 // -------------------------------------------------------------------------------------------------
1199 // netmisc_calc_checksum() calculates the checksum of a block of memory.
1202 ushort netmisc_calc_checksum( void * vptr, int len )
1204 ubyte * ptr = (ubyte *)vptr;
1205 unsigned int sum1,sum2;
1211 if (sum1 >= 255 ) sum1 -= 255;
1216 return (unsigned short)((sum1<<8)+ sum2);
1220 // -------------------------------------------------------------------------------------------------
1221 // fill_net_addr() calculates the checksum of a block of memory.
1225 void fill_net_addr(net_addr_t* addr, ubyte* address, ushort port)
1227 SDL_assert(addr != NULL);
1228 SDL_assert(address != NULL);
1230 addr->type = Multi_options_g.protocol;
1231 memcpy( addr->addr, address, IP_ADDRESS_LENGTH);
1237 // -------------------------------------------------------------------------------------------------
1238 // get_text_address()
1242 char* get_text_address( char * text, const int max_textlen, ubyte * address )
1247 memcpy(&temp_addr.s_addr, address, 4);
1248 SDL_strlcpy(text, inet_ntoa(temp_addr), max_textlen);
1253 // non-16byte version of matrix packing
1254 // return size of packed matrix
1255 void multi_pack_orient_matrix(ubyte *data,matrix *m)
1258 float x1, y1, x2, y2;
1260 if(m->v.rvec.xyz.z < 0) data[16] |= (1<<0); // X
1261 if(m->v.uvec.xyz.z < 0) data[16] |= (1<<1); // Y
1262 if(m->v.fvec.xyz.z < 0) data[16] |= (1<<2); // V
1263 if(m->v.fvec.xyz.x < 0) data[16] |= (1<<3); // Z
1264 if(m->v.fvec.xyz.y < 0) data[16] |= (1<<4); // W
1266 x1 = INTEL_FLOAT(m->v.rvec.xyz.x);
1267 y1 = INTEL_FLOAT(m->v.rvec.xyz.y);
1268 x2 = INTEL_FLOAT(m->v.uvec.xyz.x);
1269 y2 = INTEL_FLOAT(m->v.uvec.xyz.y);
1271 memcpy(&data[0], &x1, 4); // a
1272 memcpy(&data[4], &y1, 4); // b
1273 memcpy(&data[8], &x2, 4); // c
1274 memcpy(&data[12], &y2, 4); // d
1277 // return bytes processed
1278 // non-16 byte version of unpack matrix code
1279 void multi_unpack_orient_matrix(ubyte *data,matrix *m)
1281 float x1, y1, x2, y2;
1283 memcpy(&x1, &data[0], 4);
1284 memcpy(&y1, &data[4], 4);
1285 memcpy(&x2, &data[8], 4);
1286 memcpy(&y2, &data[12],4);
1288 m->v.rvec.xyz.x = INTEL_FLOAT(x1);
1289 m->v.rvec.xyz.y = INTEL_FLOAT(y1);
1290 m->v.uvec.xyz.x = INTEL_FLOAT(x2);
1291 m->v.uvec.xyz.y = INTEL_FLOAT(y2);
1293 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
1294 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
1295 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
1296 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
1297 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
1299 m->v.rvec.xyz.z *= (data[16] & (1<<0)) ? -1.0f : 1.0f;
1300 m->v.uvec.xyz.z *= (data[16] & (1<<1)) ? -1.0f : 1.0f;
1301 m->v.fvec.xyz.z *= (data[16] & (1<<2)) ? -1.0f : 1.0f;
1302 m->v.fvec.xyz.x *= (data[16] & (1<<3)) ? -1.0f : 1.0f;
1303 m->v.fvec.xyz.y *= (data[16] & (1<<4)) ? -1.0f : 1.0f;
1306 void multi_do_client_warp(float frametime)
1310 moveup = GET_FIRST(&Ship_obj_list);
1311 while(moveup!=END_OF_LIST(&Ship_obj_list)){
1312 // do all _necessary_ ship warp in (arrival) processing
1313 if ( Ships[Objects[moveup->objnum].instance].flags & SF_ARRIVING )
1314 shipfx_warpin_frame( &Objects[moveup->objnum], frametime );
1315 moveup = GET_NEXT(moveup);
1319 // ------------------------------------------------------------------------------------
1320 // ship status change stuff
1322 int lookup_ship_status(net_player *p,int unique_id,int remove)
1326 for(idx=0;idx<p->s_info.num_last_buttons;idx++){
1327 if(p->s_info.last_buttons_id[idx] == unique_id){
1329 remove_ship_status_item(p,idx);
1330 p->s_info.num_last_buttons--;
1338 void remove_ship_status_item(net_player *p,int id)
1341 for(idx=id;idx<BUTTON_INFO_SAVE_COUNT-1;idx++){
1342 p->s_info.last_buttons[idx] = p->s_info.last_buttons[idx+1];
1343 p->s_info.last_buttons_id[idx] = p->s_info.last_buttons_id[idx+1];
1344 p->s_info.last_buttons_time[idx] = p->s_info.last_buttons_time[idx+1];
1349 void add_net_button_info(net_player *p,button_info *bi,int unique_id)
1354 // if the list is full, put it in the oldest slot since it's probably a lost packet anyway
1355 if(p->s_info.num_last_buttons < BUTTON_INFO_SAVE_COUNT-1){
1356 p->s_info.last_buttons[p->s_info.num_last_buttons] = *bi;
1357 p->s_info.last_buttons_id[p->s_info.num_last_buttons] = unique_id;
1358 p->s_info.last_buttons_time[p->s_info.num_last_buttons] = timer_get_fixed_seconds();
1359 p->s_info.num_last_buttons++;
1363 for(idx=0;idx<BUTTON_INFO_SAVE_COUNT;idx++){
1364 if((p->s_info.last_buttons_time[idx] < earliest) || (earliest == 0)){
1365 earliest = p->s_info.last_buttons_time[idx];
1370 p->s_info.last_buttons[lookup] = *bi;
1371 p->s_info.last_buttons_id[lookup] = unique_id;
1372 p->s_info.last_buttons_time[lookup] = timer_get_fixed_seconds();
1377 extern int button_function_critical(int n,net_player *p = NULL);
1378 void multi_apply_ship_status(net_player *p,button_info *bi,int locally)
1381 Multi_button_info_ok=1;
1382 for ( i = 0; i < NUM_BUTTON_FIELDS; i++ ) {
1383 if ( bi->status[i] == 0 )
1385 // at least one bit is set in the status integer
1386 for ( j = 0; j < 32; j++ ) {
1388 // check if the bit is set. If button_function returns 1 (implying the action was taken), then unset the bit
1389 if ( bi->status[i] & (1<<j) ) {
1391 if(button_function_critical(32*i + j,NULL)) // will apply to this console
1392 bi->status[i] &= ~(1<<j);
1394 if(button_function_critical(32*i + j,p)) // will only apply to a net-player
1395 bi->status[i] &= ~(1<<j);
1400 Multi_button_info_ok=0;
1403 // send 10x a second MAX
1404 #define MULTI_SHIP_STATUS_TIME 350
1405 int Multi_ship_status_stamp = -1;
1406 button_info Multi_ship_status_bi;
1408 void multi_maybe_send_ship_status()
1411 button_info *bi = &Player->bi;
1413 // strip out noncritical button presses
1414 button_strip_noncritical_keys(bi);
1416 // xor all fields into the accum button info
1417 for(idx=0; idx<NUM_BUTTON_FIELDS; idx++){
1418 Multi_ship_status_bi.status[idx] |= bi->status[idx];
1422 if((Multi_ship_status_stamp < 0) || timestamp_elapsed_safe(Multi_ship_status_stamp, MULTI_SHIP_STATUS_TIME*2)){
1423 int should_send = 0;
1424 for(idx=0; idx<NUM_BUTTON_FIELDS; idx++){
1425 // we have at least something to send
1426 if(Multi_ship_status_bi.status[idx] != 0){
1431 // do we have something to send
1433 // add_net_button_info(Net_player, &Multi_ship_status_bi, Multi_button_info_id);
1434 send_ship_status_packet(Net_player, &Multi_ship_status_bi, Multi_button_info_id++);
1438 memset(&Multi_ship_status_bi, 0, sizeof(button_info));
1441 Multi_ship_status_stamp = timestamp(MULTI_SHIP_STATUS_TIME);
1445 void multi_subsys_update_all()
1449 SDL_assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
1450 for(idx=0;idx<MAX_PLAYERS;idx++){
1451 if((Net_players[idx].flags & NETINFO_FLAG_CONNECTED) && !psnet_same(&My_addr,&Net_players[idx].addr) && !(Net_players[idx].flags & NETINFO_FLAG_OBSERVER))
1452 send_subsys_update_packet(&Net_players[idx]);
1457 int multi_find_player_by_callsign(const char *callsign)
1460 for(idx=0;idx<MAX_PLAYERS;idx++){
1461 if(MULTI_CONNECTED(Net_players[idx]) && (strcmp(callsign,Net_players[idx].player->callsign)==0)){
1469 // if Game_current_mission_filename is a builtin multiplayer mission
1470 int multi_is_builtin_mission()
1475 // get the full filename
1477 SDL_strlcpy(name, Game_current_mission_filename, SDL_arraysize(name));
1478 cf_add_ext(name, FS_MISSION_FILE_EXT);
1480 // if this mission is builtin
1481 if(game_find_builtin_mission(name) != NULL){
1489 // verify that the player has a valid mission file and do 1 of 3 things
1490 void server_verify_filesig(short player_id, ushort sum_sig, int length_sig)
1492 #if defined(MULTIPLAYER_BETA_BUILD) || defined(FS2_DEMO)
1496 player = find_player_id(player_id);
1497 SDL_assert(player >= 0);
1501 pl = &Net_players[player];
1503 // all missions should be builtin, so if we don't have a match, kick the player
1504 if((sum_sig != Multi_current_file_checksum) || (length_sig != Multi_current_file_length)){
1505 multi_kick_player(player, 0, KICK_REASON_CANT_XFER);
1507 pl->flags |= NETINFO_FLAG_MISSION_OK;
1515 player = find_player_id(player_id);
1516 SDL_assert(player >= 0);
1520 pl = &Net_players[player];
1522 // if the current mission is a builtin mission, check stuff out
1523 is_builtin = multi_is_builtin_mission();
1526 // if the player doesn't have it, kick him
1527 if((sum_sig == 0xffff) && (length_sig == -1)){
1528 multi_kick_player(player, 0, KICK_REASON_CANT_XFER);
1530 pl->flags |= NETINFO_FLAG_MISSION_OK;
1537 if( (length_sig != Multi_current_file_length) || (sum_sig != Multi_current_file_checksum)){
1543 // in an ingame join situation
1544 if(pl->flags & NETINFO_FLAG_INGAME_JOIN){
1546 // if the netgame settings allow in-mission file xfers
1547 if(Netgame.options.flags & MSO_FLAG_INGAME_XFER){
1548 pl->s_info.ingame_join_flags |= INGAME_JOIN_FLAG_FILE_XFER;
1549 pl->s_info.xfer_handle = multi_xfer_send_file(pl->reliable_socket, Netgame.mission_name, CF_TYPE_MISSIONS);
1551 // otherwise send him a nak and tell him to get the hell away
1553 send_ingame_nak(ACK_FILE_ACCEPTED,pl);
1556 pl->flags |= NETINFO_FLAG_MISSION_OK;
1559 // in a normal join situation
1561 // if the file does not check out, send it to him
1563 pl->s_info.xfer_handle = multi_xfer_send_file(pl->reliable_socket, Netgame.mission_name, CF_TYPE_MISSIONS);
1565 // otherwise mark him as having a valid mission
1567 pl->flags |= NETINFO_FLAG_MISSION_OK;
1573 // check to see if every client has NETINFO_FLAG_MISSION_OK
1574 int server_all_filesigs_ok()
1579 for(idx=0;idx<MAX_PLAYERS;idx++){
1580 if(MULTI_CONNECTED(Net_players[idx]) && !(Net_players[idx].flags & NETINFO_FLAG_MISSION_OK)){
1589 void multi_untag_player_ships()
1593 moveup = GET_FIRST(&Ship_obj_list);
1594 while(moveup != END_OF_LIST(&Ship_obj_list)){
1595 if(Objects[moveup->objnum].flags & OF_PLAYER_SHIP){
1596 Objects[moveup->objnum].flags &= ~(OF_PLAYER_SHIP);
1597 obj_set_flags( &Objects[moveup->objnum], Objects[moveup->objnum].flags | OF_COULD_BE_PLAYER );
1599 moveup = GET_NEXT(moveup);
1603 // broadcast alltime stats to everyone in the game
1604 void multi_broadcast_stats(int stats_code)
1608 // broadcast everyone's stats to everyone else
1609 for(idx=0;idx<MAX_PLAYERS;idx++){
1610 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
1611 send_player_stats_block_packet(&Net_players[idx], stats_code);
1616 // check to see if all players other than the local player are in a given NETPLAYER_ state
1617 // this is _EXTREMELY_ useful when doing network sequencing with the reliable sockets
1618 int multi_netplayer_state_check(int state,int ignore_standalone)
1621 for(idx=0;idx<MAX_PLAYERS;idx++){
1622 if(MULTI_CONNECTED(Net_players[idx]) && (Net_player->player_id != Net_players[idx].player_id)){
1623 if(ignore_standalone && ((Net_players[idx].flags & NETINFO_FLAG_AM_MASTER) && !(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST)) ){
1627 if(Net_players[idx].state != state){
1635 int multi_netplayer_state_check2(int state, int state2, int ignore_standalone)
1638 for(idx=0;idx<MAX_PLAYERS;idx++){
1639 if(MULTI_CONNECTED(Net_players[idx]) && (Net_player->player_id != Net_players[idx].player_id)){
1640 if(ignore_standalone && ((Net_players[idx].flags & NETINFO_FLAG_AM_MASTER) && !(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST)) ){
1644 if((Net_players[idx].state != state) && (Net_players[idx].state != state2)){
1652 int multi_netplayer_state_check3(int state, int state2, int state3, int ignore_standalone)
1655 for(idx=0;idx<MAX_PLAYERS;idx++){
1656 if(MULTI_CONNECTED(Net_players[idx]) && (Net_player->player_id != Net_players[idx].player_id)){
1657 if(ignore_standalone && ((Net_players[idx].flags & NETINFO_FLAG_AM_MASTER) && !(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST)) ){
1661 if((Net_players[idx].state != state) && (Net_players[idx].state != state2) && (Net_players[idx].state != state3)){
1669 // check to see if all players other than the local player are in a given NETPLAYER_ state
1670 // this is _EXTREMELY_ useful when doing network sequencing with the reliable sockets
1671 int multi_netplayer_flag_check(int flags,int ignore_standalone)
1674 for(idx=0;idx<MAX_PLAYERS;idx++){
1675 if(MULTI_CONNECTED(Net_players[idx]) && (Net_player->player_id != Net_players[idx].player_id)){
1676 if(ignore_standalone && ((Net_players[idx].flags & NETINFO_FLAG_AM_MASTER) && !(Net_players[idx].flags & NETINFO_FLAG_GAME_HOST)) ){
1680 if(!(Net_players[idx].flags & flags)){
1688 // function which gets called from psnet_* code to evaluate an error received on a reliable
1689 // socket. In general, we only want to do drastic measures if the error indicates that the client
1690 // is no longer there.
1691 void multi_eval_socket_error(PSNET_SOCKET sock, int error)
1693 if ( error == WSAENOTSOCK ){
1694 nprintf(("Network","Socket connection terminated and/or nonexistent, bailing..\n"));
1696 // mwa -- don't go back to main menu. You don't want host to do this. Maybe we can ignore it
1697 // because of a leaving player.
1699 //gameseq_post_event(GS_EVENT_MAIN_MENU);
1700 // Int3(); // get allender -- something happened to socket connection!!!
1703 if ( (error != WSAECONNRESET) && (error != WSAECONNABORTED) && (error != WSAESHUTDOWN) ) {
1704 nprintf(("Network", "Error %d received on reliable socket -- ignoring\n", error));
1708 if(error == WSAESHUTDOWN){
1709 nprintf(("Network","Received WSAESHUTDOWN on client socket. Cool.\n"));
1712 // mwa -- always return for now because debugging with the stuff below is a real pain.
1713 // in essence, you can't do it!
1716 if( Net_player->flags & NETINFO_FLAG_AM_MASTER ) {
1719 nprintf(("Network", "pitching player because drop on reliable socket\n"));
1720 // find the netplayer whose socket we have an error on. Dump the player when we find him.
1721 // NOTE : make sure not to ban him
1722 for(idx=0;idx<MAX_PLAYERS;idx++){
1723 if(Net_players[idx].reliable_socket == sock){
1729 nprintf(("Network", "Communications to server lost -- quitting game\n"));
1730 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_CONTACT_LOST);
1734 // send a repair info packet with code == code to a player if his object is the one being acted upon
1735 // dest_objp is the player object (or possible player object). source_objp is the object pointer
1736 // of the repair ship that is the source of the action. code means what is happening -- queueing up
1737 // for repair, getting repaired, aborted, complete, etc.
1738 void multi_maybe_send_repair_info(object *dest_objp, object *source_objp, int code )
1740 // only send information on player objects
1741 if ( !MULTIPLAYER_MASTER )
1744 SDL_assert( dest_objp->type == OBJ_SHIP );
1745 SDL_assert( dest_objp != source_objp );
1747 send_repair_info_packet( dest_objp, source_objp, code );
1750 int multi_is_valid_unknown_packet(ubyte type){
1751 return (type == GAME_QUERY) || (type == JOIN) || (type == PING) || (type == PONG) || (type == GAME_INFO)
1752 || (type == ACCEPT) || (type == GAME_ACTIVE) || (type == INGAME_NAK) || (type == DENY)
1753 || (type == UPDATE_DESCRIPT) || (type == ACCEPT_PLAYER_DATA) ? 1 : 0;
1756 void multi_create_standalone_object()
1758 // now create a bullshit ship for the standalone
1759 matrix m = IDENTITY_MATRIX;
1761 int objnum, pobj_num;
1764 objnum = observer_create(&m,&v);
1765 Player_obj = &Objects[objnum];
1766 obj_set_flags(Player_obj, Player_obj->flags & (~OF_COLLIDES) );
1767 //obj_set_flags(Player_obj, Player_obj->flags | OF_SHOULD_BE_DEAD);
1768 obj_set_flags(Player_obj, Player_obj->flags & (~OF_PLAYER_SHIP));
1769 Net_player->player->objnum = objnum;
1771 // create the default player ship object and use that as my default virtual "ship", and make it "invisible"
1772 pobj_num = parse_create_object(&Player_start_pobject);
1773 SDL_assert(pobj_num != -1);
1774 obj_set_flags(&Objects[pobj_num],OF_PLAYER_SHIP);
1775 Objects[pobj_num].net_signature = STANDALONE_SHIP_SIG;
1776 Player_ship = &Ships[Objects[pobj_num].instance];
1778 // make ship hidden from sensors so that this observer cannot target it. Observers really have two ships
1779 // one observer, and one "Player_ship". Observer needs to ignore the Player_ship.
1780 Player_ship->flags |= SF_HIDDEN_FROM_SENSORS;
1781 SDL_strlcpy(Player_ship->ship_name, XSTR("Standalone Ship",904), SDL_arraysize(Player_ship->ship_name));
1782 Player_ai = &Ai_info[Ships[Objects[pobj_num].instance].ai_index];
1786 int multi_message_should_broadcast(int type)
1788 return (type == MESSAGE_ARRIVE_ENEMY) || (type == MESSAGE_BETA_ARRIVED) ||
1789 (type == MESSAGE_GAMMA_ARRIVED) || (type == MESSAGE_HELP) || (type == MESSAGE_REINFORCEMENTS) ? 1 : 0;
1792 // active game list handling functions
1793 active_game *multi_new_active_game( void )
1795 active_game *new_game;
1797 new_game = (active_game *)malloc(sizeof(active_game));
1798 if ( new_game == NULL ) {
1799 nprintf(("Network", "Cannot allocate space for new active game structure\n"));
1803 if ( Active_game_head != NULL ) {
1804 new_game->next = Active_game_head->next;
1805 new_game->next->prev = new_game;
1806 Active_game_head->next = new_game;
1807 new_game->prev = Active_game_head;
1809 Active_game_head = new_game;
1810 Active_game_head->next = Active_game_head->prev = Active_game_head;
1813 Active_game_count++;
1815 // notify the join game screen of this new item
1816 multi_join_notify_new_game();
1821 active_game *multi_update_active_games(active_game *ag)
1823 active_game *gp = NULL;
1824 active_game *stop = NULL;
1831 // see if we have a game from this address already -- if not, create one. In either case, get a pointer
1832 // to an active_game structure
1833 if ( Active_game_head != NULL ) { // no games on list at all
1836 gp = Active_game_head;
1837 stop = Active_game_head;
1841 if ( psnet_same(&gp->server_addr, &ag->server_addr) /*&& (gp->game.security == game->security)*/ ) {
1846 } while (gp != stop);
1848 // insert in the list
1850 gp = multi_new_active_game();
1851 // gp->ping_time = -1.0f;
1857 // copy in the game information
1858 memcpy(&gp->server_addr,&ag->server_addr,sizeof(net_addr_t));
1859 SDL_strlcpy(gp->name, ag->name, SDL_arraysize(gp->name));
1860 SDL_strlcpy(gp->mission_name, ag->mission_name, SDL_arraysize(gp->mission_name));
1861 SDL_strlcpy(gp->title, ag->title, SDL_arraysize(gp->title));
1862 gp->num_players = ag->num_players;
1863 gp->flags = ag->flags;
1867 gp->ping_start = timer_get_fixed_seconds();
1869 send_ping(&gp->server_addr);
1871 multi_ping_reset(&gp->ping);
1872 multi_ping_send(&gp->server_addr,&gp->ping);
1874 // otherwise update the netgame info we have for this guy
1876 memset(gp->name,0,MAX_GAMENAME_LEN+1);
1877 SDL_strlcpy(gp->name, ag->name, SDL_arraysize(gp->name));
1878 memset(gp->mission_name,0,NAME_LENGTH+1);
1879 SDL_strlcpy(gp->mission_name, ag->mission_name, SDL_arraysize(gp->mission_name));
1880 memset(gp->title,0,NAME_LENGTH+1);
1881 SDL_strlcpy(gp->title, ag->title, SDL_arraysize(gp->title));
1882 gp->num_players = ag->num_players;
1883 gp->flags = ag->flags;
1886 gp = multi_new_active_game();
1887 // gp->ping_time = -1.0f;
1893 // copy in the game information
1894 memcpy(&gp->server_addr,&ag->server_addr,sizeof(net_addr_t));
1895 SDL_strlcpy(gp->name, ag->name, SDL_arraysize(gp->name));
1896 SDL_strlcpy(gp->mission_name, ag->mission_name, SDL_arraysize(gp->mission_name));
1897 SDL_strlcpy(gp->title, ag->title, SDL_arraysize(gp->title));
1898 gp->num_players = ag->num_players;
1899 gp->flags = ag->flags;
1902 // gp->ping_start = timer_get_fixed_seconds();
1903 // gp->ping_end = -1;
1904 // send_ping(&gp->server_addr);
1905 multi_ping_reset(&gp->ping);
1906 multi_ping_send(&gp->server_addr,&gp->ping);
1909 // don't do anything if we don't have a game entry
1914 // update the last time we heard from him
1915 if (Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST) {
1916 gp->heard_from_timer = timestamp(MULTI_JOIN_SERVER_TIMEOUT_LOCAL);
1918 gp->heard_from_timer = timestamp(MULTI_JOIN_SERVER_TIMEOUT);
1924 void multi_free_active_games()
1926 active_game *moveup,*backup;
1928 moveup = Active_game_head;
1933 moveup = moveup->next;
1937 } while(moveup != Active_game_head);
1938 Active_game_head = NULL;
1940 Active_game_count = 0;
1943 server_item *multi_new_server_item( void )
1945 server_item *new_game;
1947 new_game = (server_item *)malloc(sizeof(server_item));
1948 if ( new_game == NULL ) {
1949 nprintf(("Network", "Cannot allocate space for new server_item structure\n"));
1953 if ( Game_server_head != NULL ) {
1954 new_game->next = Game_server_head->next;
1955 new_game->next->prev = new_game;
1956 Game_server_head->next = new_game;
1957 new_game->prev = Game_server_head;
1959 Game_server_head = new_game;
1960 Game_server_head->next = Game_server_head->prev = Game_server_head;
1966 void multi_free_server_list()
1968 server_item *moveup,*backup;
1970 moveup = Game_server_head;
1975 moveup = moveup->next;
1979 } while(moveup != Game_server_head);
1980 Game_server_head = NULL;
1984 int multi_num_players()
1988 // count the players who are actively connected
1990 for(idx=0;idx<MAX_PLAYERS;idx++){
1991 // count all connected players except the standalone server (if any)
1992 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx])){
2000 int multi_num_observers()
2004 // count the players who are actively connected
2006 for(idx=0;idx<MAX_PLAYERS;idx++){
2007 // count all connected players except the standalone server (if any)
2008 if(MULTI_CONNECTED(Net_players[idx]) && MULTI_PERM_OBSERVER(Net_players[idx])){
2017 int multi_num_connections()
2021 // count the players who are actively connected
2023 for(idx=0;idx<MAX_PLAYERS;idx++){
2024 // count all connected players except the standalone server (if any)
2025 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx])){
2033 int multi_can_message(net_player *p)
2038 // if the player is an observer of any kind, he cannot message
2039 if(p->flags & NETINFO_FLAG_OBSERVER){
2043 switch(Netgame.options.squad_set){
2044 // only those of the highest rank can message
2045 case MSO_SQUAD_RANK:
2046 max_rank = multi_get_highest_rank();
2047 if(p->player->stats.rank < max_rank){
2052 // only wing/team leaders can message
2053 case MSO_SQUAD_LEADER:
2054 // if the player has an invalid object #
2055 if(p->player->objnum < 0){
2059 // check to see if he's a wingleader
2060 sp = &Ships[Objects[p->player->objnum].instance];
2061 if(SDL_strcasecmp(sp->ship_name,NOX("alpha 1")) && SDL_strcasecmp(sp->ship_name,NOX("beta 1")) && SDL_strcasecmp(sp->ship_name,NOX("gamma 1")) && SDL_strcasecmp(sp->ship_name,NOX("zeta 1")) ){
2066 // anyone can end message
2070 // only the host can message
2071 case MSO_SQUAD_HOST:
2072 if(!(p->flags & NETINFO_FLAG_GAME_HOST)){
2081 int multi_can_end_mission(net_player *p)
2086 // the host can _always_ unpause a game
2087 if(p->flags & NETINFO_FLAG_GAME_HOST){
2091 switch(Netgame.options.endgame_set){
2092 // only those of the highest rank can end the mission
2094 max_rank = multi_get_highest_rank();
2095 if(p->player->stats.rank < max_rank){
2100 // only wing/team leaders can end the mission
2101 case MSO_END_LEADER:
2102 // if the player has an invalid object #
2103 if(p->player->objnum < 0){
2107 // check to see if he's a wingleader
2108 sp = &Ships[Objects[p->player->objnum].instance];
2109 if(SDL_strcasecmp(sp->ship_name,NOX("alpha 1")) && SDL_strcasecmp(sp->ship_name,NOX("beta 1")) && SDL_strcasecmp(sp->ship_name,NOX("gamma 1")) && SDL_strcasecmp(sp->ship_name,NOX("zeta 1")) ){
2114 // anyone can end the mission
2118 // only the host can end the mission
2120 if(!(p->flags & NETINFO_FLAG_GAME_HOST)){
2129 int multi_eval_join_request(join_request *jr,net_addr_t *addr)
2131 int team0_avail,team1_avail;
2133 // if the server versions are incompatible
2134 if(jr->version < MULTI_FS_SERVER_COMPATIBLE_VERSION){
2135 return JOIN_DENY_JR_BAD_VERSION;
2138 // check to make sure we are otherwise in a state to accept
2139 if(Netgame.game_state != NETGAME_STATE_FORMING){
2140 return JOIN_DENY_JR_STATE;
2143 // the standalone has some oddball situations which we must handle seperately
2144 if(Game_mode & GM_STANDALONE_SERVER){
2145 // if this is the first connection, he will be the host so we must always accept him
2146 if(multi_num_players() == 0){
2147 // check to see if this is a tracker game, and if so make sure this is a valid MT player
2148 // we probably eventually want to make sure he's not passing us a fake tracker id#
2149 if (MULTI_IS_TRACKER_GAME) {
2150 if(jr->tracker_id < 0){
2151 return JOIN_DENY_JR_TRACKER_INVAL;
2155 // if we're password protected
2156 if(std_is_host_passwd() && strcmp(jr->passwd, Multi_options_g.std_passwd)){
2157 return JOIN_DENY_JR_PASSWD;
2160 // don't allow the host to join as an observer
2161 if(jr->flags & JOIN_FLAG_AS_OBSERVER){
2162 return JOIN_DENY_JR_NOOBS;
2169 // first off check to see if we're violating any of our max players/observers/connections boundaries
2170 // if we've already got the full 16 (MAX_PLAYERS) connections - yow
2171 if( (multi_num_connections() >= MULTI_MAX_CONNECTIONS) ||
2172 // if we're full of observers and this guy wants to be an observer
2173 ((multi_num_observers() >= MAX_OBSERVERS) && (jr->flags & JOIN_FLAG_AS_OBSERVER)) ||
2174 // if we're up to MULTI_MAX_PLAYERS-1 and we're on the standalone
2175 ((multi_num_players() >= (MULTI_MAX_PLAYERS - 1)) && (Game_mode & GM_STANDALONE_SERVER)) ||
2176 // if we're up to MULTI_MAX_PLAYERS
2177 (multi_num_players() >= MULTI_MAX_PLAYERS) ||
2178 // if the max players for a standalone was set
2179 ((Multi_options_g.std_max_players != -1) && (multi_num_players() >= Multi_options_g.std_max_players)) ){
2181 // we're full buddy - sorry
2182 return JOIN_DENY_JR_FULL;
2185 // check to see if this is a tracker game, and if so make sure this is a valid MT player
2186 // we probably eventually want to make sure he's not passing us a fake tracker id#
2187 if (MULTI_IS_TRACKER_GAME) {
2188 if(jr->tracker_id < 0){
2189 return JOIN_DENY_JR_TRACKER_INVAL;
2193 // check to see if the player is trying to ingame join in a closed game
2194 if(MULTI_IN_MISSION && (Netgame.mode == NG_MODE_CLOSED)){
2195 return JOIN_DENY_JR_CLOSED;
2198 // check to see if the player has passed a valid password in a password protected game
2199 if((Netgame.mode == NG_MODE_PASSWORD) && strcmp(Netgame.passwd,jr->passwd)){
2200 return JOIN_DENY_JR_PASSWD;
2203 // check to see if the netgame is forming and is temporarily marked as closed
2204 if((Netgame.game_state == NETGAME_STATE_FORMING) && (Netgame.flags & NG_FLAG_TEMP_CLOSED)){
2205 return JOIN_DENY_JR_TEMP_CLOSED;
2208 // check to make sure he meets the rank requirement
2209 if((Netgame.mode == NG_MODE_RANK_ABOVE) && (jr->player_rank < Netgame.rank_base)){
2210 return JOIN_DENY_JR_RANK_LOW;
2213 // check to make sure he meets the rank requirement
2214 if((Netgame.mode == NG_MODE_RANK_BELOW) && (jr->player_rank > Netgame.rank_base)){
2215 return JOIN_DENY_JR_RANK_HIGH;
2218 // can't ingame join a non-dogfight game
2219 if((Netgame.game_state != NETGAME_STATE_FORMING) && !(Netgame.type_flags & NG_TYPE_DOGFIGHT)){
2220 return JOIN_DENY_JR_TYPE;
2223 // if the player was banned by the standalone
2224 if((Game_mode & GM_STANDALONE_SERVER) && std_player_is_banned(jr->callsign)){
2225 return JOIN_DENY_JR_BANNED;
2228 // if the game is in-mission, make sure there are ships available
2229 if(MULTI_IN_MISSION && !(jr->flags & JOIN_FLAG_AS_OBSERVER)){
2232 multi_player_ships_available(&team0_avail,&team1_avail);
2234 // if there are no ships available on either team
2235 if((team0_avail == 0) && (team1_avail == 0)){
2236 return JOIN_DENY_JR_FULL;
2240 // if my ingame joining flag is set, then deny since we only allow one ingame joiner at a time
2241 if ( Netgame.flags & NG_FLAG_INGAME_JOINING ){
2242 return JOIN_DENY_JR_INGAME_JOIN;
2245 // check to make sure the game is not full (of observers, or players, as appropriate)
2246 if((jr->flags & JOIN_FLAG_AS_OBSERVER)){
2247 if((multi_num_observers() + 1) > Netgame.options.max_observers){
2248 return JOIN_DENY_JR_FULL;
2252 // if the netgame is restricted or is team vs. team
2253 if((Netgame.mode == NG_MODE_RESTRICTED) || (Netgame.type_flags & NG_TYPE_TEAM)){
2254 // ingame, we must query the host to see if this guy is accepted
2255 if(MULTI_IN_MISSION){
2256 return JOIN_QUERY_RESTRICTED;
2260 // check to make sure this player hasn't been kick/banned
2261 if(multi_kick_is_banned(addr)){
2262 return JOIN_DENY_JR_BANNED;
2265 // check to make sure this player doesn't already exist
2266 if ( find_player(addr) >= 0 ) {
2267 return JOIN_DENY_JR_DUP;
2273 // called by any machine (client, host, server, standalone, etc), to begin warping out all player objects
2274 void multi_warpout_all_players()
2278 // i'f i'm already marked as warping out, don't do this again
2279 if(Net_player->flags & NETINFO_FLAG_WARPING_OUT){
2283 SDL_assert(Player_obj != NULL);
2285 // stop my afterburners
2286 if((Player_obj->type == OBJ_SHIP) && !(Game_mode & GM_STANDALONE_SERVER)){
2287 afterburners_stop( Player_obj, 1 );
2290 // traverse through each player
2291 for(idx=0;idx<MAX_PLAYERS;idx++) {
2294 // only warpout player _ships_ which are not mine
2295 if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx]) && (Objects[Net_players[idx].player->objnum].type == OBJ_SHIP)){
2297 objp = &Objects[Net_players[idx].player->objnum];
2299 obj_set_flags( objp, objp->flags & (~OF_COLLIDES) );
2300 shipfx_warpout_start( objp );
2304 // now, mark ourselves as needing to warp out
2305 Net_player->flags |= NETINFO_FLAG_WARPING_OUT;
2307 // if we're an observer, or we're respawning, or we can't warp out. so just jump into the debrief state
2308 if((Net_player->flags & NETINFO_FLAG_OBSERVER) || (Net_player->flags & NETINFO_FLAG_RESPAWNING) ||
2309 (Net_player->flags & NETINFO_FLAG_OBSERVER) || ((Player_obj->type == OBJ_SHIP) && (Player_ship->flags & SF_CANNOT_WARP)) ){
2311 if(Netgame.type_flags & NG_TYPE_DOGFIGHT){
2312 gameseq_post_event(GS_EVENT_MULTI_DOGFIGHT_DEBRIEF);
2314 gameseq_post_event(GS_EVENT_DEBRIEF);
2317 // if we're a ship, then begin the warpout process
2319 // turn off collision detection for my ship
2320 obj_set_flags(Player_obj, Player_obj->flags & (~OF_COLLIDES) );
2321 gameseq_post_event(GS_EVENT_PLAYER_WARPOUT_START_FORCED);
2325 // determine the highest rank of any of the players in the game
2326 int multi_get_highest_rank()
2331 // go through all the players
2332 for(idx=0;idx<MAX_PLAYERS;idx++){
2333 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)){
2334 max_rank = Net_players[idx].player->stats.rank;
2338 // return what we found
2342 // called on the machine of the player who hit alt+j
2343 void multi_handle_end_mission_request()
2347 // all clients should send the request to the server. no exceptions
2348 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
2349 send_endgame_packet();
2351 // the server of the game does some further processing
2353 ml_string("Server received endgame request, proceeding...");
2355 // first we should toss all ingame joiners
2356 for(idx=0;idx<MAX_PLAYERS;idx++){
2357 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].flags & NETINFO_FLAG_INGAME_JOIN)){
2358 multi_kick_player(idx,0,KICK_REASON_INGAME_ENDED);
2362 // send the endgame packet to all clients, who will act on it immediately
2363 send_endgame_packet();
2364 Netgame.game_state = NETGAME_STATE_ENDGAME;
2365 send_netgame_update_packet();
2367 if(Game_mode & GM_STANDALONE_SERVER){
2368 // move to the standalone postgame (which is where we'll handle stats packets, etc)
2369 gameseq_post_event(GS_EVENT_STANDALONE_POSTGAME);
2372 // begin the warpout process for all players and myself
2373 multi_warpout_all_players();
2377 // called to handle any special cases where a player is in some submemu when the game is ended
2378 void multi_handle_state_special()
2380 int stack_depth,current_depth;
2382 // first off - kill any active popups
2383 popup_kill_any_active();
2385 // kill any popupdeads
2386 if(popupdead_is_active()){
2390 // kill off the pilot info popup if its active
2391 if(multi_pinfo_popup_active()){
2392 multi_pinfo_popup_kill();
2395 // now do any special processing for being in states other then the gameplay states
2396 stack_depth = gameseq_get_depth();
2398 // if we're not pushed on top of any states, do any special state case handling here
2399 if(stack_depth == 0){
2400 // currently there are no special cases, so return
2403 // if we are pushed on any states, post events to pop them off one by one
2405 current_depth = stack_depth;
2407 switch(gameseq_get_state(stack_depth - current_depth)){
2408 // the hotkey screen
2409 case GS_STATE_HOTKEY_SCREEN :
2410 mission_hotkey_exit();
2411 Game_do_state_should_skip = 1;
2414 case GS_STATE_OPTIONS_MENU:
2415 options_cancel_exit();
2416 Game_do_state_should_skip = 1;
2418 // the hud config (1 deeper in the options menu)
2419 case GS_STATE_HUD_CONFIG:
2420 hud_config_cancel();
2421 Game_do_state_should_skip = 1;
2423 // controls config (1 deeper than the options menu)
2424 case GS_STATE_CONTROL_CONFIG:
2425 control_config_cancel_exit();
2426 Game_do_state_should_skip = 1;
2428 // mission goals screen
2429 case GS_STATE_SHOW_GOALS:
2430 mission_goal_exit();
2431 Game_do_state_should_skip = 1;
2433 // mission log scrollback
2434 case GS_STATE_MISSION_LOG_SCROLLBACK:
2435 hud_scrollback_exit();
2436 Game_do_state_should_skip = 1;
2439 case GS_STATE_MULTI_PAUSED:
2440 gameseq_pop_state();
2441 Game_do_state_should_skip = 1;
2445 // next pushed state
2447 } while(current_depth > 0);
2451 // called by the file xfer subsytem when we start receiving a file
2452 void multi_file_xfer_notify(int handle)
2458 // get the filename of the file we are receiving
2460 filename = multi_xfer_get_filename(handle);
2462 // something is messed up
2463 if(filename == NULL){
2467 // convert the filename to all lowercase
2468 SDL_strlwr(filename);
2470 // if this is a mission file
2471 is_mission = (strstr(filename, FS_MISSION_FILE_EXT) != NULL);
2473 // determine where its going to go
2475 cf_type = Net_player->p_info.options.flags & MLO_FLAG_XFER_MULTIDATA ? CF_TYPE_MULTI_CACHE : CF_TYPE_MISSIONS;
2477 cf_type = CF_TYPE_MULTI_CACHE;
2481 // check to see if the file is read-only
2482 if((strlen(filename) > 0) && !cf_access(filename, cf_type, 00) && (cf_access(filename, cf_type, 02) == -1)){
2483 multi_xfer_xor_flags(handle, MULTI_XFER_FLAG_REJECT);
2485 Net_player->flags &= ~(NETINFO_FLAG_DO_NETWORKING);
2486 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));
2487 multi_quit_game(PROMPT_NONE);
2491 // if the incoming filename is a freespace file, set my netplayer state to be "file xfer"
2493 // we'd better not be xferring a file right now
2494 SDL_assert(Net_player->s_info.xfer_handle == -1);
2496 // force into the multidata directory
2497 multi_xfer_handle_force_dir(handle, cf_type);
2499 // set my xfer handle
2500 Net_player->s_info.xfer_handle = handle;
2502 Net_player->state = NETPLAYER_STATE_MISSION_XFER;
2503 send_netplayer_update_packet();
2505 // otherwise always hand it off to the multi_data system
2507 multi_data_handle_incoming(handle);
2511 // return the lag/disconnected status of the game
2512 #define MULTI_LAG_VAL 400
2513 int multi_query_lag_status()
2515 // -1 == not lagged, 0 == lagged, 1 == disconnected
2517 // if I'm the server of the game, I can't be lagged
2518 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2522 // if we've been disconnected for some reason or another
2523 if(multi_client_server_dead()){
2527 // if our ping time to the server is over a certain time
2528 if(Netgame.server->s_info.ping.ping_avg >= MULTI_LAG_VAL){
2536 // process a valid join request
2537 void multi_process_valid_join_request(join_request *jr, net_addr_t *who_from, int ingame_join_team)
2539 int net_player_num,player_num;
2542 // create netplayer and player objects for this guy
2543 net_player_num = multi_find_open_netplayer_slot();
2544 player_num = multi_find_open_player_slot();
2545 id_num = multi_get_new_id();
2546 SDL_assert((net_player_num != -1) && (player_num != -1));
2548 // if he is requesting to join as an observer
2549 if(jr->flags & JOIN_FLAG_AS_OBSERVER){
2550 // create the (permanently) observer player
2551 if(!multi_obs_create_player(net_player_num, jr->callsign, who_from, &Players[player_num])){
2555 // copy his pilot image filename
2556 if(strlen(jr->image_filename) > 0){
2557 SDL_strlcpy(Net_players[net_player_num].player->image_filename, jr->image_filename, MAX_FILENAME_LEN);
2559 SDL_strlcpy(Net_players[net_player_num].player->image_filename, "", MAX_FILENAME_LEN);
2562 // copy his pilot squad filename
2564 Net_players[net_player_num].player->insignia_texture = -1;
2565 player_set_squad_bitmap(Net_players[net_player_num].player, jr->squad_filename);
2568 // clear his multi_data info
2569 multi_data_handle_join(net_player_num);
2571 // set some extra flags for him as appropriate
2572 if(MULTI_IN_MISSION){
2573 Net_players[net_player_num].flags |= NETINFO_FLAG_INGAME_JOIN;
2576 Net_players[net_player_num].flags |= NETINFO_FLAG_CONNECTED;
2577 Net_players[net_player_num].player_id = id_num;
2578 Net_players[net_player_num].tracker_player_id = jr->tracker_id;
2582 if(strlen(jr->pxo_squad_name) > 0){
2583 SDL_strlcpy(Net_players[net_player_num].p_info.pxo_squad_name, jr->pxo_squad_name, LOGIN_LEN);
2585 SDL_strlcpy(Net_players[net_player_num].p_info.pxo_squad_name, "", LOGIN_LEN);
2589 // if he's using hacked data
2590 if(jr->flags & JOIN_FLAG_HAXOR){
2591 Net_players[net_player_num].flags |= NETINFO_FLAG_HAXOR;
2594 // set his reliable connect time
2595 Net_players[net_player_num].s_info.reliable_connect_time = time(NULL);
2597 // send the accept packet here
2598 send_accept_packet(net_player_num, (Net_players[net_player_num].flags & NETINFO_FLAG_INGAME_JOIN) ? ACCEPT_OBSERVER | ACCEPT_INGAME : ACCEPT_OBSERVER);
2600 // create the player object
2601 if(!multi_create_player( net_player_num, &Players[player_num], jr->callsign, who_from, -1, id_num )){
2605 // copy his pilot image filename
2606 if(strlen(jr->image_filename) > 0){
2607 SDL_strlcpy(Net_players[net_player_num].player->image_filename, jr->image_filename, MAX_FILENAME_LEN);
2609 SDL_strlcpy(Net_players[net_player_num].player->image_filename, "", MAX_FILENAME_LEN);
2612 // copy his pilot squad filename
2614 Net_players[net_player_num].player->insignia_texture = -1;
2615 player_set_squad_bitmap(Net_players[net_player_num].player, jr->squad_filename);
2618 // clear his multi_data info
2619 multi_data_handle_join(net_player_num);
2621 // mark him as being connected
2622 Net_players[net_player_num].flags |= NETINFO_FLAG_CONNECTED;
2624 // set his tracker id correctly
2625 Net_players[net_player_num].tracker_player_id = jr->tracker_id;
2627 // set his player id#
2628 Net_players[net_player_num].player_id = id_num;
2632 if(strlen(jr->pxo_squad_name) > 0){
2633 SDL_strlcpy(Net_players[net_player_num].p_info.pxo_squad_name, jr->pxo_squad_name, LOGIN_LEN);
2635 SDL_strlcpy(Net_players[net_player_num].p_info.pxo_squad_name, "", LOGIN_LEN);
2639 // if he's using hacked data
2640 if(jr->flags & JOIN_FLAG_HAXOR){
2641 Net_players[net_player_num].flags |= NETINFO_FLAG_HAXOR;
2644 // flag him appropriately if he's doing an ingame join
2645 if(MULTI_IN_MISSION){
2647 Net_players[net_player_num].flags |= NETINFO_FLAG_INGAME_JOIN;
2648 Net_players[net_player_num].s_info.ingame_join_flags = 0;
2651 // set his reliable connect time
2652 Net_players[net_player_num].s_info.reliable_connect_time = time(NULL);
2654 // if he's joining as a host (on the standalone)
2655 if(Net_players[net_player_num].flags & NETINFO_FLAG_GAME_HOST){
2656 send_accept_packet(net_player_num, ACCEPT_HOST);
2658 Netgame.host = &Net_players[net_player_num];
2660 // set the game and player states appropriately
2661 Netgame.game_state = NETGAME_STATE_STD_HOST_SETUP;
2663 // if he's joining ingame
2664 else if(Net_players[net_player_num].flags & NETINFO_FLAG_INGAME_JOIN){
2665 // if we're in team vs. team mode
2666 if(Netgame.type_flags & NG_TYPE_TEAM){
2667 SDL_assert(ingame_join_team != -1);
2669 Net_players[net_player_num].p_info.team = ingame_join_team;
2672 send_accept_packet(net_player_num, ACCEPT_INGAME, ingame_join_team);
2674 // set his last full update time for updating him on ingame join ships
2675 Net_players[net_player_num].s_info.last_full_update_time = timestamp(INGAME_SHIP_UPDATE_TIME);
2677 // if he's joining as an otherwise ordinary client
2679 send_accept_packet(net_player_num, ACCEPT_CLIENT);
2683 // set my ingame joining flag if the new guy is joining ingame
2684 if ( Net_players[net_player_num].flags & NETINFO_FLAG_INGAME_JOIN ){
2686 Netgame.flags |= NG_FLAG_INGAME_JOINING;
2689 // copy in his options
2690 memcpy(&Net_players[net_player_num].p_info.options, &jr->player_options, sizeof(multi_local_options));
2692 // if on the standalone, then do any necessary gui updating
2693 if(Game_mode & GM_STANDALONE_SERVER) {
2694 std_add_player(&Net_players[net_player_num]);
2695 std_connect_set_connect_count();
2696 std_connect_set_host_connect_status();
2698 // let the create game screen know someone has joined
2699 if(gameseq_get_state() == GS_STATE_MULTI_HOST_SETUP){
2700 multi_create_handle_join(&Net_players[net_player_num]);
2704 extern int Multi_client_update_times[MAX_PLAYERS];
2705 Multi_client_update_times[net_player_num] = -1;
2708 multi_rate_reset(net_player_num);
2711 // if a player is trying to join a restricted game, evaluate the keypress (accept or not, etc)
2712 int multi_process_restricted_keys(int k)
2714 int key1=-1,key2=-1; //JAS: Get rid of optimized warning
2717 // if the query timestamp is not set, don't do anything
2718 if(Multi_restr_query_timestamp == -1){
2722 // determine what keys to look for based upon the mode we're in
2723 switch(Multi_join_restr_mode){
2724 // normal restricted join, Y or N
2725 case MULTI_JOIN_RESTR_MODE_1:
2730 // team vs team, team 0 only has ships
2731 case MULTI_JOIN_RESTR_MODE_2:
2736 // team vs team, team 1 only has ships
2737 case MULTI_JOIN_RESTR_MODE_3:
2742 // team vs team, both teams have ships
2743 case MULTI_JOIN_RESTR_MODE_4:
2753 // check the keypress
2754 if((k == key1) || (k == key2)){
2755 // unset the timestamp
2756 Multi_restr_query_timestamp = -1;
2758 // MWA -- 5/26/98. Next line commented out. It should be cleared when the ingame joiner
2759 // actually gets into the mission
2760 //Netgame.flags &= ~(NG_FLAG_INGAME_JOINING);
2762 // determine which team to put him on (if any)
2763 switch(Multi_join_restr_mode){
2764 // normal restricted join, Y or N
2765 case MULTI_JOIN_RESTR_MODE_1:
2766 team_val = (k == key1) ? 0 : -1;
2769 // team vs team, team 0 only has ships
2770 case MULTI_JOIN_RESTR_MODE_2:
2771 team_val = (k == key1) ? 0 : -1;
2774 // team vs team, team 1 only has ships
2775 case MULTI_JOIN_RESTR_MODE_3:
2776 team_val = (k == key1) ? 1 : -1;
2779 // team vs team, both teams have ships
2780 case MULTI_JOIN_RESTR_MODE_4:
2781 team_val = (k == key1) ? 0 : 1;
2786 team_val = -1; // JAS: Get rid of optimized warning
2790 // perform the proper response
2791 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2793 multi_process_valid_join_request(&Multi_restr_join_request,&Multi_restr_addr,team_val);
2796 // otherwise tell the standalone to accept him
2799 send_host_restr_packet("null",1,team_val);
2801 send_host_restr_packet("null",2,-1);
2809 // didn't process any keys
2813 // determine the status of available player ships (use team_0 for non team vs. team situations)
2814 void multi_player_ships_available(int *team_0,int *team_1)
2822 moveup = GET_FIRST(&Ship_obj_list);
2823 while(moveup!=END_OF_LIST(&Ship_obj_list)){
2824 // if this ship is flagged as OF_COULD_BE_PLAYER
2825 if(Objects[moveup->objnum].flags & OF_COULD_BE_PLAYER){
2826 // get the team # for this ship
2827 mp_team_num = multi_ts_get_team(Ships[Objects[moveup->objnum].instance].ship_name);
2828 if(mp_team_num == 0){
2830 } else if(mp_team_num == 1){
2835 moveup = GET_NEXT(moveup);
2839 // server should update the player's bank/link status with the data in the passed ship
2840 void multi_server_update_player_weapons(net_player *pl,ship *shipp)
2842 // don't process when the ship is dying.
2843 if ( (shipp->flags & SF_DYING) || NETPLAYER_IS_DEAD(pl) )
2846 // primary bank status
2847 pl->s_info.cur_primary_bank = (char)shipp->weapons.current_primary_bank;
2849 // primary link status
2850 pl->s_info.cur_link_status &= ~(1<<0);
2851 if(shipp->flags & SF_PRIMARY_LINKED){
2852 pl->s_info.cur_link_status |= (1<<0);
2855 // secondary bank status
2856 if ( shipp->weapons.current_secondary_bank < 0 ) {
2857 nprintf(("Network", "bashing %s's current sbank to 0\n", shipp->ship_name));
2858 shipp->weapons.current_secondary_bank = 0;
2860 pl->s_info.cur_secondary_bank = (char)shipp->weapons.current_secondary_bank;
2862 // secondary link status
2863 pl->s_info.cur_link_status &= ~(1<<1);
2864 if(shipp->flags & SF_SECONDARY_DUAL_FIRE){
2865 pl->s_info.cur_link_status |= (1<<1);
2869 pl->s_info.ship_ets = 0x0000;
2871 pl->s_info.ship_ets |= ((ushort)shipp->shield_recharge_index << 8);
2873 pl->s_info.ship_ets |= ((ushort)shipp->weapon_recharge_index << 4);
2875 pl->s_info.ship_ets |= ((ushort)shipp->engine_recharge_index);
2877 SDL_assert( pl->s_info.ship_ets != 0 );
2880 // flush the multidata cache directory
2881 void multi_flush_multidata_cache()
2883 nprintf(("Network","FLUSHING MULTIDATA CACHE\n"));
2885 // call the cfile function to flush the directory
2886 cfile_flush_dir(CF_TYPE_MULTI_CACHE);
2889 // flush all data from a previous mission before starting the next
2890 void multi_flush_mission_stuff()
2894 for(idx=0;idx<MAX_PLAYERS;idx++){
2895 if(MULTI_CONNECTED(Net_players[idx])){
2896 // unset all unneeded status bits
2897 Net_players[idx].flags &= ~(NETINFO_FLAG_MISSION_OK | NETINFO_FLAG_RESPAWNING | NETINFO_FLAG_WARPING_OUT);
2899 // server is always "mission ok"
2900 if(Net_players[idx].flags & NETINFO_FLAG_AM_MASTER){
2901 Net_players[idx].flags |= NETINFO_FLAG_MISSION_OK;
2904 // if this guy is a non-permanent observer, unset the appropriate flags
2905 if(MULTI_TEMP_OBSERVER(Net_players[idx])){
2906 Net_players[idx].flags &= ~(NETINFO_FLAG_OBSERVER | NETINFO_FLAG_OBS_PLAYER);
2910 multi_ping_reset(&Net_players[idx].s_info.ping);
2911 Net_players[idx].s_info.num_last_buttons = 0;
2912 Net_players[idx].s_info.wing_index_backup = 0;
2913 Net_players[idx].s_info.wing_index = 0;
2914 Net_players[idx].p_info.ship_class = -1;
2915 Net_players[idx].p_info.ship_index = -1;
2916 Net_players[idx].s_info.xfer_handle = -1;
2919 Net_players[idx].s_info.wing_index_backup = 0;
2922 Players[idx].objnum = -1;
2926 // reset netgame stuff
2927 Netgame.flags &= ~(NG_FLAG_TEMP_CLOSED);
2931 // standalone servers should clear their goal trees now
2932 if(Game_mode & GM_STANDALONE_SERVER){
2933 std_multi_setup_goal_tree();
2936 // object signatures
2937 // this will eventually get reset to Netgame.security the next time an object gets its signature assigned.
2938 // We do this to resynchronize the host/server and all clients
2939 Next_ship_signature = SHIP_SIG_MIN;
2940 Next_asteroid_signature = ASTEROID_SIG_MIN;
2941 Next_non_perm_signature = NPERM_SIG_MIN;
2943 // everyone will need to either reload the current mission, leave, or load the next mission, so in any case
2944 Multi_mission_loaded = 0;
2947 // should we ignore all controls and keypresses because of some multiplayer
2948 int multi_ignore_controls(int key)
2950 // if the multiplayer text messaging system is active, don't return any keys
2951 if((key > 0) && multi_msg_text_process(key)){
2955 // if the host of the game is being prompted to accept or deny a player in a restricted game
2956 if((key > 0) && multi_process_restricted_keys(key)){
2960 // if we're in text messaging mode, ignore controls
2961 if(multi_msg_text_mode()){
2965 // if the pause system wants to eat keys for a while
2966 if(multi_pause_eat_keys()){
2970 // multiplayer didn't eat the key
2974 // if the kill limit has been reached by any given player
2975 int multi_kill_limit_reached()
2979 // is the kill limit <= 0 ?
2980 // if so, consider it as _no_ kill limit
2981 if(Netgame.options.kill_limit <= 0){
2985 // look through all active, non-observer players
2986 for(idx=0;idx<MAX_PLAYERS;idx++){
2987 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)){
2988 // someone reached the limit
2993 // limit has not been reached yet
2997 // display a chat message (write to the correct spot - hud, standalone gui, chatbox, etc)
2998 void multi_display_chat_msg(const char *msg, int player_index, int add_id)
3000 // if i'm a standalone, always add to the gui
3001 if(Game_mode & GM_STANDALONE_SERVER){
3002 std_add_chat_text(msg,player_index,add_id);
3007 if(Game_mode & GM_IN_MISSION){
3008 // if we're paused, send it to the chatbox
3009 if(Multi_pause_status){
3010 chatbox_add_line(msg, player_index, add_id);
3012 // otherwise print to the HUD
3014 multi_msg_display_mission_text(msg, player_index);
3017 // otherwise add it to the chatbox
3019 chatbox_add_line(msg, player_index, add_id);
3023 // fill in Current_file_checksum and Current_file_length
3024 void multi_get_mission_checksum(const char *filename)
3028 Multi_current_file_checksum = 0xffff;
3029 Multi_current_file_length = -1;
3032 in = cfopen(filename,"rb");
3034 // get the length of the file
3035 Multi_current_file_length = cfilelength(in);
3038 in = cfopen(filename,"rb");
3040 // get the checksum of the file
3041 cf_chksum_short(in,&Multi_current_file_checksum);
3047 // if the file doesn't exist, setup some special values, so the server recognizes this
3049 Multi_current_file_checksum = 0xffff;
3050 Multi_current_file_length = -1;
3053 // don't transfew builtin missions
3054 if(multi_is_builtin_mission()){
3055 multi_quit_game(PROMPT_ALL, MULTI_END_NOTIFY_KICKED_CANT_XFER);
3058 nprintf(("Network","NET FILE CHECKSUM : %d %d\n",Multi_current_file_checksum,Multi_current_file_length));
3061 char multi_unit_to_char(float unit)
3074 ret = (char)(unit * 127.0f);
3078 float multi_char_to_unit(float val)
3082 ret = (float)val / 127.0f;
3095 // if we should render our ping time to the server in a multiplayer game
3096 int multi_show_ingame_ping()
3098 // always show it for now
3102 int multi_get_connection_speed()
3105 const char *connection_speed;
3107 // 'Fast' should be a safe default in 2015
3108 connection_speed = os_config_read_string("Network", "ConnectionSpeed", "Fast");
3110 if ( !SDL_strcasecmp(connection_speed, NOX("Slow")) ) {
3111 cspeed = CONNECTION_SPEED_288;
3112 } else if ( !SDL_strcasecmp(connection_speed, NOX("56K")) ) {
3113 cspeed = CONNECTION_SPEED_56K;
3114 } else if ( !SDL_strcasecmp(connection_speed, NOX("ISDN")) ) {
3115 cspeed = CONNECTION_SPEED_SISDN;
3116 } else if ( !SDL_strcasecmp(connection_speed, NOX("Cable")) ) {
3117 cspeed = CONNECTION_SPEED_CABLE;
3118 } else if ( !SDL_strcasecmp(connection_speed, NOX("Fast")) ) {
3119 cspeed = CONNECTION_SPEED_T1;
3121 cspeed = CONNECTION_SPEED_NONE;
3127 // return a MVALID_STATUS_* define based upon the passed string
3128 int multi_string_to_status(char *valid_string)
3130 if ( !SDL_strcmp(valid_string, "valid") ) {
3131 return MVALID_STATUS_VALID;
3132 } else if ( !SDL_strcmp(valid_string, "invalid") ) {
3133 return MVALID_STATUS_INVALID;
3136 return MVALID_STATUS_UNKNOWN;
3139 // if we're in tracker mode, do a validation update on all known missions
3140 void multi_update_valid_missions()
3142 char next_filename[MAX_FILENAME_LEN+1];
3143 char next_line[512];
3144 char status_string[50];
3148 int idx, file_index;
3151 // if we're a standalone, show a dialog saying "validating missions"
3152 if(Game_mode & GM_STANDALONE_SERVER){
3153 std_create_gen_dialog("Validating missions");
3154 std_gen_set_text("Querying:",1);
3157 // mark all missions on our list as being MVALID_STATUS_UNKNOWN
3158 for(idx=0; idx<Multi_create_mission_count; idx++){
3159 Multi_create_mission_list[idx].valid_status = MVALID_STATUS_UNKNOWN;
3162 // attempt to open the valid mission config file
3163 in = cfopen(MULTI_VALID_MISSION_FILE, "rt", CFILE_NORMAL, CF_TYPE_DATA);
3165 // read in all listed missions
3168 memset(next_line, 0, 512);
3169 cfgets(next_line, 512, in);
3170 drop_trailing_white_space(next_line);
3171 drop_leading_white_space(next_line);
3173 // read in a filename
3174 memset(next_filename, 0, MAX_FILENAME_LEN+1);
3175 memset(temp, 0, 256);
3176 tok = strtok(next_line, " ");
3180 SDL_strlcpy(temp, tok, SDL_arraysize(temp));
3181 drop_trailing_white_space(temp);
3182 drop_leading_white_space(temp);
3183 SDL_strlcpy(next_filename, temp, SDL_arraysize(next_filename));
3185 // read in the status string
3186 memset(status_string, 0, 50);
3187 memset(temp, 0, 256);
3188 tok = strtok(NULL," \n");
3192 SDL_strlcpy(temp, tok, SDL_arraysize(temp));
3193 drop_trailing_white_space(temp);
3194 drop_leading_white_space(temp);
3195 SDL_strlcpy(status_string, temp, SDL_arraysize(status_string));
3197 // try and find the file
3198 file_index = multi_create_lookup_mission(next_filename);
3199 if(file_index >= 0){
3200 Multi_create_mission_list[file_index].valid_status = (char)multi_string_to_status(status_string);
3209 // now poll for all unknown missions
3212 for (idx = 0; idx < Multi_create_mission_count; idx++) {
3213 if (Multi_create_mission_list[idx].valid_status != MVALID_STATUS_UNKNOWN) {
3217 int rval = multi_fs_tracker_validate_mission(Multi_create_mission_list[idx].filename);
3224 Multi_create_mission_list[idx].valid_status = (char)rval;
3227 // if the operation was cancelled, don't write anything new
3229 // if we're a standalone, kill the validate dialog
3230 if(Game_mode & GM_STANDALONE_SERVER){
3231 std_destroy_gen_dialog();
3237 // now rewrite the outfile with the new mission info
3238 in = cfopen(MULTI_VALID_MISSION_FILE, "wt", CFILE_NORMAL, CF_TYPE_DATA);
3240 // if we're a standalone, kill the validate dialog
3241 if(Game_mode & GM_STANDALONE_SERVER){
3242 std_destroy_gen_dialog();
3247 for(idx=0; idx<Multi_create_mission_count; idx++){
3248 switch(Multi_create_mission_list[idx].valid_status){
3249 case MVALID_STATUS_VALID:
3250 cfputs(Multi_create_mission_list[idx].filename, in);
3251 cfputs(NOX(" valid"), in);
3252 cfputs(NOX("\n"), in);
3255 case MVALID_STATUS_INVALID:
3256 cfputs(Multi_create_mission_list[idx].filename, in);
3257 cfputs(NOX(" invalid"), in);
3258 cfputs(NOX("\n"), in);
3263 // close the outfile
3267 // if we're a standalone, kill the validate dialog
3268 if(Game_mode & GM_STANDALONE_SERVER){
3269 std_destroy_gen_dialog();
3273 // get a new id# for a player
3274 short multi_get_new_id()
3276 if(Multi_id_num > 20000){
3280 return Multi_id_num++;
3284 // ------------------------------------
3287 DCF(multi,"changes multiplayer settings")
3290 dc_get_arg(ARG_STRING);
3292 if(strcmp(Dc_arg, "kick")==0){ // kick a player
3295 } else if(strcmp(Dc_arg, "stats")==0) {
3296 // multi_toggle_stats();
3297 } else if(strcmp(Dc_arg, "show_stats")==0) {
3298 // multi_show_basic_stats(0);
3299 } else if(strcmp(Dc_arg, "dump_stats")==0) {
3300 // multi_show_basic_stats(1);
3302 } else if(strcmp(Dc_arg, "voice")==0){ // settings for multiplayer voice
3304 } else if(strcmp(Dc_arg, "respawn_chump")==0){ // set a really large # of respawns
3305 if((Net_player != NULL) && (Net_player->flags & NETINFO_FLAG_GAME_HOST)){
3306 Netgame.respawn = 9999;
3307 Netgame.options.respawn = 9999;
3309 // if i'm the server, send a netgame update
3310 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3311 send_netgame_update_packet();
3314 } else if(strcmp(Dc_arg, "ss_leaders")==0){ // only host or team captains can modify ships
3315 if((Net_player != NULL) && (Net_player->flags & NETINFO_FLAG_GAME_HOST)){
3316 Netgame.options.flags |= MSO_FLAG_SS_LEADERS;
3317 multi_options_update_netgame();
3319 } else if(strcmp(Dc_arg, "make_players")==0){
3321 multi_make_fake_players(MAX_PLAYERS);
3323 } else if(strcmp(Dc_arg, "givecd")==0){
3324 extern int Multi_has_cd;
3327 } else if(strcmp(Dc_arg, "oo")==0){
3330 dc_get_arg(ARG_INT);
3331 if(Dc_arg_type & ARG_INT){
3332 new_flags = Dc_arg_int;
3335 dc_printf("Interesting flags\nPos : %d\nVelocity : %d\nDesired vel : %d\nOrient : %d\nRotvel : %d\nDesired rotvel %d\n",
3336 1<<0, 1<<7, 1<<8, 1<<1, 1<<9, 1<<10);
3338 } else if(strcmp(Dc_arg, "oo_sort")==0){
3343 dc_printf("Network object sorting ENABLED\n");
3345 dc_printf("Network object sorting DISABLED\n");
3353 // PXO crc checking stuff
3356 void multi_spew_pxo_checksums(int max_files, char *outfile)
3359 char full_name[MAX_FILENAME_LEN+1];
3365 // allocate filename space
3366 file_names = (char**)malloc(sizeof(char*) * max_files);
3367 if(file_names != NULL){
3368 SDL_snprintf(wild_card, SDL_arraysize(wild_card), "*%s", FS_MISSION_FILE_EXT);
3369 count = cf_get_file_list(max_files, file_names, CF_TYPE_MISSIONS, wild_card);
3372 out = fopen(outfile, "wt");
3377 // do all the checksums
3378 for(idx=0; idx<count; idx++){
3379 memset(full_name, 0, MAX_FILENAME_LEN+1);
3380 SDL_strlcpy(full_name, cf_add_ext(file_names[idx], FS_MISSION_FILE_EXT), SDL_arraysize(full_name));
3382 if(cf_chksum_long(full_name, &checksum)){
3383 fprintf(out, "%s : %d\n", full_name, (int)checksum);
3392 DCF(pxospew,"spew PXO 32 bit checksums for all visible mission files")
3396 dc_get_arg(ARG_INT);
3397 if(Dc_arg_type & ARG_INT){
3398 max_files = Dc_arg_int;
3400 dc_get_arg(ARG_STRING);
3401 if(Dc_arg_type & ARG_STRING){
3402 multi_spew_pxo_checksums(max_files, Dc_arg);
3409 // make a bunch of fake players - don't rely on this to be very safe - its mostly used for interface testing
3411 void multi_make_fake_players(int count)
3415 for(idx=0;idx<count;idx++){
3416 if(!MULTI_CONNECTED(Net_players[idx])){
3417 Net_players[idx].player = &Players[idx];
3418 SDL_snprintf(Net_players[idx].player->callsign, CALLSIGN_LEN, "Player %d", idx);
3419 Net_players[idx].flags |= NETINFO_FLAG_CONNECTED;
3425 // ---------------------------------------------------------------------------------------------------------------------
3426 // PACK UNPACK STUFF
3430 #pragma optimize("", off)
3433 typedef struct bitbuffer {
3440 void bitbuffer_init( bitbuffer *bitbuf, ubyte *data )
3443 bitbuf->mask = 0x80;
3444 bitbuf->data = data;
3445 bitbuf->org_data = data;
3448 int bitbuffer_write_flush( bitbuffer *bitbuf )
3450 // Flush to next byte
3451 if ( bitbuf->mask != 0x80 ) {
3452 *bitbuf->data++ = (ubyte)bitbuf->rack;
3454 return bitbuf->data-bitbuf->org_data;
3457 int bitbuffer_read_flush( bitbuffer *bitbuf )
3459 return bitbuf->data-bitbuf->org_data;
3462 void bitbuffer_put( bitbuffer *bitbuf, uint data, int bit_count )
3466 mask = 1L << ( bit_count - 1 );
3467 while ( mask != 0) {
3468 if ( mask & data ) {
3469 bitbuf->rack |= bitbuf->mask;
3472 if ( bitbuf->mask == 0 ) {
3473 *bitbuf->data++=(ubyte)bitbuf->rack;
3475 bitbuf->mask = 0x80;
3481 uint bitbuffer_get_unsigned( bitbuffer *bitbuf, int bit_count )
3486 mask = 1L << ( bit_count - 1 );
3489 while ( mask != 0) {
3490 if ( bitbuf->mask == 0x80 ) {
3491 bitbuf->rack = *bitbuf->data++;
3493 if ( bitbuf->rack & bitbuf->mask ) {
3494 return_value |= mask;
3498 if ( bitbuf->mask == 0 ) {
3499 bitbuf->mask = 0x80;
3503 return return_value;
3506 int bitbuffer_get_signed( bitbuffer *bitbuf, int bit_count )
3511 mask = 1L << ( bit_count - 1 );
3514 while ( mask != 0) {
3515 if ( bitbuf->mask == 0x80 ) {
3516 bitbuf->rack = *bitbuf->data++;
3518 if ( bitbuf->rack & bitbuf->mask ) {
3519 return_value |= mask;
3523 if ( bitbuf->mask == 0 ) {
3524 bitbuf->mask = 0x80;
3528 // sign extend return value
3529 return_value <<= (32-bit_count);
3531 return ((int)return_value)>>(32-bit_count);
3536 // Packs/unpacks an object position.
3537 // Returns number of bytes read or written.
3538 // #define OO_POS_RET_SIZE 9
3539 int multi_pack_unpack_position( int write, ubyte *data, vector *pos)
3543 bitbuffer_init(&buf,data);
3550 a = fl2i(pos->xyz.x*105.0f+0.5f);
3551 b = fl2i(pos->xyz.y*105.0f+0.5f);
3552 c = fl2i(pos->xyz.z*105.0f+0.5f);
3553 CAP(a,-8388608,8388607);
3554 CAP(b,-8388608,8388607);
3555 CAP(c,-8388608,8388607);
3557 bitbuffer_put( &buf, (uint)a, 24 );
3558 bitbuffer_put( &buf, (uint)b, 24 );
3559 bitbuffer_put( &buf, (uint)c, 24 );
3562 return bitbuffer_write_flush(&buf);
3567 a = bitbuffer_get_signed(&buf,24);
3568 b = bitbuffer_get_signed(&buf,24);
3569 c = bitbuffer_get_signed(&buf,24);
3571 pos->xyz.x = i2fl(a)/105.0f;
3572 pos->xyz.y = i2fl(b)/105.0f;
3573 pos->xyz.z = i2fl(c)/105.0f;
3575 return bitbuffer_read_flush(&buf);
3579 int degenerate_count = 0;
3580 int non_degenerate_count = 0;
3583 hack = ((ushort)orient->v.fvec.xyz.x * 32767);
3584 memcpy(&hack, &orient->v.fvec.xyz.x, 4);
3585 bitbuffer_put( &buf, hack, 32 );
3586 memcpy(&hack, &orient->v.fvec.xyz.y, 4);
3587 bitbuffer_put( &buf, hack, 32 );
3588 memcpy(&hack, &orient->v.fvec.xyz.z, 4);
3589 bitbuffer_put( &buf, hack, 32 );
3591 memcpy(&hack, &orient->v.uvec.xyz.x, 4);
3592 bitbuffer_put( &buf, hack, 32 );
3593 memcpy(&hack, &orient->v.uvec.xyz.y, 4);
3594 bitbuffer_put( &buf, hack, 32 );
3595 memcpy(&hack, &orient->v.uvec.xyz.z, 4);
3596 bitbuffer_put( &buf, hack, 32 );
3598 memcpy(&hack, &orient->v.rvec.xyz.x, 4);
3599 bitbuffer_put( &buf, hack, 32 );
3600 memcpy(&hack, &orient->v.rvec.xyz.y, 4);
3601 bitbuffer_put( &buf, hack, 32 );
3602 memcpy(&hack, &orient->v.rvec.xyz.z, 4);
3603 bitbuffer_put( &buf, hack, 32 );*/
3606 hack = bitbuffer_get_unsigned(&buf, 32);
3607 memcpy(&orient->v.fvec.xyz.x, &hack, 4);
3608 hack = bitbuffer_get_unsigned(&buf, 32);
3609 memcpy(&orient->v.fvec.xyz.y, &hack, 4);
3610 hack = bitbuffer_get_unsigned(&buf, 32);
3611 memcpy(&orient->v.fvec.xyz.z, &hack, 4);
3613 hack = bitbuffer_get_unsigned(&buf, 32);
3614 memcpy(&orient->v.uvec.xyz.x, &hack, 4);
3615 hack = bitbuffer_get_unsigned(&buf, 32);
3616 memcpy(&orient->v.uvec.xyz.y, &hack, 4);
3617 hack = bitbuffer_get_unsigned(&buf, 32);
3618 memcpy(&orient->v.uvec.xyz.z, &hack, 4);
3620 hack = bitbuffer_get_unsigned(&buf, 32);
3621 memcpy(&orient->v.rvec.xyz.x, &hack, 4);
3622 hack = bitbuffer_get_unsigned(&buf, 32);
3623 memcpy(&orient->v.rvec.xyz.y, &hack, 4);
3624 hack = bitbuffer_get_unsigned(&buf, 32);
3625 memcpy(&orient->v.rvec.xyz.z, &hack, 4);*/
3627 // Packs/unpacks an orientation matrix.
3628 // Returns number of bytes read or written.
3629 // #define OO_ORIENT_RET_SIZE 6
3630 int multi_pack_unpack_orient( int write, ubyte *data, matrix *orient)
3634 bitbuffer_init(&buf, data + 1);
3642 #define D_SCALE 32768.0f
3643 #define D_MAX_RANGE 32767
3644 #define D_MIN_RANGE -32768
3646 #define N_SCALE 2048.0f
3647 #define N_MAX_RANGE 2047
3648 #define N_MIN_RANGE -2048
3651 // degenerate case - send the whole orient matrix
3652 vm_extract_angles_matrix(&ang, orient);
3653 if((ang.h > 3.130) && (ang.h < 3.150)){
3659 a = fl2i(orient->v.fvec.xyz.x * D_SCALE);
3660 CAP(a, D_MIN_RANGE, D_MAX_RANGE);
3661 bitbuffer_put( &buf, a, 16 );
3662 a = fl2i(orient->v.fvec.xyz.y * D_SCALE);
3663 CAP(a, D_MIN_RANGE, D_MAX_RANGE);
3664 bitbuffer_put( &buf, a, 16 );
3665 a = fl2i(orient->v.fvec.xyz.z * D_SCALE);
3666 CAP(a, D_MIN_RANGE, D_MAX_RANGE);
3667 bitbuffer_put( &buf, a, 16 );
3669 a = fl2i(orient->v.uvec.xyz.x * D_SCALE);
3670 CAP(a, D_MIN_RANGE, D_MAX_RANGE);
3671 bitbuffer_put( &buf, a, 16 );
3672 a = fl2i(orient->v.uvec.xyz.y * D_SCALE);
3673 CAP(a, D_MIN_RANGE, D_MAX_RANGE);
3674 bitbuffer_put( &buf, a, 16 );
3675 a = fl2i(orient->v.uvec.xyz.z * D_SCALE);
3676 CAP(a, D_MIN_RANGE, D_MAX_RANGE);
3677 bitbuffer_put( &buf, a, 16 );
3679 a = fl2i(orient->v.rvec.xyz.x * D_SCALE);
3680 CAP(a, D_MIN_RANGE, D_MAX_RANGE);
3681 bitbuffer_put( &buf, a, 16 );
3682 a = fl2i(orient->v.rvec.xyz.y * D_SCALE);
3683 CAP(a, D_MIN_RANGE, D_MAX_RANGE);
3684 bitbuffer_put( &buf, a, 16 );
3685 a = fl2i(orient->v.rvec.xyz.z * D_SCALE);
3686 CAP(a, D_MIN_RANGE, D_MAX_RANGE);
3687 bitbuffer_put( &buf, a, 16 );
3689 non_degenerate_count++;
3691 vm_matrix_to_rot_axis_and_angle(orient, &theta, &rot_axis);
3692 // Have theta, which is an angle between 0 and PI.
3693 // Convert it to be between -1.0f and 1.0f
3694 theta = theta*2.0f/PI-1.0f;
3697 a = fl2i(rot_axis.xyz.x*N_SCALE);
3698 b = fl2i(rot_axis.xyz.y*N_SCALE);
3699 c = fl2i(rot_axis.xyz.z*N_SCALE);
3700 d = fl2i(theta*N_SCALE);
3702 CAP(a, N_MIN_RANGE, N_MAX_RANGE);
3703 CAP(b, N_MIN_RANGE, N_MAX_RANGE);
3704 CAP(c, N_MIN_RANGE, N_MAX_RANGE);
3705 CAP(d, N_MIN_RANGE, N_MAX_RANGE);
3707 bitbuffer_put( &buf, (uint)a, 12 );
3708 bitbuffer_put( &buf, (uint)b, 12 );
3709 bitbuffer_put( &buf, (uint)c, 12 );
3710 bitbuffer_put( &buf, (uint)d, 12 );
3713 // flag for degenerate case
3716 return bitbuffer_write_flush(&buf) + 1;
3722 a = bitbuffer_get_signed(&buf, 16);
3723 orient->v.fvec.xyz.x = i2fl(a) / D_SCALE;
3724 a = bitbuffer_get_signed(&buf, 16);
3725 orient->v.fvec.xyz.y = i2fl(a) / D_SCALE;
3726 a = bitbuffer_get_signed(&buf, 16);
3727 orient->v.fvec.xyz.z = i2fl(a) / D_SCALE;
3729 a = bitbuffer_get_signed(&buf, 16);
3730 orient->v.uvec.xyz.x = i2fl(a) / D_SCALE;
3731 a = bitbuffer_get_signed(&buf, 16);
3732 orient->v.uvec.xyz.y = i2fl(a) / D_SCALE;
3733 a = bitbuffer_get_signed(&buf, 16);
3734 orient->v.uvec.xyz.z = i2fl(a) / D_SCALE;
3736 a = bitbuffer_get_signed(&buf, 16);
3737 orient->v.rvec.xyz.x = i2fl(a) / D_SCALE;
3738 a = bitbuffer_get_signed(&buf, 16);
3739 orient->v.rvec.xyz.y = i2fl(a) / D_SCALE;
3740 a = bitbuffer_get_signed(&buf, 16);
3741 orient->v.rvec.xyz.z = i2fl(a) / D_SCALE;
3743 a = bitbuffer_get_signed(&buf,12);
3744 b = bitbuffer_get_signed(&buf,12);
3745 c = bitbuffer_get_signed(&buf,12);
3746 d = bitbuffer_get_signed(&buf,12);
3749 rot_axis.xyz.x = i2fl(a)/N_SCALE;
3750 rot_axis.xyz.y = i2fl(b)/N_SCALE;
3751 rot_axis.xyz.z = i2fl(c)/N_SCALE;
3752 theta = i2fl(d)/N_SCALE;
3754 // Convert theta back to range 0-PI
3755 theta = (theta+1.0f)*PI/2.0f;
3757 vm_quaternion_rotate(orient, theta, &rot_axis);
3759 vm_orthogonalize_matrix(orient);
3762 return bitbuffer_read_flush(&buf) + 1;
3767 // Packs/unpacks an orientation matrix.
3768 // Returns number of bytes read or written.
3769 // #define OO_ORIENT_RET_SIZE 6
3771 int multi_pack_unpack_orient( int write, ubyte *data, matrix *orient)
3775 bitbuffer_init(&buf,data);
3783 // if our heading is 3.14 radians
3785 //vm_extract_angles_matrix(&a, orient);
3786 //if((ang.h > 3.1300) && (ang.h < 3.1500)){
3789 util_matrix_to_rot_axis_and_angle(orient, &theta, &rot_axis);
3790 // Have theta, which is an angle between 0 and PI.
3791 // Convert it to be between -1.0f and 1.0f
3792 theta = theta*2.0f/PI-1.0f;
3794 #define SCALE 2048.0f
3796 #define MAX_RANGE 2047
3797 #define MIN_RANGE -2048
3800 a = fl2i(rot_axis.xyz.x*SCALE);
3801 b = fl2i(rot_axis.xyz.y*SCALE);
3802 c = fl2i(rot_axis.xyz.z*SCALE);
3803 d = fl2i(theta*SCALE);
3805 CAP(a,MIN_RANGE,MAX_RANGE);
3806 CAP(b,MIN_RANGE,MAX_RANGE);
3807 CAP(c,MIN_RANGE,MAX_RANGE);
3808 CAP(d,MIN_RANGE,MAX_RANGE);
3811 bitbuffer_put( &buf, (uint)a, 12 );
3812 bitbuffer_put( &buf, (uint)b, 12 );
3813 bitbuffer_put( &buf, (uint)c, 12 );
3814 bitbuffer_put( &buf, (uint)d, 12 );
3816 return bitbuffer_write_flush(&buf);
3820 a = bitbuffer_get_signed(&buf,12);
3821 b = bitbuffer_get_signed(&buf,12);
3822 c = bitbuffer_get_signed(&buf,12);
3823 d = bitbuffer_get_signed(&buf,12);
3826 rot_axis.xyz.x = i2fl(a)/SCALE;
3827 rot_axis.xyz.y = i2fl(b)/SCALE;
3828 rot_axis.xyz.z = i2fl(c)/SCALE;
3829 theta = i2fl(d)/SCALE;
3831 // Convert theta back to range 0-PI
3832 theta = (theta+1.0f)*PI/2.0f;
3834 vm_quaternion_rotate(orient, theta, &rot_axis);
3836 vm_orthogonalize_matrix(orient);
3838 return bitbuffer_read_flush(&buf);
3843 // Packs/unpacks velocity
3844 // Returns number of bytes read or written.
3845 // #define OO_VEL_RET_SIZE 4
3846 int multi_pack_unpack_vel( int write, ubyte *data, matrix *orient, vector *pos, physics_info *pi)
3850 bitbuffer_init(&buf,data);
3857 r = vm_vec_dot( &orient->v.rvec, &pi->vel );
3858 u = vm_vec_dot( &orient->v.uvec, &pi->vel );
3859 f = vm_vec_dot( &orient->v.fvec, &pi->vel );
3867 bitbuffer_put( &buf, (uint)a, 10 );
3868 bitbuffer_put( &buf, (uint)b, 10 );
3869 bitbuffer_put( &buf, (uint)c, 10 );
3871 return bitbuffer_write_flush(&buf);
3874 a = bitbuffer_get_signed(&buf,10);
3875 b = bitbuffer_get_signed(&buf,10);
3876 c = bitbuffer_get_signed(&buf,10);
3881 // Convert into world coordinates
3882 vm_vec_zero(&pi->vel);
3883 vm_vec_scale_add2( &pi->vel, &orient->v.rvec, r );
3884 vm_vec_scale_add2( &pi->vel, &orient->v.uvec, u );
3885 vm_vec_scale_add2( &pi->vel, &orient->v.fvec, f );
3887 return bitbuffer_read_flush(&buf);
3891 // Packs/unpacks desired_velocity
3892 // Returns number of bytes read or written.
3893 // #define OO_DESIRED_VEL_RET_SIZE 3
3894 int multi_pack_unpack_desired_vel( int write, ubyte *data, matrix *orient, vector *pos, physics_info *pi, ship_info *sip)
3898 bitbuffer_init(&buf,data);
3905 max_vel.xyz.x = SDL_max( sip->max_vel.xyz.x, sip->afterburner_max_vel.xyz.x );
3906 max_vel.xyz.y = SDL_max( sip->max_vel.xyz.y, sip->afterburner_max_vel.xyz.y );
3907 max_vel.xyz.z = SDL_max( sip->max_vel.xyz.z, sip->afterburner_max_vel.xyz.z );
3910 // Find desired vel in local coordinates
3911 // Velocity can be from -1024 to 1024
3913 // bitfields for each value
3914 if(max_vel.xyz.x > 0.0f){
3917 if(max_vel.xyz.y > 0.0f){
3920 if(max_vel.xyz.z > 0.0f){
3923 // fields = sip - Ship_info;
3924 bitbuffer_put(&buf, (uint)fields, 8);
3926 r = vm_vec_dot( &orient->v.rvec, &pi->desired_vel );
3927 u = vm_vec_dot( &orient->v.uvec, &pi->desired_vel );
3928 f = vm_vec_dot( &orient->v.fvec, &pi->desired_vel );
3930 if ( max_vel.xyz.x > 0.0f ) {
3931 r = r / max_vel.xyz.x;
3932 a = fl2i( r * 128.0f );
3934 bitbuffer_put( &buf, (uint)a, 8 );
3937 if ( max_vel.xyz.y > 0.0f ) {
3938 u = u / max_vel.xyz.y;
3939 a = fl2i( u * 128.0f );
3941 bitbuffer_put( &buf, (uint)a, 8 );
3944 if ( max_vel.xyz.z > 0.0f ) {
3945 f = f / max_vel.xyz.z;
3946 a = fl2i( f * 128.0f );
3948 bitbuffer_put( &buf, (uint)a, 8 );
3951 return bitbuffer_write_flush(&buf);
3954 // Find desired vel in local coordinates
3955 // Velocity can be from -1024 to 1024
3957 // get the fields bitbuffer
3958 fields = bitbuffer_get_signed(&buf, 8);
3960 if ( fields & (1<<0) ) {
3961 a = bitbuffer_get_signed(&buf,8);
3967 if ( fields & (1<<1) ) {
3968 a = bitbuffer_get_signed(&buf,8);
3974 if ( fields & (1<<2) ) {
3975 a = bitbuffer_get_signed(&buf,8);
3981 // Convert into world coordinates
3982 vm_vec_zero(&pi->vel);
3983 vm_vec_scale_add2( &pi->desired_vel, &orient->v.rvec, r*max_vel.xyz.x );
3984 vm_vec_scale_add2( &pi->desired_vel, &orient->v.uvec, u*max_vel.xyz.y );
3985 vm_vec_scale_add2( &pi->desired_vel, &orient->v.fvec, f*max_vel.xyz.z );
3987 return bitbuffer_read_flush(&buf);
3991 // Packs/unpacks rotational velocity
3992 // Returns number of bytes read or written.
3993 // #define OO_ROTVEL_RET_SIZE 4
3994 int multi_pack_unpack_rotvel( int write, ubyte *data, matrix *orient, vector *pos, physics_info *pi)
3998 bitbuffer_init(&buf,data);
4003 // output rotational velocity
4004 a = fl2i(pi->rotvel.xyz.x*32.0f);
4005 b = fl2i(pi->rotvel.xyz.y*32.0f);
4006 c = fl2i(pi->rotvel.xyz.z*32.0f);
4010 bitbuffer_put( &buf, (uint)a, 10 );
4011 bitbuffer_put( &buf, (uint)b, 10 );
4012 bitbuffer_put( &buf, (uint)c, 10 );
4015 return bitbuffer_write_flush(&buf);
4019 // unpack rotational velocity
4020 a = bitbuffer_get_signed(&buf,10);
4021 b = bitbuffer_get_signed(&buf,10);
4022 c = bitbuffer_get_signed(&buf,10);
4023 pi->rotvel.xyz.x = i2fl(a)/32.0f;
4024 pi->rotvel.xyz.y = i2fl(b)/32.0f;
4025 pi->rotvel.xyz.z = i2fl(c)/32.0f;
4027 return bitbuffer_read_flush(&buf);
4031 // Packs/unpacks desired rotvel
4032 // Returns number of bytes read or written.
4033 // #define OO_DESIRED_ROTVEL_RET_SIZE 3
4034 int multi_pack_unpack_desired_rotvel( int write, ubyte *data, matrix *orient, vector *pos, physics_info *pi, ship_info *sip)
4039 bitbuffer_init(&buf,data);
4045 // use ship_info values for max_rotvel instead of taking it from physics info
4047 // bitfields for each value
4048 if(sip->max_rotvel.xyz.x > 0.0f){
4051 if(sip->max_rotvel.xyz.y > 0.0f){
4054 if(sip->max_rotvel.xyz.z > 0.0f){
4058 bitbuffer_put(&buf, (uint)fields, 8);
4060 // output desired rotational velocity as a percent of max
4061 if ( sip->max_rotvel.xyz.x > 0.0f ) {
4062 a = fl2i( pi->desired_rotvel.xyz.x*128.0f / sip->max_rotvel.xyz.x );
4064 bitbuffer_put( &buf, (uint)a, 8 );
4067 if ( sip->max_rotvel.xyz.y > 0.0f ) {
4068 a = fl2i( pi->desired_rotvel.xyz.y*128.0f / sip->max_rotvel.xyz.y );
4070 bitbuffer_put( &buf, (uint)a, 8 );
4073 if ( sip->max_rotvel.xyz.z > 0.0f ) {
4074 a = fl2i( pi->desired_rotvel.xyz.z*128.0f / sip->max_rotvel.xyz.z );
4076 bitbuffer_put( &buf, (uint)a, 8 );
4079 return bitbuffer_write_flush(&buf);
4081 fields = bitbuffer_get_signed(&buf, 8);
4083 // unpack desired rotational velocity
4084 if ( fields & (1<<0) ) {
4085 a = bitbuffer_get_signed(&buf,8);
4090 if ( fields & (1<<1) ) {
4091 a = bitbuffer_get_signed(&buf,8);
4096 if ( fields & (1<<2) ) {
4097 a = bitbuffer_get_signed(&buf,8);
4102 pi->desired_rotvel.xyz.x = r*sip->max_rotvel.xyz.x;
4103 pi->desired_rotvel.xyz.y = u*sip->max_rotvel.xyz.y;
4104 pi->desired_rotvel.xyz.z = f*sip->max_rotvel.xyz.z;
4106 return bitbuffer_read_flush(&buf);
4111 #pragma optimize("", on)