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/MultiMsgs.cpp $
15 * C file that holds functions for the building and processing of multiplayer packets
18 * Revision 1.11 2006/04/26 19:48:58 taylor
19 * various big-endian fixes, mainly networking support related
21 * Revision 1.10 2005/10/02 09:30:10 taylor
22 * sync up rest of big-endian network changes. it should at least be as good as what's in FS2_Open now, only better :)
24 * Revision 1.9 2005/08/12 08:59:17 taylor
27 * Revision 1.8 2004/09/20 01:31:44 theoddone33
30 * Revision 1.7 2004/06/11 01:49:45 tigital
31 * byte-swapping changes for bigendian systems
33 * Revision 1.6 2003/08/03 16:10:29 taylor
34 * cleanup; compile warning fixes
36 * Revision 1.5 2002/06/17 06:33:10 relnev
37 * ryan's struct patch for gcc 2.95
39 * Revision 1.4 2002/06/09 04:41:24 relnev
40 * added copyright header
42 * Revision 1.3 2002/05/26 20:49:54 theoddone33
45 * Revision 1.2 2002/05/07 03:16:47 theoddone33
46 * The Great Newline Fix
48 * Revision 1.1.1.1 2002/05/03 03:28:10 root
52 * 83 9/14/99 2:21p Dave
53 * Fixed observer mode joining and ingame stuff.
55 * 82 9/14/99 3:26a Dave
56 * Fixed laser fogging problem in nebula (D3D)> Fixed multiplayer
57 * respawn-too-early problem. Made a few crash points safe.
59 * 81 9/13/99 4:52p Dave
62 * 80 9/08/99 10:01p Dave
63 * Make sure game won't run in a drive's root directory. Make sure
64 * standalone routes suqad war messages properly to the host.
66 * 79 8/28/99 4:54p Dave
67 * Fixed directives display for multiplayer clients for wings with
68 * multiple waves. Fixed hud threat indicator rendering color.
70 * 78 8/27/99 12:32a Dave
71 * Allow the user to specify a local port through the launcher.
73 * 77 8/26/99 8:51p Dave
74 * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
76 * 76 8/25/99 4:38p Dave
77 * Updated PXO stuff. Make squad war report stuff much more nicely.
79 * 75 8/24/99 1:50a Dave
80 * Fixed client-side afterburner stuttering. Added checkbox for no version
81 * checking on PXO join. Made button info passing more friendly between
84 * 74 8/22/99 5:53p Dave
85 * Scoring fixes. Added self destruct key. Put callsigns in the logfile
86 * instead of ship designations for multiplayer players.
88 * 73 8/22/99 1:55p Dave
89 * Cleaned up host/team-captain leaving code.
91 * 72 8/22/99 1:19p Dave
92 * Fixed up http proxy code. Cleaned up scoring code. Reverse the order in
93 * which d3d cards are detected.
95 * 71 8/19/99 10:59a Dave
96 * Packet loss detection.
98 * 70 8/17/99 1:12p Dave
99 * Send TvT update when a client has finished joining so he stays nice and
102 * 69 8/16/99 4:05p Dave
103 * Big honking checkin.
105 * 68 8/11/99 5:54p Dave
106 * Fixed collision problem. Fixed standalone ghost problem.
108 * 67 8/06/99 9:46p Dave
109 * Hopefully final changes for the demo.
111 * 66 8/05/99 2:06a Dave
114 * 65 7/30/99 7:01p Dave
115 * Dogfight escort gauge. Fixed up laser rendering in Glide.
117 * 64 7/29/99 5:41p Jefff
118 * Sound hooks for cmeasure success
120 * 63 7/28/99 5:34p Dave
121 * Nailed the missing stats bug to the wall. Problem was optimized build
122 * and using GET_DATA() with array elements. BLECH.
124 * 62 7/26/99 5:50p Dave
125 * Revised ingame join. Better? We'll see....
127 * 61 7/24/99 1:54p Dave
128 * Hud text flash gauge. Reworked dead popup to use 4 buttons in red-alert
131 * 60 7/22/99 7:17p Dave
132 * Fixed excessive whacks in multiplayer.
134 * 59 7/08/99 10:53a Dave
135 * New multiplayer interpolation scheme. Not 100% done yet, but still
136 * better than the old way.
138 * 58 7/03/99 5:50p Dave
139 * Make rotated bitmaps draw properly in padlock views.
141 * 57 7/03/99 4:08p Dave
142 * Fixed wss_slots size issues. Fixed potentially nasty bug in low level
145 * 56 6/21/99 7:24p Dave
146 * netplayer pain packet. Added type E unmoving beams.
148 * 55 6/18/99 5:16p Dave
149 * Added real beam weapon lighting. Fixed beam weapon sounds. Added MOTD
150 * dialog to PXO screen.
152 * 54 6/16/99 4:06p Dave
153 * New pilot info popup. Added new draw-bitmap-as-poly function.
155 * 53 6/16/99 10:20a Dave
156 * Added send-message-list sexpression.
158 * 52 6/04/99 3:52p Anoop
159 * Removed bogus assert.
161 * 51 6/01/99 8:35p Dave
162 * Finished lockarm weapons. Added proper supercap weapons/damage. Added
163 * awacs-set-radius sexpression.
165 * 50 5/21/99 5:03p Andsager
166 * Add code to display engine wash death. Modify ship_kill_packet
168 * 49 5/18/99 1:30p Dave
169 * Added muzzle flash table stuff.
171 * 48 5/14/99 1:59p Andsager
172 * Multiplayer message for subsystem cargo revealed.
174 * 47 5/14/99 12:15p Andsager
175 * Add vaporized to kill packet
177 * 46 5/03/99 8:32p Dave
178 * New version of multi host options screen.
180 * 45 4/30/99 12:18p Dave
181 * Several minor bug fixes.
183 * 44 4/29/99 2:29p Dave
184 * Made flak work much better in multiplayer.
186 * 43 4/28/99 11:13p Dave
187 * Temporary checkin of artillery code.
189 * 42 4/16/99 5:54p Dave
190 * Support for on/off style "stream" weapons. Real early support for
191 * target-painting lasers.
193 * 41 4/12/99 2:22p Dave
194 * More checks for dogfight stats.
196 * 40 4/09/99 2:21p Dave
197 * Multiplayer beta stuff. CD checking.
199 * 39 4/02/99 9:55a Dave
200 * Added a few more options in the weapons.tbl for beam weapons. Attempt
201 * at putting "pain" packets into multiplayer.
203 * 38 4/01/99 3:41p Anoop
204 * Removed bogus Int3().
206 * 37 3/19/99 9:51a Dave
207 * Checkin to repair massive source safe crash. Also added support for
208 * pof-style nebulae, and some new weapons code.
210 * 38 3/12/99 2:32p Anoop
211 * Removed bogus asserts.
213 * 37 3/11/99 11:41a Neilk
214 * Don't do multi_io_* operations in single-player
216 * 36 3/10/99 6:50p Dave
217 * Changed the way we buffer packets for all clients. Optimized turret
218 * fired packets. Did some weapon firing optimizations.
220 * 35 3/09/99 6:24p Dave
221 * More work on object update revamping. Identified several sources of
222 * unnecessary bandwidth.
224 * 34 3/08/99 7:03p Dave
225 * First run of new object update system. Looks very promising.
227 * 33 3/04/99 6:09p Dave
228 * Added in sexpressions for firing beams and checking for if a ship is
231 * 32 3/01/99 10:00a Dave
232 * Fxied several dogfight related stats bugs.
234 * 31 2/24/99 2:25p Dave
235 * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn
236 * bug for dogfight more.
238 * 30 2/23/99 2:29p Dave
239 * First run of oldschool dogfight mode.
241 * 29 2/21/99 6:01p Dave
242 * Fixed standalone WSS packets.
244 * 28 2/21/99 1:48p Dave
245 * Some code for monitoring datarate for multiplayer in detail.
247 * 27 2/17/99 2:11p Dave
248 * First full run of squad war. All freespace and tracker side stuff
251 * 26 2/12/99 6:16p Dave
252 * Pre-mission Squad War code is 95% done.
254 * 25 2/11/99 3:08p Dave
255 * PXO refresh button. Very preliminary squad war support.
257 * 24 1/29/99 5:07p Dave
258 * Fixed multiplayer stuff. Put in multiplayer support for rapid fire
261 * 23 1/27/99 9:56a Dave
262 * Temporary checkin of beam weapons for Dan to make cool sounds.
264 * 22 1/26/99 6:33p Anoop
265 * Fixed multiplayer slot switching problem (be sure to remember that
266 * hinfo->id is player id# _not_ player index #)
268 * 21 1/24/99 11:37p Dave
269 * First full rev of beam weapons. Very customizable. Removed some bogus
270 * Int3()'s in low level net code.
272 * 20 1/15/99 4:37p Dave
273 * Potential fix for weapon pair problem.
275 * 19 1/14/99 6:06p Dave
276 * 100% full squad logo support for single player and multiplayer.
278 * 18 1/14/99 12:48a Dave
279 * Todo list bug fixes. Made a pass at putting briefing icons back into
280 * FRED. Sort of works :(
282 * 17 1/12/99 5:45p Dave
283 * Moved weapon pipeline in multiplayer to almost exclusively client side.
284 * Very good results. Bandwidth goes down, playability goes up for crappy
285 * connections. Fixed object update problem for ship subsystems.
287 * 16 1/08/99 4:56p Anoop
288 * Fixed a problem with wss request packets.
290 * 15 12/18/98 12:24p Markm
291 * Fixed a dumb bug where player image_filenames were not being passed
292 * properly in new players packet.
294 * 14 12/14/98 12:13p Dave
295 * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
298 * 13 11/30/98 1:07p Dave
299 * 16 bit conversion, first run.
301 * 12 11/20/98 4:08p Dave
302 * Fixed flak effect in multiplayer.
304 * 11 11/19/98 4:19p Dave
305 * Put IPX sockets back in psnet. Consolidated all multiplayer config
308 * 10 11/19/98 8:04a Dave
309 * Full support for D3-style reliable sockets. Revamped packet lag/loss
310 * system, made it receiver side and at the lowest possible level.
312 * 9 11/17/98 11:12a Dave
313 * Removed player identification by address. Now assign explicit id #'s.
315 * 8 11/12/98 11:50a Dave
316 * Multiplayer clients set flak range to be very long.
318 * 7 11/12/98 12:13a Dave
319 * Tidied code up for multiplayer test. Put in network support for flak
322 * 6 11/05/98 5:55p Dave
323 * Big pass at reducing #includes
325 * 5 10/20/98 1:39p Andsager
326 * Make so sparks follow animated ship submodels. Modify
327 * ship_weapon_do_hit_stuff() and ship_apply_local_damage() to add
328 * submodel_num. Add submodel_num to multiplayer hit packet.
330 * 4 10/13/98 9:29a Dave
331 * Started neatening up freespace.h. Many variables renamed and
332 * reorganized. Added AlphaColors.[h,cpp]
334 * 3 10/07/98 6:27p Dave
335 * Globalized mission and campaign file extensions. Removed Silent Threat
336 * special code. Moved \cache \players and \multidata into the \data
339 * 2 10/07/98 10:53a Dave
342 * 1 10/07/98 10:50a Dave
344 * 506 10/02/98 3:22p Allender
345 * fix up the -connect option and fix the -port option
347 * 505 10/02/98 11:45a Dave
348 * Fixed stupid chat message bug.
350 * 504 9/29/98 1:33p Dave
351 * Remove standalone only conditional compiles for pre 1.04 stuff.
353 * 503 9/28/98 1:54p Dave
354 * Make sure French and German don't xfer builtin files they don't have
357 * 502 9/20/98 7:19p Dave
358 * Added CHANGE_IFF packet.
360 * 501 9/17/98 3:08p Dave
361 * PXO to non-pxo game warning popup. Player icon stuff in create and join
362 * game screens. Upped server count refresh time in PXO to 35 secs (from
371 #include <io.h> // for findfirst/findnext, etc
374 #include "multimsgs.h"
375 #include "multiutil.h"
378 #include "multiteamselect.h"
379 #include "linklist.h"
380 #include "gamesequence.h"
381 #include "hudmessage.h"
382 #include "hudsquadmsg.h"
383 #include "freespace.h"
387 #include "missiongoals.h"
388 #include "missionparse.h"
389 #include "missionlog.h"
390 #include "missionmessage.h"
391 #include "missionbrief.h"
393 #include "cmeasure.h"
394 #include "model.h" // for some limits
395 #include "afterburner.h"
396 #include "stand_server.h"
397 #include "multi_xfer.h"
403 #include "managepilot.h"
404 #include "hudsquadmsg.h"
406 #include "missionweaponchoice.h"
407 #include "missionshipchoice.h"
408 #include "fireballs.h"
411 #include "multi_ingame.h"
412 #include "multiteamselect.h"
414 #include "multi_campaign.h"
415 #include "multi_team.h"
416 #include "multi_respawn.h"
417 #include "multi_observer.h"
418 #include "multi_voice.h"
419 #include "asteroid.h"
420 #include "multi_pmsg.h"
421 #include "multi_data.h"
422 #include "multi_options.h"
423 #include "objcollide.h"
424 #include "hudreticle.h"
425 #include "multi_pause.h"
426 #include "multi_endgame.h"
427 #include "missiondebrief.h"
428 #include "multi_obj.h"
429 #include "multi_log.h"
431 #include "multi_kick.h"
435 #include "multi_rate.h"
436 #include "neblightning.h"
437 #include "hudescort.h"
438 #include "multi_fstracker.h"
440 // #define _MULTI_SUPER_WACKY_COMPRESSION
442 #ifdef _MULTI_SUPER_WACKY_COMPRESSION
444 #define MAX_CODE ( ( 1 << BITS ) - 1 )
445 #define TABLE_SIZE 35023L
446 #define END_OF_STREAM 256
447 #define BUMP_CODE 257
448 #define FLUSH_CODE 258
449 #define FIRST_CODE 259
458 static DICTIONARY dict[TABLE_SIZE];
459 static char decode_stack[TABLE_SIZE];
460 static uint next_code;
461 static int current_code_bits;
462 static uint next_bump_code;
464 typedef struct BitBuf {
470 void output_bits( BitBuf *bitbuf, uint code, int count )
474 mask = 1L << ( count - 1 );
477 bitbuf->rack |= bitbuf->mask;
479 if ( bitbuf->mask == 0 ) {
480 *bitbuf->data++=(ubyte)bitbuf->rack;
488 uint input_bits( BitBuf *bitbuf, int bit_count )
493 mask = 1L << ( bit_count - 1 );
496 if ( bitbuf->mask == 0x80 ) {
497 bitbuf->rack = *bitbuf->data++;
498 if ( bitbuf->rack == EOF )
499 return END_OF_STREAM;
501 if ( bitbuf->rack & bitbuf->mask )
502 return_value |= mask;
505 if ( bitbuf->mask == 0 )
508 return( return_value );
512 static void InitializeDictionary()
516 for ( i = 0 ; i < TABLE_SIZE ; i++ )
517 dict[i].code_value = UNUSED;
519 next_code = FIRST_CODE;
520 current_code_bits = 9;
521 next_bump_code = 511;
525 static uint find_child_node( int parent_code, int child_character )
530 index = ( child_character << ( BITS - 8 ) ) ^ parent_code;
534 offset = TABLE_SIZE - index;
536 if ( dict[ index ].code_value == UNUSED )
537 return( (uint) index );
538 if ( dict[ index ].parent_code == parent_code &&
539 dict[ index ].character == (char) child_character )
541 if ( (int) index >= offset )
544 index += TABLE_SIZE - offset;
549 static uint decode_string( uint count, uint code )
551 while ( code > 255 ) {
552 decode_stack[ count++ ] = dict[ code ].character;
553 code = dict[ code ].parent_code;
555 decode_stack[ count++ ] = (char) code;
559 int lzw_compress( ubyte *outputbuf, ubyte *inputbuf, int input_size )
567 // Init output bit buffer
570 output.data = outputbuf;
572 InitializeDictionary();
574 string_code = *inputbuf++;
576 for ( i=1 ; i<input_size ; i++ ) {
577 character = *inputbuf++;
578 index = find_child_node( string_code, character );
579 if ( dict[ index ].code_value != - 1 )
580 string_code = dict[ index ].code_value;
582 dict[ index ].code_value = next_code++;
583 dict[ index ].parent_code = string_code;
584 dict[ index ].character = (char) character;
585 output_bits( &output, (unsigned long) string_code, current_code_bits );
586 string_code = character;
587 if ( next_code > MAX_CODE ) {
588 output_bits( &output, (unsigned long) FLUSH_CODE, current_code_bits );
589 InitializeDictionary();
590 } else if ( next_code > next_bump_code ) {
591 output_bits( &output, (unsigned long) BUMP_CODE, current_code_bits );
593 next_bump_code <<= 1;
598 output_bits( &output, (unsigned long) string_code, current_code_bits );
599 output_bits( &output, (unsigned long) END_OF_STREAM, current_code_bits);
601 if ( output.mask != 0x80 )
602 *output.data++ = (ubyte)output.rack;
604 return output.data-outputbuf;
608 int lzw_expand( ubyte *outputbuf, ubyte *inputbuf )
619 input.data = inputbuf;
623 InitializeDictionary();
624 old_code = (uint) input_bits( &input, current_code_bits );
625 if ( old_code == END_OF_STREAM )
627 character = old_code;
628 outputbuf[counter++] = ( ubyte )old_code;
630 new_code = (uint) input_bits( &input, current_code_bits );
631 if ( new_code == END_OF_STREAM )
633 if ( new_code == FLUSH_CODE )
635 if ( new_code == BUMP_CODE ) {
639 if ( new_code >= next_code ) {
640 decode_stack[ 0 ] = (char) character;
641 count = decode_string( 1, old_code );
643 count = decode_string( 0, new_code );
645 character = decode_stack[ count - 1 ];
647 outputbuf[counter++] = ( ubyte )decode_stack[ --count ];
648 dict[ next_code ].parent_code = old_code;
649 dict[ next_code ].character = (char) character;
657 // process a join request packet add
658 void add_join_request(ubyte *data, int *size, join_request *jr)
660 int packet_size = *size;
661 join_request *jr_tmp = jr;
663 jr_tmp->tracker_id = INTEL_INT(jr->tracker_id);
664 jr_tmp->player_options.flags = INTEL_INT(jr->player_options.flags);
665 jr_tmp->player_options.obj_update_level = INTEL_INT(jr->player_options.obj_update_level);
672 // process a join request packet get
673 void get_join_request(ubyte *data, int *size, join_request jr)
679 jr.tracker_id = INTEL_INT(jr.tracker_id);
680 jr.player_options.flags = INTEL_INT(jr.player_options.flags);
681 jr.player_options.obj_update_level = INTEL_INT(jr.player_options.obj_update_level);
686 struct net_addr_compat {
693 SDL_COMPILE_TIME_ASSERT(net_addr_compat, sizeof(net_addr_compat) == 16);
695 void add_net_addr(ubyte *data, int &size, const net_addr *addr)
697 int packet_size = size;
698 net_addr_compat addr_c;
702 addr_c.type = INTEL_INT(addr->type);
703 addr_c.port = INTEL_SHORT(addr->port);
704 memcpy(&addr_c.addr, &addr->addr, IP_ADDRESS_LENGTH);
711 void get_net_addr(const ubyte *data, int &size, net_addr &addr)
714 net_addr_compat addr_c;
720 addr.type = INTEL_INT(addr_c.type);
721 addr.port = INTEL_SHORT(addr_c.port);
722 memcpy(&addr.addr, &addr_c.addr, IP_ADDRESS_LENGTH);
727 void add_vector_data(ubyte *data, int *size, vector vec)
729 int packet_size = *size;
731 ADD_FLOAT(vec.xyz.x);
732 ADD_FLOAT(vec.xyz.y);
733 ADD_FLOAT(vec.xyz.z);
738 void get_vector_data(ubyte *data, int *size, vector vec)
742 GET_FLOAT(vec.xyz.x);
743 GET_FLOAT(vec.xyz.y);
744 GET_FLOAT(vec.xyz.z);
749 // send the specified data packet to all players
750 void multi_io_send(net_player *pl, ubyte *data, int len)
753 if((pl == NULL) || (NET_PLAYER_NUM(pl) >= MAX_PLAYERS)){
757 // don't do it for single player
758 if(!(Game_mode & GM_MULTIPLAYER)){
763 if(MULTIPLAYER_CLIENT){
764 // SDL_assert(pl == Net_player);
765 if(pl != Net_player){
769 // SDL_assert(pl != Net_player);
770 if(pl == Net_player){
775 // If this packet will push the buffer over MAX_PACKET_SIZE, send the current send_buffer
776 if ((pl->s_info.unreliable_buffer_size + len) > MAX_PACKET_SIZE) {
777 multi_io_send_force(pl);
778 pl->s_info.unreliable_buffer_size = 0;
781 SDL_assert((pl->s_info.unreliable_buffer_size + len) <= MAX_PACKET_SIZE);
783 memcpy(pl->s_info.unreliable_buffer + pl->s_info.unreliable_buffer_size, data, len);
784 pl->s_info.unreliable_buffer_size += len;
787 void multi_io_send_to_all(ubyte *data, int length, net_player *ignore)
790 SDL_assert(MULTIPLAYER_MASTER);
792 // need to check for i > 1, hmmm... and connected. I don't know.
793 for (i = 0; i < MAX_PLAYERS; i++ ) {
794 if ( !MULTI_CONNECTED(Net_players[i]) || (Net_player == &Net_players[i])){
798 // maybe ignore a player
799 if((ignore != NULL) && (&Net_players[i] == ignore)){
803 // ingame joiners not waiting to select a ship doesn't get any packets
804 if ( (Net_players[i].flags & NETINFO_FLAG_INGAME_JOIN) && !(Net_players[i].flags & INGAME_JOIN_FLAG_PICK_SHIP) ){
809 multi_io_send(&Net_players[i], data, length);
813 void multi_io_send_force(net_player *pl)
816 if((pl == NULL) || (NET_PLAYER_NUM(pl) >= MAX_PLAYERS)){
820 // don't do it for single player
821 if(!(Game_mode & GM_MULTIPLAYER)){
825 // send everything in
826 if (MULTIPLAYER_MASTER) {
827 psnet_send(&pl->p_info.addr, pl->s_info.unreliable_buffer, pl->s_info.unreliable_buffer_size, NET_PLAYER_NUM(pl));
829 // add the bytes sent to this player
830 pl->sv_bytes_sent += pl->s_info.unreliable_buffer_size;
832 psnet_send(&Netgame.server_addr, pl->s_info.unreliable_buffer, pl->s_info.unreliable_buffer_size, NET_PLAYER_NUM(pl));
834 pl->s_info.unreliable_buffer_size = 0;
837 // send the data packet to all players via their reliable sockets
838 void multi_io_send_reliable(net_player *pl, ubyte *data, int len)
841 if((pl == NULL) || (NET_PLAYER_NUM(pl) >= MAX_PLAYERS)){
845 // don't do it for single player
846 if(!(Game_mode & GM_MULTIPLAYER)){
851 if(MULTIPLAYER_CLIENT){
852 // SDL_assert(pl == Net_player);
853 if(pl != Net_player){
857 // SDL_assert(pl != Net_player);
858 if(pl == Net_player){
863 // If this packet will push the buffer over MAX_PACKET_SIZE, send the current send_buffer
864 if ((pl->s_info.reliable_buffer_size + len) > MAX_PACKET_SIZE) {
865 multi_io_send_reliable_force(pl);
866 pl->s_info.reliable_buffer_size = 0;
869 SDL_assert((pl->s_info.reliable_buffer_size + len) <= MAX_PACKET_SIZE);
871 memcpy(pl->s_info.reliable_buffer + pl->s_info.reliable_buffer_size, data, len);
872 pl->s_info.reliable_buffer_size += len;
875 void multi_io_send_to_all_reliable(ubyte* data, int length, net_player *ignore)
878 SDL_assert(MULTIPLAYER_MASTER);
880 // need to check for i > 1, hmmm... and connected. I don't know.
881 for (i = 0; i < MAX_PLAYERS; i++ ) {
882 if ( !MULTI_CONNECTED(Net_players[i]) || (Net_player == &Net_players[i])){
886 // maybe ignore a player
887 if((ignore != NULL) && (&Net_players[i] == ignore)){
891 // ingame joiners not waiting to select a ship doesn't get any packets
892 if ( (Net_players[i].flags & NETINFO_FLAG_INGAME_JOIN) && !(Net_players[i].flags & INGAME_JOIN_FLAG_PICK_SHIP) ){
897 multi_io_send_reliable(&Net_players[i], data, length);
901 void multi_io_send_reliable_force(net_player *pl)
904 if((pl == NULL) || (NET_PLAYER_NUM(pl) >= MAX_PLAYERS)){
908 // don't do it for single player
909 if(!(Game_mode & GM_MULTIPLAYER)){
913 // send everything in
914 if(MULTIPLAYER_MASTER) {
915 psnet_rel_send(pl->reliable_socket, pl->s_info.reliable_buffer, pl->s_info.reliable_buffer_size, NET_PLAYER_NUM(pl));
916 } else if(Net_player != NULL){
917 psnet_rel_send(Net_player->reliable_socket, pl->s_info.reliable_buffer, pl->s_info.reliable_buffer_size, NET_PLAYER_NUM(pl));
919 pl->s_info.reliable_buffer_size = 0;
922 // send all buffered packets
923 void multi_io_send_buffered_packets()
927 // don't do it for single player
928 if(!(Game_mode & GM_MULTIPLAYER)){
933 if(MULTIPLAYER_MASTER){
934 for(idx=0; idx<MAX_PLAYERS; idx++){
935 if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx])){
936 // force unreliable data
937 if(Net_players[idx].s_info.unreliable_buffer_size > 0){
938 multi_io_send_force(&Net_players[idx]);
939 Net_players[idx].s_info.unreliable_buffer_size = 0;
942 // force reliable data
943 if(Net_players[idx].s_info.reliable_buffer_size > 0){
944 multi_io_send_reliable_force(&Net_players[idx]);
945 Net_players[idx].s_info.reliable_buffer_size = 0;
951 else if(Net_player != NULL){
952 // force unreliable data
953 if(Net_player->s_info.unreliable_buffer_size > 0){
954 multi_io_send_force(Net_player);
955 Net_player->s_info.unreliable_buffer_size = 0;
958 // force reliable data
959 if(Net_player->s_info.reliable_buffer_size > 0){
960 multi_io_send_reliable_force(Net_player);
961 Net_player->s_info.reliable_buffer_size = 0;
966 // send a general game chat packet (if msg_mode == MULTI_MSG_TARGET, need to pass in "to", if == MULTI_MSG_EXPR, need to pass in expr)
967 void send_game_chat_packet(net_player *from, const char *msg, int msg_mode, net_player *to, const char *expr, int server_msg)
969 ubyte data[MAX_PACKET_SIZE],mode;
972 BUILD_HEADER(GAME_CHAT);
975 ADD_SHORT(from->player_id);
977 // add the message mode and if in MSG_TARGET mode, add who the target is
979 mode = (ubyte)msg_mode;
982 case MULTI_MSG_TARGET:
983 SDL_assert(to != NULL);
984 ADD_SHORT(to->player_id);
987 SDL_assert(expr != NULL);
991 // add the message itself
994 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
996 // message all players
998 for(idx=0;idx<MAX_PLAYERS;idx++){
999 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (&Net_players[idx] != from)){
1000 multi_io_send_reliable(&Net_players[idx], data, packet_size);
1005 // message only friendly players
1006 case MULTI_MSG_FRIENDLY:
1007 for(idx=0;idx<MAX_PLAYERS;idx++){
1008 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (&Net_players[idx] != from) && (Net_players[idx].p_info.team == from->p_info.team)){
1009 multi_io_send_reliable(&Net_players[idx], data, packet_size);
1014 // message only hostile players
1015 case MULTI_MSG_HOSTILE:
1016 for(idx=0;idx<MAX_PLAYERS;idx++){
1017 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (&Net_players[idx] != from) && (Net_players[idx].p_info.team != from->p_info.team)){
1018 multi_io_send_reliable(&Net_players[idx], data, packet_size);
1023 // message the player's target
1024 case MULTI_MSG_TARGET:
1025 SDL_assert(to != NULL);
1026 if(MULTI_CONNECTED((*to)) && !MULTI_STANDALONE((*to))){
1027 multi_io_send_reliable(to, data, packet_size);
1031 // message all players who match the expression string
1032 case MULTI_MSG_EXPR:
1033 SDL_assert(expr != NULL);
1034 for(idx=0;idx<MAX_PLAYERS;idx++){
1035 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (&Net_players[idx] != from) && multi_msg_matches_expr(&Net_players[idx],expr) ){
1036 multi_io_send_reliable(&Net_players[idx], data, packet_size);
1042 // send to the server, who will take care of routing it
1044 multi_io_send_reliable(Net_player, data, packet_size);
1048 // process a general game chat packet, if we're the standalone we should rebroadcast
1049 void process_game_chat_packet( ubyte *data, header *hinfo )
1053 int color_index,player_index,to_player_index,should_display,server_msg;
1054 char msg[MULTI_MSG_MAX_TEXT_LEN+CALLSIGN_LEN+2];
1058 offset = HEADER_LENGTH;
1060 // get the id of the sender
1063 // determine if this is a server message
1064 GET_INT(server_msg);
1069 // if targeting a specific player, get the address
1072 case MULTI_MSG_TARGET:
1075 case MULTI_MSG_EXPR:
1079 // get the message itself
1083 // get the index of the sending player
1084 color_index = find_player_id(from);
1085 player_index = color_index;
1087 // if we couldn't find the player - bail
1088 if(player_index == -1){
1089 nprintf(("Network","Could not find player for processing game chat packet!\n"));
1095 // if we're the server, determine what to do with the packet here
1096 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
1097 // if he's targeting a specific player, find out who it is
1098 if(mode == MULTI_MSG_TARGET){
1099 to_player_index = find_player_id(to);
1101 to_player_index = -1;
1104 // if we couldn't find who sent the message or who should be getting the message, the bail
1105 if(((to_player_index == -1) && (mode == MULTI_MSG_TARGET)) || (player_index == -1)){
1109 // determine if _I_ should be seeing the text
1110 if(Game_mode & GM_STANDALONE_SERVER){
1113 // check against myself for several specific cases
1115 if((mode == MULTI_MSG_ALL) ||
1116 ((mode == MULTI_MSG_FRIENDLY) && (Net_player->p_info.team == Net_players[player_index].p_info.team)) ||
1117 ((mode == MULTI_MSG_HOSTILE) && (Net_player->p_info.team != Net_players[player_index].p_info.team)) ||
1118 ((mode == MULTI_MSG_TARGET) && (MY_NET_PLAYER_NUM == to_player_index)) ||
1119 ((mode == MULTI_MSG_EXPR) && multi_msg_matches_expr(Net_player,expr)) ){
1124 // if we're the server of a game, we need to rebroadcast to all other players
1126 // individual target mission
1127 case MULTI_MSG_TARGET:
1128 // if I was the inteneded target, or we couldn't find the intended target, don't rebroadcast
1129 if(to_player_index != MY_NET_PLAYER_NUM){
1130 send_game_chat_packet(&Net_players[player_index], msg, (int)mode, &Net_players[to_player_index], NULL, server_msg);
1134 case MULTI_MSG_EXPR:
1135 send_game_chat_packet(&Net_players[player_index], msg, (int)mode, NULL, expr, server_msg);
1139 send_game_chat_packet(&Net_players[player_index], msg, (int)mode, NULL, NULL, server_msg);
1143 // if a client receives this packet, its always ok for him to display it
1148 // if we're not on a standalone
1150 if(server_msg == 2){
1153 multi_display_chat_msg(msg, player_index, !server_msg);
1158 // broadcast a hud message to all players
1159 void send_hud_msg_to_all( char* msg )
1161 ubyte data[MAX_PACKET_SIZE];
1164 // only the server should be sending this packet
1165 BUILD_HEADER(HUD_MSG);
1169 multi_io_send_to_all( data, packet_size );
1172 // process an incoming hud message packet
1173 void process_hud_message(ubyte* data, header* hinfo)
1176 char msg_buffer[255];
1178 offset = HEADER_LENGTH;
1180 GET_STRING(msg_buffer);
1183 // this is the only safe place to do this since only in the mission is the HUD guaranteed to be inited
1184 if(Game_mode & GM_IN_MISSION){
1185 HUD_printf(msg_buffer);
1189 // send a join packet request to the specified address (should be a server)
1190 void send_join_packet(net_addr* addr,join_request *jr)
1192 ubyte data[MAX_PACKET_SIZE];
1195 // build the header and add the request
1198 add_join_request(data, &packet_size, jr);
1200 psnet_send(addr, data, packet_size);
1203 // process an incoming join request packet
1204 void process_join_packet(ubyte* data, header* hinfo)
1209 int host_restr_mode;
1210 // int team0_avail,team1_avail;
1211 char join_string[255];
1214 // only the server of the game should ever receive this packet
1215 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) )
1218 offset = HEADER_LENGTH;
1220 // read in the request info
1221 memset(&jr,0,sizeof(join_request));
1224 jr.tracker_id = INTEL_INT(jr.tracker_id);
1228 // fill in the address information of where this came from
1229 fill_net_addr(&addr, hinfo->addr, hinfo->port);
1231 // determine if we should accept this guy, or return a reason we should reject him
1232 // see the DENY_* codes in multi.h
1233 ret_code = multi_eval_join_request(&jr,&addr);
1235 // evaluate the return code
1237 // he should be accepted
1241 // we have to query the host because this is a restricted game
1242 case JOIN_QUERY_RESTRICTED :
1243 if(!(Game_mode & GM_STANDALONE_SERVER)){
1244 // notify the host of the event
1245 snd_play(&Snds[SND_CUE_VOICE]);
1248 // set the query timestamp
1249 Multi_restr_query_timestamp = timestamp(MULTI_QUERY_RESTR_STAMP);
1250 Netgame.flags |= NG_FLAG_INGAME_JOINING;
1252 // determine what mode we're in
1253 // host_restr_mode = -1;
1254 memset(join_string,0,255);
1255 // if(Netgame.type == NG_TYPE_TEAM){
1256 // multi_player_ships_available(&team0_avail,&team1_avail);
1258 // if(team0_avail && team1_avail){
1259 // host_restr_mode = MULTI_JOIN_RESTR_MODE_4;
1260 // sprintf(join_string,"Player %s has tried to join. Accept on team 1 or 2 ?",jr.callsign);
1261 // } else if(team0_avail && !team1_avail){
1262 // host_restr_mode = MULTI_JOIN_RESTR_MODE_2;
1263 // sprintf(join_string,"Player %s has tried to join team 0, accept y/n ? ?",jr.callsign);
1264 // } else if(!team0_avail && team1_avail){
1265 // host_restr_mode = MULTI_JOIN_RESTR_MODE_3;
1266 // sprintf(join_string,"Player %s has tried to join team 1, accept y/n ?",jr.callsign);
1268 // } else if(Netgame.mode == NG_MODE_RESTRICTED){
1269 host_restr_mode = MULTI_JOIN_RESTR_MODE_1;
1270 SDL_snprintf(join_string,SDL_arraysize(join_string),XSTR("Player %s has tried to join, accept y/n ?",715),jr.callsign);
1272 SDL_assert(host_restr_mode != -1);
1274 // store the request info
1275 memcpy(&Multi_restr_join_request,&jr,sizeof(join_request));
1276 memcpy(&Multi_restr_addr,&addr,sizeof(net_addr));
1277 Multi_join_restr_mode = host_restr_mode;
1279 // if i'm the standalone server, I need to send a query to the host
1280 if(Game_mode & GM_STANDALONE_SERVER){
1281 send_host_restr_packet(jr.callsign,0,Multi_join_restr_mode);
1283 HUD_printf(join_string);
1287 ml_printf(NOX("Receive restricted join request from %s"), jr.callsign);
1291 // he'e being denied for some reason
1293 // send him the reason he is being denied
1294 send_deny_packet(&addr,ret_code);
1298 // process the rest of the request
1299 multi_process_valid_join_request(&jr,&addr);
1302 // send a notification that a new player has joined the game (if target != NULL, broadcast the packet)
1303 void send_new_player_packet(int new_player_num,net_player *target)
1305 ubyte data[MAX_PACKET_SIZE], val;
1306 int packet_size = 0;
1308 BUILD_HEADER( NOTIFY_NEW_PLAYER );
1310 // add the new player's info
1311 ADD_INT(new_player_num);
1312 // ADD_DATA(Net_players[new_player_num].p_info.addr);
1314 add_net_addr(data, packet_size, &Net_players[new_player_num].p_info.addr);
1316 ADD_SHORT(Net_players[new_player_num].player_id);
1317 ADD_INT(Net_players[new_player_num].flags);
1318 ADD_STRING(Net_players[new_player_num].player->callsign);
1319 ADD_STRING(Net_players[new_player_num].player->image_filename);
1320 ADD_STRING(Net_players[new_player_num].player->squad_filename);
1321 ADD_STRING(Net_players[new_player_num].p_info.pxo_squad_name);
1323 val = (ubyte)Net_players[new_player_num].p_info.team;
1326 // broadcast the data
1328 multi_io_send_reliable(target, data, packet_size);
1330 multi_io_send_to_all_reliable(data, packet_size);
1334 // process a notification for a new player who has joined the game
1335 void process_new_player_packet(ubyte* data, header* hinfo)
1337 int already_in_game = 0;
1338 int offset, new_player_num,player_num,new_flags;
1340 char new_player_name[CALLSIGN_LEN+2] = "";
1341 char new_player_image[MAX_FILENAME_LEN+1] = "";
1342 char new_player_squad[MAX_FILENAME_LEN+1] = "";
1343 char new_player_pxo_squad[LOGIN_LEN+1] = "";
1344 char notify_string[256];
1348 offset = HEADER_LENGTH;
1350 // get the new players information
1351 GET_INT(new_player_num);
1352 memset(&new_addr, 0, sizeof(net_addr));
1353 get_net_addr(data, offset, new_addr);
1357 GET_STRING(new_player_name);
1358 GET_STRING(new_player_image);
1359 GET_STRING(new_player_squad);
1360 GET_STRING(new_player_pxo_squad);
1364 player_num = multi_find_open_player_slot();
1365 SDL_assert(player_num != -1);
1367 // note that this new code does not check for duplicate IPs. It merely checks to see if
1368 // the slot referenced by new_player_num is already occupied by a connected player
1369 if(MULTI_CONNECTED(Net_players[new_player_num])){
1373 // if he's not alreayd in the game for one reason or another
1374 if ( !already_in_game ) {
1375 if ( Game_mode & GM_IN_MISSION ){
1376 HUD_sourced_printf(HUD_SOURCE_COMPUTER, XSTR("%s has entered the game\n",716), new_player_name);
1379 // create the player
1380 if(new_flags & NETINFO_FLAG_OBSERVER){
1381 multi_obs_create_player(new_player_num,new_player_name,&new_addr,&Players[player_num]);
1382 Net_players[new_player_num].flags |= new_flags;
1384 multi_create_player( new_player_num, &Players[player_num],new_player_name, &new_addr, -1, new_id );
1385 Net_players[new_player_num].flags |= new_flags;
1388 // copy in the filename
1389 if(strlen(new_player_image) > 0){
1390 SDL_strlcpy(Net_players[new_player_num].player->image_filename, new_player_image, MAX_FILENAME_LEN);
1392 SDL_strlcpy(Net_players[new_player_num].player->image_filename, "", MAX_FILENAME_LEN);
1394 // copy his pilot squad filename
1395 Net_players[new_player_num].player->insignia_texture = -1;
1396 player_set_squad_bitmap(Net_players[new_player_num].player, new_player_squad);
1398 // copy in his pxo squad name
1399 SDL_strlcpy(Net_players[new_player_num].p_info.pxo_squad_name, new_player_pxo_squad, LOGIN_LEN);
1401 // since we just created the player, set the last_heard_time here.
1402 Net_players[new_player_num].last_heard_time = timer_get_fixed_seconds();
1404 Net_players[new_player_num].p_info.team = team;
1406 Net_players[new_player_num].player_id = new_id;
1408 // zero out this players ping
1409 multi_ping_reset(&Net_players[new_player_num].s_info.ping);
1411 // add a chat message
1412 if(Net_players[new_player_num].player->callsign[0]){
1413 SDL_snprintf(notify_string,SDL_arraysize(notify_string),XSTR("<%s has joined>",717),Net_players[new_player_num].player->callsign);
1414 multi_display_chat_msg(notify_string,0,0);
1419 ml_printf(NOX("Received notification of new player %s"), Net_players[new_player_num].player->callsign);
1421 // let the current ui screen know someone joined
1422 switch(gameseq_get_state()){
1423 case GS_STATE_MULTI_HOST_SETUP :
1424 multi_create_handle_join(&Net_players[new_player_num]);
1426 case GS_STATE_MULTI_CLIENT_SETUP :
1427 multi_jw_handle_join(&Net_players[new_player_num]);
1432 #define PLAYER_DATA_SLOP 100
1434 void send_accept_player_data( net_player *npp, int is_ingame )
1438 ubyte data[MAX_PACKET_SIZE], stop;
1440 BUILD_HEADER(ACCEPT_PLAYER_DATA);
1442 // add in the netplayer data for all players
1444 for (i=0; i<MAX_PLAYERS; i++) {
1445 // skip non connected players
1446 if ( !MULTI_CONNECTED(Net_players[i]) ){
1450 // skip this new player's entry
1451 if ( npp->player_id == Net_players[i].player_id ){
1455 // add the stop byte
1458 // add the player's number
1461 // add the player's address
1462 // ADD_DATA(Net_players[i].p_info.addr);
1463 add_net_addr(data, packet_size, &Net_players[i].p_info.addr);
1466 ADD_SHORT(Net_players[i].player_id);
1469 ADD_STRING(Net_players[i].player->callsign);
1471 // add his image filename
1472 ADD_STRING(Net_players[i].player->image_filename);
1474 // add his squad filename
1475 ADD_STRING(Net_players[i].player->squad_filename);
1477 // add his PXO squad name
1478 ADD_STRING(Net_players[i].p_info.pxo_squad_name);
1481 ADD_INT(Net_players[i].flags);
1483 // add his object's net sig
1485 ADD_USHORT( Objects[Net_players[i].player->objnum].net_signature );
1488 if ( (packet_size + PLAYER_DATA_SLOP) > MAX_PACKET_SIZE ) {
1489 stop = APD_END_PACKET;
1491 multi_io_send_reliable( npp, data, packet_size );
1492 BUILD_HEADER(ACCEPT_PLAYER_DATA);
1498 // add the stop byte
1499 stop = APD_END_DATA;
1501 multi_io_send_reliable(npp, data, packet_size);
1504 // send an accept packet to a client in response to a request to join the game
1505 void send_accept_packet(int new_player_num, int code, int ingame_join_team)
1507 int packet_size = 0, i;
1508 ubyte data[MAX_PACKET_SIZE],val;
1509 char notify_string[256];
1512 SDL_assert(new_player_num >= 0);
1514 // setup his "reliable" socket
1515 Net_players[new_player_num].last_heard_time = timer_get_fixed_seconds();
1517 // build the packet header
1518 BUILD_HEADER(ACCEPT);
1520 // add the accept code
1523 // add code specific accept data
1524 if (code & ACCEPT_INGAME) {
1525 // the game filename
1526 ADD_STRING(Game_current_mission_filename);
1528 // if he is joining on a specific team, mark it here
1529 if(ingame_join_team != -1){
1532 val = (ubyte)ingame_join_team;
1540 if (code & ACCEPT_OBSERVER) {
1541 SDL_assert(!(code & (ACCEPT_CLIENT | ACCEPT_HOST)));
1544 if (code & ACCEPT_HOST) {
1545 SDL_assert(!(code & (ACCEPT_CLIENT | ACCEPT_OBSERVER | ACCEPT_INGAME)));
1548 if (code & ACCEPT_CLIENT) {
1549 SDL_assert(!(code & (ACCEPT_HOST | ACCEPT_OBSERVER | ACCEPT_INGAME)));
1552 // add the current skill level setting on the host
1553 ADD_INT(Game_skill_level);
1555 // add this guys player num
1556 ADD_INT(new_player_num);
1558 // add his player id
1559 ADD_SHORT(Net_players[new_player_num].player_id);
1561 // add netgame type flags
1562 ADD_INT(Netgame.type_flags);
1565 // char buffer[100];
1566 // nprintf(("Network", "About to send accept packet to %s on port %d\n", get_text_address(buffer, addr->addr), addr->port ));
1569 // actually send the packet
1570 psnet_send(&Net_players[new_player_num].p_info.addr, data, packet_size);
1572 // if he's not an observer, inform all the other players in the game about him
1573 // inform the other players in the game about this new player
1574 for (i=0; i<MAX_PLAYERS; i++) {
1575 // skip unconnected players as well as this new guy himself
1576 if ( !MULTI_CONNECTED(Net_players[i]) || psnet_same(&Net_players[new_player_num].p_info.addr, &(Net_players[i].p_info.addr)) || (Net_player == &Net_players[i])) {
1580 // send the new packet
1581 send_new_player_packet(new_player_num,&Net_players[i]);
1584 // add a chat message
1585 if(Net_players[new_player_num].player->callsign[0]){
1586 SDL_snprintf(notify_string,SDL_arraysize(notify_string),XSTR("<%s has joined>",717), Net_players[new_player_num].player->callsign);
1587 multi_display_chat_msg(notify_string, 0, 0);
1590 // handle any team vs. team details
1591 if (!(code & ACCEPT_OBSERVER)) {
1592 multi_team_handle_join(&Net_players[new_player_num]);
1596 if(Net_players[new_player_num].tracker_player_id >= 0){
1597 ml_printf(NOX("Server accepted %s (tracker id %d) as new client"), Net_players[new_player_num].player->callsign, Net_players[new_player_num].tracker_player_id);
1599 ml_printf(NOX("Server accepted %s as new client"), Net_players[new_player_num].player->callsign);
1604 // process the player data from the server
1605 void process_accept_player_data( ubyte *data, header *hinfo )
1607 int offset, player_num, player_slot_num, new_flags;
1608 char name[CALLSIGN_LEN + 1] = "";
1609 char image_name[MAX_FILENAME_LEN + 1] = "";
1610 char squad_name[MAX_FILENAME_LEN + 1] = "";
1611 char pxo_squad_name[LOGIN_LEN+1] = "";
1615 ushort ig_signature;
1617 offset = HEADER_LENGTH;
1620 while ( stop == APD_NEXT ) {
1621 player_slot_num = multi_find_open_player_slot();
1622 SDL_assert(player_slot_num != -1);
1624 // get the player's number
1625 GET_INT(player_num);
1627 // add the player's address
1628 memset(&addr, 0, sizeof(net_addr));
1629 get_net_addr(data, offset, addr);
1631 // get the player's id#
1632 GET_SHORT(player_id);
1637 // add his image filename
1638 GET_STRING(image_name);
1640 // get his squad logo filename
1641 GET_STRING(squad_name);
1643 // get his PXO squad name
1644 GET_STRING(pxo_squad_name);
1649 if (Net_players[player_num].flags & NETINFO_FLAG_OBSERVER) {
1650 if (!multi_obs_create_player(player_num, name, &addr, &Players[player_slot_num])) {
1655 // the error handling here is less than stellar. We should probably put up a popup and go
1656 // back to the main menu. But then again, this should never ever happen!
1657 if ( !multi_create_player(player_num, &Players[player_slot_num],name, &addr, -1, player_id) ) {
1662 // copy his image filename
1663 SDL_strlcpy(Net_players[player_num].player->image_filename, image_name, MAX_FILENAME_LEN);
1665 // copy his pilot squad filename
1666 Net_players[player_num].player->insignia_texture = -1;
1667 player_set_squad_bitmap(Net_players[player_num].player, squad_name);
1669 // copy his pxo squad name
1670 SDL_strlcpy(Net_players[player_num].p_info.pxo_squad_name, pxo_squad_name, LOGIN_LEN);
1672 // set his player id#
1673 Net_players[player_num].player_id = player_id;
1675 // mark him as being connected
1676 Net_players[player_num].flags |= NETINFO_FLAG_CONNECTED;
1677 Net_players[player_num].flags |= new_flags;
1679 // set the server pointer
1680 if ( Net_players[player_num].flags & NETINFO_FLAG_AM_MASTER ) {
1681 Netgame.server = &Net_players[player_num];
1682 Netgame.server->last_heard_time = timer_get_fixed_seconds();
1684 // also - always set the server address to be where this data came from, NOT from
1685 // the data in the packet
1686 fill_net_addr(&Net_players[player_num].p_info.addr, hinfo->addr, hinfo->port);
1689 // set the host pointer
1690 if ( Net_players[player_num].flags & NETINFO_FLAG_GAME_HOST ) {
1691 Netgame.host = &Net_players[player_num];
1694 // read in the player's object net signature and store as his objnum for now
1695 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_INGAME ) {
1696 GET_USHORT( ig_signature );
1697 Net_players[player_num].player->objnum = ig_signature;
1700 // get the stop byte
1705 if ( stop == APD_END_DATA ) {
1706 // if joining a game automatically, set the connect address to NULl so we don't try and
1707 // do this next time we enter a game
1708 if (Cmdline_connect_addr != NULL) {
1709 Cmdline_connect_addr = NULL;
1712 // send my stats to the server if I'm not in observer mode
1713 if (!(Net_player->flags & NETINFO_FLAG_ACCEPT_OBSERVER)) {
1714 send_player_stats_block_packet(Net_player, STATS_ALLTIME);
1717 // if i'm being accepted as a host, then move into the host setup state
1718 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_HOST) {
1719 // set my permission bits
1720 Net_player->flags |= NETINFO_FLAG_GAME_HOST;
1721 Net_player->state = NETPLAYER_STATE_STD_HOST_SETUP;
1723 gameseq_post_event(GS_EVENT_MULTI_START_GAME);
1726 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_OBSERVER) {
1727 Net_player->flags |= NETINFO_FLAG_OBSERVER;
1729 // since observers can join 1 of 2 ways, only do this if we're not doing an ingame observer join
1730 if ( !(Net_player->flags & NETINFO_FLAG_ACCEPT_INGAME) ) {
1731 gameseq_post_event(GS_EVENT_MULTI_CLIENT_SETUP);
1735 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_CLIENT) {
1736 gameseq_post_event(GS_EVENT_MULTI_CLIENT_SETUP);
1739 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_INGAME) {
1740 // flag myself as being an ingame joiner
1741 Net_player->flags |= NETINFO_FLAG_INGAME_JOIN;
1743 // move myself into the ingame join mission sync state
1744 Multi_sync_mode = MULTI_SYNC_INGAME;
1745 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
1748 // update my options on the server
1749 multi_options_update_local();
1751 // if we're in PXO mode, mark it down in our player struct
1752 if(MULTI_IS_TRACKER_GAME){
1753 Player->flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
1754 Player->save_flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
1759 // process an accept packet from the server
1760 extern int Select_default_ship;
1762 void process_accept_packet(ubyte* data, header* hinfo)
1764 int code, my_player_num, offset;
1768 // get the accept code
1769 offset = HEADER_LENGTH;
1773 // read in the accept code specific data
1775 if (code & ACCEPT_INGAME) {
1776 // the game filename
1777 GET_STRING(Game_current_mission_filename);
1778 Select_default_ship = 0;
1780 // determine if I'm being placed on a team
1787 if (code & ACCEPT_OBSERVER) {
1788 SDL_assert(!(code & (ACCEPT_CLIENT | ACCEPT_HOST)));
1791 if (code & ACCEPT_HOST) {
1792 SDL_assert(!(code & (ACCEPT_CLIENT | ACCEPT_OBSERVER | ACCEPT_INGAME)));
1795 if (code & ACCEPT_CLIENT) {
1796 SDL_assert(!(code & (ACCEPT_HOST | ACCEPT_OBSERVER | ACCEPT_INGAME)));
1799 // fill in the netgame server address
1800 fill_net_addr( &Netgame.server_addr, hinfo->addr, hinfo->port );
1802 // get the skill level setting
1803 GET_INT(Game_skill_level);
1805 // get my netplayer number
1806 GET_INT(my_player_num);
1809 GET_SHORT(player_id);
1811 // get netgame type flags
1812 GET_INT(Netgame.type_flags);
1814 // setup the Net_players structure for myself first
1815 Net_player = &Net_players[my_player_num];
1816 Net_player->flags = 0;
1817 Net_player->tracker_player_id = Multi_tracker_id;
1818 Net_player->player_id = player_id;
1819 Net_player->s_info.xfer_handle = -1;
1820 // stuff_netplayer_info( Net_player, &Psnet_my_addr, Ships[Objects[Player->objnum].instance].ship_info_index, Player );
1821 stuff_netplayer_info( Net_player, &Psnet_my_addr, 0, Player );
1822 multi_options_local_load(&Net_player->p_info.options, Net_player);
1824 Net_player->p_info.team = team;
1827 // determine if I have a CD
1829 Net_player->flags |= NETINFO_FLAG_HAS_CD;
1832 // set accept code in netplayer for this guy
1833 if ( code & ACCEPT_INGAME ){
1834 Net_player->flags |= NETINFO_FLAG_ACCEPT_INGAME;
1836 if ( code & ACCEPT_OBSERVER ){
1837 Net_player->flags |= NETINFO_FLAG_ACCEPT_OBSERVER;
1839 if ( code & ACCEPT_HOST ){
1840 Net_player->flags |= NETINFO_FLAG_ACCEPT_HOST;
1842 if ( code & ACCEPT_CLIENT ){
1843 Net_player->flags |= NETINFO_FLAG_ACCEPT_CLIENT;
1846 // if I have hacked data
1847 if(game_hacked_data()){
1848 Net_player->flags |= NETINFO_FLAG_HAXOR;
1851 // if we're supposed to flush our local data cache, do so now
1852 if(Net_player->p_info.options.flags & MLO_FLAG_FLUSH_CACHE){
1853 multi_flush_multidata_cache();
1856 Net_player->sv_bytes_sent = 0;
1857 Net_player->sv_last_pl = -1;
1858 Net_player->cl_bytes_recvd = 0;
1859 Net_player->cl_last_pl = -1;
1861 // intiialize endgame stuff
1862 multi_endgame_init();
1866 // make a call to psnet to initialize and try to connect with the server.
1867 psnet_rel_connect_to_server( &Net_player->reliable_socket, &Netgame.server_addr );
1868 if ( Net_player->reliable_socket == INVALID_SOCKET ) {
1869 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_CONNECT_FAIL);
1873 // send a notice that the player at net_addr is leaving (if target is NULL, the broadcast the packet)
1874 void send_leave_game_packet(short player_id, int kicked_reason, net_player *target)
1876 ubyte data[MAX_PACKET_SIZE];
1878 int packet_size = 0;
1880 BUILD_HEADER(LEAVE_GAME);
1882 // add a flag indicating whether he was kicked or not
1883 val = (char)kicked_reason;
1886 if (player_id < 0) {
1887 ADD_SHORT(Net_player->player_id);
1889 // inform the host that we are leaving the game
1890 if (Net_player->flags & NETINFO_FLAG_AM_MASTER) {
1891 multi_io_send_to_all_reliable(data, packet_size);
1893 multi_io_send_reliable(Net_player, data, packet_size);
1896 // this is the case where to server is tossing a player (or indicating a respawned player has quit or become an observer)
1897 // so he has to tell everyone that this guy left
1899 nprintf(("Network","Sending a leave game packet to all players (server)\n"));
1901 // a couple of important checks
1902 SDL_assert(player_id != Net_player->player_id);
1903 SDL_assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
1905 // add the id of the guy to be kicked
1906 ADD_SHORT(player_id);
1908 // broadcast to everyone
1909 if (target == NULL) {
1910 multi_io_send_to_all_reliable(data, packet_size);
1912 multi_io_send_reliable(target, data, packet_size);
1917 // process a notification the a player has left the game
1918 void process_leave_game_packet(ubyte* data, header* hinfo)
1926 offset = HEADER_LENGTH;
1928 // get whether he was kicked
1929 GET_DATA(kicked_reason);
1931 // get the address of the guy who is to leave
1932 GET_SHORT(deader_id);
1935 // determine who is dropping and printf out a notification
1936 player_num = find_player_id(deader_id);
1937 if (player_num == -1) {
1938 nprintf(("Network", "Received leave game packet for unknown player, ignoring\n"));
1942 nprintf(("Network", "Received a leave game notice for %s\n", Net_players[player_num].player->callsign));
1945 // a hook to display that a player was kicked
1946 if (kicked_reason >= 0){
1947 // if it was me that was kicked, leave the game
1948 if((Net_player != NULL) && (Net_player->player_id == deader_id)){
1951 switch(kicked_reason){
1952 case KICK_REASON_BAD_XFER:
1953 notify_code = MULTI_END_NOTIFY_KICKED_BAD_XFER;
1955 case KICK_REASON_CANT_XFER:
1956 notify_code = MULTI_END_NOTIFY_KICKED_CANT_XFER;
1958 case KICK_REASON_INGAME_ENDED:
1959 notify_code = MULTI_END_NOTIFY_KICKED_INGAME_ENDED;
1962 notify_code = MULTI_END_NOTIFY_KICKED;
1966 multi_quit_game(PROMPT_NONE, notify_code);
1969 // otherwise indicate someone was kicked
1971 nprintf(("Network","%s was kicked\n",Net_players[player_num].player->callsign));
1973 // display the result
1974 memset(str, 0, 512);
1975 multi_kick_get_text(&Net_players[player_num], kicked_reason, str, SDL_arraysize(str));
1976 multi_display_chat_msg(str, player_num, 0);
1980 // first of all, if we're the master, we should be rebroadcasting this packet
1981 if (Net_player->flags & NETINFO_FLAG_AM_MASTER) {
1984 SDL_snprintf(msg, SDL_arraysize(msg), XSTR("%s has left the game",719), Net_players[player_num].player->callsign );
1986 if (!(Game_mode & GM_STANDALONE_SERVER)){
1987 HUD_sourced_printf(HUD_SOURCE_HIDDEN, msg);
1990 send_hud_msg_to_all(msg);
1991 multi_io_send_to_all_reliable(data, offset);
1994 // leave the game if the host and/or master has dropped
1996 if (((Net_players[player_num].flags & NETINFO_FLAG_AM_MASTER) || (Net_players[player_num].flags & NETINFO_FLAG_GAME_HOST)) ) {
1997 nprintf(("Network","Host and/or server has left the game - aborting...\n"));
2000 ml_string(NOX("Host and/or server has left the game"));
2002 // if the host leaves in the debriefing state, we should still wait until the player selects accept before we quit
2003 if (gameseq_get_state() != GS_STATE_DEBRIEF) {
2004 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_SERVER_LEFT);
2007 delete_player(player_num);
2010 delete_player(player_num);
2012 // OSAPI GUI stuff (if standalone)
2013 if (Game_mode & GM_STANDALONE_SERVER) {
2014 // returns true if we should reset the standalone
2015 if (std_remove_player(&Net_players[player_num])) {
2016 nprintf(("Network", "Should reset!!\n"));
2020 // update these gui vals
2021 std_connect_set_host_connect_status();
2022 std_connect_set_connect_count();
2026 // send information about this currently active game to the specified address
2027 void send_game_active_packet(net_addr* addr)
2031 ubyte data[MAX_PACKET_SIZE],val;
2033 // build the header and add the data
2034 BUILD_HEADER(GAME_ACTIVE);
2036 // add the server version and compatible version #
2037 val = MULTI_FS_SERVER_VERSION;
2039 val = MULTI_FS_SERVER_COMPATIBLE_VERSION;
2042 ADD_STRING(Netgame.name);
2043 ADD_STRING(Netgame.mission_name);
2044 ADD_STRING(Netgame.title);
2045 val = (ubyte)multi_num_players();
2048 // add the proper flags
2050 if((Netgame.mode == NG_MODE_PASSWORD) || ((Game_mode & GM_STANDALONE_SERVER) && (multi_num_players() == 0) && (std_is_host_passwd()))){
2051 flags |= AG_FLAG_PASSWD;
2054 // proper netgame type flags
2055 if(Netgame.type_flags & NG_TYPE_TEAM){
2056 flags |= AG_FLAG_TEAMS;
2057 } else if(Netgame.type_flags & NG_TYPE_DOGFIGHT){
2058 flags |= AG_FLAG_DOGFIGHT;
2060 flags |= AG_FLAG_COOP;
2063 // proper netgame state flags
2064 switch(Netgame.game_state){
2065 case NETGAME_STATE_FORMING:
2066 flags |= AG_FLAG_FORMING;
2069 case NETGAME_STATE_BRIEFING:
2070 case NETGAME_STATE_MISSION_SYNC:
2071 case NETGAME_STATE_HOST_SETUP:
2072 flags |= AG_FLAG_BRIEFING;
2075 case NETGAME_STATE_IN_MISSION:
2076 flags |= AG_FLAG_IN_MISSION;
2079 case NETGAME_STATE_PAUSED:
2080 flags |= AG_FLAG_PAUSE;
2083 case NETGAME_STATE_ENDGAME:
2084 case NETGAME_STATE_DEBRIEF:
2085 flags |= AG_FLAG_DEBRIEF;
2089 // if this is a standalone
2090 if(Game_mode & GM_STANDALONE_SERVER){
2091 flags |= AG_FLAG_STANDALONE;
2094 // if we're in campaign mode
2095 if(Netgame.campaign_mode == MP_CAMPAIGN){
2096 flags |= AG_FLAG_CAMPAIGN;
2099 // add the data about the connection speed of the host machine
2100 SDL_assert( (Multi_connection_speed >= 0) && (Multi_connection_speed <= 4) );
2101 flags |= (Multi_connection_speed << AG_FLAG_CONNECTION_BIT);
2106 psnet_send(addr, data, packet_size);
2109 // process information about an active game
2110 void process_game_active_packet(ubyte* data, header* hinfo)
2115 int modes_compatible;
2117 fill_net_addr(&ag.server_addr, hinfo->addr, hinfo->port);
2119 // read this game into a temporary structure
2120 offset = HEADER_LENGTH;
2122 // get the server version and compatible version
2123 GET_DATA(ag.version);
2124 GET_DATA(ag.comp_version);
2126 GET_STRING(ag.name);
2127 GET_STRING(ag.mission_name);
2128 GET_STRING(ag.title);
2130 ag.num_players = val;
2131 GET_USHORT(ag.flags);
2135 modes_compatible = 1;
2137 if((ag.flags & AG_FLAG_TRACKER) && !Multi_options_g.pxo){
2138 modes_compatible = 0;
2140 if(!(ag.flags & AG_FLAG_TRACKER) && Multi_options_g.pxo){
2141 modes_compatible = 0;
2145 // if this is a compatible version, and our modes are compatible, register it
2146 if( (ag.version == MULTI_FS_SERVER_VERSION) && modes_compatible ){
2147 multi_update_active_games(&ag);
2151 // send_game_update_packet sends an updated Netgame structure to all players currently connected. The update
2152 // is used to change the current mission, current state, etc.
2153 void send_netgame_update_packet(net_player *pl)
2155 int packet_size = 0;
2157 ubyte data[MAX_PACKET_SIZE];
2159 BUILD_HEADER(GAME_UPDATE);
2161 // with new mission description field, this becomes way to large
2162 // so we must add every element piece by piece except the
2163 ADD_STRING(Netgame.name);
2164 ADD_STRING(Netgame.mission_name);
2165 ADD_STRING(Netgame.title);
2166 ADD_STRING(Netgame.campaign_name);
2167 ADD_INT(Netgame.campaign_mode);
2168 ADD_INT(Netgame.max_players);
2169 ADD_INT(Netgame.security);
2170 ADD_UINT(Netgame.respawn);
2171 ADD_INT(Netgame.flags);
2172 ADD_INT(Netgame.type_flags);
2173 ADD_INT(Netgame.version_info);
2174 ADD_DATA(Netgame.debug_flags);
2176 // only the server should ever send the netgame state (standalone situation)
2177 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2178 ADD_INT(Netgame.game_state);
2181 // if we're the host on a standalone, send to the standalone and let him rebroadcast
2182 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2184 multi_io_send_to_all_reliable(data, packet_size);
2186 for(idx=0; idx<MAX_PLAYERS; idx++){
2187 if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx])){
2188 send_netgame_descript_packet(&Net_players[idx].p_info.addr, 1);
2192 multi_io_send_reliable(pl, data, packet_size);
2193 send_netgame_descript_packet( &pl->p_info.addr , 1 );
2196 SDL_assert( pl == NULL ); // I don't think that a host in a standalone game would get here.
2197 multi_io_send_reliable(Net_player, data, packet_size);
2200 // host should always send a netgame options update as well
2201 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
2202 multi_options_update_netgame();
2206 // process information about the netgame sent from the server/host
2207 void process_netgame_update_packet( ubyte *data, header *hinfo )
2209 int offset;//,old_flags;
2212 SDL_assert(!(Game_mode & GM_STANDALONE_SERVER));
2213 SDL_assert(!(Net_player->flags & NETINFO_FLAG_AM_MASTER));
2215 // read in the netgame information
2216 offset = HEADER_LENGTH;
2217 GET_STRING(Netgame.name);
2218 GET_STRING(Netgame.mission_name);
2219 GET_STRING(Netgame.title);
2220 GET_STRING(Netgame.campaign_name);
2221 GET_INT(Netgame.campaign_mode);
2222 GET_INT(Netgame.max_players); // ignore on the standalone, who keeps track of this himself
2223 GET_INT(Netgame.security);
2224 GET_UINT(Netgame.respawn);
2226 // be sure not to blast the quitting flag because of the "one frame extra" problem
2227 // old_flags = Netgame.flags;
2228 GET_INT(Netgame.flags);
2229 GET_INT(Netgame.type_flags);
2230 GET_INT(Netgame.version_info);
2231 GET_DATA(Netgame.debug_flags);
2238 // now compare the passed in game state to our current known state. If it has changed, then maybe
2239 // do something interesting.
2240 // move from the forming or debriefing state to the mission sync state
2241 if ( ng_state == NETGAME_STATE_MISSION_SYNC ){
2242 // if coming from the forming state
2243 if( (Netgame.game_state == NETGAME_STATE_FORMING) ||
2244 ((Netgame.game_state != NETGAME_STATE_FORMING) && ((gameseq_get_state() == GS_STATE_MULTI_HOST_SETUP) || (gameseq_get_state() == GS_STATE_MULTI_CLIENT_SETUP))) ){
2245 // do any special processing for forced state transitions
2246 multi_handle_state_special();
2248 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
2249 SDL_strlcpy( Game_current_mission_filename, Netgame.mission_name, MAX_FILENAME_LEN );
2250 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
2252 // if coming from the debriefing state
2253 else if( (Netgame.game_state == NETGAME_STATE_DEBRIEF) ||
2254 ((Netgame.game_state != NETGAME_STATE_DEBRIEF) && ((gameseq_get_state() == GS_STATE_DEBRIEF) || (gameseq_get_state() == GS_STATE_MULTI_DOGFIGHT_DEBRIEF)) ) ){
2256 // do any special processing for forced state transitions
2257 multi_handle_state_special();
2259 multi_flush_mission_stuff();
2261 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
2262 SDL_strlcpy( Game_current_mission_filename, Netgame.mission_name, MAX_FILENAME_LEN );
2263 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
2266 // move from mission sync to team select
2267 else if ( ng_state == NETGAME_STATE_BRIEFING ){
2268 if( (Netgame.game_state == NETGAME_STATE_MISSION_SYNC) ||
2269 ((Netgame.game_state != NETGAME_STATE_MISSION_SYNC) && (gameseq_get_state() == GS_STATE_MULTI_MISSION_SYNC) && (Multi_sync_mode != MULTI_SYNC_POST_BRIEFING)) ){
2271 // do any special processing for forced state transitions
2272 multi_handle_state_special();
2274 SDL_strlcpy( Game_current_mission_filename, Netgame.mission_name, MAX_FILENAME_LEN );
2275 gameseq_post_event(GS_EVENT_START_BRIEFING);
2278 // move from the debriefing to the create game screen
2279 else if ( ng_state == NETGAME_STATE_FORMING ){
2280 if( (Netgame.game_state == NETGAME_STATE_DEBRIEF) ||
2281 ((Netgame.game_state != NETGAME_STATE_DEBRIEF) && ((gameseq_get_state() == GS_STATE_DEBRIEF) || (gameseq_get_state() == GS_STATE_MULTI_DOGFIGHT_DEBRIEF)) ) ){
2282 // do any special processing for forced state transitions
2283 multi_handle_state_special();
2285 multi_flush_mission_stuff();
2287 // move to the proper screen
2288 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
2289 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2291 gameseq_post_event(GS_EVENT_MULTI_CLIENT_SETUP);
2296 Netgame.game_state = ng_state;
2299 // send a request or a reply for mission description, if code == 0, request, if code == 1, reply
2300 void send_netgame_descript_packet(net_addr *addr, int code)
2302 ubyte data[MAX_PACKET_SIZE],val;
2304 int packet_size = 0;
2307 BUILD_HEADER(UPDATE_DESCRIPT);
2313 // add as much of the description as we dare
2314 length = strlen(The_mission.mission_desc);
2315 if(length > MAX_PACKET_SIZE - 10){
2316 length = MAX_PACKET_SIZE - 10;
2318 memcpy(data+packet_size,The_mission.mission_desc,length);
2319 packet_size += length;
2321 ADD_STRING(The_mission.mission_desc);
2325 SDL_assert(addr != NULL);
2327 psnet_send(addr, data, packet_size);
2331 // process an incoming netgame description packet
2332 void process_netgame_descript_packet( ubyte *data, header *hinfo )
2336 char mission_desc[MISSION_DESC_LENGTH+2];
2339 fill_net_addr(&addr, hinfo->addr, hinfo->port);
2341 // read this game into a temporary structure
2342 offset = HEADER_LENGTH;
2345 // if this is a request for mission description
2347 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
2352 // send an update to this guy
2353 send_netgame_descript_packet(&addr, 1);
2355 memset(mission_desc,0,MISSION_DESC_LENGTH+2);
2356 GET_STRING(mission_desc);
2358 // only display if we're in the proper state
2359 state = gameseq_get_state();
2361 case GS_STATE_MULTI_JOIN_GAME:
2362 case GS_STATE_MULTI_CLIENT_SETUP:
2363 case GS_STATE_MULTI_HOST_SETUP:
2364 multi_common_set_text(mission_desc);
2372 // broadcast a query for active games. IPX will use net broadcast and TCP will either request from the MT or from the specified list
2373 void broadcast_game_query()
2377 server_item *s_moveup;
2378 ubyte data[MAX_PACKET_SIZE];
2380 BUILD_HEADER(GAME_QUERY);
2382 if (Multi_options_g.pxo) {
2384 multi_fs_tracker_send_game_request();
2387 // go through the server list and query each of those as well
2388 s_moveup = Game_server_head;
2389 if(s_moveup != NULL){
2391 send_server_query(&s_moveup->server_addr);
2392 s_moveup = s_moveup->next;
2393 } while(s_moveup != Game_server_head);
2397 fill_net_addr(&addr, Psnet_my_addr.addr, DEFAULT_GAME_PORT);
2399 // send out a broadcast if our options allow us
2400 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
2401 psnet_broadcast( &addr, data, packet_size);
2405 // send an individual query to an address to see if there is an active game
2406 void send_server_query(net_addr *addr)
2409 ubyte data[MAX_PACKET_SIZE];
2411 // build the header and send the data
2412 BUILD_HEADER(GAME_QUERY);
2413 psnet_send(addr, data, packet_size);
2416 // process a query from a client looking for active freespace games
2417 void process_game_query(ubyte* data, header* hinfo)
2422 offset = HEADER_LENGTH;
2426 // check to be sure that we don't capture our own broadcast message
2427 fill_net_addr(&addr, hinfo->addr, hinfo->port);
2428 if ( psnet_same( &addr, &Psnet_my_addr) ){
2432 // if I am not a server of a game, don't send a reply!!!
2433 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) ){
2437 // if the game options are being selected, then ignore the request
2438 // also, if Netgame.max_players == -1, the host has not chosen a mission yet and we should wait
2439 if((Netgame.game_state == NETGAME_STATE_STD_HOST_SETUP) || (Netgame.game_state == NETGAME_STATE_HOST_SETUP) || (Netgame.game_state == 0) || (Netgame.max_players == -1)){
2443 // send information about this active game
2444 send_game_active_packet(&addr);
2447 // sends information about netplayers in the game. if called on the server, broadcasts information about _all_ players
2448 void send_netplayer_update_packet( net_player *pl )
2450 int packet_size,idx;
2451 ubyte data[MAX_PACKET_SIZE],val;
2453 BUILD_HEADER(NETPLAYER_UPDATE);
2455 // if I'm the server of the game, I should send an update for _all_players in the game
2456 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2457 for(idx=0;idx<MAX_PLAYERS;idx++){
2458 // only send info for connected players
2459 if(MULTI_CONNECTED(Net_players[idx])){
2464 // add the net player's information
2465 ADD_SHORT(Net_players[idx].player_id);
2466 ADD_INT(Net_players[idx].state);
2467 ADD_INT(Net_players[idx].p_info.ship_class);
2468 ADD_INT(Net_players[idx].tracker_player_id);
2470 if(Net_players[idx].flags & NETINFO_FLAG_HAS_CD){
2478 // add the final stop byte
2482 // broadcast the packet
2483 if(!(Game_mode & GM_IN_MISSION)){
2485 multi_io_send_to_all_reliable(data, packet_size);
2487 multi_io_send_reliable(pl, data, packet_size);
2491 multi_io_send_to_all(data, packet_size);
2493 multi_io_send(pl, data, packet_size);
2501 // add my current state in the netgame to this packet
2502 ADD_SHORT(Net_player->player_id);
2503 ADD_INT(Net_player->state);
2504 ADD_INT(Net_player->p_info.ship_class);
2505 ADD_INT(Multi_tracker_id);
2507 // add if I have a CD or not
2515 // add a final stop byte
2519 // send the packet to the server
2520 SDL_assert( pl == NULL ); // shouldn't ever be the case that pl is non-null here.
2521 if(!(Game_mode & GM_IN_MISSION)){
2522 multi_io_send_reliable(Net_player, data, packet_size);
2524 multi_io_send(Net_player, data, packet_size);
2529 // process an incoming netplayer state update. if we're the server, we should rebroadcast
2530 void process_netplayer_update_packet( ubyte *data, header *hinfo )
2532 int offset, player_num;
2538 offset = HEADER_LENGTH;
2540 // get the first stop byte
2543 while(stop != 0xff){
2544 // look the player up
2545 GET_SHORT(player_id);
2546 player_num = find_player_id(player_id);
2547 // if we couldn't find him, read in the bogus data
2548 if((player_num == -1) || (Net_player == &Net_players[player_num])){
2549 GET_INT(bogus.state);
2550 GET_INT(bogus.p_info.ship_class);
2551 GET_INT(bogus.tracker_player_id);
2555 // otherwise read in the data correctly
2558 GET_INT(Net_players[player_num].p_info.ship_class);
2559 GET_INT(Net_players[player_num].tracker_player_id);
2562 Net_players[player_num].flags |= NETINFO_FLAG_HAS_CD;
2564 Net_players[player_num].flags &= ~(NETINFO_FLAG_HAS_CD);
2567 // if he's changing state to joined, send a team update
2568 if((Net_players[player_num].state == NETPLAYER_STATE_JOINING) && (new_state == NETPLAYER_STATE_JOINED) && (Netgame.type_flags & NG_TYPE_TEAM)){
2569 multi_team_send_update();
2573 Net_players[player_num].state = new_state;
2576 // get the next stop byte
2582 // if I'm the host or the server of the game, update everyone else so things are synched up as tightly as possible
2583 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2584 send_netplayer_update_packet(NULL);
2587 // if i'm the standalone and this is an update from the host, maybe change some netgame settings
2588 if((Game_mode & GM_STANDALONE_SERVER) && (player_num != -1) && (Net_players[player_num].flags & NETINFO_FLAG_GAME_HOST)){
2589 switch(Net_players[player_num].state){
2590 case NETPLAYER_STATE_STD_HOST_SETUP:
2591 Netgame.game_state = NETGAME_STATE_STD_HOST_SETUP;
2594 case NETPLAYER_STATE_HOST_SETUP:
2595 // check for race conditions
2596 if(Netgame.game_state != NETGAME_STATE_MISSION_SYNC){
2597 Netgame.game_state = NETGAME_STATE_FORMING;
2604 #define EXTRA_DEATH_VAPORIZED (1<<0)
2605 #define EXTRA_DEATH_WASHED (1<<1)
2606 // send a packet indicating a ship has been killed
2607 void send_ship_kill_packet( object *objp, object *other_objp, float percent_killed, int self_destruct )
2609 int packet_size, model;
2610 ubyte data[MAX_PACKET_SIZE], was_player, extra_death_info, vaporized;
2611 ushort debris_signature;
2615 // only sendable from the master
2616 SDL_assert ( Net_player->flags & NETINFO_FLAG_AM_MASTER );
2619 vaporized = ( (Ships[objp->instance].flags & SF_VAPORIZE) > 0 );
2621 extra_death_info = 0;
2623 extra_death_info |= EXTRA_DEATH_VAPORIZED;
2626 if ( Ships[objp->instance].wash_killed ) {
2627 extra_death_info |= EXTRA_DEATH_WASHED;
2630 // find out the next network signature that will be used for the debris pieces.
2631 model = Ships[objp->instance].modelnum;
2632 pm = model_get(model);
2633 debris_signature = 0;
2634 if ( pm && !vaporized ) {
2635 debris_signature = multi_get_next_network_signature( MULTI_SIG_DEBRIS );
2636 multi_set_network_signature( (ushort)(debris_signature + pm->num_debris_objects), MULTI_SIG_DEBRIS );
2637 Ships[objp->instance].arrival_distance = debris_signature;
2640 BUILD_HEADER(SHIP_KILL);
2641 ADD_USHORT(objp->net_signature);
2643 // ships which are initially killed get the rest of the data sent. self destructed ships and
2644 if ( other_objp == NULL ) {
2649 nprintf(("Network","Don't know other_obj for ship kill packet, sending NULL\n"));
2651 ADD_USHORT( other_objp->net_signature );
2654 ADD_USHORT( debris_signature );
2655 ADD_FLOAT( percent_killed );
2656 sd = (ubyte)self_destruct;
2658 ADD_DATA( extra_death_info );
2660 // if the ship who died is a player, then send some extra info, like who killed him, etc.
2662 if ( objp->flags & OF_PLAYER_SHIP ) {
2666 pnum = multi_find_player_by_object( objp );
2669 ADD_DATA( was_player );
2671 SDL_assert(Net_players[pnum].player->killer_objtype < CHAR_MAX);
2672 temp = (char)Net_players[pnum].player->killer_objtype;
2675 SDL_assert(Net_players[pnum].player->killer_species < CHAR_MAX);
2676 temp = (char)Net_players[pnum].player->killer_species;
2679 SDL_assert(Net_players[pnum].player->killer_weapon_index < CHAR_MAX);
2680 temp = (char)Net_players[pnum].player->killer_weapon_index;
2683 ADD_STRING( Net_players[pnum].player->killer_parent_name );
2685 ADD_DATA( was_player );
2688 ADD_DATA( was_player );
2691 // send the packet reliably!!!
2692 multi_io_send_to_all_reliable(data, packet_size);
2695 // process a packet indicating that a ship has been killed
2696 void process_ship_kill_packet( ubyte *data, header *hinfo )
2699 ushort ship_sig, other_sig, debris_sig;
2700 object *sobjp, *oobjp;
2701 float percent_killed;
2702 ubyte was_player, extra_death_info, sd;
2703 char killer_name[NAME_LENGTH], killer_objtype = OBJ_NONE, killer_species = SPECIES_TERRAN, killer_weapon_index = -1;
2705 offset = HEADER_LENGTH;
2706 GET_USHORT(ship_sig);
2708 GET_USHORT( other_sig );
2709 GET_USHORT( debris_sig );
2710 GET_FLOAT( percent_killed );
2712 GET_DATA( extra_death_info );
2713 GET_DATA( was_player );
2716 // pnum is >=0 when the dying ship is a pleyer ship. Get the info about how he died
2717 if ( was_player != 0 ) {
2718 GET_DATA( killer_objtype );
2719 GET_DATA( killer_species );
2720 GET_DATA( killer_weapon_index );
2721 GET_STRING( killer_name );
2726 sobjp = multi_get_network_object( ship_sig );
2728 // if I am unable to find the ship object which was killed, I have to bail and rely on getting
2729 // another message from the server that this happened!
2730 if ( sobjp == NULL ) {
2731 nprintf(("Network", "Couldn't find net signature %d for kill packet\n", ship_sig));
2735 // set this ship's hull value to 0
2736 sobjp->hull_strength = 0.0f;
2738 // maybe set vaporized
2739 if (extra_death_info & EXTRA_DEATH_VAPORIZED) {
2740 Ships[sobjp->instance].flags |= SF_VAPORIZE;
2743 // maybe set wash_killed
2744 if (extra_death_info & EXTRA_DEATH_VAPORIZED) {
2745 Ships[sobjp->instance].wash_killed = 1;
2748 oobjp = multi_get_network_object( other_sig );
2750 if ( was_player != 0 ) {
2753 pnum = multi_find_player_by_object( sobjp );
2755 Net_players[pnum].player->killer_objtype = killer_objtype;
2756 Net_players[pnum].player->killer_species = killer_species;
2757 Net_players[pnum].player->killer_weapon_index = killer_weapon_index;
2758 SDL_strlcpy( Net_players[pnum].player->killer_parent_name, killer_name, NAME_LENGTH );
2762 // check to see if I need to respawn myself
2763 multi_respawn_check(sobjp);
2765 // store the debris signature in the arrival distance which will never get used for player ships
2766 Ships[sobjp->instance].arrival_distance = debris_sig;
2768 // set this bit so that we don't accidentally start switching targets when we die
2769 if(sobjp == Player_obj){
2770 Game_mode |= GM_DEAD_DIED;
2773 nprintf(("Network", "Killing off %s\n", Ships[sobjp->instance].ship_name));
2775 // do the normal thing when not ingame joining. When ingame joining, simply kill off the ship.
2776 if ( !(Net_player->flags & NETINFO_FLAG_INGAME_JOIN) ) {
2777 ship_hit_kill( sobjp, oobjp, percent_killed, sd );
2779 extern void ship_destroyed( int shipnum );
2780 ship_destroyed( sobjp->instance );
2781 sobjp->flags |= OF_SHOULD_BE_DEAD;
2782 obj_delete( OBJ_INDEX(sobjp) );
2786 // send a packet indicating a ship should be created
2787 void send_ship_create_packet( object *objp, int is_support )
2790 ubyte data[MAX_PACKET_SIZE];
2792 // We will pass the ship to create by name.
2793 BUILD_HEADER(SHIP_CREATE);
2794 ADD_USHORT(objp->net_signature);
2795 ADD_INT( is_support );
2797 add_vector_data(data, &packet_size, objp->pos);
2800 // broadcast the packet
2801 multi_io_send_to_all_reliable(data, packet_size);
2804 // process a packet indicating a ship should be created
2805 void process_ship_create_packet( ubyte *data, header *hinfo )
2807 int offset, objnum, is_support;
2810 vector pos = ZERO_VECTOR;
2812 SDL_assert ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
2813 offset = HEADER_LENGTH;
2814 GET_USHORT(signature);
2815 GET_INT( is_support );
2817 get_vector_data(data, &offset, pos);
2822 // find the name of this ship on ship ship arrival list. if found, pass it to parse_object_create
2823 if ( !is_support ) {
2824 objp = mission_parse_get_arrival_ship( signature );
2825 if ( objp != NULL ) {
2826 parse_create_object(objp);
2828 nprintf(("Network", "Ship with sig %d not found on ship arrival list -- not creating!!\n", signature));
2831 SDL_assert( Arriving_support_ship );
2832 if(Arriving_support_ship == NULL){
2835 Arriving_support_ship->pos = pos;
2836 Arriving_support_ship->net_signature = signature;
2837 objnum = parse_create_object( Arriving_support_ship );
2838 SDL_assert( objnum != -1 );
2840 mission_parse_support_arrived( objnum );
2845 // send a packet indicating a wing of ships should be created
2846 void send_wing_create_packet( wing *wingp, int num_to_create, int pre_create_count )
2848 int packet_size, index, ship_instance;
2849 ubyte data[MAX_PACKET_SIZE];
2853 // for creating wing -- we just send the index into the wing array of this wing.
2854 // all players load the same mission, and so their array's should all match. We also
2855 // need to send the signature of the first ship that was created. We can find this by
2856 // looking num_to_create places back in the ship_index field in the wing structure.
2858 index = WING_INDEX(wingp);
2859 ship_instance = wingp->ship_index[wingp->current_count - num_to_create];
2860 signature = Objects[Ships[ship_instance].objnum].net_signature;
2862 BUILD_HEADER( WING_CREATE );
2864 ADD_INT(num_to_create);
2865 ADD_USHORT(signature);
2866 ADD_INT(pre_create_count);
2867 val = wingp->current_wave - 1;
2870 multi_io_send_to_all_reliable(data, packet_size);
2873 // process a packet saying that a wing should be created
2874 void process_wing_create_packet( ubyte *data, header *hinfo )
2876 int offset, index, num_to_create;
2878 int total_arrived_count, current_wave;
2880 offset = HEADER_LENGTH;
2882 GET_INT(num_to_create);
2883 GET_USHORT(signature);
2884 GET_INT(total_arrived_count);
2885 GET_INT(current_wave);
2889 // do a sanity check on the wing to be sure that we are actually working on a valid wing
2890 if ( (index < 0) || (index >= num_wings) || (Wings[index].num_waves == -1) ) {
2891 nprintf(("Network", "invalid index %d for wing create packet\n"));
2894 if ( (num_to_create <= 0) || (num_to_create > Wings[index].wave_count) ) {
2895 nprintf(("Network", "Invalid number of ships to create (%d) for wing %s\n", num_to_create, Wings[index].name));
2900 Wings[index].current_count = 0;
2901 Wings[index].total_arrived_count = total_arrived_count;
2902 Wings[index].current_wave = current_wave;
2904 // set the network signature that was passed. The client should create ships in the same order
2905 // as the server -- so all ships should get the same sigs as assigned by the server. We also
2906 // need to set some timestamps and cues correctly to be sure that these things get created on
2907 // the clients correctly
2908 multi_set_network_signature( signature, MULTI_SIG_SHIP );
2909 parse_wing_create_ships( &Wings[index], num_to_create, 1 );
2912 // packet indicating a ship is departing
2913 void send_ship_depart_packet( object *objp )
2915 ubyte data[MAX_PACKET_SIZE];
2919 signature = objp->net_signature;
2921 BUILD_HEADER(SHIP_DEPART);
2922 ADD_USHORT( signature );
2924 multi_io_send_to_all_reliable(data, packet_size);
2927 // process a packet indicating a ship is departing
2928 void process_ship_depart_packet( ubyte *data, header *hinfo )
2934 offset = HEADER_LENGTH;
2935 GET_USHORT( signature );
2938 // find the object which is departing
2939 objp = multi_get_network_object( signature );
2940 if ( objp == NULL ) {
2941 nprintf(("network", "Couldn't find object with net signature %d to depart\n", signature ));
2945 // start warping him out
2946 shipfx_warpout_start( objp );
2949 // packet to tell clients cargo of a ship was revealed to all
2950 void send_cargo_revealed_packet( ship *shipp )
2952 ubyte data[MAX_PACKET_SIZE];
2955 // build the header and add the data
2956 BUILD_HEADER(CARGO_REVEALED);
2957 ADD_USHORT( Objects[shipp->objnum].net_signature );
2959 // server sends to all players
2960 if(MULTIPLAYER_MASTER){
2961 multi_io_send_to_all_reliable(data, packet_size);
2963 // clients just send to the server
2965 multi_io_send_reliable(Net_player, data, packet_size);
2969 // process a cargo revealed packet
2970 void process_cargo_revealed_packet( ubyte *data, header *hinfo )
2976 offset = HEADER_LENGTH;
2977 GET_USHORT(signature);
2980 // get a ship pointer and call the ship function to reveal the cargo
2981 objp = multi_get_network_object( signature );
2982 if ( objp == NULL ) {
2983 nprintf(("Network", "Could not find object with net signature %d for cargo revealed\n", signature ));
2987 // SDL_assert( objp->type == OBJ_SHIP );
2988 if((objp->type != OBJ_SHIP) || (objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
2992 // this will take care of re-routing to all other clients
2993 ship_do_cargo_revealed( &Ships[objp->instance], 1);
2995 // server should rebroadcast
2996 if(MULTIPLAYER_MASTER){
2997 send_cargo_revealed_packet(&Ships[objp->instance]);
3001 // defines used for secondary fire packet
3002 #define SFPF_ALLOW_SWARM (1<<7)
3003 #define SFPF_DUAL_FIRE (1<<6)
3004 #define SFPF_TARGET_LOCKED (1<<5)
3006 // send a packet indicating a secondary weapon was fired
3007 void send_secondary_fired_packet( ship *shipp, ushort starting_sig, int starting_count, int num_fired, int allow_swarm )
3009 int packet_size, net_player_num;
3010 ubyte data[MAX_PACKET_SIZE], sinfo, current_bank;
3012 ushort target_signature;
3016 // SDL_assert ( starting_count < UCHAR_MAX );
3018 // get the object for this ship. If it is an AI object, send all the info to all player. Otherwise,
3019 // we might send the info to the other player different than the one who fired
3020 objp = &Objects[shipp->objnum];
3021 if ( !(objp->flags & OF_PLAYER_SHIP) ) {
3022 if ( num_fired == 0 ) {
3027 aip = &Ai_info[shipp->ai_index];
3029 current_bank = (ubyte)shipp->weapons.current_secondary_bank;
3030 //SDL_assert( (current_bank >= 0) && (current_bank < MAX_SECONDARY_BANKS) ); // always true
3032 // build up the header portion
3033 BUILD_HEADER( SECONDARY_FIRED_AI );
3035 ADD_USHORT( Objects[shipp->objnum].net_signature );
3036 ADD_USHORT( starting_sig );
3038 // add a couple of bits for swarm missiles and dual fire secondary weaspons
3041 sinfo = current_bank;
3044 sinfo |= SFPF_ALLOW_SWARM;
3047 if ( shipp->flags & SF_SECONDARY_DUAL_FIRE ){
3048 sinfo |= SFPF_DUAL_FIRE;
3051 if ( aip->current_target_is_locked ){
3052 sinfo |= SFPF_TARGET_LOCKED;
3057 // add the ship's target and any targeted subsystem
3058 target_signature = 0;
3060 if ( aip->target_objnum != -1) {
3061 target_signature = Objects[aip->target_objnum].net_signature;
3062 if ( (Objects[aip->target_objnum].type == OBJ_SHIP) && (aip->targeted_subsys != NULL) ) {
3065 s_index = ship_get_index_from_subsys( aip->targeted_subsys, aip->target_objnum );
3066 SDL_assert( s_index < CHAR_MAX ); // better be less than this!!!!
3067 t_subsys = (char)s_index;
3070 if ( Objects[aip->target_objnum].type == OBJ_WEAPON ) {
3071 SDL_assert(Weapon_info[Weapons[Objects[aip->target_objnum].instance].weapon_info_index].wi_flags & WIF_BOMB);
3076 ADD_USHORT( target_signature );
3077 ADD_DATA( t_subsys );
3079 // just send this packet to everyone, then bail if an AI ship fired.
3080 if ( !(objp->flags & OF_PLAYER_SHIP) ) {
3081 multi_io_send_to_all(data, packet_size);
3085 net_player_num = multi_find_player_by_object( objp );
3087 // getting here means a player fired. Send the current packet to all players except the player
3088 // who fired. If nothing got fired, then don't send to the other players -- we will just send
3089 // a packet to the player who will find out that he didn't fire anything
3090 if ( num_fired > 0 ) {
3091 multi_io_send_to_all_reliable(data, packet_size, &Net_players[net_player_num]);
3094 // if I (the master) fired, then return
3095 if ( Net_players[net_player_num].flags & NETINFO_FLAG_AM_MASTER ){
3099 // now build up the packet to send to the player who actually fired.
3100 BUILD_HEADER( SECONDARY_FIRED_PLR );
3101 ADD_USHORT(starting_sig);
3104 // add the targeting information so that the player's weapons will always home on the correct
3106 ADD_USHORT( target_signature );
3107 ADD_DATA( t_subsys );
3109 multi_io_send_reliable(&Net_players[net_player_num], data, packet_size);
3112 /// process a packet indicating a secondary weapon was fired
3113 void process_secondary_fired_packet(ubyte* data, header* hinfo, int from_player)
3115 int offset, allow_swarm, target_objnum_save;
3116 ushort net_signature, starting_sig, target_signature;
3117 ubyte sinfo, current_bank;
3118 object* objp, *target_objp;
3122 ship_subsys *targeted_subsys_save;
3124 offset = HEADER_LENGTH; // size of the header
3126 // if from_player is false, it means that the secondary weapon info in this packet was
3127 // fired by an ai object (or another player). from_player == 1 means tha me (the person
3128 // receiving this packet) fired the secondary weapon
3129 if ( !from_player ) {
3130 GET_USHORT( net_signature );
3131 GET_USHORT( starting_sig );
3132 GET_DATA( sinfo ); // are we firing swarm missiles
3134 GET_USHORT( target_signature );
3135 GET_DATA( t_subsys );
3139 // find the object (based on network signatures) for the object that fired
3140 objp = multi_get_network_object( net_signature );
3141 if ( objp == NULL ) {
3142 nprintf(("Network", "Could not find ship for fire secondary packet!"));
3146 // set up the ships current secondary bank and that bank's mode. Below, we will set the timeout
3147 // of the next fire time of this bank to 0 so we can fire right away
3148 shipp = &Ships[objp->instance];
3151 GET_USHORT( starting_sig );
3154 GET_USHORT( target_signature );
3155 GET_DATA( t_subsys );
3159 // get the object and ship
3161 shipp = Player_ship;
3164 // check the allow swarm bit
3166 if ( sinfo & SFPF_ALLOW_SWARM ){
3170 // set the dual fire properties of the ship
3171 if ( sinfo & SFPF_DUAL_FIRE ){
3172 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
3174 shipp->flags &= ~SF_SECONDARY_DUAL_FIRE;
3177 // determine whether current target is locked
3178 SDL_assert( shipp->ai_index != -1 );
3179 aip = &Ai_info[shipp->ai_index];
3180 if ( sinfo & SFPF_TARGET_LOCKED ) {
3181 aip->current_target_is_locked = 1;
3183 aip->current_target_is_locked = 0;
3186 // find out the current bank
3187 current_bank = (ubyte)(sinfo & 0x3);
3188 //SDL_assert( (current_bank >= 0) && (current_bank < MAX_SECONDARY_BANKS) ); // always true
3189 shipp->weapons.current_secondary_bank = current_bank;
3191 // make it so we can fire this ship's secondary bank immediately!!!
3192 shipp->weapons.next_secondary_fire_stamp[shipp->weapons.current_secondary_bank] = timestamp(0);
3193 shipp->weapons.detonate_weapon_time = timestamp(5000); // be sure that we don't detonate a remote weapon before it is time.
3195 // set this ship's target and subsystem information. We will save and restore target and
3196 // targeted subsystem so that we do not accidentally change targets for this player or
3197 // any AI ships on his system.
3198 target_objnum_save = aip->target_objnum;
3199 targeted_subsys_save = aip->targeted_subsys;
3201 // reset these variables for accuracy. They will get reassigned at the end of this fuction
3202 aip->target_objnum = -1;
3203 aip->targeted_subsys = NULL;
3205 target_objp = multi_get_network_object( target_signature );
3206 if ( target_objp != NULL ) {
3207 aip->target_objnum = OBJ_INDEX(target_objp);
3209 if ( (t_subsys != -1) && (target_objp->type == OBJ_SHIP) ) {
3210 aip->targeted_subsys = ship_get_indexed_subsys( &Ships[target_objp->instance], t_subsys);
3214 if ( starting_sig != 0 ){
3215 multi_set_network_signature( starting_sig, MULTI_SIG_NON_PERMANENT );
3217 shipp->weapons.detonate_weapon_time = timestamp(0); // signature of -1 say detonate remote weapon
3220 ship_fire_secondary( objp, allow_swarm );
3222 // restore targeted object and targeted subsystem
3223 aip->target_objnum = target_objnum_save;
3224 aip->targeted_subsys = targeted_subsys_save;
3227 // send a packet indicating a countermeasure was fired
3228 void send_countermeasure_fired_packet( object *objp, int cmeasure_count, int rand_val )
3230 ubyte data[MAX_PACKET_SIZE];
3235 SDL_assert ( cmeasure_count < UCHAR_MAX );
3236 BUILD_HEADER(COUNTERMEASURE_FIRED);
3237 ADD_USHORT( objp->net_signature );
3238 ADD_INT( rand_val );
3240 multi_io_send_to_all(data, packet_size);
3243 // process a packet indicating a countermeasure was fired
3244 void process_countermeasure_fired_packet( ubyte *data, header *hinfo )
3246 int offset, rand_val;
3252 offset = HEADER_LENGTH;
3254 GET_USHORT( signature );
3255 GET_INT( rand_val );
3258 objp = multi_get_network_object( signature );
3259 if ( objp == NULL ) {
3260 nprintf(("network", "Could find object whose countermeasures are being launched!!!\n"));
3263 if(objp->type != OBJ_SHIP){
3266 // SDL_assert ( objp->type == OBJ_SHIP );
3268 // make it so ship can fire right away!
3269 Ships[objp->instance].cmeasure_fire_stamp = timestamp(0);
3270 if ( objp == Player_obj ){
3271 nprintf(("network", "firing countermeasure from my ship\n"));
3274 ship_launch_countermeasure( objp, rand_val );
3277 // send a packet indicating that a turret has been fired
3278 void send_turret_fired_packet( int ship_objnum, int subsys_index, int weapon_objnum )
3281 ushort pnet_signature;
3282 ubyte data[MAX_PACKET_SIZE], cindex;
3289 if((weapon_objnum < 0) || (Objects[weapon_objnum].type != OBJ_WEAPON) || (Objects[weapon_objnum].instance < 0) || (Weapons[Objects[weapon_objnum].instance].weapon_info_index < 0)){
3293 // local setup -- be sure we are actually passing a weapon!!!!
3294 objp = &Objects[weapon_objnum];
3295 SDL_assert ( objp->type == OBJ_WEAPON );
3296 if(Weapon_info[Weapons[objp->instance].weapon_info_index].subtype == WP_MISSILE){
3300 pnet_signature = Objects[ship_objnum].net_signature;
3302 SDL_assert( subsys_index < UCHAR_MAX );
3303 cindex = (ubyte)subsys_index;
3305 ssp = ship_get_indexed_subsys( &Ships[Objects[ship_objnum].instance], subsys_index, NULL );
3310 // build the fire turret packet.
3311 BUILD_HEADER(FIRE_TURRET_WEAPON);
3312 packet_size += multi_pack_unpack_position(1, data + packet_size, &objp->orient.v.fvec);
3313 ADD_DATA( has_sig );
3314 ADD_USHORT( pnet_signature );
3316 ADD_USHORT( objp->net_signature );
3319 val = (short)ssp->submodel_info_1.angs.h;
3321 val = (short)ssp->submodel_info_2.angs.p;
3324 multi_io_send_to_all(data, packet_size);
3326 multi_rate_add(1, "tur", packet_size);
3329 // process a packet indicating a turret has been fired
3330 void process_turret_fired_packet( ubyte *data, header *hinfo )
3332 int offset, weapon_objnum, wid;
3333 ushort pnet_signature, wnet_signature;
3342 short pitch, heading;
3344 // get the data for the turret fired packet
3345 offset = HEADER_LENGTH;
3346 offset += multi_pack_unpack_position(0, data + offset, &o_fvec);
3347 GET_DATA( has_sig );
3348 GET_USHORT( pnet_signature );
3350 GET_USHORT( wnet_signature );
3354 GET_DATA( turret_index );
3355 GET_SHORT( heading );
3357 PACKET_SET_SIZE(); // move our counter forward the number of bytes we have read
3360 objp = multi_get_network_object( pnet_signature );
3361 if ( objp == NULL ) {
3362 nprintf(("network", "could find parent object with net signature %d for turret firing\n", pnet_signature));
3366 // if this isn't a ship, do nothing
3367 if ( objp->type != OBJ_SHIP ){
3371 // make an orientation matrix from the o_fvec
3372 vm_vector_2_matrix(&orient, &o_fvec, NULL, NULL);
3374 // find this turret, and set the position of the turret that just fired to be where it fired. Quite a
3375 // hack, but should be suitable.
3376 shipp = &Ships[objp->instance];
3377 ssp = ship_get_indexed_subsys( shipp, turret_index, NULL );
3381 wid = ssp->system_info->turret_weapon_type;
3383 // bash the position and orientation of the turret
3384 ssp->submodel_info_1.angs.h = (float)heading;
3385 ssp->submodel_info_2.angs.p = (float)pitch;
3387 // get the world position of the weapon
3388 ship_get_global_turret_info(objp, ssp->system_info, &pos, &temp);
3390 // create the weapon object
3391 if(wnet_signature != 0){
3392 multi_set_network_signature( wnet_signature, MULTI_SIG_NON_PERMANENT );
3394 weapon_objnum = weapon_create( &pos, &orient, wid, OBJ_INDEX(objp), 0, -1, 1);
3395 if (weapon_objnum != -1) {
3396 if ( Weapon_info[wid].launch_snd != -1 ) {
3397 snd_play_3d( &Snds[Weapon_info[wid].launch_snd], &pos, &View_position );
3402 // send a mission log item packet
3403 void send_mission_log_packet( int num )
3406 ubyte data[MAX_PACKET_SIZE];
3411 SDL_assert ( (Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER) );
3413 // get the data from the log
3414 entry = &log_entries[num];
3415 type = (ubyte)entry->type; // do the type casting thing to save on packet space
3416 sindex = (ushort)entry->index;
3418 BUILD_HEADER(MISSION_LOG_ENTRY);
3420 ADD_INT(entry->flags);
3422 ADD_DATA(entry->timestamp);
3423 ADD_STRING(entry->pname);
3424 ADD_STRING(entry->sname);
3426 // broadcast the packet to all players
3427 multi_io_send_to_all_reliable(data, packet_size);
3430 // process a mission log item packet
3431 void process_mission_log_packet( ubyte *data, header *hinfo )
3436 char pname[NAME_LENGTH], sname[NAME_LENGTH];
3439 SDL_assert ( (Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
3441 offset = HEADER_LENGTH;
3445 GET_DATA(timestamp);
3451 mission_log_add_entry_multi( type, pname, sname, sindex, timestamp, flags );
3454 // send a mission message packet
3455 void send_mission_message_packet( int id, const char *who_from, int priority, int timing, int source, int builtin_type, int multi_target, int multi_team_filter)
3458 ubyte data[MAX_PACKET_SIZE], up, us, utime;
3460 SDL_assert ( Net_player->flags & NETINFO_FLAG_AM_MASTER );
3461 SDL_assert ( (priority >= 0) && (priority < UCHAR_MAX) );
3462 SDL_assert ( (timing >= 0) && (timing < UCHAR_MAX) );
3464 up = (ubyte) priority;
3465 us = (ubyte) source;
3466 utime = (ubyte)timing;
3468 BUILD_HEADER(MISSION_MESSAGE);
3470 ADD_STRING(who_from);
3474 ADD_INT(builtin_type);
3475 ADD_INT(multi_team_filter);
3477 if (multi_target == -1){
3478 multi_io_send_to_all_reliable(data, packet_size);
3480 multi_io_send_reliable(&Net_players[multi_target], data, packet_size);
3484 // process a mission message packet
3485 void process_mission_message_packet( ubyte *data, header *hinfo )
3487 int offset, id, builtin_type;
3488 ubyte priority, source, utiming;
3489 char who_from[NAME_LENGTH];
3490 int multi_team_filter;
3492 SDL_assert( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
3494 offset = HEADER_LENGTH;
3496 GET_STRING(who_from);
3500 GET_INT(builtin_type);
3501 GET_INT(multi_team_filter);
3505 // filter out builtin ones in TvT
3506 if((builtin_type >= 0) && (Netgame.type_flags & NG_TYPE_TEAM) && (Net_player != NULL) && (Net_player->p_info.team != multi_team_filter)){
3510 // maybe filter this out
3511 if(!message_filter_multi(id)){
3512 // send the message as if it came from an sexpression
3513 message_queue_message( id, priority, utiming, who_from, source, 0, 0, builtin_type );
3517 // just send them a pong back as fast as possible
3518 void process_ping_packet(ubyte *data, header *hinfo)
3523 offset = HEADER_LENGTH;
3526 // get the address to return the pong to
3527 fill_net_addr(&addr, hinfo->addr, hinfo->port);
3533 // right now it just routes the pong through to the standalone gui, which is the only
3534 // system which uses ping and pong right now.
3535 void process_pong_packet(ubyte *data, header *hinfo)
3541 offset = HEADER_LENGTH;
3543 fill_net_addr(&addr, hinfo->addr, hinfo->port);
3547 // if we're connected , see who sent us this pong
3548 if(Net_player->flags & NETINFO_FLAG_CONNECTED){
3549 lookup = find_player_id(hinfo->id);
3554 p = &Net_players[lookup];
3556 // evaluate the ping
3557 multi_ping_eval_pong(&Net_players[lookup].s_info.ping);
3559 // put in calls to any functions which may want to know about the ping times from
3561 if(Game_mode & GM_STANDALONE_SERVER){
3562 std_update_player_ping(p);
3566 // mark his socket as still alive (extra precaution)
3567 psnet_mark_received(Net_players[lookup].reliable_socket);
3570 // otherwise, do any special processing
3572 // if we're in the join game state, see if this pong came from a server on our
3574 if(gameseq_get_state() == GS_STATE_MULTI_JOIN_GAME){
3575 multi_join_eval_pong(&addr, timer_get_fixed_seconds());
3580 // send a ping packet
3581 void send_ping(net_addr *addr)
3583 unsigned char data[8];
3586 // build the header and send the packet
3587 BUILD_HEADER( PING );
3588 psnet_send(addr, &data[0], packet_size);
3591 // send a pong packet
3592 void send_pong(net_addr *addr)
3594 unsigned char data[8];
3597 // build the header and send the packet
3599 psnet_send(addr, &data[0], packet_size);
3602 // sent from host to master. give me the list of missions you have.
3603 // this will be used only in a standalone mode
3604 void send_mission_list_request( int what )
3606 ubyte data[MAX_PACKET_SIZE];
3609 // build the header and ask for a list of missions or campaigns (depending
3610 // on the 'what' flag).
3611 BUILD_HEADER(MISSION_REQUEST);
3613 multi_io_send_reliable(Net_player, data, packet_size);
3616 // maximum number of bytes that we can send in a mission items packet.
3617 #define MAX_MISSION_ITEMS_BYTES (MAX_PACKET_SIZE - (sizeof(multi_create_info) + 1) )
3619 // defines used to tell what type of packets are being sent
3620 #define MISSION_LIST_ITEMS 1
3621 #define CAMPAIGN_LIST_ITEMS 2
3623 // send an individual mission file item
3624 void send_mission_items( net_player *pl )
3626 ubyte data[MAX_PACKET_SIZE];
3631 BUILD_HEADER(MISSION_ITEM);
3633 // send the list of missions and campaigns avilable on the server. Stop when
3634 // reaching a certain maximum
3635 type = MISSION_LIST_ITEMS;
3637 for (i = 0; i < Multi_create_mission_count; i++ ) {
3641 ADD_STRING( Multi_create_mission_list[i].filename );
3642 ADD_STRING( Multi_create_mission_list[i].name );
3643 ADD_INT( Multi_create_mission_list[i].flags );
3644 ADD_DATA( Multi_create_mission_list[i].max_players );
3645 ADD_UINT( Multi_create_mission_list[i].respawn );
3648 ADD_DATA( Multi_create_mission_list[i].valid_status );
3650 if ( packet_size > (int)MAX_MISSION_ITEMS_BYTES ) {
3653 multi_io_send_reliable(pl, data, packet_size);
3654 BUILD_HEADER( MISSION_ITEM );
3660 multi_io_send_reliable(pl, data, packet_size);
3662 // send the campaign information
3663 type = CAMPAIGN_LIST_ITEMS;
3664 BUILD_HEADER(MISSION_ITEM);
3666 for (i = 0; i < Multi_create_campaign_count; i++ ) {
3670 ADD_STRING( Multi_create_campaign_list[i].filename );
3671 ADD_STRING( Multi_create_campaign_list[i].name );
3672 ADD_INT( Multi_create_campaign_list[i].flags );
3673 ADD_DATA( Multi_create_campaign_list[i].max_players );
3675 if ( packet_size > (int)MAX_MISSION_ITEMS_BYTES ) {
3678 multi_io_send_reliable(pl, data, packet_size);
3679 BUILD_HEADER( MISSION_ITEM );
3685 multi_io_send_reliable(pl, data, packet_size);
3688 // process a request for a list of missions
3689 void process_mission_request_packet(ubyte *data, header *hinfo)
3691 int player_num,offset;
3693 offset = HEADER_LENGTH;
3696 // fill in the address information of where this came from
3697 player_num = find_player_id(hinfo->id);
3698 if(player_num == -1){
3699 nprintf(("Network","Could not find player to send mission list items to!\n"));
3703 send_mission_items( &Net_players[player_num] );
3706 // process an individual mission file item
3707 void process_mission_item_packet(ubyte *data,header *hinfo)
3710 char filename[MAX_FILENAME_LEN], name[NAME_LENGTH], valid_status;
3711 ubyte stop, type,max_players;
3714 SDL_assert(gameseq_get_state() == GS_STATE_MULTI_HOST_SETUP);
3715 offset = HEADER_LENGTH;
3720 GET_STRING( filename );
3723 GET_DATA( max_players );
3725 // missions also have respawns and a crc32 associated with them
3726 if(type == MISSION_LIST_ITEMS){
3730 GET_DATA(valid_status);
3732 if ( Multi_create_mission_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
3733 SDL_strlcpy(Multi_create_mission_list[Multi_create_mission_count].filename, filename, MAX_FILENAME_LEN );
3734 SDL_strlcpy(Multi_create_mission_list[Multi_create_mission_count].name, name, NAME_LENGTH );
3735 Multi_create_mission_list[Multi_create_mission_count].flags = flags;
3736 Multi_create_mission_list[Multi_create_mission_count].respawn = respawn;
3737 Multi_create_mission_list[Multi_create_mission_count].max_players = max_players;
3740 Multi_create_mission_list[Multi_create_mission_count].valid_status = valid_status;
3742 Multi_create_mission_count++;
3744 } else if ( type == CAMPAIGN_LIST_ITEMS ) {
3745 if ( Multi_create_campaign_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
3746 SDL_strlcpy(Multi_create_campaign_list[Multi_create_campaign_count].filename, filename, MAX_FILENAME_LEN );
3747 SDL_strlcpy(Multi_create_campaign_list[Multi_create_campaign_count].name, name, NAME_LENGTH );
3748 Multi_create_campaign_list[Multi_create_campaign_count].flags = flags;
3749 Multi_create_campaign_list[Multi_create_campaign_count].respawn = 0;
3750 Multi_create_campaign_list[Multi_create_campaign_count].max_players = max_players;
3751 Multi_create_campaign_count++;
3760 // this will cause whatever list to get resorted (although they should be appearing in order)
3761 multi_create_setup_list_data(-1);
3764 // send a request to the server to pause or unpause the game
3765 void send_multi_pause_packet(int pause)
3767 ubyte data[MAX_PACKET_SIZE];
3769 int packet_size = 0;
3771 SDL_assert(!(Net_player->flags & NETINFO_FLAG_AM_MASTER));
3774 BUILD_HEADER(MULTI_PAUSE_REQUEST);
3775 val = (ubyte) pause;
3777 // add the pause info
3780 // send the request to the server
3781 multi_io_send_reliable(Net_player, data, packet_size);
3784 // process a pause update packet (pause, unpause, etc)
3785 void process_multi_pause_packet(ubyte *data, header *hinfo)
3791 offset = HEADER_LENGTH;
3797 // get who sent the packet
3798 player_index = find_player_id(hinfo->id);
3799 // if we don't know who sent the packet, don't do anything
3800 if(player_index == -1){
3804 // if we're the server, we should evaluate whether this guy is allowed to send the packet
3805 multi_pause_server_eval_request(&Net_players[player_index],(int)val);
3808 // send a game information update
3809 void send_game_info_packet()
3812 ubyte data[MAX_PACKET_SIZE], paused;
3814 // set the paused variable
3815 paused = (ubyte)((Netgame.game_state == NETGAME_STATE_PAUSED)?1:0);
3817 BUILD_HEADER(GAME_INFO);
3818 ADD_INT( Missiontime );
3821 multi_io_send_to_all(data, packet_size);
3824 // process a game information update
3825 void process_game_info_packet( ubyte *data, header *hinfo )
3831 offset = HEADER_LENGTH;
3833 // get the mission time -- we should examine our time and the time from the server. If off by some delta
3834 // time, set our time to server time (should take ping time into account!!!)
3835 GET_DATA( mission_time );
3840 // send an ingame nak packet
3841 void send_ingame_nak(int state, net_player *p)
3843 ubyte data[MAX_PACKET_SIZE];
3844 int packet_size = 0;
3846 BUILD_HEADER(INGAME_NAK);
3850 multi_io_send_reliable(p, data, packet_size);
3853 // process an ingame nak packet
3854 void process_ingame_nak(ubyte *data, header *hinfo)
3856 int offset,state,pid;
3858 offset = HEADER_LENGTH;
3862 pid = find_player_id(hinfo->id);
3868 case ACK_FILE_ACCEPTED :
3869 SDL_assert(Net_players[pid].flags & NETINFO_FLAG_INGAME_JOIN);
3870 nprintf(("Network","Mission file rejected by server, aborting...\n"));
3871 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_FILE_REJECTED);
3876 // send a packet telling players to end the mission
3877 void send_endgame_packet(net_player *pl)
3879 ubyte data[MAX_PACKET_SIZE];
3880 int packet_size = 0;
3882 BUILD_HEADER(MISSION_END);
3884 // sending to a specific player?
3886 SDL_assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
3887 multi_io_send_reliable(pl, data, packet_size);
3891 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3892 // send all player stats here
3893 multi_broadcast_stats(STATS_MISSION);
3895 // if in dogfight mode, send all dogfight stats as well
3896 ml_string("Before dogfight stats!");
3897 if(Netgame.type_flags & NG_TYPE_DOGFIGHT){
3898 ml_string("Sending dogfight stats!");
3900 multi_broadcast_stats(STATS_DOGFIGHT_KILLS);
3902 ml_string("After dogfight stats!");
3904 // tell everyone to leave the game
3905 multi_io_send_to_all_reliable(data, packet_size);
3907 multi_io_send_reliable(Net_player, data, packet_size);
3911 // process a packet indicating we should end the current mission
3912 void process_endgame_packet(ubyte *data, header *hinfo)
3917 offset = HEADER_LENGTH;
3921 ml_string("Receiving endgame packet");
3923 // if I'm the server, I should evaluate whether the sender is authorized to end the game
3924 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3925 // determine who this came from and make sure he is allowed to end the game
3926 player_num = find_player_id(hinfo->id);
3927 SDL_assert(player_num != -1);
3932 // if the player is allowed to end the mission
3933 if(!multi_can_end_mission(&Net_players[player_num])){
3937 // act as if we hit alt+j locally
3938 multi_handle_end_mission_request();
3940 // all clients process immediately
3942 // ingame joiners should quit when they receive an endgame packet since the game is over
3943 if(Net_player->flags & NETINFO_FLAG_INGAME_JOIN){
3944 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_EARLY_END);
3948 // do any special processing for being in a state other than the gameplay state
3949 multi_handle_state_special();
3951 // make sure we're not already in the debrief state
3952 if((gameseq_get_state() != GS_STATE_DEBRIEF) && (gameseq_get_state() != GS_STATE_MULTI_DOGFIGHT_DEBRIEF)){
3953 multi_warpout_all_players();
3958 // send a position/orientation update for myself (if I'm an observer)
3959 void send_observer_update_packet()
3961 ubyte data[MAX_PACKET_SIZE];
3962 int packet_size = 0;
3966 // its possible for the master to be an observer if has run out of respawns. In this case, he doesn't need
3967 // to send any update packets to anyone.
3968 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3972 if((Player_obj == NULL) || (Player_obj->type != OBJ_OBSERVER) || (Net_player == NULL) || !(Net_player->flags & NETINFO_FLAG_OBSERVER)){
3976 BUILD_HEADER(OBSERVER_UPDATE);
3978 ret = multi_pack_unpack_position( 1, data + packet_size, &Player_obj->pos );
3980 ret = multi_pack_unpack_orient( 1, data + packet_size, &Player_obj->orient );
3983 // add targeting infomation
3984 if((Player_ai != NULL) && (Player_ai->target_objnum >= 0)){
3985 target_sig = Objects[Player_ai->target_objnum].net_signature;
3989 ADD_USHORT(target_sig);
3991 multi_io_send(Net_player, data, packet_size);
3994 // process a position/orientation update from an observer
3995 void process_observer_update_packet(ubyte *data, header *hinfo)
4001 physics_info bogus_pi;
4004 offset = HEADER_LENGTH;
4006 obs_num = find_player_id(hinfo->id);
4008 memset(&bogus_pi,0,sizeof(physics_info));
4009 ret = multi_pack_unpack_position( 0, data + offset, &g_vec );
4011 ret = multi_pack_unpack_orient( 0, data + offset, &g_mat );
4014 // targeting information
4015 GET_USHORT(target_sig);
4018 if((obs_num < 0) || (Net_players[obs_num].player->objnum < 0)){
4022 // set targeting info
4023 if(target_sig == 0){
4024 Net_players[obs_num].s_info.target_objnum = -1;
4026 target_obj = multi_get_network_object(target_sig);
4027 Net_players[obs_num].s_info.target_objnum = (target_obj == NULL) ? -1 : OBJ_INDEX(target_obj);
4030 Objects[Net_players[obs_num].player->objnum].pos = g_vec;
4031 Objects[Net_players[obs_num].player->objnum].orient = g_mat;
4032 Net_players[obs_num].s_info.eye_pos = g_vec;
4033 Net_players[obs_num].s_info.eye_orient = g_mat;
4036 void send_netplayer_slot_packet()
4038 ubyte data[MAX_PACKET_SIZE];
4039 int packet_size = 0, idx;
4044 BUILD_HEADER(NETPLAYER_SLOTS_P);
4045 for(idx=0;idx<MAX_PLAYERS;idx++){
4046 if( MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_OBSERVER(Net_players[idx])){
4048 ADD_SHORT(Net_players[idx].player_id);
4049 ADD_USHORT(Objects[Net_players[idx].player->objnum].net_signature);
4050 ADD_INT(Net_players[idx].p_info.ship_class);
4051 ADD_INT(Net_players[idx].p_info.ship_index);
4057 // standalone case or not
4058 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4059 multi_io_send_to_all_reliable(data, packet_size);
4061 multi_io_send_reliable(Net_player, data, packet_size);
4065 void process_netplayer_slot_packet(ubyte *data, header *hinfo)
4068 int player_num,ship_class,ship_index;
4074 offset = HEADER_LENGTH;
4076 // first untag all of the player ships and make them OF_COULD_BE_PLAYER
4077 multi_untag_player_ships();
4081 GET_SHORT(player_id);
4082 GET_USHORT(net_sig);
4083 GET_INT(ship_class);
4084 GET_INT(ship_index);
4085 player_num = find_player_id(player_id);
4087 nprintf(("Network","Error looking up player for object/slot assignment!!\n"));
4089 // call the function in multiutil.cpp to set up the player object stuff
4090 // being careful not to muck with the standalone object
4091 if(!((player_num == 0) && (Game_mode & GM_STANDALONE_SERVER))){
4092 objp = multi_get_network_object(net_sig);
4093 SDL_assert(objp != NULL);
4094 multi_assign_player_ship( player_num, objp, ship_class );
4095 Net_players[player_num].p_info.ship_index = ship_index;
4096 objp->flags &= ~(OF_COULD_BE_PLAYER);
4097 objp->flags |= OF_PLAYER_SHIP;
4104 // standalone should forward the packet and wait for a response
4105 if(Game_mode & GM_STANDALONE_SERVER){
4106 send_netplayer_slot_packet();
4109 Net_player->state = NETPLAYER_STATE_SLOT_ACK;
4110 send_netplayer_update_packet();
4113 // two functions to deal with ships changing their primary/secondary weapon status. 'what' indicates
4114 // if this change is a primary or secondary change. new_bank is the new current primary/secondary
4115 // bank, link_status is whether primaries are linked or not, or secondaries are dual fire or not
4116 void send_ship_weapon_change( ship *shipp, int what, int new_bank, int link_status )
4118 ubyte data[MAX_PACKET_SIZE], utmp;
4121 BUILD_HEADER(SHIP_WSTATE_CHANGE);
4122 ADD_USHORT( Objects[shipp->objnum].net_signature );
4123 utmp = (ubyte)(what);
4125 utmp = (ubyte)(new_bank);
4127 utmp = (ubyte)(link_status);
4130 // Removed the above psnet_send() call - it didn't appear to do anything since it was called only from the server anyway - DB
4131 multi_io_send_to_all_reliable(data, packet_size);
4134 void process_ship_weapon_change( ubyte *data, header *hinfo )
4138 ubyte what, new_bank, link_status;
4142 offset = HEADER_LENGTH;
4143 GET_USHORT( signature );
4145 GET_DATA( new_bank );
4146 GET_DATA( link_status );
4149 objp = multi_get_network_object( signature );
4150 if ( objp == NULL ) {
4151 nprintf(("network", "Unable to locate ship with signature %d for weapon state change\n", signature));
4154 // SDL_assert( objp->type == OBJ_SHIP );
4155 if(objp->type != OBJ_SHIP){
4159 // if this is my data, do nothing since I already have my own data
4160 if ( objp == Player_obj ){
4164 // now, get the ship and set the new bank and link modes based on the 'what' value
4165 shipp = &Ships[objp->instance];
4166 if ( what == MULTI_PRIMARY_CHANGED ) {
4167 shipp->weapons.current_primary_bank = new_bank;
4169 shipp->flags |= SF_PRIMARY_LINKED;
4171 shipp->flags &= ~SF_PRIMARY_LINKED;
4174 shipp->weapons.current_secondary_bank = new_bank;
4176 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
4178 shipp->flags &= ~SF_SECONDARY_DUAL_FIRE;
4183 // ship status change procedure
4184 // 1.) <client> - Client runs through the normal button_function procedure. Any remaining control bits are implied as being
4186 // 2.) <client> - Client puts this button_info item into his last_buttons array and sends a bunch of SHIP_STATUS packets
4187 // for added redundancy.
4188 // 3.) <server> - Receives the packet. Checks to see if the net_player on his side already has this one defined. If so, it
4189 // ignores as a repeat packet. Otherwise it puts it in the last_buttons array for the net_player
4190 // 4.) <server> - Server applies the command on his side (with multi_apply_ship_status(...) and sends the ack (also a SHIP_STATUS)
4191 // back to the client. Also sends multiple times for redundancy
4192 // 5.) <client> - Receives the packet back. Does a lookup into his last_buttons array. If he finds the match, apply the functions
4193 // and remove the item from the list. If no match is found it means that either he has received an ack, has acted
4194 // on it and removed it, or that it has been "timed out" and replaced by a newer button_info.
4196 #define SHIP_STATUS_REPEAT 2
4197 void send_ship_status_packet(net_player *pl, button_info *bi, int id)
4200 ubyte data[MAX_PACKET_SIZE];
4201 int packet_size = 0;
4207 BUILD_HEADER(SHIP_STATUS_CHANGE);
4209 for(idx=0;idx<NUM_BUTTON_FIELDS;idx++){
4210 temp = bi->status[idx];
4214 // server should send reliably (response packet)
4215 if(MULTIPLAYER_MASTER){
4216 multi_io_send_reliable(pl, data, packet_size);
4218 multi_io_send(pl, data, packet_size);
4222 void process_ship_status_packet(ubyte *data, header *hinfo)
4226 int player_num,unique_id;
4230 offset = HEADER_LENGTH;
4232 // zero out the button info structure for good measure
4233 memset(&bi,0,sizeof(button_info));
4235 // read the button-info
4238 for(idx=0;idx<NUM_BUTTON_FIELDS;idx++){
4240 bi.status[idx] = i_tmp;
4245 // this will be handled differently client and server side. Duh.
4246 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){ // SERVER SIDE
4247 // find which net-player has sent us butotn information
4248 player_num = find_player_id(hinfo->id);
4249 SDL_assert(player_num >= 0);
4254 // don't process critical button information for observers
4255 // its a new button_info for this guy. apply and ack
4256 if(!MULTI_OBSERVER(Net_players[player_num]) && !lookup_ship_status(&Net_players[player_num],unique_id)){
4257 // mark that he's pressed this button
4258 // add_net_button_info(&Net_players[player_num], &bi, unique_id);
4260 // send a return packet
4261 send_ship_status_packet(&Net_players[player_num], &bi,unique_id);
4263 // apply the button presses to his ship as normal
4264 multi_apply_ship_status(&Net_players[player_num], &bi, 0);
4266 // else ignore it as a repeat from the same guy
4267 } else { // CLIENT SIDE
4268 // this is the return from the server, so we should now apply them locally
4269 // if(lookup_ship_status(Net_player,unique_id,1)){
4270 multi_apply_ship_status(Net_player, &bi, 1);
4275 // MWA 4/28/9 -- redid this function since message all fighers was really broken
4276 // for clients. Left all details to this function instead of higher level messaging
4278 void send_player_order_packet(int type, int index, int cmd)
4280 ubyte data[MAX_PACKET_SIZE];
4282 ushort target_signature;
4284 int packet_size = 0;
4286 BUILD_HEADER(PLAYER_ORDER_PACKET);
4289 ADD_DATA(val); // ship order or wing order, or message all fighters
4291 // if we are not messaging all ships or wings, add the index, which is the shipnum or wingnum
4292 if ( val != SQUAD_MSG_ALL ){
4293 ADD_INT(index); // net signature of target ship
4296 ADD_INT(cmd); // the command itself
4299 target_signature = 0;
4300 if ( Player_ai->target_objnum != -1 ){
4301 target_signature = Objects[Player_ai->target_objnum].net_signature;
4304 ADD_USHORT( target_signature );
4307 if ( (Player_ai->target_objnum != -1) && (Player_ai->targeted_subsys != NULL) ) {
4310 s_index = ship_get_index_from_subsys( Player_ai->targeted_subsys, Player_ai->target_objnum );
4311 SDL_assert( s_index < CHAR_MAX ); // better be less than this!!!!
4312 t_subsys = (char)s_index;
4316 multi_io_send_reliable(Net_player, data, packet_size);
4319 // brief explanation :
4320 // in either case (wing or ship command), we need to send in a pseudo-ai object. Basically, both command handler
4321 // functions "normally" (non multiplayer) use a couple of the Player_ai fields. So, we just fill in the ones necessary
4322 // (which we can reconstruct from the packet data), and pass this as the default variable ai_info *local
4323 // Its kind of a hack, but it eliminates the need to go in and screw around with quite a bit of code
4324 void process_player_order_packet(ubyte *data, header *hinfo)
4326 int offset, player_num, command, index = 0, tobjnum_save;
4327 ushort target_signature;
4328 char t_subsys, type;
4329 object *objp, *target_objp;
4332 ship_subsys *tsubsys_save, *targeted_subsys;
4334 SDL_assert(MULTIPLAYER_MASTER);
4336 // packet values - its easier to read all of these in first
4338 offset = HEADER_LENGTH;
4341 if ( type != SQUAD_MSG_ALL ){
4346 GET_USHORT( target_signature );
4347 GET_DATA( t_subsys );
4351 player_num = find_player_id(hinfo->id);
4352 if(player_num == -1){
4353 nprintf(("Network","Received player order packet from unknown player\n"));
4357 objp = &Objects[Net_players[player_num].player->objnum];
4358 if ( objp->type != OBJ_SHIP ) {
4359 nprintf(("Network", "not doing player order because object requestting is not a ship\n"));
4363 // HACK HACK HACK HACK HACK HACK
4364 // if the player has sent a rearm-repair me message, we should bail here after evaluating it, since most likely the rest of
4365 // the data is BOGUS. All people should be able to to these things as well.
4366 if(command == REARM_REPAIR_ME_ITEM){
4367 hud_squadmsg_repair_rearm(0,&Objects[Net_players[player_num].player->objnum]);
4369 } else if(command == ABORT_REARM_REPAIR_ITEM){
4370 hud_squadmsg_repair_rearm_abort(0,&Objects[Net_players[player_num].player->objnum]);
4374 // if this player is not allowed to do messaging, quit here
4375 if( !multi_can_message(&Net_players[player_num]) ){
4376 nprintf(("Network","Recieved player order packet from player not allowed to give orders!!\n"));
4380 // check to see if the type of order is a reinforcement call. If so, intercept it, and
4381 // then call them in.
4382 if ( type == SQUAD_MSG_REINFORCEMENT ) {
4383 SDL_assert( (index >= 0) && (index < Num_reinforcements) );
4384 hud_squadmsg_call_reinforcement(index, player_num);
4388 // set the player's ai information here
4389 shipp = &Ships[objp->instance];
4390 aip = &Ai_info[shipp->ai_index];
4392 // get the target objnum and targeted subsystem. Quick out if we don't have an object to act on.
4393 target_objp = multi_get_network_object( target_signature );
4394 if ( target_objp == NULL ) {
4398 targeted_subsys = NULL;
4399 if ( t_subsys != -1 ) {
4400 SDL_assert( target_objp != NULL );
4401 targeted_subsys = ship_get_indexed_subsys( &Ships[target_objp->instance], t_subsys);
4404 // save and restore the target objnum and targeted subsystem so that we don't mess up other things
4406 tobjnum_save = aip->target_objnum;
4407 tsubsys_save = aip->targeted_subsys;
4409 if ( target_objp ) {
4410 aip->target_objnum = OBJ_INDEX(target_objp);
4412 aip->target_objnum = -1;
4415 aip->targeted_subsys = targeted_subsys;
4417 if ( type == SQUAD_MSG_SHIP ) {
4418 hud_squadmsg_send_ship_command(index, command, 1, player_num);
4419 } else if ( type == SQUAD_MSG_WING ) {
4420 hud_squadmsg_send_wing_command(index, command, 1, player_num);
4421 } else if ( type == SQUAD_MSG_ALL ) {
4422 hud_squadmsg_send_to_all_fighters( command, player_num );
4425 SDL_assert(tobjnum_save != Ships[aip->shipnum].objnum); // make sure not targeting self
4426 aip->target_objnum = tobjnum_save;
4427 aip->targeted_subsys = tsubsys_save;
4430 // FILE SIGNATURE stuff :
4431 // there are 2 cases for file signature sending which are handled very differently
4432 // 1.) Pregame. In this case, the host requires that all clients send a filesig packet (when process_file_sig() is called, it
4433 // posts an ACK_FILE_ACCEPTED packet to ack_evaluate, so he thinks they have acked).
4434 // 2.) Ingame join. In this case, the client sends his filesig packet automatically to the server and the _client_ waits for
4435 // the ack, before continuing to join. It would be way too messy to have the server wait on the clients ack, since he
4436 // would have to keep track of up to potentially 14 other ack handles (ouch).
4437 void send_file_sig_packet(ushort sum_sig,int length_sig)
4439 ubyte data[MAX_PACKET_SIZE];
4440 int packet_size = 0;
4442 BUILD_HEADER(FILE_SIG_INFO);
4443 ADD_USHORT(sum_sig);
4444 ADD_INT(length_sig);
4446 multi_io_send_reliable(Net_player, data, packet_size);
4449 void process_file_sig_packet(ubyte *data, header *hinfo)
4454 offset = HEADER_LENGTH;
4456 // should only be received on the server-side
4457 SDL_assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
4459 GET_USHORT(sum_sig);
4460 GET_INT(length_sig);
4462 server_verify_filesig(hinfo->id, sum_sig, length_sig);
4465 void send_file_sig_request(char *file_name)
4467 ubyte data[MAX_PACKET_SIZE];
4468 int packet_size = 0;
4470 BUILD_HEADER(FILE_SIG_REQUEST);
4471 ADD_STRING(file_name);
4473 SDL_assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
4475 multi_io_send_to_all_reliable(data, packet_size);
4478 void process_file_sig_request(ubyte *data, header *hinfo)
4480 int offset = HEADER_LENGTH;
4482 // get the mission name
4483 GET_STRING(Netgame.mission_name);
4486 // set the current mission filename
4487 SDL_strlcpy(Game_current_mission_filename, Netgame.mission_name, SDL_arraysize(Game_current_mission_filename));
4490 multi_get_mission_checksum(Game_current_mission_filename);
4492 if(!multi_endgame_ending()){
4493 // reply to the server
4494 send_file_sig_packet(Multi_current_file_checksum,Multi_current_file_length);
4498 // functions to deal with subsystems getting whacked
4499 void send_subsystem_destroyed_packet( ship *shipp, int index, vector world_hitpos )
4501 ubyte data[MAX_PACKET_SIZE];
4504 vector tmp, local_hitpos;
4507 SDL_assert ( index < UCHAR_MAX );
4508 uindex = (ubyte)(index);
4510 objp = &Objects[shipp->objnum];
4512 vm_vec_sub(&tmp, &world_hitpos, &objp->pos );
4513 vm_vec_rotate( &local_hitpos, &tmp, &objp->orient );
4515 BUILD_HEADER(SUBSYSTEM_DESTROYED);
4516 ADD_USHORT( Objects[shipp->objnum].net_signature );
4518 // ADD_DATA( local_hitpos );
4519 add_vector_data(data, &packet_size, local_hitpos);
4521 multi_io_send_to_all_reliable(data, packet_size);
4524 void process_subsystem_destroyed_packet( ubyte *data, header *hinfo )
4530 vector local_hit_pos = ZERO_VECTOR, world_hit_pos;
4532 offset = HEADER_LENGTH;
4534 GET_USHORT( signature );
4536 // GET_DATA( local_hit_pos );
4537 get_vector_data(data, &offset, local_hit_pos);
4539 // get the network object. process it if we find it.
4540 objp = multi_get_network_object( signature );
4541 if ( objp != NULL ) {
4543 ship_subsys *subsysp;
4545 // be sure we have a ship!!!
4546 // SDL_assert ( objp->type == OBJ_SHIP );
4547 if(objp->type != OBJ_SHIP){
4552 shipp = &Ships[objp->instance];
4554 // call to get the pointer to the subsystem we should be working on
4555 subsysp = ship_get_indexed_subsys( shipp, (int)uindex );
4556 vm_vec_unrotate( &world_hit_pos, &local_hit_pos, &objp->orient );
4557 vm_vec_add2( &world_hit_pos, &objp->pos );
4559 do_subobj_destroyed_stuff( shipp, subsysp, &world_hit_pos );
4560 if ( objp == Player_obj ) {
4561 hud_gauge_popup_start(HUD_DAMAGE_GAUGE, 5000);
4569 // packet to tell clients cargo of a ship was revealed to all
4570 void send_subsystem_cargo_revealed_packet( ship *shipp, int index )
4572 ubyte data[MAX_PACKET_SIZE], uindex;
4575 SDL_assert ( index < UCHAR_MAX );
4576 uindex = (ubyte)(index);
4578 // build the header and add the data
4579 BUILD_HEADER(SUBSYS_CARGO_REVEALED);
4580 ADD_USHORT( Objects[shipp->objnum].net_signature );
4583 // server sends to all players
4584 if(MULTIPLAYER_MASTER){
4585 multi_io_send_to_all_reliable(data, packet_size);
4587 // clients just send to the server
4589 multi_io_send_reliable(Net_player, data, packet_size);
4593 // process a subsystem cargo revealed packet
4594 void process_subsystem_cargo_revealed_packet( ubyte *data, header *hinfo )
4601 ship_subsys *subsysp;
4603 offset = HEADER_LENGTH;
4604 GET_USHORT( signature );
4608 // get a ship pointer and call the ship function to reveal the cargo
4609 objp = multi_get_network_object( signature );
4610 if ( objp == NULL ) {
4611 nprintf(("Network", "Could not find object with net signature %d for cargo revealed\n", signature ));
4615 // SDL_assert( objp->type == OBJ_SHIP );
4616 if((objp->type != OBJ_SHIP) || (objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
4620 shipp = &Ships[objp->instance];
4622 // call to get the pointer to the subsystem we should be working on
4623 subsysp = ship_get_indexed_subsys( shipp, (int)uindex );
4624 if (subsysp == NULL) {
4625 nprintf(("Network", "Could not find subsys for ship %s for cargo revealed\n", Ships[objp->instance].ship_name ));
4629 // this will take care of re-routing to all other clients
4630 void ship_do_cap_subsys_cargo_revealed( ship *shipp, ship_subsys *subsys, int from_network );
4631 ship_do_cap_subsys_cargo_revealed( shipp, subsysp, 1 );
4633 // server should rebroadcast
4634 if(MULTIPLAYER_MASTER){
4635 send_subsystem_cargo_revealed_packet(&Ships[objp->instance], (int)uindex);
4639 void send_netplayer_load_packet(net_player *pl)
4641 ubyte data[MAX_PACKET_SIZE];
4642 int packet_size = 0;
4644 BUILD_HEADER(LOAD_MISSION_NOW);
4645 ADD_STRING(Netgame.mission_name);
4648 multi_io_send_to_all_reliable(data, packet_size);
4650 multi_io_send_reliable(pl, data, packet_size);
4654 void process_netplayer_load_packet(ubyte *data, header *hinfo)
4657 int offset = HEADER_LENGTH;
4662 SDL_strlcpy(Netgame.mission_name, str, SDL_arraysize(Netgame.mission_name));
4663 SDL_strlcpy(Game_current_mission_filename, str, SDL_arraysize(Game_current_mission_filename));
4664 if(!Multi_mission_loaded){
4666 // MWA 2/3/98 -- ingame join changes!!!
4667 // everyone can go through the same mission loading path here!!!!
4668 nprintf(("Network","Loading mission..."));
4670 // notify everyone that I'm loading the mission
4671 Net_player->state = NETPLAYER_STATE_MISSION_LOADING;
4672 send_netplayer_update_packet();
4674 // do the load itself
4675 game_start_mission();
4677 // ingame joiners need to "untag" all player ships as could_be_players. The ingame joining
4678 // code will remark the correct player ships
4679 if ( Net_player->flags & NETINFO_FLAG_INGAME_JOIN ) {
4680 multi_untag_player_ships();
4683 Net_player->flags |= NETINFO_FLAG_MISSION_OK;
4684 Net_player->state = NETPLAYER_STATE_MISSION_LOADED;
4685 send_netplayer_update_packet();
4687 Multi_mission_loaded = 1;
4688 nprintf(("Network","Finished loading mission\n"));
4692 void send_jump_into_mission_packet(net_player *pl)
4694 ubyte data[MAX_PACKET_SIZE];
4695 int packet_size = 0;
4697 SDL_assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
4699 BUILD_HEADER(JUMP_INTO_GAME);
4701 // ingame joiners will get special data. We need to tell them about the state of the mission, like paused,
4702 // and possible other things.
4704 if ( pl->flags & NETINFO_FLAG_INGAME_JOIN ) {
4705 ADD_INT(Netgame.game_state);
4711 multi_io_send_to_all_reliable(data, packet_size);
4713 // send to a specific player
4715 multi_io_send_reliable(pl, data, packet_size);
4719 void process_jump_into_mission_packet(ubyte *data, header *hinfo)
4721 int offset = HEADER_LENGTH;
4724 // if I am ingame joining, there should be extra data. For now, this data is the netgame state.
4725 // the game could be paused, so ingame joiner needs to deal with it.
4726 if ( Net_player->flags & NETINFO_FLAG_INGAME_JOIN ) {
4728 Netgame.game_state = state;
4733 // handle any special processing for being in a weird substate
4734 multi_handle_state_special();
4736 // if I'm an ingame joiner, go to the ship select screen, or if I'm an observer, jump right in!
4737 if(Net_player->flags & NETINFO_FLAG_INGAME_JOIN){
4738 if(Net_player->flags & NETINFO_FLAG_OBSERVER){
4739 multi_ingame_observer_finish();
4741 gameseq_post_event(GS_EVENT_INGAME_PRE_JOIN);
4742 Net_player->state = NETPLAYER_STATE_INGAME_SHIP_SELECT;
4743 send_netplayer_update_packet();
4746 // start the mission!!
4747 if(!(Game_mode & GM_IN_MISSION) && !(Game_mode & GM_STANDALONE_SERVER)){
4748 Netgame.game_state = NETGAME_STATE_IN_MISSION;
4749 gameseq_post_event(GS_EVENT_ENTER_GAME);
4750 Net_player->state = NETPLAYER_STATE_IN_MISSION;
4751 send_netplayer_update_packet();
4755 extern time_t Player_multi_died_check;
4756 Player_multi_died_check = -1;
4758 // recalc all object pairs now
4759 extern void obj_reset_all_collisions();
4760 obj_reset_all_collisions();
4762 // display some cool text
4763 multi_common_add_text(XSTR("Received mission start\n",720),1);
4766 ml_string(NOX("Client received mission start from server - entering mission"));
4771 const char *repair_text[] = {
4773 "REPAIR_INFO_BEGIN",
4775 "REPAIR_INFO_UPDATE",
4776 "REPAIR_INFO_QUEUE",
4777 "REPAIR_INFO_ABORT",
4778 "REPAIR_INFO_BROKEN",
4779 "REPAIR_INFO_WARP_ADD",
4780 "REPAIR_INFO_WARP_REMOVE",
4781 "REPAIR_INFO_ONWAY",
4782 "REPAIR_INFO_KILLED",
4783 "REPAIR_INFO_COMPLETE",
4788 // the following two routines deal with updating and sending information regarding players
4789 // rearming and repairing during the game. The process function calls the routines to deal with
4790 // setting flags and other interesting things.
4791 void send_repair_info_packet(object *repaired_objp, object *repair_objp, int code )
4793 int packet_size = 0;
4794 ushort repaired_signature, repair_signature;
4795 ubyte data[MAX_PACKET_SIZE];
4798 // use the network signature of the destination object if there is one, -1 otherwise.
4799 // client will piece it all together
4800 repaired_signature = repaired_objp->net_signature;
4802 // the repair ship may be NULL here since it might have been destroyed
4803 repair_signature = 0;
4805 repair_signature = repair_objp->net_signature;
4808 BUILD_HEADER(CLIENT_REPAIR_INFO);
4811 ADD_USHORT( repaired_signature );
4812 ADD_USHORT( repair_signature );
4814 multi_io_send_to_all_reliable(data, packet_size);
4816 nprintf(("Network", "Repair: %s sent to all players (%s/%s)\n", repair_text[cd], Ships[repaired_objp->instance].ship_name, (repair_objp==NULL)?"<none>":Ships[repair_objp->instance].ship_name));
4819 void process_repair_info_packet(ubyte *data, header *hinfo)
4821 int offset = HEADER_LENGTH;
4822 ushort repaired_signature, repair_signature;
4823 object *repaired_objp, *repair_objp;
4827 GET_USHORT( repaired_signature );
4828 GET_USHORT( repair_signature );
4831 repaired_objp = multi_get_network_object( repaired_signature );
4832 repair_objp = multi_get_network_object( repair_signature );
4834 nprintf(("Network", "Repair: %s received (%s/%s)\n", repair_text[code], (repaired_objp==NULL)?"<None>":Ships[repaired_objp->instance].ship_name, (repair_objp==NULL)?"<None>":Ships[repair_objp->instance].ship_name));
4836 if ( Net_player->flags & NETINFO_FLAG_WARPING_OUT ){
4840 if ( repaired_objp == NULL ) {
4841 Int3(); // Sandeep says this is bad bad bad. No ship to repair.
4845 // the hope is to simply call the routine in the ai code to set/unset flags
4846 // based on the code value and everything else should happen..I hope....
4847 if ( (code != REPAIR_INFO_WARP_ADD) && (code != REPAIR_INFO_WARP_REMOVE ) ) {
4849 ai_do_objects_repairing_stuff( repaired_objp, repair_objp, (int)code );
4851 // set the dock flags when repair begins. Prevents problem in lagging docking
4852 // packet. Also set any other flags/modes which need to be set to prevent Asserts.
4854 if ( (code == REPAIR_INFO_BEGIN) && (repair_objp != NULL) ) {
4855 ai_do_objects_docked_stuff( repaired_objp, repair_objp );
4856 Ai_info[Ships[repair_objp->instance].ai_index].mode = AIM_DOCK;
4859 // if the repair is done (either by abort, or ending), mark the repair ship's goal
4861 if ( ((code == REPAIR_INFO_ABORT) || (code == REPAIR_INFO_END)) && repair_objp ){
4862 ai_mission_goal_complete( &Ai_info[Ships[repair_objp->instance].ai_index] );
4865 if ( code == REPAIR_INFO_WARP_ADD ){
4866 mission_warp_in_support_ship( repaired_objp );
4868 mission_remove_scheduled_repair( repaired_objp );
4873 // sends information updating clients on certain AI information that clients will
4874 // need to know about to keep HUD information up to date. objp is the object that we
4875 // are updating, and what is the type of stuff that we are updating.
4876 void send_ai_info_update_packet( object *objp, char what )
4879 ushort other_signature;
4880 ubyte data[MAX_PACKET_SIZE];
4882 ubyte dock_index, dockee_index;
4884 // SDL_assert( objp->type == OBJ_SHIP );
4885 if(objp->type != OBJ_SHIP){
4888 aip = &Ai_info[Ships[objp->instance].ai_index];
4891 if ( Ships[objp->instance].flags & (SF_DEPARTING | SF_DYING) )
4894 BUILD_HEADER( AI_INFO_UPDATE );
4895 ADD_USHORT( objp->net_signature );
4898 // depending on the "what" value, we will send different information
4902 case AI_UPDATE_DOCK:
4903 // for docking ships, add the signature of the ship that we are docking with.
4904 SDL_assert( aip->dock_objnum != -1 );
4905 other_signature = Objects[aip->dock_objnum].net_signature;
4906 dock_index = (ubyte)(aip->dock_index);
4907 dockee_index = (ubyte)(aip->dockee_index);
4908 ADD_USHORT( other_signature );
4909 ADD_DATA(dock_index);
4910 ADD_DATA(dockee_index);
4913 case AI_UPDATE_UNDOCK:
4914 // for undocking ships, check the dock_objnum since we might or might not have it
4915 // depending on whether or not a ship was destroyed while we were docked.
4916 other_signature = 0;
4917 if ( aip->dock_objnum != -1 )
4918 other_signature = Objects[aip->dock_objnum].net_signature;
4919 ADD_USHORT( other_signature );
4923 case AI_UPDATE_ORDERS: {
4926 // for orders, we only need to send a little bit of information here. Be sure that the
4927 // first order for this ship is active
4928 SDL_assert( (aip->active_goal != AI_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) );
4929 ADD_INT( aip->goals[0].ai_mode );
4930 ADD_INT( aip->goals[0].ai_submode );
4932 if ( aip->goals[0].ship_name != NULL )
4933 shipnum = ship_name_lookup( aip->goals[0].ship_name );
4935 // the ship_name member of the goals structure may or may not contain a real shipname. If we don't
4936 // have a valid shipnum, then don't sweat it since it may not really be a ship.
4937 if ( shipnum != -1 ) {
4938 SDL_assert( Ships[shipnum].objnum != -1 );
4939 other_signature = Objects[Ships[shipnum].objnum].net_signature;
4941 other_signature = 0;
4943 ADD_USHORT( other_signature );
4945 // for docking, add the dock and dockee index
4946 if ( aip->goals[0].ai_mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
4947 SDL_assert( (aip->goals[0].docker.index >= 0) && (aip->goals[0].docker.index < UCHAR_MAX) );
4948 SDL_assert( (aip->goals[0].dockee.index >= 0) && (aip->goals[0].dockee.index < UCHAR_MAX) );
4949 dock_index = (ubyte)aip->goals[0].docker.index;
4950 dockee_index = (ubyte)aip->goals[0].dockee.index;
4951 ADD_DATA( dock_index );
4952 ADD_DATA( dockee_index );
4961 multi_rate_add(1, "aiu", packet_size);
4962 multi_io_send_to_all_reliable(data, packet_size);
4965 // process an ai_info update packet. Docking/undocking, ai orders, etc. are taken care of here. This
4966 // information is mainly used to keep the clients HUD up to date with the appropriate information.
4967 void process_ai_info_update_packet( ubyte *data, header *hinfo)
4969 int offset = HEADER_LENGTH;
4971 ushort net_signature, other_net_signature;
4972 object *objp, *other_objp;
4975 ubyte dock_index = 0, dockee_index = 0;
4977 GET_USHORT( net_signature ); // signature of the object that we are dealing with.
4978 GET_DATA( code ); // code of what we are doing.
4979 objp = multi_get_network_object( net_signature );
4981 nprintf(("Network", "Couldn't find object for ai update\n"));
4985 case AI_UPDATE_DOCK:
4986 GET_USHORT( other_net_signature );
4987 GET_DATA( dock_index );
4988 GET_DATA( dockee_index );
4989 other_objp = multi_get_network_object( other_net_signature );
4990 if ( !other_objp ) {
4991 nprintf(("Network", "Couldn't find other object for ai update on dock\n"));
4994 // if we don't have an object to work with, break out of loop
4995 if ( !objp || !other_objp || (objp->type != OBJ_SHIP) || (other_objp->type != OBJ_SHIP)){
4999 SDL_assert( other_objp->type == OBJ_SHIP );
5000 Ai_info[Ships[objp->instance].ai_index].dock_index = dock_index;
5001 Ai_info[Ships[objp->instance].ai_index].dockee_index = dockee_index;
5003 Ai_info[Ships[other_objp->instance].ai_index].dock_index = dockee_index;
5004 Ai_info[Ships[other_objp->instance].ai_index].dockee_index = dock_index;
5006 ai_do_objects_docked_stuff( objp, other_objp );
5009 case AI_UPDATE_UNDOCK:
5010 GET_USHORT( other_net_signature );
5011 other_objp = multi_get_network_object( other_net_signature );
5013 // if we don't have an object to work with, break out of loop
5017 ai_do_objects_undocked_stuff( objp, other_objp );
5020 case AI_UPDATE_ORDERS:
5023 GET_USHORT( other_net_signature );
5024 if ( mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
5025 GET_DATA(dock_index);
5026 GET_DATA(dockee_index);
5029 // be sure that we have a ship object!!!
5030 if ( !objp || (objp->type != OBJ_SHIP) )
5033 // set up the information in the first goal element of the object in question
5034 aip = &Ai_info[Ships[objp->instance].ai_index];
5035 aip->active_goal = 0;
5036 aip->goals[0].ai_mode = mode;
5037 aip->goals[0].ai_submode = submode;
5039 // for docking, add the dock and dockee index
5040 if ( mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
5041 aip->dock_index = dock_index;
5042 aip->dockee_index = dockee_index;
5045 // get a shipname if we can.
5046 other_objp = multi_get_network_object( other_net_signature );
5047 if ( other_objp && (other_objp->type == OBJ_SHIP) ) {
5048 // get a pointer to the shipname in question. Use the ship_name value in the
5049 // ship. We are only using this for HUD display, so I think that using this
5050 // method will be fine.
5051 aip->goals[0].ship_name = Ships[other_objp->instance].ship_name;
5053 // special case for destroy subsystem -- get the ai_info pointer to our target ship
5054 // so that we can properly set up what subsystem this ship is attacking.
5055 if ( (mode == AI_GOAL_DESTROY_SUBSYSTEM ) && (submode >= 0) )
5056 aip->targeted_subsys = ship_get_indexed_subsys( &Ships[other_objp->instance], submode);
5058 // if docking -- set the dock index and dockee index of this other ship
5059 if ( mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
5060 Ai_info[Ships[other_objp->instance].ai_index].dock_index = dockee_index;
5061 Ai_info[Ships[other_objp->instance].ai_index].dockee_index = dock_index;
5068 Int3(); // this Int3() should be temporary
5069 nprintf(("Network", "Invalid code for ai update: %d\n", code));
5075 // tell the standalone to move into the MISSION_SYNC_STATE
5076 void send_mission_sync_packet(int mode,int start_campaign)
5078 ubyte data[MAX_PACKET_SIZE],is_campaign;
5079 int packet_size = 0;
5081 SDL_assert((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER));
5083 // build the header and add the sync mode (pre or post briefing)
5084 BUILD_HEADER(MISSION_SYNC_DATA);
5087 // if this is a campaign game
5088 if(mode == MULTI_SYNC_PRE_BRIEFING){
5089 if(Game_mode & GM_CAMPAIGN_MODE){
5090 // add a byte indicating campaign mode
5092 ADD_DATA(is_campaign);
5094 // add a byte indicating if we should be starting a campaign or continuing it
5095 is_campaign = (ubyte)start_campaign;
5096 ADD_DATA(is_campaign);
5098 // add the campaign filename
5099 ADD_STRING(Netgame.campaign_name);
5101 // otherwise if this is a single mission
5103 // add a byte indicating single mission mode
5105 ADD_DATA(is_campaign);
5107 // add the mission filename
5108 ADD_STRING(Game_current_mission_filename);
5111 multi_io_send_reliable(Net_player, data, packet_size);
5114 // move into the MISSION_SYNC state when this is received
5115 // this packet is sent only from a game host to a standalone
5116 void process_mission_sync_packet(ubyte *data, header *hinfo)
5119 ubyte campaign_flag;
5120 int offset = HEADER_LENGTH;
5122 SDL_assert(Game_mode & GM_STANDALONE_SERVER);
5124 // if this is a team vs team situation, lock the players send a final team update
5125 if(Netgame.type_flags & NG_TYPE_TEAM){
5126 multi_team_host_lock_all();
5127 multi_team_send_update();
5130 // get the sync mode (pre or post briefing)
5133 if(mode == MULTI_SYNC_PRE_BRIEFING){
5134 // get the flag indicating if this is a single mission or a campaign mode
5135 GET_DATA(campaign_flag);
5137 // get the flag indicating whether we should be starting a new campaign
5138 GET_DATA(campaign_flag);
5140 // get the campaign filename
5141 GET_STRING(Netgame.campaign_name);
5143 // either start a new campaign or continue on to the next mission in the current campaign
5145 multi_campaign_start(Netgame.campaign_name);
5147 multi_campaign_next_mission();
5150 // make sure we remove the campaign mode flag
5151 Game_mode &= ~(GM_CAMPAIGN_MODE);
5153 // get the single mission filename
5154 GET_STRING(Game_current_mission_filename);
5155 SDL_strlcpy(Netgame.mission_name, Game_current_mission_filename, SDL_arraysize(Netgame.mission_name));
5160 // set the correct mode and m ove into the state
5161 Multi_sync_mode = mode;
5162 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
5165 // tell a player to merge his mission stats into his alltime stats
5166 void send_store_stats_packet(int accept)
5169 int packet_size = 0;
5171 BUILD_HEADER(STORE_MISSION_STATS);
5173 // add whether we're accepting or tossing
5174 val = (ubyte)accept;
5177 // if I'm the server, send to everyone, else send to the standalone to be rebroadcasted
5178 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5179 multi_io_send_to_all_reliable(data, packet_size);
5181 multi_io_send_reliable(Net_player, data, packet_size);
5185 void process_store_stats_packet(ubyte *data, header *hinfo)
5187 int offset = HEADER_LENGTH;
5193 // if I'm the standalone, rebroadcast. Otherwise, if I'm a client, merge my mission stats with my alltime stats
5194 if(Game_mode & GM_STANDALONE_SERVER){
5195 // rebroadcast the packet to all others in the game
5196 nprintf(("Network","Standalone received store stats packet - rebroadcasting..\n"));
5197 multi_io_send_to_all_reliable(data, offset);
5200 // all players should mark the stats as being accepted in the debriefing
5201 multi_debrief_stats_accept();
5203 // all players should mark the stats as being "tossed" in the debriefing
5204 multi_debrief_stats_toss();
5209 void send_debris_update_packet(object *objp,int code)
5211 ubyte data[MAX_PACKET_SIZE];
5213 int packet_size = 0;
5215 BUILD_HEADER(DEBRIS_UPDATE);
5216 ADD_USHORT(objp->net_signature);
5220 // add any extra relevant data
5222 case DEBRIS_UPDATE_UPDATE:
5223 // ADD_DATA(objp->pos); // add position
5224 add_vector_data(data, &packet_size, objp->pos);
5225 ADD_ORIENT(objp->orient); // add orientation
5226 // ADD_DATA(objp->phys_info.vel); // add velocity
5227 add_vector_data(data, &packet_size, objp->phys_info.vel);
5228 // ADD_DATA(objp->phys_info.rotvel); // add rotational velocity
5229 add_vector_data(data, &packet_size, objp->phys_info.rotvel);
5232 multi_io_send_to_all(data, packet_size);
5235 void process_debris_update_packet(ubyte *data, header *hinfo)
5239 object bogus_object;
5241 int offset = HEADER_LENGTH;
5243 GET_USHORT(net_sig);
5247 objp = multi_get_network_object(net_sig);
5249 objp = &bogus_object;
5253 // update the object
5254 case DEBRIS_UPDATE_UPDATE:
5255 //GET_DATA(objp->pos);
5256 get_vector_data( data, &offset, objp->pos );
5258 GET_ORIENT(objp->orient);
5259 GET_DATA(objp->phys_info.vel);
5260 GET_DATA(objp->phys_info.rotvel);
5262 // simply remove it (no explosion)
5263 case DEBRIS_UPDATE_REMOVE:
5264 if(objp != &bogus_object){
5265 SDL_assert(objp->type == OBJ_DEBRIS);
5266 obj_delete(OBJ_INDEX(objp));
5270 case DEBRIS_UPDATE_NUKE:
5271 if(objp != &bogus_object)
5272 debris_hit(objp,NULL,&objp->pos,1000000.0f);
5280 void send_wss_request_packet(short player_id, int from_slot, int from_index, int to_slot, int to_index, int wl_ship_slot, int ship_class, int mode, net_player *p)
5282 ubyte data[MAX_PACKET_SIZE];
5284 int packet_size = 0;
5286 BUILD_HEADER(WSS_REQUEST_PACKET);
5288 // add the request information
5289 ADD_SHORT(player_id);
5291 ADD_INT(from_index);
5294 ADD_INT(wl_ship_slot); // only used in weapons loadout
5295 ADD_INT(ship_class);
5298 // a standard request
5300 multi_io_send_reliable(Net_player, data, packet_size);
5302 // being routed through the standalone to the host of the game
5304 SDL_assert(Game_mode & GM_STANDALONE_SERVER);
5305 multi_io_send_reliable(p, data, packet_size);
5309 void process_wss_request_packet(ubyte *data, header *hinfo)
5311 int offset = HEADER_LENGTH;
5312 int from_slot,from_index;
5313 int to_slot,to_index;
5315 int wl_ship_slot,ship_class;
5319 // determine who this request is from
5320 GET_SHORT(player_id);
5321 player_num = find_player_id(player_id);
5323 // read in the request data
5325 GET_INT(from_index);
5328 GET_INT(wl_ship_slot); // only used in weapons loadout
5329 GET_INT(ship_class); // only used in multi team select
5333 SDL_assert(player_num != -1);
5334 if(player_num == -1){
5338 // if we're the standalone, we have to route this packet to the host of the game
5339 if(Game_mode & GM_STANDALONE_SERVER){
5340 send_wss_request_packet(player_id, from_slot, from_index, to_slot, to_index, wl_ship_slot, ship_class, mode, Netgame.host);
5342 // otherwise we're the host and should process the request
5345 case WSS_WEAPON_SELECT :
5346 wl_drop(from_slot,from_index,to_slot,to_index,wl_ship_slot,player_num);
5348 case WSS_SHIP_SELECT :
5349 multi_ts_drop(from_slot,from_index,to_slot,to_index,ship_class,player_num);
5357 void send_wss_update_packet(int team_num,ubyte *wss_data,int size)
5359 ubyte data[MAX_PACKET_SIZE],team;
5360 int packet_size = 0;
5362 SDL_assert(size <= (MAX_PACKET_SIZE - 10));
5364 BUILD_HEADER(WSS_UPDATE_PACKET);
5366 // add the team/pool # this is for
5367 team = (ubyte)team_num;
5370 // add the data block size
5373 // add the data itself
5374 memcpy(data + packet_size,wss_data,size);
5375 packet_size += size;
5377 // if we're also the master of the game (not on a standalone)
5378 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5379 multi_io_send_to_all_reliable(data, packet_size);
5381 // if we're only the host on the standalone, then send the packet to the standalone to be routed
5383 multi_io_send_reliable(Net_player, data, packet_size);
5387 void process_wss_update_packet(ubyte *data, header *hinfo)
5390 int size,player_index,idx;
5391 int offset = HEADER_LENGTH;
5393 // get the team/pool #
5396 // get the data size
5399 // if we're the standalone, then we should be routing this data to all the other clients
5400 if(Game_mode & GM_STANDALONE_SERVER){
5405 // determine where this came from
5406 player_index = find_player_id(hinfo->id);
5407 SDL_assert(player_index != -1);
5408 if(player_index < 0){
5412 // route the packet (don't resend it to the host)
5413 for(idx=0;idx<MAX_PLAYERS;idx++){
5414 if(MULTI_CONNECTED(Net_players[idx]) && (&Net_players[idx] != Net_player) && (&Net_players[idx] != &Net_players[player_index]) ){
5415 multi_io_send_reliable(&Net_players[idx], data, offset);
5419 // set the proper pool pointers
5420 ss_set_team_pointers((int)team);
5422 // read in the block of data, and apply it to the weapons/ship pools
5423 offset += restore_wss_data(data + offset);
5426 // set the pool pointers back to my own team
5427 ss_set_team_pointers(Net_player->p_info.team);
5429 // sync the interface if this was for my own team
5430 if((int)team == Net_player->p_info.team){
5431 multi_ts_sync_interface();
5438 // function to send firing information from the client to the server once they reach
5439 // the final sync screen.
5440 void send_firing_info_packet()
5442 ubyte data[MAX_PACKET_SIZE];
5444 ubyte plinked, sdual;
5446 SDL_assert( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
5448 BUILD_HEADER(FIRING_INFO);
5449 plinked = (ubyte)((Player_ship->flags & SF_PRIMARY_LINKED)?1:0);
5450 sdual = (ubyte)((Player_ship->flags & SF_SECONDARY_DUAL_FIRE)?1:0);
5451 ADD_DATA( plinked );
5454 multi_io_send_reliable(Net_player, data, packet_size);
5457 void process_firing_info_packet( ubyte *data, header *hinfo )
5459 int offset, player_num;
5460 ubyte plinked, sdual;
5463 // only the master of the game should be dealing with these packets
5464 SDL_assert( Net_player->flags & NETINFO_FLAG_AM_MASTER );
5466 offset = HEADER_LENGTH;
5467 GET_DATA( plinked );
5471 player_num = find_player_id(hinfo->id);
5473 nprintf(("Network","Received firing info packet from unknown player, ignoring\n"));
5477 // get the ship pointer for this player and set the flags accordingly.
5478 shipp = &(Ships[Objects[Net_players[player_num].player->objnum].instance]);
5480 shipp->flags |= SF_PRIMARY_LINKED;
5482 shipp->flags &= ~SF_PRIMARY_LINKED;
5485 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
5487 shipp->flags &= ~SF_SECONDARY_DUAL_FIRE;
5490 // packet to deal with changing status of mission goals. used to be sent every so often from server
5491 // to clients, but with addition of reliable sockets, send when complete, invalid, etc.
5492 // goal_num is the index into mission_goals. new_status means failed, success, etc. -1 if not used.
5493 // valid means goal is changing to invalid(0) or valid(1). only applies if new_status == -1
5494 void send_mission_goal_info_packet( int goal_num, int new_status, int valid )
5496 ubyte data[MAX_PACKET_SIZE];
5499 BUILD_HEADER(MISSION_GOAL_INFO);
5502 ADD_INT(new_status);
5505 multi_io_send_to_all_reliable(data, packet_size);
5508 void process_mission_goal_info_packet( ubyte *data, header *hinfo )
5510 int offset, goal_num, new_status, valid;
5512 offset = HEADER_LENGTH;
5514 GET_INT(new_status);
5518 // if new_status != -1, then this is a change in goal status (i.e. goal failed, or is successful)
5519 if ( new_status != -1 ){
5520 mission_goal_status_change( goal_num, new_status );
5522 mission_goal_validation_change( goal_num, valid );
5526 void send_player_settings_packet(net_player *p)
5528 ubyte data[MAX_PACKET_SIZE];
5531 int packet_size = 0;
5534 BUILD_HEADER(PLAYER_SETTINGS);
5536 // add all the data for all the players
5538 for(idx=0;idx<MAX_PLAYERS;idx++){
5539 if(MULTI_CONNECTED(Net_players[idx])){
5541 ADD_SHORT(Net_players[idx].player_id);
5543 // break the p_info structure by member, so we don't overwrite any absolute pointers
5544 // ADD_DATA(Net_players[idx].p_info);
5545 ADD_INT(Net_players[idx].p_info.team);
5546 ADD_INT(Net_players[idx].p_info.ship_index);
5547 ADD_INT(Net_players[idx].p_info.ship_class);
5550 // add the stop byte
5554 // either broadcast the data or send to a specific player
5556 multi_io_send_to_all_reliable(data, packet_size);
5558 multi_io_send_reliable(p, data, packet_size);
5562 void process_player_settings_packet(ubyte *data, header *hinfo)
5564 int offset,player_num;
5565 net_player_info bogus,*ptr;
5569 offset = HEADER_LENGTH;
5571 // read in the data for all the players
5573 while(stop != 0xff){
5574 // lookup the player
5575 GET_SHORT(player_id);
5576 player_num = find_player_id(player_id);
5578 // make sure this is a valid player
5579 if(player_num == -1){
5582 ptr = &Net_players[player_num].p_info;
5586 GET_INT(ptr->ship_index);
5587 GET_INT(ptr->ship_class);
5594 // update the server with my new state
5595 // MWA -- 3/31/98 -- check for in mission instead of state.
5596 //if ( Netgame.game_state == NETGAME_STATE_MISSION_SYNC) {
5597 if( !(Game_mode & GM_IN_MISSION) ) {
5598 Net_player->state = NETPLAYER_STATE_SETTINGS_ACK;
5599 send_netplayer_update_packet();
5603 // display some cool text
5604 multi_common_add_text(XSTR("Received player settings packet\n",721),1);
5607 void send_deny_packet(net_addr *addr, int code)
5610 int packet_size = 0;
5612 // build the header and add the rejection code
5618 psnet_send(addr, data, packet_size);
5621 void process_deny_packet(ubyte *data, header *hinfo)
5625 // get the denial code
5626 offset = HEADER_LENGTH;
5630 // if there is already a dialog active, do nothing - who cares at this point.
5635 // display the appropriate dialog
5637 case JOIN_DENY_JR_STATE :
5638 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because the game is not in an appropriate state to accept",722));
5640 case JOIN_DENY_JR_TRACKER_INVAL :
5641 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because this is a Parallax Online game and you are not a registered Parallax Online pilot",723));
5643 case JOIN_DENY_JR_PASSWD :
5644 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because this is a password protected game",724));
5646 case JOIN_DENY_JR_CLOSED :
5647 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because this is a closed game and the mission is in progress",725));
5649 case JOIN_DENY_JR_TEMP_CLOSED :
5650 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because the netgame is forming and the host has temporarily closed it",726));
5652 case JOIN_DENY_JR_RANK_HIGH :
5653 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because this is a rank limited game and your rank is too high",727));
5655 case JOIN_DENY_JR_RANK_LOW :
5656 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because this is a rank limited game and your rank is too low",728));
5658 case JOIN_DENY_JR_DUP :
5659 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because there is an identical player already in the game",729));
5661 case JOIN_DENY_JR_FULL :
5662 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because the game is full",730));
5664 case JOIN_DENY_JR_BANNED :
5665 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because you are banned from this server",731));
5667 case JOIN_DENY_JR_NOOBS :
5668 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because this game does not allow observers",732));
5670 case JOIN_DENY_JR_INGAME_JOIN :
5671 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You cannot join at this time since someone else is currently joining. Try again shortly.",733));
5673 case JOIN_DENY_JR_BAD_VERSION :
5674 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You cannot join this game because you are running an older version of FreeSpace than the server. Exit FreeSpace, and choose the 'Update FreeSpace' button in the FreeSpace launcher to download the latest version of FreeSpace.",734));
5676 case JOIN_DENY_JR_TYPE :
5677 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You cannot join a game in progress unless it is a dogfight mission.",1433));
5681 // call this so that the join request timestamp automatically expires when we hear back from the server
5682 multi_join_reset_join_stamp();
5685 // this packet will consist of
5686 // 1.) netplayer ship classes (85 bytes max)
5687 // 2.) ship weapon state data (241 bytes max)
5688 // 3.) player settings et. al. (133 bytes max)
5689 // TOTAL 459 NOTE : keep this in mind when/if adding new data to this packet
5690 void send_post_sync_data_packet(net_player *p, int std_request)
5692 ubyte data[MAX_PACKET_SIZE], val;
5697 ushort sval, ship_ets;
5698 int idx, player_index;
5699 int packet_size = 0;
5703 BUILD_HEADER(POST_SYNC_DATA);
5705 // some header information for standalone packet routing purposes
5706 val = (ubyte)std_request;
5709 // the standalone has two situations
5710 // 1.) sending a request to the host to distribute this block of data
5711 // 2.) having recevied this block of data from the host, it redistributes it
5712 if((Game_mode & GM_STANDALONE_SERVER) && std_request && (Netgame.host != NULL)){
5713 // case 1, send the request
5714 multi_io_send_reliable(Netgame.host, data, packet_size);
5717 // case 2 for the standalone is below (as normal)
5719 // otherwise build the data now
5721 // add all deleted ships
5722 val = (ubyte)Multi_ts_num_deleted;
5724 for(idx=0;idx<Multi_ts_num_deleted;idx++){
5725 sval = (ushort)Objects[Multi_ts_deleted_objnums[idx]].net_signature;
5731 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5732 shipp = &Ships[Objects[so->objnum].instance];
5734 // don't process non player wing ships
5735 if ( !(shipp->flags & SF_FROM_PLAYER_WING ) )
5741 // # of ships - used multiple times in the packet
5742 val = (ubyte)ship_count;
5745 // add ship class information (85 bytes max)
5746 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5747 shipp = &Ships[Objects[so->objnum].instance];
5749 // don't process non player wing ships
5750 if ( !(shipp->flags & SF_FROM_PLAYER_WING ) )
5753 // add the net signature of the object for look up
5754 ADD_USHORT( Objects[so->objnum].net_signature );
5756 // add the ship info index
5757 val = (ubyte)(shipp->ship_info_index);
5760 // add the ships's team select index
5761 val = (ubyte)shipp->ts_index;
5765 // add weapon state information for all starting ships (241 bytes max)
5766 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5767 shipp = &Ships[Objects[so->objnum].instance];
5769 // don't process non player wing ships
5770 if ( !(shipp->flags & SF_FROM_PLAYER_WING ) )
5773 // if this is a ship owned by a player, we should mark down his weapons bank/link settings now if we're the server
5775 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5776 player_index = multi_find_player_by_net_signature(Objects[so->objnum].net_signature);
5777 if(player_index == -1){
5780 pl = &Net_players[player_index];
5784 // add the net signature and other weapon information
5785 ADD_USHORT( Objects[so->objnum].net_signature );
5787 // add number of primary and secondary banks
5788 bval = (char)(shipp->weapons.num_primary_banks);
5790 bval = (char)(shipp->weapons.num_secondary_banks);
5793 // add weapon bank status
5794 bval = (char)(shipp->weapons.current_primary_bank);
5796 pl->s_info.cur_primary_bank = bval;
5798 // SDL_assert(bval != -1);
5801 bval = (char)(shipp->weapons.current_secondary_bank);
5803 pl->s_info.cur_secondary_bank = bval;
5805 // SDL_assert(bval != -1);
5808 // primary weapon info
5809 bval = (char)(shipp->weapons.primary_bank_weapons[0]);
5811 bval = (char)(shipp->weapons.primary_bank_weapons[1]);
5814 // secondary weapon info
5815 bval = (char)(shipp->weapons.secondary_bank_weapons[0]);
5817 val_short = (short)(shipp->weapons.secondary_bank_ammo[0]);
5818 ADD_SHORT(val_short);
5819 bval = (char)(shipp->weapons.secondary_bank_weapons[1]);
5821 val_short = (short)(shipp->weapons.secondary_bank_ammo[1]);
5822 ADD_SHORT(val_short);
5823 bval = (char)(shipp->weapons.secondary_bank_weapons[2]);
5825 val_short = (short)(shipp->weapons.secondary_bank_ammo[2]);
5826 ADD_SHORT(val_short);
5828 // send primary and secondary weapon link status
5830 if(shipp->flags & SF_PRIMARY_LINKED){
5832 pl->s_info.cur_link_status |= (1<<0);
5836 if(shipp->flags & SF_SECONDARY_DUAL_FIRE){
5838 pl->s_info.cur_link_status |= (1<<1);
5842 // if this is a player ship add (1<<2)
5843 if(Objects[shipp->objnum].flags & OF_PLAYER_SHIP){
5848 // add a ship ets value
5851 ship_ets |= ((ushort)shipp->shield_recharge_index << 8);
5853 ship_ets |= ((ushort)shipp->weapon_recharge_index << 4);
5855 ship_ets |= ((ushort)shipp->engine_recharge_index);
5856 ADD_USHORT(ship_ets);
5860 // 2 cases, if I'm the host on a standalone, I should be sending this to the standalone only
5861 // or if I'm the server as well as the host, I should be sending this to all players
5862 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
5863 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5866 multi_io_send_to_all_reliable(data, packet_size);
5868 // send to a specific player
5870 multi_io_send_reliable(p, data, packet_size);
5873 multi_io_send_reliable(Net_player, data, packet_size);
5880 multi_io_send_to_all_reliable(data, packet_size);
5882 // send to a specific player
5884 multi_io_send_reliable(p, data, packet_size);
5889 void process_post_sync_data_packet(ubyte *data, header *hinfo)
5891 ubyte val, sinfo_index, ts_index;
5893 ushort net_sig, ship_ets, sval;
5897 int offset = HEADER_LENGTH;
5901 // packet routing information
5904 // if I'm the host on a standalone, this is going to be a request to send the data to the standalone to be routed
5905 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) && val){
5908 // at this point we want to delete all necessary ships, change all necessary ship classes, and set all weapons up
5909 multi_ts_create_wings();
5911 // send to the standalone through my socket
5912 send_post_sync_data_packet(Net_player);
5918 // add all deleted ships
5920 Multi_ts_num_deleted = (int)val;
5921 for(idx=0;idx<Multi_ts_num_deleted;idx++){
5922 // get the ship's objnum
5925 objp = multi_get_network_object(sval);
5927 // delete the ship appropriately
5928 // mark the object as having been deleted
5929 Multi_ts_deleted_objnums[idx] = OBJ_INDEX(objp);
5932 ship_add_exited_ship(&Ships[objp->instance], SEF_PLAYER_DELETED);
5933 obj_delete(Multi_ts_deleted_objnums[idx]);
5934 ship_wing_cleanup(objp->instance,&Wings[Ships[objp->instance].wingnum]);
5936 Multi_ts_num_deleted--;
5937 nprintf(("Network","Couldn't find object by net signature for ship delete in post sync data packet\n"));
5945 // process ship class information
5946 for(idx=0; idx<ship_count; idx++){
5947 // get the object's net signature
5948 GET_USHORT(net_sig);
5949 GET_DATA(sinfo_index);
5952 // attempt to get the object
5953 objp = multi_get_network_object(net_sig);
5955 // make sure we found a ship
5956 SDL_assert((objp != NULL) && (objp->type == OBJ_SHIP));
5958 // set the ship to be the right class
5959 change_ship_type(objp->instance,(int)sinfo_index);
5961 // set the ship's team select index
5962 Ships[objp->instance].ts_index = (int)ts_index;
5965 // process ship weapon state info
5966 for(idx=0; idx<ship_count; idx++){
5967 // get the object's net signature
5968 GET_USHORT(net_sig);
5970 // attempt to get the object
5971 objp = multi_get_network_object(net_sig);
5973 // make sure we found a ship
5974 SDL_assert((objp != NULL) && (objp->type == OBJ_SHIP));
5976 // get a pointer to the ship
5977 shipp = &Ships[objp->instance];
5979 // get number of primary and secondary banks;
5981 SDL_assert( b != -1 );
5982 shipp->weapons.num_primary_banks = (int)b;
5985 SDL_assert( b != -1 );
5986 shipp->weapons.num_secondary_banks = (int)b;
5988 // get bank selection info
5993 shipp->weapons.current_primary_bank = (int)b;
5999 shipp->weapons.current_secondary_bank = (int)b;
6001 // primary weapon info
6003 shipp->weapons.primary_bank_weapons[0] = (int)b;
6006 shipp->weapons.primary_bank_weapons[1] = (int)b;
6008 // secondary weapon info
6010 shipp->weapons.secondary_bank_weapons[0] = (int)b;
6011 GET_SHORT(val_short);
6012 shipp->weapons.secondary_bank_ammo[0] = (int)val_short;
6015 shipp->weapons.secondary_bank_weapons[1] = (int)b;
6016 GET_SHORT(val_short);
6017 shipp->weapons.secondary_bank_ammo[1] = (int)val_short;
6020 shipp->weapons.secondary_bank_weapons[2] = (int)b;
6021 GET_SHORT(val_short);
6022 shipp->weapons.secondary_bank_ammo[2] = (int)val_short;
6029 shipp->flags |= SF_PRIMARY_LINKED;
6032 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
6034 Objects[shipp->objnum].flags &= ~(OF_PLAYER_SHIP);
6035 Objects[shipp->objnum].flags &= ~(OF_COULD_BE_PLAYER);
6037 Objects[shipp->objnum].flags |= OF_PLAYER_SHIP;
6039 obj_set_flags( &Objects[shipp->objnum], Objects[shipp->objnum].flags | OF_COULD_BE_PLAYER );
6043 GET_USHORT(ship_ets);
6045 shipp->shield_recharge_index = ((ship_ets & 0x0f00) >> 8);
6047 shipp->weapon_recharge_index = ((ship_ets & 0x00f0) >> 4);
6049 shipp->engine_recharge_index = (ship_ets & 0x000f);
6054 Net_player->state = NETPLAYER_STATE_POST_DATA_ACK;
6055 send_netplayer_update_packet();
6057 // the standalone server will receive this packet from the host of the game, to be applied locally and
6058 // also to be rebroadcast.
6059 if(Game_mode & GM_STANDALONE_SERVER){
6060 // update player ets settings
6061 for(idx=0;idx<MAX_PLAYERS;idx++){
6062 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player->objnum != -1)){
6063 multi_server_update_player_weapons(&Net_players[idx],&Ships[Objects[Net_players[idx].player->objnum].instance]);
6067 send_post_sync_data_packet(NULL,0);
6071 void send_wss_slots_data_packet(int team_num,int final,net_player *p,int std_request)
6073 ubyte data[MAX_PACKET_SIZE],val;
6076 int packet_size = 0;
6079 BUILD_HEADER(WSS_SLOTS_DATA);
6081 // some header information for standalone packet routing purposes
6082 val = (ubyte)std_request;
6086 val = (ubyte)team_num;
6089 // add whether this is the final packet or not
6093 // the standalone has two situations
6094 // 1.) sending a request to the host to distribute this block of data
6095 // 2.) having recevied this block of data from the host, it redistributes it
6096 if((Game_mode & GM_STANDALONE_SERVER) && std_request){
6097 // case 1, send the request
6098 multi_io_send_reliable(Netgame.host, data, packet_size);
6101 // case 2 for the standalone is below (as normal)
6103 // add all the slots
6104 for(idx=0;idx<MULTI_TS_NUM_SHIP_SLOTS;idx++){
6105 // add the ship class
6106 val = (ubyte)Wss_slots_teams[team_num][idx].ship_class;
6110 for(i = 0;i<MAX_WL_WEAPONS;i++){
6111 val = (ubyte)Wss_slots_teams[team_num][idx].wep[i];
6115 // add the weapon counts
6116 for(i = 0;i<MAX_WL_WEAPONS;i++){
6117 val_short = (short)Wss_slots_teams[team_num][idx].wep_count[i];
6118 ADD_SHORT(val_short);
6122 // 2 cases, if I'm the host on a standalone, I should be sending this to the standalone only
6123 // or if I'm the server as well as the host, I should be sending this to all players
6124 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
6125 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
6128 multi_io_send_to_all_reliable(data, packet_size);
6130 // send to a specific player
6132 multi_io_send_reliable(p, data, packet_size);
6135 multi_io_send_reliable(Net_player, data, packet_size);
6142 multi_io_send_to_all_reliable(data, packet_size);
6144 // send to a specific player
6146 multi_io_send_reliable(p, data, packet_size);
6151 void process_wss_slots_data_packet(ubyte *data, header *hinfo)
6153 ubyte val,team_num,final;
6155 int offset = HEADER_LENGTH;
6158 // packet routing information
6164 // get whether this is the final packet or not
6167 // if I'm the host on a standalone, this is going to be a request to send the data to the standalone to be routed
6168 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) && val){
6171 // send to the standalone through my socket
6172 send_wss_slots_data_packet((int)team_num,(int)final,Net_player);
6176 // read in all the slot data
6177 for(idx=0;idx<MULTI_TS_NUM_SHIP_SLOTS;idx++){
6178 memset(&Wss_slots_teams[team_num][idx],0,sizeof(wss_unit));
6180 // get the ship class
6182 Wss_slots_teams[team_num][idx].ship_class = (int)val;
6185 for(i = 0;i<MAX_WL_WEAPONS;i++){
6187 Wss_slots_teams[team_num][idx].wep[i] = (int)val;
6189 // check for signed/unsigned problems
6190 if(Wss_slots_teams[team_num][idx].wep[i] == 255){
6191 Wss_slots_teams[team_num][idx].wep[i] = -1;
6195 // get the weapon counts
6196 for(i = 0;i<MAX_WL_WEAPONS;i++){
6197 GET_SHORT(val_short);
6198 Wss_slots_teams[team_num][idx].wep_count[i] = (int)val_short;
6203 // update my netplayer state if this is the final packet
6205 Net_player->state = NETPLAYER_STATE_WSS_ACK;
6206 send_netplayer_update_packet();
6209 // the standalone server will receive this packet from the host of the game, to be applied locally and
6210 // also to be rebroadcast.
6211 if(Game_mode & GM_STANDALONE_SERVER){
6212 send_wss_slots_data_packet((int)team_num,(int)final,NULL,0);
6214 // add some mission sync text
6215 multi_common_add_text(XSTR("Weapon slots packet\n",735),1);
6219 #define OBJ_VISIBILITY_DOT 0.6f
6221 // send and receive packets for shield explosion information
6222 void send_shield_explosion_packet( int objnum, int tri_num, vector hit_pos )
6225 ubyte data[MAX_PACKET_SIZE], utri_num;
6228 // SDL_assert(!(Netgame.debug_flags & NETD_FLAG_CLIENT_NODAMAGE));
6230 SDL_assert( tri_num < UCHAR_MAX );
6231 utri_num = (ubyte)tri_num;
6233 // for each player, determine if this object is behind the player -- if so, don't
6235 for ( i = 0; i < MAX_PLAYERS; i++ ) {
6236 if ( MULTI_CONNECTED(Net_players[i]) && (&Net_players[i] != Net_player) ) {
6238 vector eye_to_obj_vec, diff, eye_pos;
6241 eye_pos = Net_players[i].s_info.eye_pos;
6242 eye_orient = Net_players[i].s_info.eye_orient;
6244 // check for same vectors
6245 vm_vec_sub(&diff, &Objects[objnum].pos, &eye_pos);
6246 if ( vm_vec_mag_quick(&diff) < 0.01 ){
6250 vm_vec_normalized_dir(&eye_to_obj_vec, &Objects[objnum].pos, &eye_pos);
6251 dot = vm_vec_dot(&eye_orient.v.fvec, &eye_to_obj_vec);
6253 if ( dot < OBJ_VISIBILITY_DOT ){
6257 BUILD_HEADER(SHIELD_EXPLOSION);
6259 ADD_USHORT( Objects[objnum].net_signature );
6262 multi_io_send(&Net_players[i], data, packet_size);
6267 void add_shield_point_multi(int objnum, int tri_num, vector *hit_pos);
6269 void process_shield_explosion_packet( ubyte *data, header *hinfo)
6276 // get the shield hit data
6277 offset = HEADER_LENGTH;
6278 GET_USHORT(signature);
6280 //GET_DATA(hit_pos);
6283 // find the object with this signature. If found, then do a shield explosion
6284 objp = multi_get_network_object( signature );
6287 shield_info *shieldp;
6292 // given the tri num, find the local position which is the average of the
6293 // three vertices of the triangle affected. Use this average point as the hit
6295 // SDL_assert( objp->type == OBJ_SHIP );
6296 if(objp->type != OBJ_SHIP){
6300 pm = model_get(Ships[objp->instance].modelnum);
6301 shieldp = &pm->shield;
6302 SDL_assert( utri_num < shieldp->ntris );
6303 stri = shieldp->tris[utri_num];
6304 vm_vec_zero(&hit_pos);
6305 for ( i = 0; i < 3; i++ ) {
6306 vm_vec_add2( &hit_pos, &(shieldp->verts[stri.verts[i]].pos) );
6308 vm_vec_scale( &hit_pos, 1.0f/3.0f );
6309 add_shield_point_multi( OBJ_INDEX(objp), utri_num, &hit_pos );
6313 void send_player_stats_block_packet(net_player *pl, int stats_code, net_player *target)
6316 ubyte data[MAX_PACKET_SIZE], val;
6318 int packet_size = 0;
6323 sc = &pl->player->stats;
6326 BUILD_HEADER(PLAYER_STATS);
6328 // add the player id
6329 ADD_SHORT(pl->player_id);
6331 // add the byte indicating whether these stats are all-time or not
6332 val = (ubyte)stats_code;
6335 // kill information - alltime
6339 for(idx=0;idx<MAX_SHIP_TYPES;idx++){
6340 u_tmp = sc->kills[idx];
6343 // medal information
6344 for(idx=0;idx<NUM_MEDALS;idx++){
6345 i_tmp = sc->medals[idx];
6351 ADD_INT(sc->assists);
6352 ADD_INT(sc->kill_count);
6353 ADD_INT(sc->kill_count_ok);
6354 ADD_UINT(sc->p_shots_fired);
6355 ADD_UINT(sc->s_shots_fired);
6356 ADD_UINT(sc->p_shots_hit);
6357 ADD_UINT(sc->s_shots_hit);
6358 ADD_UINT(sc->p_bonehead_hits);
6359 ADD_UINT(sc->s_bonehead_hits);
6360 ADD_INT(sc->bonehead_kills);
6362 ADD_UINT(sc->missions_flown);
6363 ADD_UINT(sc->flight_time);
6364 ADD_INT(sc->last_flown);
6365 ADD_INT(sc->last_backup);
6370 for(idx=0;idx<MAX_SHIP_TYPES;idx++){
6371 u_tmp = sc->m_okKills[idx];
6375 ADD_INT(sc->m_score);
6376 ADD_INT(sc->m_assists);
6377 ADD_INT(sc->m_kill_count);
6378 ADD_INT(sc->m_kill_count_ok);
6379 ADD_UINT(sc->mp_shots_fired);
6380 ADD_UINT(sc->ms_shots_fired);
6381 ADD_UINT(sc->mp_shots_hit);
6382 ADD_UINT(sc->ms_shots_hit);
6383 ADD_UINT(sc->mp_bonehead_hits);
6384 ADD_UINT(sc->ms_bonehead_hits);
6385 ADD_INT(sc->m_bonehead_kills);
6386 ADD_INT(sc->m_player_deaths);
6387 ADD_INT(sc->m_medal_earned);
6390 case STATS_MISSION_KILLS:
6391 ADD_INT(sc->m_kill_count);
6392 ADD_INT(sc->m_kill_count_ok);
6393 ADD_INT(sc->m_assists);
6396 case STATS_DOGFIGHT_KILLS:
6397 for(idx=0; idx<MAX_PLAYERS; idx++){
6399 u_tmp = sc->m_dogfight_kills[idx];
6405 ADD_INT(sc->m_kill_count);
6406 ADD_INT(sc->m_kill_count_ok);
6407 ADD_INT(sc->m_assists);
6411 SDL_assert(packet_size < MAX_PACKET_SIZE);
6413 // if we're a client, always send the data to the server
6414 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
6415 multi_io_send_reliable(Net_player, data, packet_size);
6417 // otherwise do server specific stuff
6419 // send to a specific target
6421 multi_io_send_reliable(target, data, packet_size);
6423 // otherwise, send to everyone
6425 multi_io_send_to_all_reliable(data, packet_size);
6430 void process_player_stats_block_packet(ubyte *data, header *hinfo)
6434 scoring_struct *sc,bogus;
6436 int offset = HEADER_LENGTH;
6440 // nprintf(("Network","----------++++++++++********RECEIVED STATS***********+++++++++----------\n"));
6442 // get the player who these stats are for
6443 GET_SHORT(player_id);
6444 player_num = find_player_id(player_id);
6445 if (player_num == -1) {
6446 nprintf(("Network", "Couldn't find player for stats update!\n"));
6447 ml_string("Couldn't find player for stats update!");
6452 sc = &Net_players[player_num].player->stats;
6455 // get the stats code
6459 ml_string("Received STATS_ALLTIME");
6462 for (idx=0; idx<MAX_SHIP_TYPES; idx++) {
6464 sc->kills[idx] = u_tmp;
6467 // read in the stats
6468 for (idx=0; idx<NUM_MEDALS; idx++) {
6470 sc->medals[idx] = i_tmp;
6475 GET_INT(sc->assists);
6476 GET_INT(sc->kill_count);
6477 GET_INT(sc->kill_count_ok);
6478 GET_UINT(sc->p_shots_fired);
6479 GET_UINT(sc->s_shots_fired);
6480 GET_UINT(sc->p_shots_hit);
6481 GET_UINT(sc->s_shots_hit);
6482 GET_UINT(sc->p_bonehead_hits);
6483 GET_UINT(sc->s_bonehead_hits);
6484 GET_INT(sc->bonehead_kills);
6486 GET_UINT(sc->missions_flown);
6487 GET_UINT(sc->flight_time);
6488 GET_INT(sc->last_flown);
6489 GET_INT(sc->last_backup);
6493 ml_string("Received STATS_MISSION");
6495 // kills - mission OK
6496 for (idx=0; idx<MAX_SHIP_TYPES; idx++) {
6498 sc->m_okKills[idx] = u_tmp;
6501 GET_INT(sc->m_score);
6502 GET_INT(sc->m_assists);
6503 GET_INT(sc->m_kill_count);
6504 GET_INT(sc->m_kill_count_ok);
6505 GET_UINT(sc->mp_shots_fired);
6506 GET_UINT(sc->ms_shots_fired);
6507 GET_UINT(sc->mp_shots_hit);
6508 GET_UINT(sc->ms_shots_hit);
6509 GET_UINT(sc->mp_bonehead_hits);
6510 GET_UINT(sc->ms_bonehead_hits);
6511 GET_INT(sc->m_bonehead_kills);
6512 GET_INT(sc->m_player_deaths);
6513 GET_INT(sc->m_medal_earned);
6516 case STATS_MISSION_KILLS:
6517 ml_string("Received STATS_MISSION_KILLS");
6519 GET_INT(sc->m_kill_count);
6520 GET_INT(sc->m_kill_count_ok);
6521 GET_INT(sc->m_assists);
6524 case STATS_DOGFIGHT_KILLS:
6525 ml_string("Received STATS_DOGFIGHT_KILLS");
6526 if(player_num >= 0){
6527 ml_printf("Dogfight stats for %s", Net_players[player_num].player->callsign);
6529 for(idx=0; idx<MAX_PLAYERS; idx++){
6532 sc->m_dogfight_kills[idx] = u_tmp;
6533 if(player_num >= 0){
6534 ml_printf("%d", Net_players[player_num].player->stats.m_dogfight_kills[idx]);
6538 GET_INT(sc->m_kill_count);
6539 GET_INT(sc->m_kill_count_ok);
6540 GET_INT(sc->m_assists);
6545 // if I'm the server of the game, I should always rebroadcast these stats
6546 if ((Net_player->flags & NETINFO_FLAG_AM_MASTER) && (sc != &bogus)) {
6547 // make sure these are alltime stats
6548 SDL_assert(val == STATS_ALLTIME);
6550 multi_broadcast_stats(STATS_ALLTIME);
6554 // called to create asteroid stuff
6555 void send_asteroid_create( object *new_objp, object *parent_objp, int asteroid_type, vector *relvec )
6558 ubyte data[MAX_PACKET_SIZE];
6559 ubyte packet_type, atype;
6563 if (relvec != NULL )
6566 BUILD_HEADER( ASTEROID_INFO );
6567 packet_type = ASTEROID_CREATE;
6569 SDL_assert( asteroid_type < UCHAR_MAX );
6570 atype = (ubyte)asteroid_type;
6572 ADD_DATA( packet_type );
6573 ADD_USHORT( parent_objp->net_signature );
6574 ADD_USHORT( new_objp->net_signature );
6577 add_vector_data( data, &packet_size, vec );
6579 multi_io_send_to_all(data, packet_size);
6582 void send_asteroid_throw( object *objp )
6585 ubyte data[MAX_PACKET_SIZE], packet_type;
6587 BUILD_HEADER( ASTEROID_INFO );
6589 // this packet type is an asteroid throw
6590 packet_type = ASTEROID_THROW;
6591 ADD_DATA( packet_type );
6592 ADD_USHORT( objp->net_signature );
6593 //ADD_DATA( objp->pos );
6594 add_vector_data( data, &packet_size, objp->pos );
6595 //ADD_DATA( objp->phys_info.vel );
6596 add_vector_data( data, &packet_size, objp->phys_info.vel );
6598 multi_io_send_to_all(data, packet_size);
6601 void send_asteroid_hit( object *objp, object *other_objp, vector *hitpos, float damage )
6604 ubyte data[MAX_PACKET_SIZE], packet_type;
6608 if ( hitpos != NULL )
6611 // build up an asteroid hit packet
6612 BUILD_HEADER( ASTEROID_INFO );
6613 packet_type = ASTEROID_HIT;
6614 ADD_DATA( packet_type );
6615 ADD_USHORT( objp->net_signature );
6617 if(other_objp == NULL){
6618 ushort invalid_sig = 0xffff;
6619 ADD_USHORT(invalid_sig);
6621 ADD_USHORT( other_objp->net_signature );
6624 add_vector_data( data, &packet_size, vec );
6625 ADD_FLOAT( damage );
6627 multi_io_send_to_all(data, packet_size);
6630 void process_asteroid_info( ubyte *data, header *hinfo )
6635 offset = HEADER_LENGTH;
6636 GET_DATA( packet_type );
6638 // based on the packet type, do something interesting with an asteroid!
6639 switch( packet_type ) {
6641 case ASTEROID_CREATE: {
6642 ushort psignature, signature;
6644 vector relvec = ZERO_VECTOR;
6645 object *parent_objp;
6647 GET_USHORT( psignature );
6648 GET_USHORT( signature );
6650 //GET_DATA( relvec );
6651 get_vector_data( data, &offset, relvec );
6653 // after getting the values, set the next network signature, and call the create sub function
6654 multi_set_network_signature( signature, MULTI_SIG_ASTEROID );
6655 parent_objp = multi_get_network_object( psignature );
6656 if ( parent_objp ) {
6657 asteroid_sub_create( parent_objp, atype, &relvec );
6659 nprintf(("Network", "Couldn't create asteroid because parent wasn't found!!!\n"));
6666 // asteroid throw packet -- asteroid has wrapped bounds
6667 case ASTEROID_THROW: {
6669 vector pos = ZERO_VECTOR, vel = ZERO_VECTOR;
6672 GET_USHORT( signature );
6674 get_vector_data( data, &offset, pos );
6676 get_vector_data( data, &offset, vel );
6677 objp = multi_get_network_object( signature );
6679 nprintf(("Network", "Couldn't throw asteroid because couldn't find it\n"));
6683 objp->phys_info.vel = vel;
6684 objp->phys_info.desired_vel = vel;
6688 case ASTEROID_HIT: {
6689 ushort signature, osignature;
6690 object *objp, *other_objp;
6691 vector hitpos = ZERO_VECTOR;
6694 GET_USHORT( signature );
6695 GET_USHORT( osignature );
6696 //GET_DATA( hitpos );
6697 get_vector_data( data, &offset, hitpos );
6698 GET_FLOAT( damage );
6700 objp = multi_get_network_object( signature );
6701 if(osignature == 0xffff){
6704 other_objp = multi_get_network_object( osignature );
6707 nprintf(("Network", "Cannot hit asteroid because signature isn't found\n"));
6711 if ( IS_VEC_NULL(&hitpos) ){
6712 asteroid_hit( objp, other_objp, NULL, damage );
6714 asteroid_hit( objp, other_objp, &hitpos, damage );
6717 // if we know the other object is a weapon, then do a weapon hit to kill the weapon
6718 if ( other_objp && (other_objp->type == OBJ_WEAPON) ){
6719 weapon_hit( other_objp, objp, &hitpos );
6732 void send_host_restr_packet(const char *callsign, int code, int mode)
6734 ubyte data[MAX_PACKET_SIZE],val;
6735 int packet_size = 0;
6737 // build the header and add the opcode
6738 BUILD_HEADER(HOST_RESTR_QUERY);
6745 ADD_STRING(callsign);
6747 // if I'm the standalone server, I should be sending this to the game host
6748 if((Game_mode & GM_STANDALONE_SERVER) && (Netgame.host != NULL)){
6749 multi_io_send_reliable(Netgame.host, data, packet_size);
6751 // otherwise if I'm the host, I should be sending a reply back to the standalone server
6753 SDL_assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
6754 multi_io_send_reliable(Net_player, data, packet_size);
6758 void process_host_restr_packet(ubyte *data, header *hinfo)
6762 int offset = HEADER_LENGTH;
6764 // get the opcode and the callsign
6767 GET_STRING(callsign);
6770 // do code specific operations
6772 // query to the host from standalone
6774 SDL_assert((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER));
6776 // set the join mode
6777 Multi_join_restr_mode = mode;
6779 // set the timestamp
6780 Multi_restr_query_timestamp = timestamp(MULTI_QUERY_RESTR_STAMP);
6782 // notify the host of the event
6783 gamesnd_play_iface(SND_BRIEF_STAGE_CHG_FAIL);
6784 HUD_printf(XSTR("Player %s has tried to join - allow (y/n) ?",736),callsign);
6787 // affirmative reply from the host to the standalone
6789 SDL_assert(Game_mode & GM_STANDALONE_SERVER);
6791 // let the player join if the timestamp has not already elapsed on the server
6792 if(Multi_restr_query_timestamp != -1){
6793 multi_process_valid_join_request(&Multi_restr_join_request,&Multi_restr_addr,(int)mode);
6799 SDL_assert(Game_mode & GM_STANDALONE_SERVER);
6800 Netgame.flags &= ~(NG_FLAG_INGAME_JOINING);
6801 Multi_restr_query_timestamp = -1;
6806 void send_netgame_end_error_packet(int notify_code,int err_code)
6810 int packet_size = 0;
6812 // only the server should ever be here - although this might change if for some reason the host wants to end the game
6813 SDL_assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
6815 // build the header and add the notification and error codes
6816 BUILD_HEADER(NETGAME_END_ERROR);
6817 code = (char)notify_code;
6819 code = (char)err_code;
6823 multi_io_send_to_all_reliable(data, packet_size);
6826 void process_netgame_end_error_packet(ubyte *data, header *hinfo)
6828 int offset = HEADER_LENGTH;
6829 char notify_code,error_code;
6831 // get the error and notification codes
6832 GET_DATA(notify_code);
6833 GET_DATA(error_code);
6837 multi_quit_game(PROMPT_NONE,notify_code,error_code);
6840 // sends info that a countermeasure succeeded.
6841 void send_countermeasure_success_packet( int objnum )
6843 int pnum, packet_size;
6844 ubyte data[MAX_PACKET_SIZE];
6846 pnum = multi_find_player_by_object( &Objects[objnum] );
6848 nprintf(("Network", "Coulnd't find player for countermeasure success packet\n"));
6852 BUILD_HEADER(COUNTERMEASURE_SUCCESS);
6853 multi_io_send(&Net_players[pnum], data, packet_size);
6856 // start the flashing of my hud gauge
6857 void process_countermeasure_success_packet( ubyte *data, header *hinfo )
6861 offset = HEADER_LENGTH;
6864 hud_start_text_flash(XSTR("Evaded", 1430), 800);
6865 snd_play(&Snds[SND_MISSILE_EVADED_POPUP]);
6868 #define UPDATE_IS_PAUSED (1<<0)
6869 #define UPDATE_HULL_INFO (1<<1)
6871 void send_client_update_packet(net_player *pl)
6873 ubyte data[MAX_PACKET_SIZE],val;
6874 int packet_size = 0;
6877 BUILD_HEADER(CLIENT_UPDATE);
6881 // add the pause status
6882 if ( Multi_pause_status ) {
6883 val |= UPDATE_IS_PAUSED;
6884 } else if ( (Game_mode & GM_IN_MISSION) && !(pl->flags & NETINFO_FLAG_INGAME_JOIN) && !(NETPLAYER_IS_OBSERVER(pl)) && !(NETPLAYER_IS_DEAD(pl)) && (Objects[pl->player->objnum].type == OBJ_SHIP) ) {
6885 val |= UPDATE_HULL_INFO;
6886 SDL_assert( Player_ship ); // I"d better have one of these!!!!
6891 // if paused, add the net address of the guy who paused
6892 if(val & UPDATE_IS_PAUSED){
6893 SDL_assert(Multi_pause_pauser != NULL);
6894 ADD_SHORT(Multi_pause_pauser->player_id);
6897 // when not paused, send hull/shield/subsystem updates to all clients (except for ingame joiners)
6898 if ( val & UPDATE_HULL_INFO ) {
6900 ubyte percent, ns, threats;
6903 ship_subsys *subsysp;
6906 // get the object for the player
6907 SDL_assert( pl->player->objnum != -1 );
6909 objp = &Objects[pl->player->objnum];
6911 SDL_assert ( objp->type == OBJ_SHIP );
6912 shipp = &Ships[objp->instance];
6913 sip = &Ship_info[shipp->ship_info_index];
6915 // hull strength and sheild mesh information are floats (as a percentage). Pass the integer
6916 // percentage value since that should be close enough
6917 float temp = (objp->hull_strength / sip->initial_hull_strength * 100.0f);
6921 percent = (ubyte)temp;
6923 ADD_DATA( percent );
6925 for (i = 0; i < MAX_SHIELD_SECTIONS; i++ ) {
6926 percent = (ubyte)(objp->shields[i] / (sip->shields / MAX_SHIELD_SECTIONS) * 100.0f);
6927 ADD_DATA( percent );
6930 // add the data for the subsystem hits. We can assume that the lists will be the same side of
6931 // both machines. Added as percent since that number <= 100
6933 // also write out the number of subsystems. We do this because the client might not know
6934 // about the object he is getting data for. (i.e. he killed the object already).
6935 ns = (ubyte)sip->n_subsystems;
6938 // now the subsystems.
6939 for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) {
6940 percent = (ubyte)(subsysp->current_hits / subsysp->system_info->max_hits * 100.0f);
6941 ADD_DATA( percent );
6944 // compute the threats for this player. Only compute the threats if this player is actually
6945 // playing (i.e. he has a ship)
6946 hud_update_reticle( pl->player );
6947 threats = (ubyte)pl->player->threat_flags;
6948 ADD_DATA( threats );
6950 // add his energy level for guns
6951 ADD_FLOAT(shipp->weapon_energy);
6953 // add his secondary bank ammo
6954 ADD_INT(shipp->weapons.num_secondary_banks);
6955 for(i=0; i<shipp->weapons.num_secondary_banks; i++){
6956 ADD_INT(shipp->weapons.secondary_bank_ammo[i]);
6961 ADD_INT(pl->sv_last_pl);
6963 // send the packet reliably to the player
6964 multi_io_send(pl, data, packet_size);
6967 void process_client_update_packet(ubyte *data, header *hinfo)
6972 int is_paused, have_hull_info;
6975 float weapon_energy;
6976 int offset = HEADER_LENGTH;
6978 // get the header byte containing useful information
6981 is_paused = (val & UPDATE_IS_PAUSED)?1:0;
6982 have_hull_info = (val & UPDATE_HULL_INFO)?1:0;
6984 // if we are paused, get who paused
6987 player_index = find_player_id(pauser);
6988 if(player_index != -1){
6989 Multi_pause_pauser = &Net_players[player_index];
6991 Multi_pause_pauser = NULL;
6995 // if we have hull information, then read it in.
6996 if ( have_hull_info ) {
7000 ubyte hull_percent, shield_percent[MAX_SHIELD_SECTIONS], n_subsystems, subsystem_percent[MAX_MODEL_SUBSYSTEMS], threats;
7002 ship_subsys *subsysp;
7006 // hull strength and sheild mesh information are floats (as a percentage). Pass the integer
7007 // percentage value since that should be close enough
7008 GET_DATA( hull_percent );
7010 for (i = 0; i < MAX_SHIELD_SECTIONS; i++ ){
7012 shield_percent[i] = ub_tmp;
7015 // get the data for the subsystems
7016 GET_DATA( n_subsystems );
7017 for ( i = 0; i < n_subsystems; i++ ){
7019 subsystem_percent[i] = ub_tmp;
7022 GET_DATA( threats );
7024 // add his energy level for guns
7025 GET_FLOAT(weapon_energy);
7027 // add his secondary bank ammo
7028 GET_INT(ammo_count);
7029 for(i=0; i<ammo_count; i++){
7033 // assign the above information to my ship, assuming that I can find it! Ingame joiners might get this
7034 // packet because of delay between reliable packet acknowledging my ingame ship and the start of these
7035 // UDP client update packets. Only read this info if I have a ship.
7036 if ( !(Net_player->flags & NETINFO_FLAG_INGAME_JOIN) && (Player_ship != NULL) && (Player_obj != NULL) && (Net_player != NULL)) {
7037 shipp = Player_ship;
7039 sip = &Ship_info[shipp->ship_info_index];
7041 fval = hull_percent * sip->initial_hull_strength / 100.0f;
7042 objp->hull_strength = fval;
7044 for ( i = 0; i < MAX_SHIELD_SECTIONS; i++ ) {
7045 fval = (shield_percent[i] * sip->shields / 100.0f) / MAX_SHIELD_SECTIONS;
7046 objp->shields[i] = fval;
7049 // for sanity, be sure that the number of susbystems that I read in matches the player. If not,
7050 // then don't read these in.
7051 if ( n_subsystems == sip->n_subsystems ) {
7053 n_subsystems = 0; // reuse this variable
7054 for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) {
7057 fval = subsystem_percent[n_subsystems] * subsysp->system_info->max_hits / 100.0f;
7058 subsysp->current_hits = fval;
7060 // add the value just generated (it was zero'ed above) into the array of generic system types
7061 subsys_type = subsysp->system_info->type; // this is the generic type of subsystem
7062 SDL_assert ( subsys_type < SUBSYSTEM_MAX );
7063 shipp->subsys_info[subsys_type].current_hits += fval;
7067 ship_recalc_subsys_strength( shipp );
7069 shipp->weapon_energy = weapon_energy;
7070 for(i=0; i<ammo_count; i++){
7071 shipp->weapons.secondary_bank_ammo[i] = ammo[i];
7074 // update my threat flags.
7075 // temporarily commented out until tested.
7076 Net_player->player->threat_flags = threats;
7083 if(Net_player != NULL){
7084 Net_player->cl_last_pl = pl;
7088 // note, if we're already paused or unpaused, calling these will have no effect, so it is safe to do so
7089 if(!popup_active() && !(Net_player->flags & NETINFO_FLAG_RESPAWNING) && !(Net_player->flags & NETINFO_FLAG_LIMBO)){
7091 multi_pause_pause();
7093 multi_pause_unpause();
7098 void send_countdown_packet(int time)
7102 int packet_size = 0;
7104 // build the header and add the time
7105 BUILD_HEADER(COUNTDOWN);
7109 // if we're the server, we should broadcast to everyone
7110 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
7111 multi_io_send_to_all_reliable(data, packet_size);
7113 // otherwise we'de better be a host sending to the standalone
7115 SDL_assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
7116 multi_io_send_reliable(Net_player, data, packet_size);
7120 void process_countdown_packet(ubyte *data, header *hinfo)
7122 int offset = HEADER_LENGTH;
7129 // if we're not in the post sync data screen, ignore it
7130 if(gameseq_get_state() != GS_STATE_MULTI_MISSION_SYNC){
7134 // if we're the standalone, this should be a -1 telling us to start the countdown
7135 if(Game_mode & GM_STANDALONE_SERVER){
7136 SDL_assert((int)time == -1);
7138 // start the countdown
7139 multi_sync_start_countdown();
7141 // otherwise if we're clients, just bash the countdown
7143 Multi_sync_countdown = (int)time;
7147 // packets for debriefing information
7148 void send_debrief_info( int stage_count[], int *stages[] )
7150 ubyte data[MAX_PACKET_SIZE];
7151 int packet_size, i, j;
7154 BUILD_HEADER(DEBRIEF_INFO);
7156 // add the data for the teams
7157 for ( i = 0; i < Num_teams; i++ ) {
7160 count = stage_count[i];
7162 for ( j = 0; j < count; j++ ) {
7163 i_tmp = stages[i][j];
7168 multi_io_send_to_all_reliable(data, packet_size);
7171 // process a debrief info packet from the server
7172 void process_debrief_info( ubyte *data, header *hinfo )
7175 int stage_counts[MAX_TEAMS], active_stages[MAX_TEAMS][MAX_DEBRIEF_STAGES], *stages[MAX_TEAMS];
7178 offset = HEADER_LENGTH;
7179 for ( i = 0; i < Num_teams; i++ ) {
7183 stage_counts[i] = count;
7184 stages[i] = active_stages[i];
7185 for ( j = 0; j < count; j++ ) {
7187 active_stages[i][j] = i_tmp;
7192 // now that we have the stage data for the debriefing stages, call debrief function with the
7193 // data so that clients can now see the debriefing stuff. Do it only for my team.
7194 SDL_assert( (Net_player->p_info.team >= 0) && (Net_player->p_info.team < Num_teams) );
7195 debrief_set_multi_clients( stage_counts[Net_player->p_info.team], stages[Net_player->p_info.team] );
7198 // sends homing information to all clients. We only need signature and num_missiles (because of hornets).
7199 // sends homing_object and homing_subsystem to all clients.
7200 void send_homing_weapon_info( int weapon_num )
7202 ubyte data[MAX_PACKET_SIZE];
7205 object *homing_object;
7206 ushort homing_signature;
7209 wp = &Weapons[weapon_num];
7211 // be sure that this weapon object is a homing object.
7212 if ( !(Weapon_info[wp->weapon_info_index].wi_flags & WIF_HOMING) )
7215 // get the homing signature. If this weapon isn't homing on anything, then sent 0 as the
7216 // homing signature.
7217 homing_signature = 0;
7218 homing_object = wp->homing_object;
7219 if ( homing_object != NULL ) {
7220 homing_signature = homing_object->net_signature;
7222 // get the subsystem index.
7224 if ( (homing_object->type == OBJ_SHIP) && (wp->homing_subsys != NULL) ) {
7227 s_index = ship_get_index_from_subsys( wp->homing_subsys, OBJ_INDEX(homing_object), 1 );
7228 SDL_assert( s_index < CHAR_MAX ); // better be less than this!!!!
7229 t_subsys = (char)s_index;
7233 BUILD_HEADER(HOMING_WEAPON_UPDATE);
7234 ADD_USHORT( Objects[wp->objnum].net_signature );
7235 ADD_USHORT( homing_signature );
7236 ADD_DATA( t_subsys );
7238 multi_io_send_to_all(data, packet_size);
7241 // process a homing weapon info change packet. multiple_missiles parameter specifies is this
7242 // packet contains information for multiple weapons (like hornets).
7243 void process_homing_weapon_info( ubyte *data, header *hinfo )
7246 ushort weapon_signature, homing_signature;
7248 object *homing_object, *weapon_objp;
7251 offset = HEADER_LENGTH;
7253 // get the data for the packet
7254 GET_USHORT( weapon_signature );
7255 GET_USHORT( homing_signature );
7256 GET_DATA( h_subsys );
7259 // deal with changing this weapons homing information
7260 weapon_objp = multi_get_network_object( weapon_signature );
7261 if ( weapon_objp == NULL ) {
7262 nprintf(("Network", "Couldn't find weapon object for homing update -- skipping update\n"));
7265 SDL_assert( weapon_objp->type == OBJ_WEAPON );
7266 wp = &Weapons[weapon_objp->instance];
7268 // be sure that we can find these weapons and
7269 homing_object = multi_get_network_object( homing_signature );
7270 if ( homing_object == NULL ) {
7271 nprintf(("Network", "Couldn't find homing object for homing update\n"));
7275 if ( homing_object->type == OBJ_WEAPON ) {
7276 SDL_assert(Weapon_info[Weapons[homing_object->instance].weapon_info_index].wi_flags & WIF_BOMB);
7279 wp->homing_object = homing_object;
7280 wp->homing_subsys = NULL;
7281 wp->target_num = OBJ_INDEX(homing_object);
7282 wp->target_sig = homing_object->signature;
7283 if ( h_subsys != -1 ) {
7284 SDL_assert( homing_object->type == OBJ_SHIP );
7285 wp->homing_subsys = ship_get_indexed_subsys( &Ships[homing_object->instance], h_subsys);
7288 if ( homing_object->type == OBJ_SHIP ) {
7289 nprintf(("Network", "Updating homing information for weapon -- homing on %s\n", Ships[homing_object->instance].ship_name));
7293 void send_emp_effect(ushort net_sig, float intensity, float time)
7298 SDL_assert(MULTIPLAYER_MASTER);
7300 // build the packet and add the opcode
7301 BUILD_HEADER(EMP_EFFECT);
7302 ADD_USHORT(net_sig);
7303 ADD_FLOAT(intensity);
7306 // send it to the player
7307 multi_io_send_to_all(data, packet_size);
7310 void process_emp_effect(ubyte *data, header *hinfo)
7312 float intensity, time;
7315 int offset = HEADER_LENGTH;
7317 // read in the EMP effect data
7318 GET_USHORT(net_sig);
7319 GET_FLOAT(intensity);
7323 // try and find the object
7324 objp = multi_get_network_object(net_sig);
7325 if((objp != NULL) && (objp->type == OBJ_SHIP)){
7326 // if i'm not an observer and I have a valid ship, play the EMP effect
7327 if(!(Net_player->flags & NETINFO_FLAG_OBSERVER) && (Player_obj != NULL) && (Player_obj->type == OBJ_SHIP) && (Player_obj == objp)){
7328 emp_start_local(intensity, time);
7331 // start the effect for the ship itself
7332 emp_start_ship(objp, intensity, time);
7336 // tells whether or not reinforcements are available
7337 void send_reinforcement_avail( int rnum )
7342 BUILD_HEADER(REINFORCEMENT_AVAIL);
7344 multi_io_send_to_all_reliable(data, packet_size);
7347 void process_reinforcement_avail( ubyte *data, header *hinfo )
7352 offset = HEADER_LENGTH;
7356 // sanity check for a valid reinforcement number
7357 if ( (rnum >= 0) && (rnum < Num_reinforcements) ) {
7358 Reinforcements[rnum].flags |= RF_IS_AVAILABLE;
7362 void send_change_iff_packet(ushort net_signature, int new_team)
7364 ubyte data[MAX_PACKET_SIZE];
7365 int packet_size = 0;
7367 if(Net_player == NULL){
7370 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
7374 // build the packet and add the data
7375 BUILD_HEADER(CHANGE_IFF);
7376 ADD_USHORT(net_signature);
7379 // send to all players
7380 multi_io_send_to_all_reliable(data, packet_size);
7383 void process_change_iff_packet( ubyte *data, header *hinfo)
7385 int offset = HEADER_LENGTH;
7386 ushort net_signature;
7391 GET_USHORT(net_signature);
7395 // lookup the object
7396 objp = multi_get_network_object(net_signature);
7397 if((objp != NULL) && (objp->type == OBJ_SHIP) && (objp->instance >=0)){
7398 Ships[objp->instance].team = new_team;
7402 void send_NEW_primary_fired_packet(ship *shipp, int banks_fired)
7404 int packet_size, objnum;
7405 ubyte data[MAX_PACKET_SIZE]; // ubanks_fired, current_bank;
7408 net_player *ignore = NULL;
7410 // sanity checking for now
7411 SDL_assert ( banks_fired <= 3 );
7413 // get an object pointer for this ship.
7414 objnum = shipp->objnum;
7415 objp = &Objects[objnum];
7417 // if i'm a multiplayer client, I should never send primary fired packets for anyone except me
7418 if(MULTIPLAYER_CLIENT && (Player_obj != objp)){
7422 // just in case nothing got fired
7423 if(banks_fired <= 0){
7427 // ubanks_fired = (ubyte)banks_fired;
7428 // current_bank = (ubyte)shipp->weapons.current_primary_bank;
7429 // SDL_assert( current_bank <= 3 );
7431 // insert the current primary bank into this byte
7432 // ubanks_fired |= (current_bank << CURRENT_BANK_BIT);
7434 // append the SF_PRIMARY_LINKED flag on the top nibble of the banks_fired
7435 // if ( shipp->flags & SF_PRIMARY_LINKED ){
7436 // ubanks_fired |= (1<<7);
7439 // determine if its a player ship and don't send to him if we're in "client firing" mode
7440 // if((Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING) && MULTIPLAYER_MASTER){
7441 if(MULTIPLAYER_MASTER){
7442 np_index = multi_find_player_by_net_signature(objp->net_signature);
7443 if((np_index >= 0) && (np_index < MAX_PLAYERS)){
7444 ignore = &Net_players[np_index];
7448 // build up the standard weapon fired packet. This packet will get sent to all players if an AI
7449 // ship fired the primary weapons. If a player fired the weaspon, then this packet will get sent
7450 // to every player but the guy who actullly fired the weapon. This method is used to help keep client
7451 // and server in sync w.r.t. weapon energy for player ship
7452 BUILD_HEADER( PRIMARY_FIRED_NEW );
7453 ADD_USHORT(objp->net_signature);
7454 // ADD_DATA(ubanks_fired);
7456 // if I'm a server, broadcast to all players
7457 if(MULTIPLAYER_MASTER){
7458 multi_io_send_to_all(data, packet_size, ignore);
7461 multi_rate_add(1, "wfi", packet_size);
7463 // otherwise just send to the server
7465 multi_io_send(Net_player, data, packet_size);
7469 void process_NEW_primary_fired_packet(ubyte *data, header *hinfo)
7471 int offset; // linked;
7472 // ubyte banks_fired, current_bank;
7477 // read all packet info
7478 offset = HEADER_LENGTH;
7479 GET_USHORT(shooter_sig);
7480 // GET_DATA(banks_fired);
7483 // find the object this fired packet is operating on
7484 objp = multi_get_network_object( shooter_sig );
7485 if ( objp == NULL ) {
7486 nprintf(("Network", "Could not find ship for fire primary packet NEW!"));
7489 // if this object is not actually a valid ship, don't do anything
7490 if(objp->type != OBJ_SHIP){
7493 if(objp->instance < 0){
7496 // shipp = &Ships[objp->instance];
7498 // get the link status of the primary banks
7500 // if ( banks_fired & (1<<7) ) {
7502 // banks_fired ^= (1<<7);
7505 // get the current primary bank
7506 // current_bank = (ubyte)(banks_fired >> CURRENT_BANK_BIT);
7507 // current_bank &= 0x3;
7508 // SDL_assert( (current_bank >= 0) && (current_bank < MAX_PRIMARY_BANKS) );
7509 // shipp->weapons.current_primary_bank = current_bank;
7511 // strip off all remaining bits and just keep which banks were actually fired.
7512 // banks_fired &= 0x3;
7514 // set the link status of the ship if not the player. If it is the player, we will do sanity checking
7517 // shipp->flags &= ~SF_PRIMARY_LINKED;
7519 // shipp->flags |= SF_PRIMARY_LINKED;
7522 // if we're in client firing mode, ignore ones for myself
7523 if((Player_obj != NULL) && (Player_obj == objp)){
7527 ship_fire_primary( objp, 0, 1 );
7530 void send_NEW_countermeasure_fired_packet(object *objp, int cmeasure_count, int rand_val)
7532 ubyte data[MAX_PACKET_SIZE];
7535 net_player *ignore = NULL;
7537 // if i'm a multiplayer client, I should never send primary fired packets for anyone except me
7538 if(MULTIPLAYER_CLIENT && (Player_obj != objp)){
7542 SDL_assert ( cmeasure_count < UCHAR_MAX );
7543 BUILD_HEADER(COUNTERMEASURE_NEW);
7544 ADD_USHORT( objp->net_signature );
7545 ADD_INT( rand_val );
7547 nprintf(("Network","Sending NEW countermeasure packet!\n"));
7549 // determine if its a player ship and don't send to him if we're in "client firing" mode
7550 // if((Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING) && MULTIPLAYER_MASTER){
7551 if(MULTIPLAYER_MASTER){
7552 np_index = multi_find_player_by_net_signature(objp->net_signature);
7553 if((np_index >= 0) && (np_index < MAX_PLAYERS)){
7554 ignore = &Net_players[np_index];
7558 // if I'm the server, send to all players
7559 if(MULTIPLAYER_MASTER){
7560 multi_io_send_to_all(data, packet_size, ignore);
7562 // otherwise send to the server
7564 multi_io_send(Net_player, data, packet_size);
7568 void process_NEW_countermeasure_fired_packet(ubyte *data, header *hinfo)
7575 offset = HEADER_LENGTH;
7576 GET_USHORT( signature );
7577 GET_INT( rand_val );
7580 objp = multi_get_network_object( signature );
7581 if ( objp == NULL ) {
7582 nprintf(("network", "Could find object whose countermeasures are being launched!!!\n"));
7585 if(objp->type != OBJ_SHIP){
7589 // if we're in client firing mode, ignore ones for myself
7590 // if((Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING) && (Player_obj != NULL) && (Player_obj == objp)){
7591 if((Player_obj != NULL) && (Player_obj == objp)){
7595 // make it so ship can fire right away!
7596 Ships[objp->instance].cmeasure_fire_stamp = timestamp(0);
7597 if ( objp == Player_obj ){
7598 nprintf(("network", "firing countermeasure from my ship\n"));
7600 ship_launch_countermeasure( objp, rand_val );
7603 void send_beam_fired_packet(object *shooter, ship_subsys *turret, object *target, int beam_info_index, beam_info *override)
7605 ubyte data[MAX_PACKET_SIZE];
7606 int packet_size = 0;
7611 // only the server should ever be doing this
7612 SDL_assert(MULTIPLAYER_MASTER);
7614 // setup outgoing data
7615 SDL_assert(shooter != NULL);
7616 SDL_assert(turret != NULL);
7617 SDL_assert(target != NULL);
7618 SDL_assert(override != NULL);
7619 if((shooter == NULL) || (turret == NULL) || (target == NULL) || (override == NULL)){
7622 u_beam_info = (ubyte)beam_info_index;
7623 subsys_index = (char)ship_get_index_from_subsys(turret, OBJ_INDEX(shooter));
7624 SDL_assert(subsys_index >= 0);
7625 if(subsys_index < 0){
7629 // swap the beam_info override info into little endian byte order
7630 b_info.dir_a.xyz.x = INTEL_FLOAT(override->dir_a.xyz.x);
7631 b_info.dir_a.xyz.y = INTEL_FLOAT(override->dir_a.xyz.y);
7632 b_info.dir_a.xyz.z = INTEL_FLOAT(override->dir_a.xyz.z);
7634 b_info.dir_b.xyz.x = INTEL_FLOAT(override->dir_b.xyz.x);
7635 b_info.dir_b.xyz.y = INTEL_FLOAT(override->dir_b.xyz.y);
7636 b_info.dir_b.xyz.z = INTEL_FLOAT(override->dir_b.xyz.z);
7638 b_info.delta_ang = INTEL_FLOAT(override->delta_ang);
7639 b_info.shot_count = override->shot_count;
7641 for (int i = 0; i < b_info.shot_count; i++) {
7642 b_info.shot_aim[i] = INTEL_FLOAT(override->shot_aim[i]);
7646 BUILD_HEADER(BEAM_FIRED);
7647 ADD_USHORT(shooter->net_signature);
7648 ADD_DATA(subsys_index);
7649 ADD_USHORT(target->net_signature);
7650 ADD_DATA(u_beam_info);
7651 ADD_DATA(b_info); // FIXME: This is still wrong, we shouldn't be sending an entire struct over the wire - taylor
7653 // send to all clients
7654 multi_io_send_to_all_reliable(data, packet_size);
7657 void process_beam_fired_packet(ubyte *data, header *hinfo)
7660 ushort shooter_sig, target_sig;
7664 beam_fire_info fire_info;
7666 // only clients should ever get this
7667 SDL_assert(MULTIPLAYER_CLIENT);
7669 // read in packet data
7670 offset = HEADER_LENGTH;
7671 GET_USHORT(shooter_sig);
7672 GET_DATA(subsys_index);
7673 GET_USHORT(target_sig);
7674 GET_DATA(u_beam_info);
7679 // swap the beam_info override info into native byte order
7680 b_info.dir_a.xyz.x = INTEL_FLOAT( b_info.dir_a.xyz.x );
7681 b_info.dir_a.xyz.y = INTEL_FLOAT( b_info.dir_a.xyz.y );
7682 b_info.dir_a.xyz.z = INTEL_FLOAT( b_info.dir_a.xyz.z );
7683 b_info.dir_b.xyz.x = INTEL_FLOAT( b_info.dir_b.xyz.x );
7684 b_info.dir_b.xyz.y = INTEL_FLOAT( b_info.dir_b.xyz.y );
7685 b_info.dir_b.xyz.z = INTEL_FLOAT( b_info.dir_b.xyz.z );
7686 b_info.delta_ang = INTEL_FLOAT( b_info.delta_ang );
7688 for (i = 0; i < MAX_BEAM_SHOTS; i++) {
7689 b_info.shot_aim[i] = INTEL_FLOAT(b_info.shot_aim[i]);
7692 // lookup all relevant data
7693 fire_info.beam_info_index = (int)u_beam_info;
7694 fire_info.shooter = NULL;
7695 fire_info.target = NULL;
7696 fire_info.turret = NULL;
7697 fire_info.target_subsys = NULL;
7698 fire_info.beam_info_override = NULL;
7699 fire_info.shooter = multi_get_network_object(shooter_sig);
7700 fire_info.target = multi_get_network_object(target_sig);
7701 fire_info.beam_info_override = &b_info;
7702 fire_info.accuracy = 1.0f;
7703 if((fire_info.shooter == NULL) || (fire_info.shooter->type != OBJ_SHIP) || (fire_info.shooter->instance < 0) || (fire_info.shooter->instance > MAX_SHIPS) || (fire_info.target == NULL)){
7704 nprintf(("Network", "Couldn't get shooter/target info for BEAM weapon!\n"));
7707 fire_info.turret = ship_get_indexed_subsys( &Ships[fire_info.shooter->instance], (int)subsys_index);
7708 if(fire_info.turret == NULL){
7709 nprintf(("Network", "Couldn't get turret for BEAM weapon!\n"));
7714 beam_fire(&fire_info);
7717 void send_sw_query_packet(ubyte code, char *txt)
7719 ubyte data[MAX_PACKET_SIZE];
7720 int packet_size = 0;
7722 // build the packet and add the code
7723 BUILD_HEADER(SW_STD_QUERY);
7725 if((code == SW_STD_START) || (code == SW_STD_BAD)){
7729 // if I'm the host, send to standalone
7730 if(MULTIPLAYER_HOST){
7731 SDL_assert(!MULTIPLAYER_MASTER);
7732 SDL_assert(code == SW_STD_START);
7733 multi_io_send_reliable(Net_player, data, packet_size);
7735 // otherwise standalone sends back to host
7737 SDL_assert(Game_mode & GM_STANDALONE_SERVER);
7738 SDL_assert(code != SW_STD_START);
7739 SDL_assert(Netgame.host != NULL);
7740 if(Netgame.host != NULL){
7741 multi_io_send_reliable(Netgame.host, data, packet_size);
7746 void process_sw_query_packet(ubyte *data, header *hinfo)
7750 void send_event_update_packet(int event)
7752 ubyte data[MAX_PACKET_SIZE];
7753 ushort u_event = (ushort)event;
7754 int packet_size = 0;
7756 // build the header and add the event
7757 BUILD_HEADER(EVENT_UPDATE);
7758 ADD_USHORT(u_event);
7759 ADD_INT(Mission_events[event].flags);
7760 ADD_INT(Mission_events[event].formula);
7761 ADD_INT(Mission_events[event].result);
7762 ADD_INT(Mission_events[event].count);
7764 // send to all players
7765 multi_io_send_to_all_reliable(data, packet_size);
7768 void process_event_update_packet(ubyte *data, header *hinfo)
7770 int offset = HEADER_LENGTH;
7775 GET_USHORT(u_event);
7776 store_flags = Mission_events[u_event].flags;
7777 GET_INT(Mission_events[u_event].flags);
7778 GET_INT(Mission_events[u_event].formula);
7779 GET_INT(Mission_events[u_event].result);
7780 GET_INT(Mission_events[u_event].count);
7783 // went from non directive special to directive special
7784 if(!(store_flags & MEF_DIRECTIVE_SPECIAL) && (Mission_events[u_event].flags & MEF_DIRECTIVE_SPECIAL)){
7785 mission_event_set_directive_special(u_event);
7787 // if we went directive special to non directive special
7788 else if((store_flags & MEF_DIRECTIVE_SPECIAL) & !(Mission_events[u_event].flags & MEF_DIRECTIVE_SPECIAL)){
7789 mission_event_unset_directive_special(u_event);
7793 // weapon detonate packet
7794 void send_weapon_detonate_packet(object *objp)
7796 ubyte data[MAX_PACKET_SIZE];
7797 int packet_size = 0;
7800 SDL_assert(MULTIPLAYER_MASTER);
7801 if(!MULTIPLAYER_MASTER){
7804 SDL_assert(objp != NULL);
7809 // build the header and add the data
7810 BUILD_HEADER(WEAPON_DET);
7811 ADD_USHORT(objp->net_signature);
7813 // send to all players
7814 multi_io_send_to_all(data, packet_size);
7817 void process_weapon_detonate_packet(ubyte *data, header *hinfo)
7820 int offset = HEADER_LENGTH;
7821 object *objp = NULL;
7823 // get the weapon signature
7824 GET_USHORT(net_sig);
7827 // lookup the weapon
7828 objp = multi_get_network_object(net_sig);
7829 if((objp != NULL) && (objp->type == OBJ_WEAPON) && (objp->instance >= 0)){
7830 weapon_detonate(objp);
7834 // flak fired packet
7835 void send_flak_fired_packet(int ship_objnum, int subsys_index, int weapon_objnum, float flak_range)
7838 ushort pnet_signature;
7839 ubyte data[MAX_PACKET_SIZE], cindex;
7845 if((weapon_objnum < 0) || (Objects[weapon_objnum].type != OBJ_WEAPON) || (Objects[weapon_objnum].instance < 0) || (Weapons[Objects[weapon_objnum].instance].weapon_info_index < 0)){
7849 // local setup -- be sure we are actually passing a weapon!!!!
7850 objp = &Objects[weapon_objnum];
7851 SDL_assert ( objp->type == OBJ_WEAPON );
7852 pnet_signature = Objects[ship_objnum].net_signature;
7854 SDL_assert( subsys_index < UCHAR_MAX );
7855 cindex = (ubyte)subsys_index;
7857 ssp = ship_get_indexed_subsys( &Ships[Objects[ship_objnum].instance], subsys_index, NULL );
7862 // build the fire turret packet.
7863 BUILD_HEADER(FLAK_FIRED);
7864 packet_size += multi_pack_unpack_position(1, data + packet_size, &objp->orient.v.fvec);
7865 ADD_USHORT( pnet_signature );
7867 val = (short)ssp->submodel_info_1.angs.h;
7869 val = (short)ssp->submodel_info_2.angs.p;
7871 ADD_FLOAT( flak_range );
7873 multi_io_send_to_all(data, packet_size);
7875 multi_rate_add(1, "flk", packet_size);
7878 void process_flak_fired_packet(ubyte *data, header *hinfo)
7880 int offset, weapon_objnum, wid;
7881 ushort pnet_signature;
7889 short pitch, heading;
7892 // get the data for the turret fired packet
7893 offset = HEADER_LENGTH;
7894 offset += multi_pack_unpack_position(0, data + offset, &o_fvec);
7895 GET_USHORT( pnet_signature );
7896 GET_DATA( turret_index );
7897 GET_SHORT( heading );
7899 GET_FLOAT( flak_range );
7900 PACKET_SET_SIZE(); // move our counter forward the number of bytes we have read
7903 objp = multi_get_network_object( pnet_signature );
7904 if ( objp == NULL ) {
7905 nprintf(("network", "could find parent object with net signature %d for flak firing\n", pnet_signature));
7909 // if this isn't a ship, do nothing
7910 if ( objp->type != OBJ_SHIP ){
7914 // make an orientation matrix from the o_fvec
7915 vm_vector_2_matrix(&orient, &o_fvec, NULL, NULL);
7917 // find this turret, and set the position of the turret that just fired to be where it fired. Quite a
7918 // hack, but should be suitable.
7919 shipp = &Ships[objp->instance];
7920 ssp = ship_get_indexed_subsys( shipp, turret_index, NULL );
7924 wid = ssp->system_info->turret_weapon_type;
7925 if((wid < 0) || !(Weapon_info[wid].wi_flags & WIF_FLAK)){
7929 // bash the position and orientation of the turret
7930 ssp->submodel_info_1.angs.h = (float)heading;
7931 ssp->submodel_info_2.angs.p = (float)pitch;
7933 // get the world position of the weapon
7934 ship_get_global_turret_info(objp, ssp->system_info, &pos, &dir);
7936 // create the weapon object
7937 weapon_objnum = weapon_create( &pos, &orient, wid, OBJ_INDEX(objp), 0, -1, 1);
7938 if (weapon_objnum != -1) {
7939 if ( Weapon_info[wid].launch_snd != -1 ) {
7940 snd_play_3d( &Snds[Weapon_info[wid].launch_snd], &pos, &View_position );
7943 // create a muzzle flash from a flak gun based upon firing position and weapon type
7944 flak_muzzle_flash(&pos, &dir, wid);
7946 // set its range explicitly - make it long enough so that it's guaranteed to still exist when the server tells us it blew up
7947 flak_set_range(&Objects[weapon_objnum], &pos, (float)flak_range);
7951 #define ADD_NORM_VEC(d) do { SDL_assert((packet_size + 3) < MAX_PACKET_SIZE); char vnorm[3] = { (char)(d.x * 127.0f), (char)(d.y * 127.0f), (char)(d.z * 127.0f) }; memcpy(data + packet_size, vnorm, 3); packet_size += 3; } while(0);
7952 #define GET_NORM_VEC(d) do { char vnorm[3]; memcpy(vnorm, data+offset, 3); d.x = (float)vnorm[0] / 127.0f; d.y = (float)vnorm[1] / 127.0f; d.z = (float)vnorm[2] / 127.0f; } while(0);
7954 // player pain packet
7955 void send_player_pain_packet(net_player *pl, int weapon_info_index, float damage, vector *force, vector *hitpos)
7957 ubyte data[MAX_PACKET_SIZE];
7960 int packet_size = 0;
7962 SDL_assert(MULTIPLAYER_MASTER);
7963 if(!MULTIPLAYER_MASTER){
7966 SDL_assert(pl != NULL);
7971 // build the packet and add the code
7972 BUILD_HEADER(NETPLAYER_PAIN);
7973 windex = (ubyte)weapon_info_index;
7975 udamage = (ushort)damage;
7976 ADD_USHORT(udamage);
7977 //ADD_DATA((*force));
7978 add_vector_data( data, &packet_size, *force );
7979 //ADD_DATA((*hitpos));
7980 add_vector_data( data, &packet_size, *hitpos );
7982 // send to the player
7983 multi_io_send(pl, data, packet_size);
7985 multi_rate_add(1, "pai", packet_size);
7988 void process_player_pain_packet(ubyte *data, header *hinfo)
7993 vector force = ZERO_VECTOR;
7994 vector local_hit_pos = ZERO_VECTOR;
7997 // get the data for the pain packet
7998 offset = HEADER_LENGTH;
8000 GET_USHORT(udamage);
8002 get_vector_data( data, &offset, force );
8003 //GET_DATA(local_hit_pos);
8004 get_vector_data( data, &offset, local_hit_pos );
8007 mprintf(("PAIN!\n"));
8009 // get weapon info pointer
8010 //SDL_assert((windex >= 0) && (windex < Num_weapon_types) && (Weapon_info[windex].subtype == WP_LASER)); // always true
8011 if(! ((windex != 255) && (windex < Num_weapon_types) && (Weapon_info[windex].subtype == WP_LASER)) ){
8014 wip = &Weapon_info[windex];
8016 // play the weapon hit sound
8017 SDL_assert(Player_obj != NULL);
8018 if(Player_obj == NULL){
8021 weapon_hit_do_sound(Player_obj, wip, &Player_obj->pos);
8023 // we need to do 3 things here. player pain (game flash), weapon hit sound, ship_apply_whack()
8024 ship_hit_pain((float)udamage);
8027 ship_apply_whack(&force, &local_hit_pos, Player_obj);
8031 void send_lightning_packet(int bolt_type, vector *start, vector *strike)
8033 ubyte data[MAX_PACKET_SIZE];
8035 int packet_size = 0;
8037 // build the header and add the data
8038 BUILD_HEADER(LIGHTNING_PACKET);
8039 val = (char)bolt_type;
8041 //ADD_DATA((*start));
8042 add_vector_data( data, &packet_size, *start );
8043 //ADD_DATA((*strike));
8044 add_vector_data( data, &packet_size, *strike );
8046 // send to everyone unreliable for now
8047 multi_io_send_to_all(data, packet_size);
8050 void process_lightning_packet(ubyte *data, header *hinfo)
8054 vector start = ZERO_VECTOR, strike = ZERO_VECTOR;
8057 offset = HEADER_LENGTH;
8058 GET_DATA(bolt_type);
8060 get_vector_data(data, &offset, start);
8061 // GET_DATA(strike);
8062 get_vector_data(data, &offset, strike);
8071 nebl_bolt(bolt_type, &start, &strike);
8074 void send_bytes_recvd_packet(net_player *pl)
8076 // only clients should ever be doing this
8081 ubyte data[MAX_PACKET_SIZE];
8082 int packet_size = 0;
8083 BUILD_HEADER(BYTES_SENT);
8084 ADD_INT(pl->cl_bytes_recvd);
8086 // send to the server
8087 multi_io_send_reliable(pl, data, packet_size);
8090 void process_bytes_recvd_packet(ubyte *data, header *hinfo)
8094 net_player *pl = NULL;
8095 int offset = HEADER_LENGTH;
8101 if(Net_player == NULL){
8104 if(!MULTIPLAYER_MASTER){
8108 // make sure we know what player sent this
8109 pid = find_player_id(hinfo->id);
8110 if((pid < 0) || (pid >= MAX_PLAYERS)){
8113 pl = &Net_players[pid];
8116 pl->cl_bytes_recvd = bytes;
8120 pl->sv_last_pl = (int)(100.0f * (1.0f - ((float)pl->cl_bytes_recvd / (float)pl->sv_bytes_sent)));
8123 pl->sv_bytes_sent = 0;
8127 void send_host_captain_change_packet(short player_id, int captain_change)
8129 ubyte data[MAX_PACKET_SIZE];
8130 int packet_size = 0;
8133 BUILD_HEADER(TRANSFER_HOST);
8134 ADD_SHORT(player_id);
8135 ADD_INT(captain_change);
8138 multi_io_send_to_all_reliable(data, packet_size);
8141 void process_host_captain_change_packet(ubyte *data, header *hinfo)
8143 int offset = HEADER_LENGTH;
8144 int idx, found_player, captain_change;
8147 // get the player id
8148 GET_SHORT(player_id);
8149 GET_INT(captain_change);
8155 for(idx=0; idx<MAX_PLAYERS; idx++){
8156 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].player_id == player_id)){
8157 HUD_printf("%s is the new captain of team %d", Net_players[idx].player->callsign, Net_players[idx].p_info.team + 1);
8162 // unflag all old players
8163 for(idx=0; idx<MAX_PLAYERS; idx++){
8164 Net_players[idx].flags &= ~NETINFO_FLAG_GAME_HOST;
8169 for(idx=0; idx<MAX_PLAYERS; idx++){
8170 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].player_id == player_id)){
8171 Net_players[idx].flags |= NETINFO_FLAG_GAME_HOST;
8173 // spew to the HUD config
8174 if(Net_players[idx].player != NULL){
8175 HUD_printf("%s is the new game host", Net_players[idx].player->callsign);
8185 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_HOST_LEFT);
8190 void send_self_destruct_packet()
8192 ubyte data[MAX_PACKET_SIZE];
8193 int packet_size = 0;
8196 if(Net_player == NULL){
8200 // if i'm the server, I shouldn't be here
8201 SDL_assert(!(Net_player->flags & NETINFO_FLAG_AM_MASTER));
8202 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8206 // only if this is valid
8207 if(MULTI_OBSERVER(Net_players[MY_NET_PLAYER_NUM])){
8212 if((Player_ship == NULL) || (Player_obj == NULL)){
8217 BUILD_HEADER(SELF_DESTRUCT);
8218 ADD_USHORT(Player_obj->net_signature);
8220 // send to the server
8221 multi_io_send_reliable(Net_player, data, packet_size);
8224 void process_self_destruct_packet(ubyte *data, header *hinfo)
8226 int offset = HEADER_LENGTH;
8230 // get the net signature
8231 GET_USHORT(net_sig);
8235 np_index = find_player_id(hinfo->id);
8239 if(MULTI_OBSERVER(Net_players[np_index])){
8242 if(Net_players[np_index].player == NULL){
8245 if((Net_players[np_index].player->objnum < 0) || (Net_players[np_index].player->objnum >= MAX_OBJECTS)){
8248 if(Objects[Net_players[np_index].player->objnum].net_signature != net_sig){
8251 if(Objects[Net_players[np_index].player->objnum].type != OBJ_SHIP){
8254 if((Objects[Net_players[np_index].player->objnum].instance < 0) || (Objects[Net_players[np_index].player->objnum].instance >= MAX_SHIPS)){
8259 ship_self_destruct(&Objects[Net_players[np_index].player->objnum]);