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();
2386 // go through the server list and query each of those as well
2387 s_moveup = Game_server_head;
2388 if(s_moveup != NULL){
2390 send_server_query(&s_moveup->server_addr);
2391 s_moveup = s_moveup->next;
2392 } while(s_moveup != Game_server_head);
2396 fill_net_addr(&addr, Psnet_my_addr.addr, DEFAULT_GAME_PORT);
2398 // send out a broadcast if our options allow us
2399 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
2400 psnet_broadcast( &addr, data, packet_size);
2404 // send an individual query to an address to see if there is an active game
2405 void send_server_query(net_addr *addr)
2408 ubyte data[MAX_PACKET_SIZE];
2410 // build the header and send the data
2411 BUILD_HEADER(GAME_QUERY);
2412 psnet_send(addr, data, packet_size);
2415 // process a query from a client looking for active freespace games
2416 void process_game_query(ubyte* data, header* hinfo)
2421 offset = HEADER_LENGTH;
2425 // check to be sure that we don't capture our own broadcast message
2426 fill_net_addr(&addr, hinfo->addr, hinfo->port);
2427 if ( psnet_same( &addr, &Psnet_my_addr) ){
2431 // if I am not a server of a game, don't send a reply!!!
2432 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) ){
2436 // if the game options are being selected, then ignore the request
2437 // also, if Netgame.max_players == -1, the host has not chosen a mission yet and we should wait
2438 if((Netgame.game_state == NETGAME_STATE_STD_HOST_SETUP) || (Netgame.game_state == NETGAME_STATE_HOST_SETUP) || (Netgame.game_state == 0) || (Netgame.max_players == -1)){
2442 // send information about this active game
2443 send_game_active_packet(&addr);
2446 // sends information about netplayers in the game. if called on the server, broadcasts information about _all_ players
2447 void send_netplayer_update_packet( net_player *pl )
2449 int packet_size,idx;
2450 ubyte data[MAX_PACKET_SIZE],val;
2452 BUILD_HEADER(NETPLAYER_UPDATE);
2454 // if I'm the server of the game, I should send an update for _all_players in the game
2455 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2456 for(idx=0;idx<MAX_PLAYERS;idx++){
2457 // only send info for connected players
2458 if(MULTI_CONNECTED(Net_players[idx])){
2463 // add the net player's information
2464 ADD_SHORT(Net_players[idx].player_id);
2465 ADD_INT(Net_players[idx].state);
2466 ADD_INT(Net_players[idx].p_info.ship_class);
2467 ADD_INT(Net_players[idx].tracker_player_id);
2469 if(Net_players[idx].flags & NETINFO_FLAG_HAS_CD){
2477 // add the final stop byte
2481 // broadcast the packet
2482 if(!(Game_mode & GM_IN_MISSION)){
2484 multi_io_send_to_all_reliable(data, packet_size);
2486 multi_io_send_reliable(pl, data, packet_size);
2490 multi_io_send_to_all(data, packet_size);
2492 multi_io_send(pl, data, packet_size);
2500 // add my current state in the netgame to this packet
2501 ADD_SHORT(Net_player->player_id);
2502 ADD_INT(Net_player->state);
2503 ADD_INT(Net_player->p_info.ship_class);
2504 ADD_INT(Multi_tracker_id);
2506 // add if I have a CD or not
2514 // add a final stop byte
2518 // send the packet to the server
2519 SDL_assert( pl == NULL ); // shouldn't ever be the case that pl is non-null here.
2520 if(!(Game_mode & GM_IN_MISSION)){
2521 multi_io_send_reliable(Net_player, data, packet_size);
2523 multi_io_send(Net_player, data, packet_size);
2528 // process an incoming netplayer state update. if we're the server, we should rebroadcast
2529 void process_netplayer_update_packet( ubyte *data, header *hinfo )
2531 int offset, player_num;
2537 offset = HEADER_LENGTH;
2539 // get the first stop byte
2542 while(stop != 0xff){
2543 // look the player up
2544 GET_SHORT(player_id);
2545 player_num = find_player_id(player_id);
2546 // if we couldn't find him, read in the bogus data
2547 if((player_num == -1) || (Net_player == &Net_players[player_num])){
2548 GET_INT(bogus.state);
2549 GET_INT(bogus.p_info.ship_class);
2550 GET_INT(bogus.tracker_player_id);
2554 // otherwise read in the data correctly
2557 GET_INT(Net_players[player_num].p_info.ship_class);
2558 GET_INT(Net_players[player_num].tracker_player_id);
2561 Net_players[player_num].flags |= NETINFO_FLAG_HAS_CD;
2563 Net_players[player_num].flags &= ~(NETINFO_FLAG_HAS_CD);
2566 // if he's changing state to joined, send a team update
2567 if((Net_players[player_num].state == NETPLAYER_STATE_JOINING) && (new_state == NETPLAYER_STATE_JOINED) && (Netgame.type_flags & NG_TYPE_TEAM)){
2568 multi_team_send_update();
2572 Net_players[player_num].state = new_state;
2575 // get the next stop byte
2581 // if I'm the host or the server of the game, update everyone else so things are synched up as tightly as possible
2582 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2583 send_netplayer_update_packet(NULL);
2586 // if i'm the standalone and this is an update from the host, maybe change some netgame settings
2587 if((Game_mode & GM_STANDALONE_SERVER) && (player_num != -1) && (Net_players[player_num].flags & NETINFO_FLAG_GAME_HOST)){
2588 switch(Net_players[player_num].state){
2589 case NETPLAYER_STATE_STD_HOST_SETUP:
2590 Netgame.game_state = NETGAME_STATE_STD_HOST_SETUP;
2593 case NETPLAYER_STATE_HOST_SETUP:
2594 // check for race conditions
2595 if(Netgame.game_state != NETGAME_STATE_MISSION_SYNC){
2596 Netgame.game_state = NETGAME_STATE_FORMING;
2603 #define EXTRA_DEATH_VAPORIZED (1<<0)
2604 #define EXTRA_DEATH_WASHED (1<<1)
2605 // send a packet indicating a ship has been killed
2606 void send_ship_kill_packet( object *objp, object *other_objp, float percent_killed, int self_destruct )
2608 int packet_size, model;
2609 ubyte data[MAX_PACKET_SIZE], was_player, extra_death_info, vaporized;
2610 ushort debris_signature;
2614 // only sendable from the master
2615 SDL_assert ( Net_player->flags & NETINFO_FLAG_AM_MASTER );
2618 vaporized = ( (Ships[objp->instance].flags & SF_VAPORIZE) > 0 );
2620 extra_death_info = 0;
2622 extra_death_info |= EXTRA_DEATH_VAPORIZED;
2625 if ( Ships[objp->instance].wash_killed ) {
2626 extra_death_info |= EXTRA_DEATH_WASHED;
2629 // find out the next network signature that will be used for the debris pieces.
2630 model = Ships[objp->instance].modelnum;
2631 pm = model_get(model);
2632 debris_signature = 0;
2633 if ( pm && !vaporized ) {
2634 debris_signature = multi_get_next_network_signature( MULTI_SIG_DEBRIS );
2635 multi_set_network_signature( (ushort)(debris_signature + pm->num_debris_objects), MULTI_SIG_DEBRIS );
2636 Ships[objp->instance].arrival_distance = debris_signature;
2639 BUILD_HEADER(SHIP_KILL);
2640 ADD_USHORT(objp->net_signature);
2642 // ships which are initially killed get the rest of the data sent. self destructed ships and
2643 if ( other_objp == NULL ) {
2648 nprintf(("Network","Don't know other_obj for ship kill packet, sending NULL\n"));
2650 ADD_USHORT( other_objp->net_signature );
2653 ADD_USHORT( debris_signature );
2654 ADD_FLOAT( percent_killed );
2655 sd = (ubyte)self_destruct;
2657 ADD_DATA( extra_death_info );
2659 // if the ship who died is a player, then send some extra info, like who killed him, etc.
2661 if ( objp->flags & OF_PLAYER_SHIP ) {
2665 pnum = multi_find_player_by_object( objp );
2668 ADD_DATA( was_player );
2670 SDL_assert(Net_players[pnum].player->killer_objtype < CHAR_MAX);
2671 temp = (char)Net_players[pnum].player->killer_objtype;
2674 SDL_assert(Net_players[pnum].player->killer_species < CHAR_MAX);
2675 temp = (char)Net_players[pnum].player->killer_species;
2678 SDL_assert(Net_players[pnum].player->killer_weapon_index < CHAR_MAX);
2679 temp = (char)Net_players[pnum].player->killer_weapon_index;
2682 ADD_STRING( Net_players[pnum].player->killer_parent_name );
2684 ADD_DATA( was_player );
2687 ADD_DATA( was_player );
2690 // send the packet reliably!!!
2691 multi_io_send_to_all_reliable(data, packet_size);
2694 // process a packet indicating that a ship has been killed
2695 void process_ship_kill_packet( ubyte *data, header *hinfo )
2698 ushort ship_sig, other_sig, debris_sig;
2699 object *sobjp, *oobjp;
2700 float percent_killed;
2701 ubyte was_player, extra_death_info, sd;
2702 char killer_name[NAME_LENGTH], killer_objtype = OBJ_NONE, killer_species = SPECIES_TERRAN, killer_weapon_index = -1;
2704 offset = HEADER_LENGTH;
2705 GET_USHORT(ship_sig);
2707 GET_USHORT( other_sig );
2708 GET_USHORT( debris_sig );
2709 GET_FLOAT( percent_killed );
2711 GET_DATA( extra_death_info );
2712 GET_DATA( was_player );
2715 // pnum is >=0 when the dying ship is a pleyer ship. Get the info about how he died
2716 if ( was_player != 0 ) {
2717 GET_DATA( killer_objtype );
2718 GET_DATA( killer_species );
2719 GET_DATA( killer_weapon_index );
2720 GET_STRING( killer_name );
2725 sobjp = multi_get_network_object( ship_sig );
2727 // if I am unable to find the ship object which was killed, I have to bail and rely on getting
2728 // another message from the server that this happened!
2729 if ( sobjp == NULL ) {
2730 nprintf(("Network", "Couldn't find net signature %d for kill packet\n", ship_sig));
2734 // set this ship's hull value to 0
2735 sobjp->hull_strength = 0.0f;
2737 // maybe set vaporized
2738 if (extra_death_info & EXTRA_DEATH_VAPORIZED) {
2739 Ships[sobjp->instance].flags |= SF_VAPORIZE;
2742 // maybe set wash_killed
2743 if (extra_death_info & EXTRA_DEATH_VAPORIZED) {
2744 Ships[sobjp->instance].wash_killed = 1;
2747 oobjp = multi_get_network_object( other_sig );
2749 if ( was_player != 0 ) {
2752 pnum = multi_find_player_by_object( sobjp );
2754 Net_players[pnum].player->killer_objtype = killer_objtype;
2755 Net_players[pnum].player->killer_species = killer_species;
2756 Net_players[pnum].player->killer_weapon_index = killer_weapon_index;
2757 SDL_strlcpy( Net_players[pnum].player->killer_parent_name, killer_name, NAME_LENGTH );
2761 // check to see if I need to respawn myself
2762 multi_respawn_check(sobjp);
2764 // store the debris signature in the arrival distance which will never get used for player ships
2765 Ships[sobjp->instance].arrival_distance = debris_sig;
2767 // set this bit so that we don't accidentally start switching targets when we die
2768 if(sobjp == Player_obj){
2769 Game_mode |= GM_DEAD_DIED;
2772 nprintf(("Network", "Killing off %s\n", Ships[sobjp->instance].ship_name));
2774 // do the normal thing when not ingame joining. When ingame joining, simply kill off the ship.
2775 if ( !(Net_player->flags & NETINFO_FLAG_INGAME_JOIN) ) {
2776 ship_hit_kill( sobjp, oobjp, percent_killed, sd );
2778 extern void ship_destroyed( int shipnum );
2779 ship_destroyed( sobjp->instance );
2780 sobjp->flags |= OF_SHOULD_BE_DEAD;
2781 obj_delete( OBJ_INDEX(sobjp) );
2785 // send a packet indicating a ship should be created
2786 void send_ship_create_packet( object *objp, int is_support )
2789 ubyte data[MAX_PACKET_SIZE];
2791 // We will pass the ship to create by name.
2792 BUILD_HEADER(SHIP_CREATE);
2793 ADD_USHORT(objp->net_signature);
2794 ADD_INT( is_support );
2796 add_vector_data(data, &packet_size, objp->pos);
2799 // broadcast the packet
2800 multi_io_send_to_all_reliable(data, packet_size);
2803 // process a packet indicating a ship should be created
2804 void process_ship_create_packet( ubyte *data, header *hinfo )
2806 int offset, objnum, is_support;
2809 vector pos = ZERO_VECTOR;
2811 SDL_assert ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
2812 offset = HEADER_LENGTH;
2813 GET_USHORT(signature);
2814 GET_INT( is_support );
2816 get_vector_data(data, &offset, pos);
2821 // find the name of this ship on ship ship arrival list. if found, pass it to parse_object_create
2822 if ( !is_support ) {
2823 objp = mission_parse_get_arrival_ship( signature );
2824 if ( objp != NULL ) {
2825 parse_create_object(objp);
2827 nprintf(("Network", "Ship with sig %d not found on ship arrival list -- not creating!!\n", signature));
2830 SDL_assert( Arriving_support_ship );
2831 if(Arriving_support_ship == NULL){
2834 Arriving_support_ship->pos = pos;
2835 Arriving_support_ship->net_signature = signature;
2836 objnum = parse_create_object( Arriving_support_ship );
2837 SDL_assert( objnum != -1 );
2839 mission_parse_support_arrived( objnum );
2844 // send a packet indicating a wing of ships should be created
2845 void send_wing_create_packet( wing *wingp, int num_to_create, int pre_create_count )
2847 int packet_size, index, ship_instance;
2848 ubyte data[MAX_PACKET_SIZE];
2852 // for creating wing -- we just send the index into the wing array of this wing.
2853 // all players load the same mission, and so their array's should all match. We also
2854 // need to send the signature of the first ship that was created. We can find this by
2855 // looking num_to_create places back in the ship_index field in the wing structure.
2857 index = WING_INDEX(wingp);
2858 ship_instance = wingp->ship_index[wingp->current_count - num_to_create];
2859 signature = Objects[Ships[ship_instance].objnum].net_signature;
2861 BUILD_HEADER( WING_CREATE );
2863 ADD_INT(num_to_create);
2864 ADD_USHORT(signature);
2865 ADD_INT(pre_create_count);
2866 val = wingp->current_wave - 1;
2869 multi_io_send_to_all_reliable(data, packet_size);
2872 // process a packet saying that a wing should be created
2873 void process_wing_create_packet( ubyte *data, header *hinfo )
2875 int offset, index, num_to_create;
2877 int total_arrived_count, current_wave;
2879 offset = HEADER_LENGTH;
2881 GET_INT(num_to_create);
2882 GET_USHORT(signature);
2883 GET_INT(total_arrived_count);
2884 GET_INT(current_wave);
2888 // do a sanity check on the wing to be sure that we are actually working on a valid wing
2889 if ( (index < 0) || (index >= num_wings) || (Wings[index].num_waves == -1) ) {
2890 nprintf(("Network", "invalid index %d for wing create packet\n"));
2893 if ( (num_to_create <= 0) || (num_to_create > Wings[index].wave_count) ) {
2894 nprintf(("Network", "Invalid number of ships to create (%d) for wing %s\n", num_to_create, Wings[index].name));
2899 Wings[index].current_count = 0;
2900 Wings[index].total_arrived_count = total_arrived_count;
2901 Wings[index].current_wave = current_wave;
2903 // set the network signature that was passed. The client should create ships in the same order
2904 // as the server -- so all ships should get the same sigs as assigned by the server. We also
2905 // need to set some timestamps and cues correctly to be sure that these things get created on
2906 // the clients correctly
2907 multi_set_network_signature( signature, MULTI_SIG_SHIP );
2908 parse_wing_create_ships( &Wings[index], num_to_create, 1 );
2911 // packet indicating a ship is departing
2912 void send_ship_depart_packet( object *objp )
2914 ubyte data[MAX_PACKET_SIZE];
2918 signature = objp->net_signature;
2920 BUILD_HEADER(SHIP_DEPART);
2921 ADD_USHORT( signature );
2923 multi_io_send_to_all_reliable(data, packet_size);
2926 // process a packet indicating a ship is departing
2927 void process_ship_depart_packet( ubyte *data, header *hinfo )
2933 offset = HEADER_LENGTH;
2934 GET_USHORT( signature );
2937 // find the object which is departing
2938 objp = multi_get_network_object( signature );
2939 if ( objp == NULL ) {
2940 nprintf(("network", "Couldn't find object with net signature %d to depart\n", signature ));
2944 // start warping him out
2945 shipfx_warpout_start( objp );
2948 // packet to tell clients cargo of a ship was revealed to all
2949 void send_cargo_revealed_packet( ship *shipp )
2951 ubyte data[MAX_PACKET_SIZE];
2954 // build the header and add the data
2955 BUILD_HEADER(CARGO_REVEALED);
2956 ADD_USHORT( Objects[shipp->objnum].net_signature );
2958 // server sends to all players
2959 if(MULTIPLAYER_MASTER){
2960 multi_io_send_to_all_reliable(data, packet_size);
2962 // clients just send to the server
2964 multi_io_send_reliable(Net_player, data, packet_size);
2968 // process a cargo revealed packet
2969 void process_cargo_revealed_packet( ubyte *data, header *hinfo )
2975 offset = HEADER_LENGTH;
2976 GET_USHORT(signature);
2979 // get a ship pointer and call the ship function to reveal the cargo
2980 objp = multi_get_network_object( signature );
2981 if ( objp == NULL ) {
2982 nprintf(("Network", "Could not find object with net signature %d for cargo revealed\n", signature ));
2986 // SDL_assert( objp->type == OBJ_SHIP );
2987 if((objp->type != OBJ_SHIP) || (objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
2991 // this will take care of re-routing to all other clients
2992 ship_do_cargo_revealed( &Ships[objp->instance], 1);
2994 // server should rebroadcast
2995 if(MULTIPLAYER_MASTER){
2996 send_cargo_revealed_packet(&Ships[objp->instance]);
3000 // defines used for secondary fire packet
3001 #define SFPF_ALLOW_SWARM (1<<7)
3002 #define SFPF_DUAL_FIRE (1<<6)
3003 #define SFPF_TARGET_LOCKED (1<<5)
3005 // send a packet indicating a secondary weapon was fired
3006 void send_secondary_fired_packet( ship *shipp, ushort starting_sig, int starting_count, int num_fired, int allow_swarm )
3008 int packet_size, net_player_num;
3009 ubyte data[MAX_PACKET_SIZE], sinfo, current_bank;
3011 ushort target_signature;
3015 // SDL_assert ( starting_count < UCHAR_MAX );
3017 // get the object for this ship. If it is an AI object, send all the info to all player. Otherwise,
3018 // we might send the info to the other player different than the one who fired
3019 objp = &Objects[shipp->objnum];
3020 if ( !(objp->flags & OF_PLAYER_SHIP) ) {
3021 if ( num_fired == 0 ) {
3026 aip = &Ai_info[shipp->ai_index];
3028 current_bank = (ubyte)shipp->weapons.current_secondary_bank;
3029 //SDL_assert( (current_bank >= 0) && (current_bank < MAX_SECONDARY_BANKS) ); // always true
3031 // build up the header portion
3032 BUILD_HEADER( SECONDARY_FIRED_AI );
3034 ADD_USHORT( Objects[shipp->objnum].net_signature );
3035 ADD_USHORT( starting_sig );
3037 // add a couple of bits for swarm missiles and dual fire secondary weaspons
3040 sinfo = current_bank;
3043 sinfo |= SFPF_ALLOW_SWARM;
3046 if ( shipp->flags & SF_SECONDARY_DUAL_FIRE ){
3047 sinfo |= SFPF_DUAL_FIRE;
3050 if ( aip->current_target_is_locked ){
3051 sinfo |= SFPF_TARGET_LOCKED;
3056 // add the ship's target and any targeted subsystem
3057 target_signature = 0;
3059 if ( aip->target_objnum != -1) {
3060 target_signature = Objects[aip->target_objnum].net_signature;
3061 if ( (Objects[aip->target_objnum].type == OBJ_SHIP) && (aip->targeted_subsys != NULL) ) {
3064 s_index = ship_get_index_from_subsys( aip->targeted_subsys, aip->target_objnum );
3065 SDL_assert( s_index < CHAR_MAX ); // better be less than this!!!!
3066 t_subsys = (char)s_index;
3069 if ( Objects[aip->target_objnum].type == OBJ_WEAPON ) {
3070 SDL_assert(Weapon_info[Weapons[Objects[aip->target_objnum].instance].weapon_info_index].wi_flags & WIF_BOMB);
3075 ADD_USHORT( target_signature );
3076 ADD_DATA( t_subsys );
3078 // just send this packet to everyone, then bail if an AI ship fired.
3079 if ( !(objp->flags & OF_PLAYER_SHIP) ) {
3080 multi_io_send_to_all(data, packet_size);
3084 net_player_num = multi_find_player_by_object( objp );
3086 // getting here means a player fired. Send the current packet to all players except the player
3087 // who fired. If nothing got fired, then don't send to the other players -- we will just send
3088 // a packet to the player who will find out that he didn't fire anything
3089 if ( num_fired > 0 ) {
3090 multi_io_send_to_all_reliable(data, packet_size, &Net_players[net_player_num]);
3093 // if I (the master) fired, then return
3094 if ( Net_players[net_player_num].flags & NETINFO_FLAG_AM_MASTER ){
3098 // now build up the packet to send to the player who actually fired.
3099 BUILD_HEADER( SECONDARY_FIRED_PLR );
3100 ADD_USHORT(starting_sig);
3103 // add the targeting information so that the player's weapons will always home on the correct
3105 ADD_USHORT( target_signature );
3106 ADD_DATA( t_subsys );
3108 multi_io_send_reliable(&Net_players[net_player_num], data, packet_size);
3111 /// process a packet indicating a secondary weapon was fired
3112 void process_secondary_fired_packet(ubyte* data, header* hinfo, int from_player)
3114 int offset, allow_swarm, target_objnum_save;
3115 ushort net_signature, starting_sig, target_signature;
3116 ubyte sinfo, current_bank;
3117 object* objp, *target_objp;
3121 ship_subsys *targeted_subsys_save;
3123 offset = HEADER_LENGTH; // size of the header
3125 // if from_player is false, it means that the secondary weapon info in this packet was
3126 // fired by an ai object (or another player). from_player == 1 means tha me (the person
3127 // receiving this packet) fired the secondary weapon
3128 if ( !from_player ) {
3129 GET_USHORT( net_signature );
3130 GET_USHORT( starting_sig );
3131 GET_DATA( sinfo ); // are we firing swarm missiles
3133 GET_USHORT( target_signature );
3134 GET_DATA( t_subsys );
3138 // find the object (based on network signatures) for the object that fired
3139 objp = multi_get_network_object( net_signature );
3140 if ( objp == NULL ) {
3141 nprintf(("Network", "Could not find ship for fire secondary packet!"));
3145 // set up the ships current secondary bank and that bank's mode. Below, we will set the timeout
3146 // of the next fire time of this bank to 0 so we can fire right away
3147 shipp = &Ships[objp->instance];
3150 GET_USHORT( starting_sig );
3153 GET_USHORT( target_signature );
3154 GET_DATA( t_subsys );
3158 // get the object and ship
3160 shipp = Player_ship;
3163 // check the allow swarm bit
3165 if ( sinfo & SFPF_ALLOW_SWARM ){
3169 // set the dual fire properties of the ship
3170 if ( sinfo & SFPF_DUAL_FIRE ){
3171 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
3173 shipp->flags &= ~SF_SECONDARY_DUAL_FIRE;
3176 // determine whether current target is locked
3177 SDL_assert( shipp->ai_index != -1 );
3178 aip = &Ai_info[shipp->ai_index];
3179 if ( sinfo & SFPF_TARGET_LOCKED ) {
3180 aip->current_target_is_locked = 1;
3182 aip->current_target_is_locked = 0;
3185 // find out the current bank
3186 current_bank = (ubyte)(sinfo & 0x3);
3187 //SDL_assert( (current_bank >= 0) && (current_bank < MAX_SECONDARY_BANKS) ); // always true
3188 shipp->weapons.current_secondary_bank = current_bank;
3190 // make it so we can fire this ship's secondary bank immediately!!!
3191 shipp->weapons.next_secondary_fire_stamp[shipp->weapons.current_secondary_bank] = timestamp(0);
3192 shipp->weapons.detonate_weapon_time = timestamp(5000); // be sure that we don't detonate a remote weapon before it is time.
3194 // set this ship's target and subsystem information. We will save and restore target and
3195 // targeted subsystem so that we do not accidentally change targets for this player or
3196 // any AI ships on his system.
3197 target_objnum_save = aip->target_objnum;
3198 targeted_subsys_save = aip->targeted_subsys;
3200 // reset these variables for accuracy. They will get reassigned at the end of this fuction
3201 aip->target_objnum = -1;
3202 aip->targeted_subsys = NULL;
3204 target_objp = multi_get_network_object( target_signature );
3205 if ( target_objp != NULL ) {
3206 aip->target_objnum = OBJ_INDEX(target_objp);
3208 if ( (t_subsys != -1) && (target_objp->type == OBJ_SHIP) ) {
3209 aip->targeted_subsys = ship_get_indexed_subsys( &Ships[target_objp->instance], t_subsys);
3213 if ( starting_sig != 0 ){
3214 multi_set_network_signature( starting_sig, MULTI_SIG_NON_PERMANENT );
3216 shipp->weapons.detonate_weapon_time = timestamp(0); // signature of -1 say detonate remote weapon
3219 ship_fire_secondary( objp, allow_swarm );
3221 // restore targeted object and targeted subsystem
3222 aip->target_objnum = target_objnum_save;
3223 aip->targeted_subsys = targeted_subsys_save;
3226 // send a packet indicating a countermeasure was fired
3227 void send_countermeasure_fired_packet( object *objp, int cmeasure_count, int rand_val )
3229 ubyte data[MAX_PACKET_SIZE];
3234 SDL_assert ( cmeasure_count < UCHAR_MAX );
3235 BUILD_HEADER(COUNTERMEASURE_FIRED);
3236 ADD_USHORT( objp->net_signature );
3237 ADD_INT( rand_val );
3239 multi_io_send_to_all(data, packet_size);
3242 // process a packet indicating a countermeasure was fired
3243 void process_countermeasure_fired_packet( ubyte *data, header *hinfo )
3245 int offset, rand_val;
3251 offset = HEADER_LENGTH;
3253 GET_USHORT( signature );
3254 GET_INT( rand_val );
3257 objp = multi_get_network_object( signature );
3258 if ( objp == NULL ) {
3259 nprintf(("network", "Could find object whose countermeasures are being launched!!!\n"));
3262 if(objp->type != OBJ_SHIP){
3265 // SDL_assert ( objp->type == OBJ_SHIP );
3267 // make it so ship can fire right away!
3268 Ships[objp->instance].cmeasure_fire_stamp = timestamp(0);
3269 if ( objp == Player_obj ){
3270 nprintf(("network", "firing countermeasure from my ship\n"));
3273 ship_launch_countermeasure( objp, rand_val );
3276 // send a packet indicating that a turret has been fired
3277 void send_turret_fired_packet( int ship_objnum, int subsys_index, int weapon_objnum )
3280 ushort pnet_signature;
3281 ubyte data[MAX_PACKET_SIZE], cindex;
3288 if((weapon_objnum < 0) || (Objects[weapon_objnum].type != OBJ_WEAPON) || (Objects[weapon_objnum].instance < 0) || (Weapons[Objects[weapon_objnum].instance].weapon_info_index < 0)){
3292 // local setup -- be sure we are actually passing a weapon!!!!
3293 objp = &Objects[weapon_objnum];
3294 SDL_assert ( objp->type == OBJ_WEAPON );
3295 if(Weapon_info[Weapons[objp->instance].weapon_info_index].subtype == WP_MISSILE){
3299 pnet_signature = Objects[ship_objnum].net_signature;
3301 SDL_assert( subsys_index < UCHAR_MAX );
3302 cindex = (ubyte)subsys_index;
3304 ssp = ship_get_indexed_subsys( &Ships[Objects[ship_objnum].instance], subsys_index, NULL );
3309 // build the fire turret packet.
3310 BUILD_HEADER(FIRE_TURRET_WEAPON);
3311 packet_size += multi_pack_unpack_position(1, data + packet_size, &objp->orient.v.fvec);
3312 ADD_DATA( has_sig );
3313 ADD_USHORT( pnet_signature );
3315 ADD_USHORT( objp->net_signature );
3318 val = (short)ssp->submodel_info_1.angs.h;
3320 val = (short)ssp->submodel_info_2.angs.p;
3323 multi_io_send_to_all(data, packet_size);
3325 multi_rate_add(1, "tur", packet_size);
3328 // process a packet indicating a turret has been fired
3329 void process_turret_fired_packet( ubyte *data, header *hinfo )
3331 int offset, weapon_objnum, wid;
3332 ushort pnet_signature, wnet_signature;
3341 short pitch, heading;
3343 // get the data for the turret fired packet
3344 offset = HEADER_LENGTH;
3345 offset += multi_pack_unpack_position(0, data + offset, &o_fvec);
3346 GET_DATA( has_sig );
3347 GET_USHORT( pnet_signature );
3349 GET_USHORT( wnet_signature );
3353 GET_DATA( turret_index );
3354 GET_SHORT( heading );
3356 PACKET_SET_SIZE(); // move our counter forward the number of bytes we have read
3359 objp = multi_get_network_object( pnet_signature );
3360 if ( objp == NULL ) {
3361 nprintf(("network", "could find parent object with net signature %d for turret firing\n", pnet_signature));
3365 // if this isn't a ship, do nothing
3366 if ( objp->type != OBJ_SHIP ){
3370 // make an orientation matrix from the o_fvec
3371 vm_vector_2_matrix(&orient, &o_fvec, NULL, NULL);
3373 // find this turret, and set the position of the turret that just fired to be where it fired. Quite a
3374 // hack, but should be suitable.
3375 shipp = &Ships[objp->instance];
3376 ssp = ship_get_indexed_subsys( shipp, turret_index, NULL );
3380 wid = ssp->system_info->turret_weapon_type;
3382 // bash the position and orientation of the turret
3383 ssp->submodel_info_1.angs.h = (float)heading;
3384 ssp->submodel_info_2.angs.p = (float)pitch;
3386 // get the world position of the weapon
3387 ship_get_global_turret_info(objp, ssp->system_info, &pos, &temp);
3389 // create the weapon object
3390 if(wnet_signature != 0){
3391 multi_set_network_signature( wnet_signature, MULTI_SIG_NON_PERMANENT );
3393 weapon_objnum = weapon_create( &pos, &orient, wid, OBJ_INDEX(objp), 0, -1, 1);
3394 if (weapon_objnum != -1) {
3395 if ( Weapon_info[wid].launch_snd != -1 ) {
3396 snd_play_3d( &Snds[Weapon_info[wid].launch_snd], &pos, &View_position );
3401 // send a mission log item packet
3402 void send_mission_log_packet( int num )
3405 ubyte data[MAX_PACKET_SIZE];
3410 SDL_assert ( (Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER) );
3412 // get the data from the log
3413 entry = &log_entries[num];
3414 type = (ubyte)entry->type; // do the type casting thing to save on packet space
3415 sindex = (ushort)entry->index;
3417 BUILD_HEADER(MISSION_LOG_ENTRY);
3419 ADD_INT(entry->flags);
3421 ADD_DATA(entry->timestamp);
3422 ADD_STRING(entry->pname);
3423 ADD_STRING(entry->sname);
3425 // broadcast the packet to all players
3426 multi_io_send_to_all_reliable(data, packet_size);
3429 // process a mission log item packet
3430 void process_mission_log_packet( ubyte *data, header *hinfo )
3435 char pname[NAME_LENGTH], sname[NAME_LENGTH];
3438 SDL_assert ( (Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
3440 offset = HEADER_LENGTH;
3444 GET_DATA(timestamp);
3450 mission_log_add_entry_multi( type, pname, sname, sindex, timestamp, flags );
3453 // send a mission message packet
3454 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)
3457 ubyte data[MAX_PACKET_SIZE], up, us, utime;
3459 SDL_assert ( Net_player->flags & NETINFO_FLAG_AM_MASTER );
3460 SDL_assert ( (priority >= 0) && (priority < UCHAR_MAX) );
3461 SDL_assert ( (timing >= 0) && (timing < UCHAR_MAX) );
3463 up = (ubyte) priority;
3464 us = (ubyte) source;
3465 utime = (ubyte)timing;
3467 BUILD_HEADER(MISSION_MESSAGE);
3469 ADD_STRING(who_from);
3473 ADD_INT(builtin_type);
3474 ADD_INT(multi_team_filter);
3476 if (multi_target == -1){
3477 multi_io_send_to_all_reliable(data, packet_size);
3479 multi_io_send_reliable(&Net_players[multi_target], data, packet_size);
3483 // process a mission message packet
3484 void process_mission_message_packet( ubyte *data, header *hinfo )
3486 int offset, id, builtin_type;
3487 ubyte priority, source, utiming;
3488 char who_from[NAME_LENGTH];
3489 int multi_team_filter;
3491 SDL_assert( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
3493 offset = HEADER_LENGTH;
3495 GET_STRING(who_from);
3499 GET_INT(builtin_type);
3500 GET_INT(multi_team_filter);
3504 // filter out builtin ones in TvT
3505 if((builtin_type >= 0) && (Netgame.type_flags & NG_TYPE_TEAM) && (Net_player != NULL) && (Net_player->p_info.team != multi_team_filter)){
3509 // maybe filter this out
3510 if(!message_filter_multi(id)){
3511 // send the message as if it came from an sexpression
3512 message_queue_message( id, priority, utiming, who_from, source, 0, 0, builtin_type );
3516 // just send them a pong back as fast as possible
3517 void process_ping_packet(ubyte *data, header *hinfo)
3522 offset = HEADER_LENGTH;
3525 // get the address to return the pong to
3526 fill_net_addr(&addr, hinfo->addr, hinfo->port);
3532 // right now it just routes the pong through to the standalone gui, which is the only
3533 // system which uses ping and pong right now.
3534 void process_pong_packet(ubyte *data, header *hinfo)
3540 offset = HEADER_LENGTH;
3542 fill_net_addr(&addr, hinfo->addr, hinfo->port);
3546 // if we're connected , see who sent us this pong
3547 if(Net_player->flags & NETINFO_FLAG_CONNECTED){
3548 lookup = find_player_id(hinfo->id);
3553 p = &Net_players[lookup];
3555 // evaluate the ping
3556 multi_ping_eval_pong(&Net_players[lookup].s_info.ping);
3558 // put in calls to any functions which may want to know about the ping times from
3560 if(Game_mode & GM_STANDALONE_SERVER){
3561 std_update_player_ping(p);
3565 // mark his socket as still alive (extra precaution)
3566 psnet_mark_received(Net_players[lookup].reliable_socket);
3569 // otherwise, do any special processing
3571 // if we're in the join game state, see if this pong came from a server on our
3573 if(gameseq_get_state() == GS_STATE_MULTI_JOIN_GAME){
3574 multi_join_eval_pong(&addr, timer_get_fixed_seconds());
3579 // send a ping packet
3580 void send_ping(net_addr *addr)
3582 unsigned char data[8];
3585 // build the header and send the packet
3586 BUILD_HEADER( PING );
3587 psnet_send(addr, &data[0], packet_size);
3590 // send a pong packet
3591 void send_pong(net_addr *addr)
3593 unsigned char data[8];
3596 // build the header and send the packet
3598 psnet_send(addr, &data[0], packet_size);
3601 // sent from host to master. give me the list of missions you have.
3602 // this will be used only in a standalone mode
3603 void send_mission_list_request( int what )
3605 ubyte data[MAX_PACKET_SIZE];
3608 // build the header and ask for a list of missions or campaigns (depending
3609 // on the 'what' flag).
3610 BUILD_HEADER(MISSION_REQUEST);
3612 multi_io_send_reliable(Net_player, data, packet_size);
3615 // maximum number of bytes that we can send in a mission items packet.
3616 #define MAX_MISSION_ITEMS_BYTES (MAX_PACKET_SIZE - (sizeof(multi_create_info) + 1) )
3618 // defines used to tell what type of packets are being sent
3619 #define MISSION_LIST_ITEMS 1
3620 #define CAMPAIGN_LIST_ITEMS 2
3622 // send an individual mission file item
3623 void send_mission_items( net_player *pl )
3625 ubyte data[MAX_PACKET_SIZE];
3630 BUILD_HEADER(MISSION_ITEM);
3632 // send the list of missions and campaigns avilable on the server. Stop when
3633 // reaching a certain maximum
3634 type = MISSION_LIST_ITEMS;
3636 for (i = 0; i < Multi_create_mission_count; i++ ) {
3640 ADD_STRING( Multi_create_mission_list[i].filename );
3641 ADD_STRING( Multi_create_mission_list[i].name );
3642 ADD_INT( Multi_create_mission_list[i].flags );
3643 ADD_DATA( Multi_create_mission_list[i].max_players );
3644 ADD_UINT( Multi_create_mission_list[i].respawn );
3647 ADD_DATA( Multi_create_mission_list[i].valid_status );
3649 if ( packet_size > (int)MAX_MISSION_ITEMS_BYTES ) {
3652 multi_io_send_reliable(pl, data, packet_size);
3653 BUILD_HEADER( MISSION_ITEM );
3659 multi_io_send_reliable(pl, data, packet_size);
3661 // send the campaign information
3662 type = CAMPAIGN_LIST_ITEMS;
3663 BUILD_HEADER(MISSION_ITEM);
3665 for (i = 0; i < Multi_create_campaign_count; i++ ) {
3669 ADD_STRING( Multi_create_campaign_list[i].filename );
3670 ADD_STRING( Multi_create_campaign_list[i].name );
3671 ADD_INT( Multi_create_campaign_list[i].flags );
3672 ADD_DATA( Multi_create_campaign_list[i].max_players );
3674 if ( packet_size > (int)MAX_MISSION_ITEMS_BYTES ) {
3677 multi_io_send_reliable(pl, data, packet_size);
3678 BUILD_HEADER( MISSION_ITEM );
3684 multi_io_send_reliable(pl, data, packet_size);
3687 // process a request for a list of missions
3688 void process_mission_request_packet(ubyte *data, header *hinfo)
3690 int player_num,offset;
3692 offset = HEADER_LENGTH;
3695 // fill in the address information of where this came from
3696 player_num = find_player_id(hinfo->id);
3697 if(player_num == -1){
3698 nprintf(("Network","Could not find player to send mission list items to!\n"));
3702 send_mission_items( &Net_players[player_num] );
3705 // process an individual mission file item
3706 void process_mission_item_packet(ubyte *data,header *hinfo)
3709 char filename[MAX_FILENAME_LEN], name[NAME_LENGTH], valid_status;
3710 ubyte stop, type,max_players;
3713 SDL_assert(gameseq_get_state() == GS_STATE_MULTI_HOST_SETUP);
3714 offset = HEADER_LENGTH;
3719 GET_STRING( filename );
3722 GET_DATA( max_players );
3724 // missions also have respawns and a crc32 associated with them
3725 if(type == MISSION_LIST_ITEMS){
3729 GET_DATA(valid_status);
3731 if ( Multi_create_mission_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
3732 SDL_strlcpy(Multi_create_mission_list[Multi_create_mission_count].filename, filename, MAX_FILENAME_LEN );
3733 SDL_strlcpy(Multi_create_mission_list[Multi_create_mission_count].name, name, NAME_LENGTH );
3734 Multi_create_mission_list[Multi_create_mission_count].flags = flags;
3735 Multi_create_mission_list[Multi_create_mission_count].respawn = respawn;
3736 Multi_create_mission_list[Multi_create_mission_count].max_players = max_players;
3739 Multi_create_mission_list[Multi_create_mission_count].valid_status = valid_status;
3741 Multi_create_mission_count++;
3743 } else if ( type == CAMPAIGN_LIST_ITEMS ) {
3744 if ( Multi_create_campaign_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
3745 SDL_strlcpy(Multi_create_campaign_list[Multi_create_campaign_count].filename, filename, MAX_FILENAME_LEN );
3746 SDL_strlcpy(Multi_create_campaign_list[Multi_create_campaign_count].name, name, NAME_LENGTH );
3747 Multi_create_campaign_list[Multi_create_campaign_count].flags = flags;
3748 Multi_create_campaign_list[Multi_create_campaign_count].respawn = 0;
3749 Multi_create_campaign_list[Multi_create_campaign_count].max_players = max_players;
3750 Multi_create_campaign_count++;
3759 // this will cause whatever list to get resorted (although they should be appearing in order)
3760 multi_create_setup_list_data(-1);
3763 // send a request to the server to pause or unpause the game
3764 void send_multi_pause_packet(int pause)
3766 ubyte data[MAX_PACKET_SIZE];
3768 int packet_size = 0;
3770 SDL_assert(!(Net_player->flags & NETINFO_FLAG_AM_MASTER));
3773 BUILD_HEADER(MULTI_PAUSE_REQUEST);
3774 val = (ubyte) pause;
3776 // add the pause info
3779 // send the request to the server
3780 multi_io_send_reliable(Net_player, data, packet_size);
3783 // process a pause update packet (pause, unpause, etc)
3784 void process_multi_pause_packet(ubyte *data, header *hinfo)
3790 offset = HEADER_LENGTH;
3796 // get who sent the packet
3797 player_index = find_player_id(hinfo->id);
3798 // if we don't know who sent the packet, don't do anything
3799 if(player_index == -1){
3803 // if we're the server, we should evaluate whether this guy is allowed to send the packet
3804 multi_pause_server_eval_request(&Net_players[player_index],(int)val);
3807 // send a game information update
3808 void send_game_info_packet()
3811 ubyte data[MAX_PACKET_SIZE], paused;
3813 // set the paused variable
3814 paused = (ubyte)((Netgame.game_state == NETGAME_STATE_PAUSED)?1:0);
3816 BUILD_HEADER(GAME_INFO);
3817 ADD_INT( Missiontime );
3820 multi_io_send_to_all(data, packet_size);
3823 // process a game information update
3824 void process_game_info_packet( ubyte *data, header *hinfo )
3830 offset = HEADER_LENGTH;
3832 // get the mission time -- we should examine our time and the time from the server. If off by some delta
3833 // time, set our time to server time (should take ping time into account!!!)
3834 GET_DATA( mission_time );
3839 // send an ingame nak packet
3840 void send_ingame_nak(int state, net_player *p)
3842 ubyte data[MAX_PACKET_SIZE];
3843 int packet_size = 0;
3845 BUILD_HEADER(INGAME_NAK);
3849 multi_io_send_reliable(p, data, packet_size);
3852 // process an ingame nak packet
3853 void process_ingame_nak(ubyte *data, header *hinfo)
3855 int offset,state,pid;
3857 offset = HEADER_LENGTH;
3861 pid = find_player_id(hinfo->id);
3867 case ACK_FILE_ACCEPTED :
3868 SDL_assert(Net_players[pid].flags & NETINFO_FLAG_INGAME_JOIN);
3869 nprintf(("Network","Mission file rejected by server, aborting...\n"));
3870 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_FILE_REJECTED);
3875 // send a packet telling players to end the mission
3876 void send_endgame_packet(net_player *pl)
3878 ubyte data[MAX_PACKET_SIZE];
3879 int packet_size = 0;
3881 BUILD_HEADER(MISSION_END);
3883 // sending to a specific player?
3885 SDL_assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
3886 multi_io_send_reliable(pl, data, packet_size);
3890 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3891 // send all player stats here
3892 multi_broadcast_stats(STATS_MISSION);
3894 // if in dogfight mode, send all dogfight stats as well
3895 ml_string("Before dogfight stats!");
3896 if(Netgame.type_flags & NG_TYPE_DOGFIGHT){
3897 ml_string("Sending dogfight stats!");
3899 multi_broadcast_stats(STATS_DOGFIGHT_KILLS);
3901 ml_string("After dogfight stats!");
3903 // tell everyone to leave the game
3904 multi_io_send_to_all_reliable(data, packet_size);
3906 multi_io_send_reliable(Net_player, data, packet_size);
3910 // process a packet indicating we should end the current mission
3911 void process_endgame_packet(ubyte *data, header *hinfo)
3916 offset = HEADER_LENGTH;
3920 ml_string("Receiving endgame packet");
3922 // if I'm the server, I should evaluate whether the sender is authorized to end the game
3923 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3924 // determine who this came from and make sure he is allowed to end the game
3925 player_num = find_player_id(hinfo->id);
3926 SDL_assert(player_num != -1);
3931 // if the player is allowed to end the mission
3932 if(!multi_can_end_mission(&Net_players[player_num])){
3936 // act as if we hit alt+j locally
3937 multi_handle_end_mission_request();
3939 // all clients process immediately
3941 // ingame joiners should quit when they receive an endgame packet since the game is over
3942 if(Net_player->flags & NETINFO_FLAG_INGAME_JOIN){
3943 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_EARLY_END);
3947 // do any special processing for being in a state other than the gameplay state
3948 multi_handle_state_special();
3950 // make sure we're not already in the debrief state
3951 if((gameseq_get_state() != GS_STATE_DEBRIEF) && (gameseq_get_state() != GS_STATE_MULTI_DOGFIGHT_DEBRIEF)){
3952 multi_warpout_all_players();
3957 // send a position/orientation update for myself (if I'm an observer)
3958 void send_observer_update_packet()
3960 ubyte data[MAX_PACKET_SIZE];
3961 int packet_size = 0;
3965 // its possible for the master to be an observer if has run out of respawns. In this case, he doesn't need
3966 // to send any update packets to anyone.
3967 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3971 if((Player_obj == NULL) || (Player_obj->type != OBJ_OBSERVER) || (Net_player == NULL) || !(Net_player->flags & NETINFO_FLAG_OBSERVER)){
3975 BUILD_HEADER(OBSERVER_UPDATE);
3977 ret = multi_pack_unpack_position( 1, data + packet_size, &Player_obj->pos );
3979 ret = multi_pack_unpack_orient( 1, data + packet_size, &Player_obj->orient );
3982 // add targeting infomation
3983 if((Player_ai != NULL) && (Player_ai->target_objnum >= 0)){
3984 target_sig = Objects[Player_ai->target_objnum].net_signature;
3988 ADD_USHORT(target_sig);
3990 multi_io_send(Net_player, data, packet_size);
3993 // process a position/orientation update from an observer
3994 void process_observer_update_packet(ubyte *data, header *hinfo)
4000 physics_info bogus_pi;
4003 offset = HEADER_LENGTH;
4005 obs_num = find_player_id(hinfo->id);
4007 memset(&bogus_pi,0,sizeof(physics_info));
4008 ret = multi_pack_unpack_position( 0, data + offset, &g_vec );
4010 ret = multi_pack_unpack_orient( 0, data + offset, &g_mat );
4013 // targeting information
4014 GET_USHORT(target_sig);
4017 if((obs_num < 0) || (Net_players[obs_num].player->objnum < 0)){
4021 // set targeting info
4022 if(target_sig == 0){
4023 Net_players[obs_num].s_info.target_objnum = -1;
4025 target_obj = multi_get_network_object(target_sig);
4026 Net_players[obs_num].s_info.target_objnum = (target_obj == NULL) ? -1 : OBJ_INDEX(target_obj);
4029 Objects[Net_players[obs_num].player->objnum].pos = g_vec;
4030 Objects[Net_players[obs_num].player->objnum].orient = g_mat;
4031 Net_players[obs_num].s_info.eye_pos = g_vec;
4032 Net_players[obs_num].s_info.eye_orient = g_mat;
4035 void send_netplayer_slot_packet()
4037 ubyte data[MAX_PACKET_SIZE];
4038 int packet_size = 0, idx;
4043 BUILD_HEADER(NETPLAYER_SLOTS_P);
4044 for(idx=0;idx<MAX_PLAYERS;idx++){
4045 if( MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_OBSERVER(Net_players[idx])){
4047 ADD_SHORT(Net_players[idx].player_id);
4048 ADD_USHORT(Objects[Net_players[idx].player->objnum].net_signature);
4049 ADD_INT(Net_players[idx].p_info.ship_class);
4050 ADD_INT(Net_players[idx].p_info.ship_index);
4056 // standalone case or not
4057 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4058 multi_io_send_to_all_reliable(data, packet_size);
4060 multi_io_send_reliable(Net_player, data, packet_size);
4064 void process_netplayer_slot_packet(ubyte *data, header *hinfo)
4067 int player_num,ship_class,ship_index;
4073 offset = HEADER_LENGTH;
4075 // first untag all of the player ships and make them OF_COULD_BE_PLAYER
4076 multi_untag_player_ships();
4080 GET_SHORT(player_id);
4081 GET_USHORT(net_sig);
4082 GET_INT(ship_class);
4083 GET_INT(ship_index);
4084 player_num = find_player_id(player_id);
4086 nprintf(("Network","Error looking up player for object/slot assignment!!\n"));
4088 // call the function in multiutil.cpp to set up the player object stuff
4089 // being careful not to muck with the standalone object
4090 if(!((player_num == 0) && (Game_mode & GM_STANDALONE_SERVER))){
4091 objp = multi_get_network_object(net_sig);
4092 SDL_assert(objp != NULL);
4093 multi_assign_player_ship( player_num, objp, ship_class );
4094 Net_players[player_num].p_info.ship_index = ship_index;
4095 objp->flags &= ~(OF_COULD_BE_PLAYER);
4096 objp->flags |= OF_PLAYER_SHIP;
4103 // standalone should forward the packet and wait for a response
4104 if(Game_mode & GM_STANDALONE_SERVER){
4105 send_netplayer_slot_packet();
4108 Net_player->state = NETPLAYER_STATE_SLOT_ACK;
4109 send_netplayer_update_packet();
4112 // two functions to deal with ships changing their primary/secondary weapon status. 'what' indicates
4113 // if this change is a primary or secondary change. new_bank is the new current primary/secondary
4114 // bank, link_status is whether primaries are linked or not, or secondaries are dual fire or not
4115 void send_ship_weapon_change( ship *shipp, int what, int new_bank, int link_status )
4117 ubyte data[MAX_PACKET_SIZE], utmp;
4120 BUILD_HEADER(SHIP_WSTATE_CHANGE);
4121 ADD_USHORT( Objects[shipp->objnum].net_signature );
4122 utmp = (ubyte)(what);
4124 utmp = (ubyte)(new_bank);
4126 utmp = (ubyte)(link_status);
4129 // Removed the above psnet_send() call - it didn't appear to do anything since it was called only from the server anyway - DB
4130 multi_io_send_to_all_reliable(data, packet_size);
4133 void process_ship_weapon_change( ubyte *data, header *hinfo )
4137 ubyte what, new_bank, link_status;
4141 offset = HEADER_LENGTH;
4142 GET_USHORT( signature );
4144 GET_DATA( new_bank );
4145 GET_DATA( link_status );
4148 objp = multi_get_network_object( signature );
4149 if ( objp == NULL ) {
4150 nprintf(("network", "Unable to locate ship with signature %d for weapon state change\n", signature));
4153 // SDL_assert( objp->type == OBJ_SHIP );
4154 if(objp->type != OBJ_SHIP){
4158 // if this is my data, do nothing since I already have my own data
4159 if ( objp == Player_obj ){
4163 // now, get the ship and set the new bank and link modes based on the 'what' value
4164 shipp = &Ships[objp->instance];
4165 if ( what == MULTI_PRIMARY_CHANGED ) {
4166 shipp->weapons.current_primary_bank = new_bank;
4168 shipp->flags |= SF_PRIMARY_LINKED;
4170 shipp->flags &= ~SF_PRIMARY_LINKED;
4173 shipp->weapons.current_secondary_bank = new_bank;
4175 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
4177 shipp->flags &= ~SF_SECONDARY_DUAL_FIRE;
4182 // ship status change procedure
4183 // 1.) <client> - Client runs through the normal button_function procedure. Any remaining control bits are implied as being
4185 // 2.) <client> - Client puts this button_info item into his last_buttons array and sends a bunch of SHIP_STATUS packets
4186 // for added redundancy.
4187 // 3.) <server> - Receives the packet. Checks to see if the net_player on his side already has this one defined. If so, it
4188 // ignores as a repeat packet. Otherwise it puts it in the last_buttons array for the net_player
4189 // 4.) <server> - Server applies the command on his side (with multi_apply_ship_status(...) and sends the ack (also a SHIP_STATUS)
4190 // back to the client. Also sends multiple times for redundancy
4191 // 5.) <client> - Receives the packet back. Does a lookup into his last_buttons array. If he finds the match, apply the functions
4192 // and remove the item from the list. If no match is found it means that either he has received an ack, has acted
4193 // on it and removed it, or that it has been "timed out" and replaced by a newer button_info.
4195 #define SHIP_STATUS_REPEAT 2
4196 void send_ship_status_packet(net_player *pl, button_info *bi, int id)
4199 ubyte data[MAX_PACKET_SIZE];
4200 int packet_size = 0;
4206 BUILD_HEADER(SHIP_STATUS_CHANGE);
4208 for(idx=0;idx<NUM_BUTTON_FIELDS;idx++){
4209 temp = bi->status[idx];
4213 // server should send reliably (response packet)
4214 if(MULTIPLAYER_MASTER){
4215 multi_io_send_reliable(pl, data, packet_size);
4217 multi_io_send(pl, data, packet_size);
4221 void process_ship_status_packet(ubyte *data, header *hinfo)
4225 int player_num,unique_id;
4229 offset = HEADER_LENGTH;
4231 // zero out the button info structure for good measure
4232 memset(&bi,0,sizeof(button_info));
4234 // read the button-info
4237 for(idx=0;idx<NUM_BUTTON_FIELDS;idx++){
4239 bi.status[idx] = i_tmp;
4244 // this will be handled differently client and server side. Duh.
4245 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){ // SERVER SIDE
4246 // find which net-player has sent us butotn information
4247 player_num = find_player_id(hinfo->id);
4248 SDL_assert(player_num >= 0);
4253 // don't process critical button information for observers
4254 // its a new button_info for this guy. apply and ack
4255 if(!MULTI_OBSERVER(Net_players[player_num]) && !lookup_ship_status(&Net_players[player_num],unique_id)){
4256 // mark that he's pressed this button
4257 // add_net_button_info(&Net_players[player_num], &bi, unique_id);
4259 // send a return packet
4260 send_ship_status_packet(&Net_players[player_num], &bi,unique_id);
4262 // apply the button presses to his ship as normal
4263 multi_apply_ship_status(&Net_players[player_num], &bi, 0);
4265 // else ignore it as a repeat from the same guy
4266 } else { // CLIENT SIDE
4267 // this is the return from the server, so we should now apply them locally
4268 // if(lookup_ship_status(Net_player,unique_id,1)){
4269 multi_apply_ship_status(Net_player, &bi, 1);
4274 // MWA 4/28/9 -- redid this function since message all fighers was really broken
4275 // for clients. Left all details to this function instead of higher level messaging
4277 void send_player_order_packet(int type, int index, int cmd)
4279 ubyte data[MAX_PACKET_SIZE];
4281 ushort target_signature;
4283 int packet_size = 0;
4285 BUILD_HEADER(PLAYER_ORDER_PACKET);
4288 ADD_DATA(val); // ship order or wing order, or message all fighters
4290 // if we are not messaging all ships or wings, add the index, which is the shipnum or wingnum
4291 if ( val != SQUAD_MSG_ALL ){
4292 ADD_INT(index); // net signature of target ship
4295 ADD_INT(cmd); // the command itself
4298 target_signature = 0;
4299 if ( Player_ai->target_objnum != -1 ){
4300 target_signature = Objects[Player_ai->target_objnum].net_signature;
4303 ADD_USHORT( target_signature );
4306 if ( (Player_ai->target_objnum != -1) && (Player_ai->targeted_subsys != NULL) ) {
4309 s_index = ship_get_index_from_subsys( Player_ai->targeted_subsys, Player_ai->target_objnum );
4310 SDL_assert( s_index < CHAR_MAX ); // better be less than this!!!!
4311 t_subsys = (char)s_index;
4315 multi_io_send_reliable(Net_player, data, packet_size);
4318 // brief explanation :
4319 // in either case (wing or ship command), we need to send in a pseudo-ai object. Basically, both command handler
4320 // functions "normally" (non multiplayer) use a couple of the Player_ai fields. So, we just fill in the ones necessary
4321 // (which we can reconstruct from the packet data), and pass this as the default variable ai_info *local
4322 // Its kind of a hack, but it eliminates the need to go in and screw around with quite a bit of code
4323 void process_player_order_packet(ubyte *data, header *hinfo)
4325 int offset, player_num, command, index = 0, tobjnum_save;
4326 ushort target_signature;
4327 char t_subsys, type;
4328 object *objp, *target_objp;
4331 ship_subsys *tsubsys_save, *targeted_subsys;
4333 SDL_assert(MULTIPLAYER_MASTER);
4335 // packet values - its easier to read all of these in first
4337 offset = HEADER_LENGTH;
4340 if ( type != SQUAD_MSG_ALL ){
4345 GET_USHORT( target_signature );
4346 GET_DATA( t_subsys );
4350 player_num = find_player_id(hinfo->id);
4351 if(player_num == -1){
4352 nprintf(("Network","Received player order packet from unknown player\n"));
4356 objp = &Objects[Net_players[player_num].player->objnum];
4357 if ( objp->type != OBJ_SHIP ) {
4358 nprintf(("Network", "not doing player order because object requestting is not a ship\n"));
4362 // HACK HACK HACK HACK HACK HACK
4363 // if the player has sent a rearm-repair me message, we should bail here after evaluating it, since most likely the rest of
4364 // the data is BOGUS. All people should be able to to these things as well.
4365 if(command == REARM_REPAIR_ME_ITEM){
4366 hud_squadmsg_repair_rearm(0,&Objects[Net_players[player_num].player->objnum]);
4368 } else if(command == ABORT_REARM_REPAIR_ITEM){
4369 hud_squadmsg_repair_rearm_abort(0,&Objects[Net_players[player_num].player->objnum]);
4373 // if this player is not allowed to do messaging, quit here
4374 if( !multi_can_message(&Net_players[player_num]) ){
4375 nprintf(("Network","Recieved player order packet from player not allowed to give orders!!\n"));
4379 // check to see if the type of order is a reinforcement call. If so, intercept it, and
4380 // then call them in.
4381 if ( type == SQUAD_MSG_REINFORCEMENT ) {
4382 SDL_assert( (index >= 0) && (index < Num_reinforcements) );
4383 hud_squadmsg_call_reinforcement(index, player_num);
4387 // set the player's ai information here
4388 shipp = &Ships[objp->instance];
4389 aip = &Ai_info[shipp->ai_index];
4391 // get the target objnum and targeted subsystem. Quick out if we don't have an object to act on.
4392 target_objp = multi_get_network_object( target_signature );
4393 if ( target_objp == NULL ) {
4397 targeted_subsys = NULL;
4398 if ( t_subsys != -1 ) {
4399 SDL_assert( target_objp != NULL );
4400 targeted_subsys = ship_get_indexed_subsys( &Ships[target_objp->instance], t_subsys);
4403 // save and restore the target objnum and targeted subsystem so that we don't mess up other things
4405 tobjnum_save = aip->target_objnum;
4406 tsubsys_save = aip->targeted_subsys;
4408 if ( target_objp ) {
4409 aip->target_objnum = OBJ_INDEX(target_objp);
4411 aip->target_objnum = -1;
4414 aip->targeted_subsys = targeted_subsys;
4416 if ( type == SQUAD_MSG_SHIP ) {
4417 hud_squadmsg_send_ship_command(index, command, 1, player_num);
4418 } else if ( type == SQUAD_MSG_WING ) {
4419 hud_squadmsg_send_wing_command(index, command, 1, player_num);
4420 } else if ( type == SQUAD_MSG_ALL ) {
4421 hud_squadmsg_send_to_all_fighters( command, player_num );
4424 SDL_assert(tobjnum_save != Ships[aip->shipnum].objnum); // make sure not targeting self
4425 aip->target_objnum = tobjnum_save;
4426 aip->targeted_subsys = tsubsys_save;
4429 // FILE SIGNATURE stuff :
4430 // there are 2 cases for file signature sending which are handled very differently
4431 // 1.) Pregame. In this case, the host requires that all clients send a filesig packet (when process_file_sig() is called, it
4432 // posts an ACK_FILE_ACCEPTED packet to ack_evaluate, so he thinks they have acked).
4433 // 2.) Ingame join. In this case, the client sends his filesig packet automatically to the server and the _client_ waits for
4434 // the ack, before continuing to join. It would be way too messy to have the server wait on the clients ack, since he
4435 // would have to keep track of up to potentially 14 other ack handles (ouch).
4436 void send_file_sig_packet(ushort sum_sig,int length_sig)
4438 ubyte data[MAX_PACKET_SIZE];
4439 int packet_size = 0;
4441 BUILD_HEADER(FILE_SIG_INFO);
4442 ADD_USHORT(sum_sig);
4443 ADD_INT(length_sig);
4445 multi_io_send_reliable(Net_player, data, packet_size);
4448 void process_file_sig_packet(ubyte *data, header *hinfo)
4453 offset = HEADER_LENGTH;
4455 // should only be received on the server-side
4456 SDL_assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
4458 GET_USHORT(sum_sig);
4459 GET_INT(length_sig);
4461 server_verify_filesig(hinfo->id, sum_sig, length_sig);
4464 void send_file_sig_request(char *file_name)
4466 ubyte data[MAX_PACKET_SIZE];
4467 int packet_size = 0;
4469 BUILD_HEADER(FILE_SIG_REQUEST);
4470 ADD_STRING(file_name);
4472 SDL_assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
4474 multi_io_send_to_all_reliable(data, packet_size);
4477 void process_file_sig_request(ubyte *data, header *hinfo)
4479 int offset = HEADER_LENGTH;
4481 // get the mission name
4482 GET_STRING(Netgame.mission_name);
4485 // set the current mission filename
4486 SDL_strlcpy(Game_current_mission_filename, Netgame.mission_name, SDL_arraysize(Game_current_mission_filename));
4489 multi_get_mission_checksum(Game_current_mission_filename);
4491 if(!multi_endgame_ending()){
4492 // reply to the server
4493 send_file_sig_packet(Multi_current_file_checksum,Multi_current_file_length);
4497 // functions to deal with subsystems getting whacked
4498 void send_subsystem_destroyed_packet( ship *shipp, int index, vector world_hitpos )
4500 ubyte data[MAX_PACKET_SIZE];
4503 vector tmp, local_hitpos;
4506 SDL_assert ( index < UCHAR_MAX );
4507 uindex = (ubyte)(index);
4509 objp = &Objects[shipp->objnum];
4511 vm_vec_sub(&tmp, &world_hitpos, &objp->pos );
4512 vm_vec_rotate( &local_hitpos, &tmp, &objp->orient );
4514 BUILD_HEADER(SUBSYSTEM_DESTROYED);
4515 ADD_USHORT( Objects[shipp->objnum].net_signature );
4517 // ADD_DATA( local_hitpos );
4518 add_vector_data(data, &packet_size, local_hitpos);
4520 multi_io_send_to_all_reliable(data, packet_size);
4523 void process_subsystem_destroyed_packet( ubyte *data, header *hinfo )
4529 vector local_hit_pos = ZERO_VECTOR, world_hit_pos;
4531 offset = HEADER_LENGTH;
4533 GET_USHORT( signature );
4535 // GET_DATA( local_hit_pos );
4536 get_vector_data(data, &offset, local_hit_pos);
4538 // get the network object. process it if we find it.
4539 objp = multi_get_network_object( signature );
4540 if ( objp != NULL ) {
4542 ship_subsys *subsysp;
4544 // be sure we have a ship!!!
4545 // SDL_assert ( objp->type == OBJ_SHIP );
4546 if(objp->type != OBJ_SHIP){
4551 shipp = &Ships[objp->instance];
4553 // call to get the pointer to the subsystem we should be working on
4554 subsysp = ship_get_indexed_subsys( shipp, (int)uindex );
4555 vm_vec_unrotate( &world_hit_pos, &local_hit_pos, &objp->orient );
4556 vm_vec_add2( &world_hit_pos, &objp->pos );
4558 do_subobj_destroyed_stuff( shipp, subsysp, &world_hit_pos );
4559 if ( objp == Player_obj ) {
4560 hud_gauge_popup_start(HUD_DAMAGE_GAUGE, 5000);
4568 // packet to tell clients cargo of a ship was revealed to all
4569 void send_subsystem_cargo_revealed_packet( ship *shipp, int index )
4571 ubyte data[MAX_PACKET_SIZE], uindex;
4574 SDL_assert ( index < UCHAR_MAX );
4575 uindex = (ubyte)(index);
4577 // build the header and add the data
4578 BUILD_HEADER(SUBSYS_CARGO_REVEALED);
4579 ADD_USHORT( Objects[shipp->objnum].net_signature );
4582 // server sends to all players
4583 if(MULTIPLAYER_MASTER){
4584 multi_io_send_to_all_reliable(data, packet_size);
4586 // clients just send to the server
4588 multi_io_send_reliable(Net_player, data, packet_size);
4592 // process a subsystem cargo revealed packet
4593 void process_subsystem_cargo_revealed_packet( ubyte *data, header *hinfo )
4600 ship_subsys *subsysp;
4602 offset = HEADER_LENGTH;
4603 GET_USHORT( signature );
4607 // get a ship pointer and call the ship function to reveal the cargo
4608 objp = multi_get_network_object( signature );
4609 if ( objp == NULL ) {
4610 nprintf(("Network", "Could not find object with net signature %d for cargo revealed\n", signature ));
4614 // SDL_assert( objp->type == OBJ_SHIP );
4615 if((objp->type != OBJ_SHIP) || (objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
4619 shipp = &Ships[objp->instance];
4621 // call to get the pointer to the subsystem we should be working on
4622 subsysp = ship_get_indexed_subsys( shipp, (int)uindex );
4623 if (subsysp == NULL) {
4624 nprintf(("Network", "Could not find subsys for ship %s for cargo revealed\n", Ships[objp->instance].ship_name ));
4628 // this will take care of re-routing to all other clients
4629 void ship_do_cap_subsys_cargo_revealed( ship *shipp, ship_subsys *subsys, int from_network );
4630 ship_do_cap_subsys_cargo_revealed( shipp, subsysp, 1 );
4632 // server should rebroadcast
4633 if(MULTIPLAYER_MASTER){
4634 send_subsystem_cargo_revealed_packet(&Ships[objp->instance], (int)uindex);
4638 void send_netplayer_load_packet(net_player *pl)
4640 ubyte data[MAX_PACKET_SIZE];
4641 int packet_size = 0;
4643 BUILD_HEADER(LOAD_MISSION_NOW);
4644 ADD_STRING(Netgame.mission_name);
4647 multi_io_send_to_all_reliable(data, packet_size);
4649 multi_io_send_reliable(pl, data, packet_size);
4653 void process_netplayer_load_packet(ubyte *data, header *hinfo)
4656 int offset = HEADER_LENGTH;
4661 SDL_strlcpy(Netgame.mission_name, str, SDL_arraysize(Netgame.mission_name));
4662 SDL_strlcpy(Game_current_mission_filename, str, SDL_arraysize(Game_current_mission_filename));
4663 if(!Multi_mission_loaded){
4665 // MWA 2/3/98 -- ingame join changes!!!
4666 // everyone can go through the same mission loading path here!!!!
4667 nprintf(("Network","Loading mission..."));
4669 // notify everyone that I'm loading the mission
4670 Net_player->state = NETPLAYER_STATE_MISSION_LOADING;
4671 send_netplayer_update_packet();
4673 // do the load itself
4674 game_start_mission();
4676 // ingame joiners need to "untag" all player ships as could_be_players. The ingame joining
4677 // code will remark the correct player ships
4678 if ( Net_player->flags & NETINFO_FLAG_INGAME_JOIN ) {
4679 multi_untag_player_ships();
4682 Net_player->flags |= NETINFO_FLAG_MISSION_OK;
4683 Net_player->state = NETPLAYER_STATE_MISSION_LOADED;
4684 send_netplayer_update_packet();
4686 Multi_mission_loaded = 1;
4687 nprintf(("Network","Finished loading mission\n"));
4691 void send_jump_into_mission_packet(net_player *pl)
4693 ubyte data[MAX_PACKET_SIZE];
4694 int packet_size = 0;
4696 SDL_assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
4698 BUILD_HEADER(JUMP_INTO_GAME);
4700 // ingame joiners will get special data. We need to tell them about the state of the mission, like paused,
4701 // and possible other things.
4703 if ( pl->flags & NETINFO_FLAG_INGAME_JOIN ) {
4704 ADD_INT(Netgame.game_state);
4710 multi_io_send_to_all_reliable(data, packet_size);
4712 // send to a specific player
4714 multi_io_send_reliable(pl, data, packet_size);
4718 void process_jump_into_mission_packet(ubyte *data, header *hinfo)
4720 int offset = HEADER_LENGTH;
4723 // if I am ingame joining, there should be extra data. For now, this data is the netgame state.
4724 // the game could be paused, so ingame joiner needs to deal with it.
4725 if ( Net_player->flags & NETINFO_FLAG_INGAME_JOIN ) {
4727 Netgame.game_state = state;
4732 // handle any special processing for being in a weird substate
4733 multi_handle_state_special();
4735 // if I'm an ingame joiner, go to the ship select screen, or if I'm an observer, jump right in!
4736 if(Net_player->flags & NETINFO_FLAG_INGAME_JOIN){
4737 if(Net_player->flags & NETINFO_FLAG_OBSERVER){
4738 multi_ingame_observer_finish();
4740 gameseq_post_event(GS_EVENT_INGAME_PRE_JOIN);
4741 Net_player->state = NETPLAYER_STATE_INGAME_SHIP_SELECT;
4742 send_netplayer_update_packet();
4745 // start the mission!!
4746 if(!(Game_mode & GM_IN_MISSION) && !(Game_mode & GM_STANDALONE_SERVER)){
4747 Netgame.game_state = NETGAME_STATE_IN_MISSION;
4748 gameseq_post_event(GS_EVENT_ENTER_GAME);
4749 Net_player->state = NETPLAYER_STATE_IN_MISSION;
4750 send_netplayer_update_packet();
4754 extern time_t Player_multi_died_check;
4755 Player_multi_died_check = -1;
4757 // recalc all object pairs now
4758 extern void obj_reset_all_collisions();
4759 obj_reset_all_collisions();
4761 // display some cool text
4762 multi_common_add_text(XSTR("Received mission start\n",720),1);
4765 ml_string(NOX("Client received mission start from server - entering mission"));
4770 const char *repair_text[] = {
4772 "REPAIR_INFO_BEGIN",
4774 "REPAIR_INFO_UPDATE",
4775 "REPAIR_INFO_QUEUE",
4776 "REPAIR_INFO_ABORT",
4777 "REPAIR_INFO_BROKEN",
4778 "REPAIR_INFO_WARP_ADD",
4779 "REPAIR_INFO_WARP_REMOVE",
4780 "REPAIR_INFO_ONWAY",
4781 "REPAIR_INFO_KILLED",
4782 "REPAIR_INFO_COMPLETE",
4787 // the following two routines deal with updating and sending information regarding players
4788 // rearming and repairing during the game. The process function calls the routines to deal with
4789 // setting flags and other interesting things.
4790 void send_repair_info_packet(object *repaired_objp, object *repair_objp, int code )
4792 int packet_size = 0;
4793 ushort repaired_signature, repair_signature;
4794 ubyte data[MAX_PACKET_SIZE];
4797 // use the network signature of the destination object if there is one, -1 otherwise.
4798 // client will piece it all together
4799 repaired_signature = repaired_objp->net_signature;
4801 // the repair ship may be NULL here since it might have been destroyed
4802 repair_signature = 0;
4804 repair_signature = repair_objp->net_signature;
4807 BUILD_HEADER(CLIENT_REPAIR_INFO);
4810 ADD_USHORT( repaired_signature );
4811 ADD_USHORT( repair_signature );
4813 multi_io_send_to_all_reliable(data, packet_size);
4815 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));
4818 void process_repair_info_packet(ubyte *data, header *hinfo)
4820 int offset = HEADER_LENGTH;
4821 ushort repaired_signature, repair_signature;
4822 object *repaired_objp, *repair_objp;
4826 GET_USHORT( repaired_signature );
4827 GET_USHORT( repair_signature );
4830 repaired_objp = multi_get_network_object( repaired_signature );
4831 repair_objp = multi_get_network_object( repair_signature );
4833 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));
4835 if ( Net_player->flags & NETINFO_FLAG_WARPING_OUT ){
4839 if ( repaired_objp == NULL ) {
4840 Int3(); // Sandeep says this is bad bad bad. No ship to repair.
4844 // the hope is to simply call the routine in the ai code to set/unset flags
4845 // based on the code value and everything else should happen..I hope....
4846 if ( (code != REPAIR_INFO_WARP_ADD) && (code != REPAIR_INFO_WARP_REMOVE ) ) {
4848 ai_do_objects_repairing_stuff( repaired_objp, repair_objp, (int)code );
4850 // set the dock flags when repair begins. Prevents problem in lagging docking
4851 // packet. Also set any other flags/modes which need to be set to prevent Asserts.
4853 if ( (code == REPAIR_INFO_BEGIN) && (repair_objp != NULL) ) {
4854 ai_do_objects_docked_stuff( repaired_objp, repair_objp );
4855 Ai_info[Ships[repair_objp->instance].ai_index].mode = AIM_DOCK;
4858 // if the repair is done (either by abort, or ending), mark the repair ship's goal
4860 if ( ((code == REPAIR_INFO_ABORT) || (code == REPAIR_INFO_END)) && repair_objp ){
4861 ai_mission_goal_complete( &Ai_info[Ships[repair_objp->instance].ai_index] );
4864 if ( code == REPAIR_INFO_WARP_ADD ){
4865 mission_warp_in_support_ship( repaired_objp );
4867 mission_remove_scheduled_repair( repaired_objp );
4872 // sends information updating clients on certain AI information that clients will
4873 // need to know about to keep HUD information up to date. objp is the object that we
4874 // are updating, and what is the type of stuff that we are updating.
4875 void send_ai_info_update_packet( object *objp, char what )
4878 ushort other_signature;
4879 ubyte data[MAX_PACKET_SIZE];
4881 ubyte dock_index, dockee_index;
4883 // SDL_assert( objp->type == OBJ_SHIP );
4884 if(objp->type != OBJ_SHIP){
4887 aip = &Ai_info[Ships[objp->instance].ai_index];
4890 if ( Ships[objp->instance].flags & (SF_DEPARTING | SF_DYING) )
4893 BUILD_HEADER( AI_INFO_UPDATE );
4894 ADD_USHORT( objp->net_signature );
4897 // depending on the "what" value, we will send different information
4901 case AI_UPDATE_DOCK:
4902 // for docking ships, add the signature of the ship that we are docking with.
4903 SDL_assert( aip->dock_objnum != -1 );
4904 other_signature = Objects[aip->dock_objnum].net_signature;
4905 dock_index = (ubyte)(aip->dock_index);
4906 dockee_index = (ubyte)(aip->dockee_index);
4907 ADD_USHORT( other_signature );
4908 ADD_DATA(dock_index);
4909 ADD_DATA(dockee_index);
4912 case AI_UPDATE_UNDOCK:
4913 // for undocking ships, check the dock_objnum since we might or might not have it
4914 // depending on whether or not a ship was destroyed while we were docked.
4915 other_signature = 0;
4916 if ( aip->dock_objnum != -1 )
4917 other_signature = Objects[aip->dock_objnum].net_signature;
4918 ADD_USHORT( other_signature );
4922 case AI_UPDATE_ORDERS: {
4925 // for orders, we only need to send a little bit of information here. Be sure that the
4926 // first order for this ship is active
4927 SDL_assert( (aip->active_goal != AI_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) );
4928 ADD_INT( aip->goals[0].ai_mode );
4929 ADD_INT( aip->goals[0].ai_submode );
4931 if ( aip->goals[0].ship_name != NULL )
4932 shipnum = ship_name_lookup( aip->goals[0].ship_name );
4934 // the ship_name member of the goals structure may or may not contain a real shipname. If we don't
4935 // have a valid shipnum, then don't sweat it since it may not really be a ship.
4936 if ( shipnum != -1 ) {
4937 SDL_assert( Ships[shipnum].objnum != -1 );
4938 other_signature = Objects[Ships[shipnum].objnum].net_signature;
4940 other_signature = 0;
4942 ADD_USHORT( other_signature );
4944 // for docking, add the dock and dockee index
4945 if ( aip->goals[0].ai_mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
4946 SDL_assert( (aip->goals[0].docker.index >= 0) && (aip->goals[0].docker.index < UCHAR_MAX) );
4947 SDL_assert( (aip->goals[0].dockee.index >= 0) && (aip->goals[0].dockee.index < UCHAR_MAX) );
4948 dock_index = (ubyte)aip->goals[0].docker.index;
4949 dockee_index = (ubyte)aip->goals[0].dockee.index;
4950 ADD_DATA( dock_index );
4951 ADD_DATA( dockee_index );
4960 multi_rate_add(1, "aiu", packet_size);
4961 multi_io_send_to_all_reliable(data, packet_size);
4964 // process an ai_info update packet. Docking/undocking, ai orders, etc. are taken care of here. This
4965 // information is mainly used to keep the clients HUD up to date with the appropriate information.
4966 void process_ai_info_update_packet( ubyte *data, header *hinfo)
4968 int offset = HEADER_LENGTH;
4970 ushort net_signature, other_net_signature;
4971 object *objp, *other_objp;
4974 ubyte dock_index = 0, dockee_index = 0;
4976 GET_USHORT( net_signature ); // signature of the object that we are dealing with.
4977 GET_DATA( code ); // code of what we are doing.
4978 objp = multi_get_network_object( net_signature );
4980 nprintf(("Network", "Couldn't find object for ai update\n"));
4984 case AI_UPDATE_DOCK:
4985 GET_USHORT( other_net_signature );
4986 GET_DATA( dock_index );
4987 GET_DATA( dockee_index );
4988 other_objp = multi_get_network_object( other_net_signature );
4989 if ( !other_objp ) {
4990 nprintf(("Network", "Couldn't find other object for ai update on dock\n"));
4993 // if we don't have an object to work with, break out of loop
4994 if ( !objp || !other_objp || (objp->type != OBJ_SHIP) || (other_objp->type != OBJ_SHIP)){
4998 SDL_assert( other_objp->type == OBJ_SHIP );
4999 Ai_info[Ships[objp->instance].ai_index].dock_index = dock_index;
5000 Ai_info[Ships[objp->instance].ai_index].dockee_index = dockee_index;
5002 Ai_info[Ships[other_objp->instance].ai_index].dock_index = dockee_index;
5003 Ai_info[Ships[other_objp->instance].ai_index].dockee_index = dock_index;
5005 ai_do_objects_docked_stuff( objp, other_objp );
5008 case AI_UPDATE_UNDOCK:
5009 GET_USHORT( other_net_signature );
5010 other_objp = multi_get_network_object( other_net_signature );
5012 // if we don't have an object to work with, break out of loop
5016 ai_do_objects_undocked_stuff( objp, other_objp );
5019 case AI_UPDATE_ORDERS:
5022 GET_USHORT( other_net_signature );
5023 if ( mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
5024 GET_DATA(dock_index);
5025 GET_DATA(dockee_index);
5028 // be sure that we have a ship object!!!
5029 if ( !objp || (objp->type != OBJ_SHIP) )
5032 // set up the information in the first goal element of the object in question
5033 aip = &Ai_info[Ships[objp->instance].ai_index];
5034 aip->active_goal = 0;
5035 aip->goals[0].ai_mode = mode;
5036 aip->goals[0].ai_submode = submode;
5038 // for docking, add the dock and dockee index
5039 if ( mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
5040 aip->dock_index = dock_index;
5041 aip->dockee_index = dockee_index;
5044 // get a shipname if we can.
5045 other_objp = multi_get_network_object( other_net_signature );
5046 if ( other_objp && (other_objp->type == OBJ_SHIP) ) {
5047 // get a pointer to the shipname in question. Use the ship_name value in the
5048 // ship. We are only using this for HUD display, so I think that using this
5049 // method will be fine.
5050 aip->goals[0].ship_name = Ships[other_objp->instance].ship_name;
5052 // special case for destroy subsystem -- get the ai_info pointer to our target ship
5053 // so that we can properly set up what subsystem this ship is attacking.
5054 if ( (mode == AI_GOAL_DESTROY_SUBSYSTEM ) && (submode >= 0) )
5055 aip->targeted_subsys = ship_get_indexed_subsys( &Ships[other_objp->instance], submode);
5057 // if docking -- set the dock index and dockee index of this other ship
5058 if ( mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
5059 Ai_info[Ships[other_objp->instance].ai_index].dock_index = dockee_index;
5060 Ai_info[Ships[other_objp->instance].ai_index].dockee_index = dock_index;
5067 Int3(); // this Int3() should be temporary
5068 nprintf(("Network", "Invalid code for ai update: %d\n", code));
5074 // tell the standalone to move into the MISSION_SYNC_STATE
5075 void send_mission_sync_packet(int mode,int start_campaign)
5077 ubyte data[MAX_PACKET_SIZE],is_campaign;
5078 int packet_size = 0;
5080 SDL_assert((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER));
5082 // build the header and add the sync mode (pre or post briefing)
5083 BUILD_HEADER(MISSION_SYNC_DATA);
5086 // if this is a campaign game
5087 if(mode == MULTI_SYNC_PRE_BRIEFING){
5088 if(Game_mode & GM_CAMPAIGN_MODE){
5089 // add a byte indicating campaign mode
5091 ADD_DATA(is_campaign);
5093 // add a byte indicating if we should be starting a campaign or continuing it
5094 is_campaign = (ubyte)start_campaign;
5095 ADD_DATA(is_campaign);
5097 // add the campaign filename
5098 ADD_STRING(Netgame.campaign_name);
5100 // otherwise if this is a single mission
5102 // add a byte indicating single mission mode
5104 ADD_DATA(is_campaign);
5106 // add the mission filename
5107 ADD_STRING(Game_current_mission_filename);
5110 multi_io_send_reliable(Net_player, data, packet_size);
5113 // move into the MISSION_SYNC state when this is received
5114 // this packet is sent only from a game host to a standalone
5115 void process_mission_sync_packet(ubyte *data, header *hinfo)
5118 ubyte campaign_flag;
5119 int offset = HEADER_LENGTH;
5121 SDL_assert(Game_mode & GM_STANDALONE_SERVER);
5123 // if this is a team vs team situation, lock the players send a final team update
5124 if(Netgame.type_flags & NG_TYPE_TEAM){
5125 multi_team_host_lock_all();
5126 multi_team_send_update();
5129 // get the sync mode (pre or post briefing)
5132 if(mode == MULTI_SYNC_PRE_BRIEFING){
5133 // get the flag indicating if this is a single mission or a campaign mode
5134 GET_DATA(campaign_flag);
5136 // get the flag indicating whether we should be starting a new campaign
5137 GET_DATA(campaign_flag);
5139 // get the campaign filename
5140 GET_STRING(Netgame.campaign_name);
5142 // either start a new campaign or continue on to the next mission in the current campaign
5144 multi_campaign_start(Netgame.campaign_name);
5146 multi_campaign_next_mission();
5149 // make sure we remove the campaign mode flag
5150 Game_mode &= ~(GM_CAMPAIGN_MODE);
5152 // get the single mission filename
5153 GET_STRING(Game_current_mission_filename);
5154 SDL_strlcpy(Netgame.mission_name, Game_current_mission_filename, SDL_arraysize(Netgame.mission_name));
5159 // set the correct mode and m ove into the state
5160 Multi_sync_mode = mode;
5161 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
5164 // tell a player to merge his mission stats into his alltime stats
5165 void send_store_stats_packet(int accept)
5168 int packet_size = 0;
5170 BUILD_HEADER(STORE_MISSION_STATS);
5172 // add whether we're accepting or tossing
5173 val = (ubyte)accept;
5176 // if I'm the server, send to everyone, else send to the standalone to be rebroadcasted
5177 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5178 multi_io_send_to_all_reliable(data, packet_size);
5180 multi_io_send_reliable(Net_player, data, packet_size);
5184 void process_store_stats_packet(ubyte *data, header *hinfo)
5186 int offset = HEADER_LENGTH;
5192 // if I'm the standalone, rebroadcast. Otherwise, if I'm a client, merge my mission stats with my alltime stats
5193 if(Game_mode & GM_STANDALONE_SERVER){
5194 // rebroadcast the packet to all others in the game
5195 nprintf(("Network","Standalone received store stats packet - rebroadcasting..\n"));
5196 multi_io_send_to_all_reliable(data, offset);
5199 // all players should mark the stats as being accepted in the debriefing
5200 multi_debrief_stats_accept();
5202 // all players should mark the stats as being "tossed" in the debriefing
5203 multi_debrief_stats_toss();
5208 void send_debris_update_packet(object *objp,int code)
5210 ubyte data[MAX_PACKET_SIZE];
5212 int packet_size = 0;
5214 BUILD_HEADER(DEBRIS_UPDATE);
5215 ADD_USHORT(objp->net_signature);
5219 // add any extra relevant data
5221 case DEBRIS_UPDATE_UPDATE:
5222 // ADD_DATA(objp->pos); // add position
5223 add_vector_data(data, &packet_size, objp->pos);
5224 ADD_ORIENT(objp->orient); // add orientation
5225 // ADD_DATA(objp->phys_info.vel); // add velocity
5226 add_vector_data(data, &packet_size, objp->phys_info.vel);
5227 // ADD_DATA(objp->phys_info.rotvel); // add rotational velocity
5228 add_vector_data(data, &packet_size, objp->phys_info.rotvel);
5231 multi_io_send_to_all(data, packet_size);
5234 void process_debris_update_packet(ubyte *data, header *hinfo)
5238 object bogus_object;
5240 int offset = HEADER_LENGTH;
5242 GET_USHORT(net_sig);
5246 objp = multi_get_network_object(net_sig);
5248 objp = &bogus_object;
5252 // update the object
5253 case DEBRIS_UPDATE_UPDATE:
5254 //GET_DATA(objp->pos);
5255 get_vector_data( data, &offset, objp->pos );
5257 GET_ORIENT(objp->orient);
5258 GET_DATA(objp->phys_info.vel);
5259 GET_DATA(objp->phys_info.rotvel);
5261 // simply remove it (no explosion)
5262 case DEBRIS_UPDATE_REMOVE:
5263 if(objp != &bogus_object){
5264 SDL_assert(objp->type == OBJ_DEBRIS);
5265 obj_delete(OBJ_INDEX(objp));
5269 case DEBRIS_UPDATE_NUKE:
5270 if(objp != &bogus_object)
5271 debris_hit(objp,NULL,&objp->pos,1000000.0f);
5279 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)
5281 ubyte data[MAX_PACKET_SIZE];
5283 int packet_size = 0;
5285 BUILD_HEADER(WSS_REQUEST_PACKET);
5287 // add the request information
5288 ADD_SHORT(player_id);
5290 ADD_INT(from_index);
5293 ADD_INT(wl_ship_slot); // only used in weapons loadout
5294 ADD_INT(ship_class);
5297 // a standard request
5299 multi_io_send_reliable(Net_player, data, packet_size);
5301 // being routed through the standalone to the host of the game
5303 SDL_assert(Game_mode & GM_STANDALONE_SERVER);
5304 multi_io_send_reliable(p, data, packet_size);
5308 void process_wss_request_packet(ubyte *data, header *hinfo)
5310 int offset = HEADER_LENGTH;
5311 int from_slot,from_index;
5312 int to_slot,to_index;
5314 int wl_ship_slot,ship_class;
5318 // determine who this request is from
5319 GET_SHORT(player_id);
5320 player_num = find_player_id(player_id);
5322 // read in the request data
5324 GET_INT(from_index);
5327 GET_INT(wl_ship_slot); // only used in weapons loadout
5328 GET_INT(ship_class); // only used in multi team select
5332 SDL_assert(player_num != -1);
5333 if(player_num == -1){
5337 // if we're the standalone, we have to route this packet to the host of the game
5338 if(Game_mode & GM_STANDALONE_SERVER){
5339 send_wss_request_packet(player_id, from_slot, from_index, to_slot, to_index, wl_ship_slot, ship_class, mode, Netgame.host);
5341 // otherwise we're the host and should process the request
5344 case WSS_WEAPON_SELECT :
5345 wl_drop(from_slot,from_index,to_slot,to_index,wl_ship_slot,player_num);
5347 case WSS_SHIP_SELECT :
5348 multi_ts_drop(from_slot,from_index,to_slot,to_index,ship_class,player_num);
5356 void send_wss_update_packet(int team_num,ubyte *wss_data,int size)
5358 ubyte data[MAX_PACKET_SIZE],team;
5359 int packet_size = 0;
5361 SDL_assert(size <= (MAX_PACKET_SIZE - 10));
5363 BUILD_HEADER(WSS_UPDATE_PACKET);
5365 // add the team/pool # this is for
5366 team = (ubyte)team_num;
5369 // add the data block size
5372 // add the data itself
5373 memcpy(data + packet_size,wss_data,size);
5374 packet_size += size;
5376 // if we're also the master of the game (not on a standalone)
5377 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5378 multi_io_send_to_all_reliable(data, packet_size);
5380 // if we're only the host on the standalone, then send the packet to the standalone to be routed
5382 multi_io_send_reliable(Net_player, data, packet_size);
5386 void process_wss_update_packet(ubyte *data, header *hinfo)
5389 int size,player_index,idx;
5390 int offset = HEADER_LENGTH;
5392 // get the team/pool #
5395 // get the data size
5398 // if we're the standalone, then we should be routing this data to all the other clients
5399 if(Game_mode & GM_STANDALONE_SERVER){
5404 // determine where this came from
5405 player_index = find_player_id(hinfo->id);
5406 SDL_assert(player_index != -1);
5407 if(player_index < 0){
5411 // route the packet (don't resend it to the host)
5412 for(idx=0;idx<MAX_PLAYERS;idx++){
5413 if(MULTI_CONNECTED(Net_players[idx]) && (&Net_players[idx] != Net_player) && (&Net_players[idx] != &Net_players[player_index]) ){
5414 multi_io_send_reliable(&Net_players[idx], data, offset);
5418 // set the proper pool pointers
5419 ss_set_team_pointers((int)team);
5421 // read in the block of data, and apply it to the weapons/ship pools
5422 offset += restore_wss_data(data + offset);
5425 // set the pool pointers back to my own team
5426 ss_set_team_pointers(Net_player->p_info.team);
5428 // sync the interface if this was for my own team
5429 if((int)team == Net_player->p_info.team){
5430 multi_ts_sync_interface();
5437 // function to send firing information from the client to the server once they reach
5438 // the final sync screen.
5439 void send_firing_info_packet()
5441 ubyte data[MAX_PACKET_SIZE];
5443 ubyte plinked, sdual;
5445 SDL_assert( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
5447 BUILD_HEADER(FIRING_INFO);
5448 plinked = (ubyte)((Player_ship->flags & SF_PRIMARY_LINKED)?1:0);
5449 sdual = (ubyte)((Player_ship->flags & SF_SECONDARY_DUAL_FIRE)?1:0);
5450 ADD_DATA( plinked );
5453 multi_io_send_reliable(Net_player, data, packet_size);
5456 void process_firing_info_packet( ubyte *data, header *hinfo )
5458 int offset, player_num;
5459 ubyte plinked, sdual;
5462 // only the master of the game should be dealing with these packets
5463 SDL_assert( Net_player->flags & NETINFO_FLAG_AM_MASTER );
5465 offset = HEADER_LENGTH;
5466 GET_DATA( plinked );
5470 player_num = find_player_id(hinfo->id);
5472 nprintf(("Network","Received firing info packet from unknown player, ignoring\n"));
5476 // get the ship pointer for this player and set the flags accordingly.
5477 shipp = &(Ships[Objects[Net_players[player_num].player->objnum].instance]);
5479 shipp->flags |= SF_PRIMARY_LINKED;
5481 shipp->flags &= ~SF_PRIMARY_LINKED;
5484 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
5486 shipp->flags &= ~SF_SECONDARY_DUAL_FIRE;
5489 // packet to deal with changing status of mission goals. used to be sent every so often from server
5490 // to clients, but with addition of reliable sockets, send when complete, invalid, etc.
5491 // goal_num is the index into mission_goals. new_status means failed, success, etc. -1 if not used.
5492 // valid means goal is changing to invalid(0) or valid(1). only applies if new_status == -1
5493 void send_mission_goal_info_packet( int goal_num, int new_status, int valid )
5495 ubyte data[MAX_PACKET_SIZE];
5498 BUILD_HEADER(MISSION_GOAL_INFO);
5501 ADD_INT(new_status);
5504 multi_io_send_to_all_reliable(data, packet_size);
5507 void process_mission_goal_info_packet( ubyte *data, header *hinfo )
5509 int offset, goal_num, new_status, valid;
5511 offset = HEADER_LENGTH;
5513 GET_INT(new_status);
5517 // if new_status != -1, then this is a change in goal status (i.e. goal failed, or is successful)
5518 if ( new_status != -1 ){
5519 mission_goal_status_change( goal_num, new_status );
5521 mission_goal_validation_change( goal_num, valid );
5525 void send_player_settings_packet(net_player *p)
5527 ubyte data[MAX_PACKET_SIZE];
5530 int packet_size = 0;
5533 BUILD_HEADER(PLAYER_SETTINGS);
5535 // add all the data for all the players
5537 for(idx=0;idx<MAX_PLAYERS;idx++){
5538 if(MULTI_CONNECTED(Net_players[idx])){
5540 ADD_SHORT(Net_players[idx].player_id);
5542 // break the p_info structure by member, so we don't overwrite any absolute pointers
5543 // ADD_DATA(Net_players[idx].p_info);
5544 ADD_INT(Net_players[idx].p_info.team);
5545 ADD_INT(Net_players[idx].p_info.ship_index);
5546 ADD_INT(Net_players[idx].p_info.ship_class);
5549 // add the stop byte
5553 // either broadcast the data or send to a specific player
5555 multi_io_send_to_all_reliable(data, packet_size);
5557 multi_io_send_reliable(p, data, packet_size);
5561 void process_player_settings_packet(ubyte *data, header *hinfo)
5563 int offset,player_num;
5564 net_player_info bogus,*ptr;
5568 offset = HEADER_LENGTH;
5570 // read in the data for all the players
5572 while(stop != 0xff){
5573 // lookup the player
5574 GET_SHORT(player_id);
5575 player_num = find_player_id(player_id);
5577 // make sure this is a valid player
5578 if(player_num == -1){
5581 ptr = &Net_players[player_num].p_info;
5585 GET_INT(ptr->ship_index);
5586 GET_INT(ptr->ship_class);
5593 // update the server with my new state
5594 // MWA -- 3/31/98 -- check for in mission instead of state.
5595 //if ( Netgame.game_state == NETGAME_STATE_MISSION_SYNC) {
5596 if( !(Game_mode & GM_IN_MISSION) ) {
5597 Net_player->state = NETPLAYER_STATE_SETTINGS_ACK;
5598 send_netplayer_update_packet();
5602 // display some cool text
5603 multi_common_add_text(XSTR("Received player settings packet\n",721),1);
5606 void send_deny_packet(net_addr *addr, int code)
5609 int packet_size = 0;
5611 // build the header and add the rejection code
5617 psnet_send(addr, data, packet_size);
5620 void process_deny_packet(ubyte *data, header *hinfo)
5624 // get the denial code
5625 offset = HEADER_LENGTH;
5629 // if there is already a dialog active, do nothing - who cares at this point.
5634 // display the appropriate dialog
5636 case JOIN_DENY_JR_STATE :
5637 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));
5639 case JOIN_DENY_JR_TRACKER_INVAL :
5640 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));
5642 case JOIN_DENY_JR_PASSWD :
5643 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because this is a password protected game",724));
5645 case JOIN_DENY_JR_CLOSED :
5646 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));
5648 case JOIN_DENY_JR_TEMP_CLOSED :
5649 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));
5651 case JOIN_DENY_JR_RANK_HIGH :
5652 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));
5654 case JOIN_DENY_JR_RANK_LOW :
5655 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));
5657 case JOIN_DENY_JR_DUP :
5658 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because there is an identical player already in the game",729));
5660 case JOIN_DENY_JR_FULL :
5661 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because the game is full",730));
5663 case JOIN_DENY_JR_BANNED :
5664 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because you are banned from this server",731));
5666 case JOIN_DENY_JR_NOOBS :
5667 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because this game does not allow observers",732));
5669 case JOIN_DENY_JR_INGAME_JOIN :
5670 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));
5672 case JOIN_DENY_JR_BAD_VERSION :
5673 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));
5675 case JOIN_DENY_JR_TYPE :
5676 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You cannot join a game in progress unless it is a dogfight mission.",1433));
5680 // call this so that the join request timestamp automatically expires when we hear back from the server
5681 multi_join_reset_join_stamp();
5684 // this packet will consist of
5685 // 1.) netplayer ship classes (85 bytes max)
5686 // 2.) ship weapon state data (241 bytes max)
5687 // 3.) player settings et. al. (133 bytes max)
5688 // TOTAL 459 NOTE : keep this in mind when/if adding new data to this packet
5689 void send_post_sync_data_packet(net_player *p, int std_request)
5691 ubyte data[MAX_PACKET_SIZE], val;
5696 ushort sval, ship_ets;
5697 int idx, player_index;
5698 int packet_size = 0;
5702 BUILD_HEADER(POST_SYNC_DATA);
5704 // some header information for standalone packet routing purposes
5705 val = (ubyte)std_request;
5708 // the standalone has two situations
5709 // 1.) sending a request to the host to distribute this block of data
5710 // 2.) having recevied this block of data from the host, it redistributes it
5711 if((Game_mode & GM_STANDALONE_SERVER) && std_request && (Netgame.host != NULL)){
5712 // case 1, send the request
5713 multi_io_send_reliable(Netgame.host, data, packet_size);
5716 // case 2 for the standalone is below (as normal)
5718 // otherwise build the data now
5720 // add all deleted ships
5721 val = (ubyte)Multi_ts_num_deleted;
5723 for(idx=0;idx<Multi_ts_num_deleted;idx++){
5724 sval = (ushort)Objects[Multi_ts_deleted_objnums[idx]].net_signature;
5730 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5731 shipp = &Ships[Objects[so->objnum].instance];
5733 // don't process non player wing ships
5734 if ( !(shipp->flags & SF_FROM_PLAYER_WING ) )
5740 // # of ships - used multiple times in the packet
5741 val = (ubyte)ship_count;
5744 // add ship class information (85 bytes max)
5745 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5746 shipp = &Ships[Objects[so->objnum].instance];
5748 // don't process non player wing ships
5749 if ( !(shipp->flags & SF_FROM_PLAYER_WING ) )
5752 // add the net signature of the object for look up
5753 ADD_USHORT( Objects[so->objnum].net_signature );
5755 // add the ship info index
5756 val = (ubyte)(shipp->ship_info_index);
5759 // add the ships's team select index
5760 val = (ubyte)shipp->ts_index;
5764 // add weapon state information for all starting ships (241 bytes max)
5765 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5766 shipp = &Ships[Objects[so->objnum].instance];
5768 // don't process non player wing ships
5769 if ( !(shipp->flags & SF_FROM_PLAYER_WING ) )
5772 // if this is a ship owned by a player, we should mark down his weapons bank/link settings now if we're the server
5774 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5775 player_index = multi_find_player_by_net_signature(Objects[so->objnum].net_signature);
5776 if(player_index == -1){
5779 pl = &Net_players[player_index];
5783 // add the net signature and other weapon information
5784 ADD_USHORT( Objects[so->objnum].net_signature );
5786 // add number of primary and secondary banks
5787 bval = (char)(shipp->weapons.num_primary_banks);
5789 bval = (char)(shipp->weapons.num_secondary_banks);
5792 // add weapon bank status
5793 bval = (char)(shipp->weapons.current_primary_bank);
5795 pl->s_info.cur_primary_bank = bval;
5797 // SDL_assert(bval != -1);
5800 bval = (char)(shipp->weapons.current_secondary_bank);
5802 pl->s_info.cur_secondary_bank = bval;
5804 // SDL_assert(bval != -1);
5807 // primary weapon info
5808 bval = (char)(shipp->weapons.primary_bank_weapons[0]);
5810 bval = (char)(shipp->weapons.primary_bank_weapons[1]);
5813 // secondary weapon info
5814 bval = (char)(shipp->weapons.secondary_bank_weapons[0]);
5816 val_short = (short)(shipp->weapons.secondary_bank_ammo[0]);
5817 ADD_SHORT(val_short);
5818 bval = (char)(shipp->weapons.secondary_bank_weapons[1]);
5820 val_short = (short)(shipp->weapons.secondary_bank_ammo[1]);
5821 ADD_SHORT(val_short);
5822 bval = (char)(shipp->weapons.secondary_bank_weapons[2]);
5824 val_short = (short)(shipp->weapons.secondary_bank_ammo[2]);
5825 ADD_SHORT(val_short);
5827 // send primary and secondary weapon link status
5829 if(shipp->flags & SF_PRIMARY_LINKED){
5831 pl->s_info.cur_link_status |= (1<<0);
5835 if(shipp->flags & SF_SECONDARY_DUAL_FIRE){
5837 pl->s_info.cur_link_status |= (1<<1);
5841 // if this is a player ship add (1<<2)
5842 if(Objects[shipp->objnum].flags & OF_PLAYER_SHIP){
5847 // add a ship ets value
5850 ship_ets |= ((ushort)shipp->shield_recharge_index << 8);
5852 ship_ets |= ((ushort)shipp->weapon_recharge_index << 4);
5854 ship_ets |= ((ushort)shipp->engine_recharge_index);
5855 ADD_USHORT(ship_ets);
5859 // 2 cases, if I'm the host on a standalone, I should be sending this to the standalone only
5860 // or if I'm the server as well as the host, I should be sending this to all players
5861 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
5862 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5865 multi_io_send_to_all_reliable(data, packet_size);
5867 // send to a specific player
5869 multi_io_send_reliable(p, data, packet_size);
5872 multi_io_send_reliable(Net_player, data, packet_size);
5879 multi_io_send_to_all_reliable(data, packet_size);
5881 // send to a specific player
5883 multi_io_send_reliable(p, data, packet_size);
5888 void process_post_sync_data_packet(ubyte *data, header *hinfo)
5890 ubyte val, sinfo_index, ts_index;
5892 ushort net_sig, ship_ets, sval;
5896 int offset = HEADER_LENGTH;
5900 // packet routing information
5903 // 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
5904 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) && val){
5907 // at this point we want to delete all necessary ships, change all necessary ship classes, and set all weapons up
5908 multi_ts_create_wings();
5910 // send to the standalone through my socket
5911 send_post_sync_data_packet(Net_player);
5917 // add all deleted ships
5919 Multi_ts_num_deleted = (int)val;
5920 for(idx=0;idx<Multi_ts_num_deleted;idx++){
5921 // get the ship's objnum
5924 objp = multi_get_network_object(sval);
5926 // delete the ship appropriately
5927 // mark the object as having been deleted
5928 Multi_ts_deleted_objnums[idx] = OBJ_INDEX(objp);
5931 ship_add_exited_ship(&Ships[objp->instance], SEF_PLAYER_DELETED);
5932 obj_delete(Multi_ts_deleted_objnums[idx]);
5933 ship_wing_cleanup(objp->instance,&Wings[Ships[objp->instance].wingnum]);
5935 Multi_ts_num_deleted--;
5936 nprintf(("Network","Couldn't find object by net signature for ship delete in post sync data packet\n"));
5944 // process ship class information
5945 for(idx=0; idx<ship_count; idx++){
5946 // get the object's net signature
5947 GET_USHORT(net_sig);
5948 GET_DATA(sinfo_index);
5951 // attempt to get the object
5952 objp = multi_get_network_object(net_sig);
5954 // make sure we found a ship
5955 SDL_assert((objp != NULL) && (objp->type == OBJ_SHIP));
5957 // set the ship to be the right class
5958 change_ship_type(objp->instance,(int)sinfo_index);
5960 // set the ship's team select index
5961 Ships[objp->instance].ts_index = (int)ts_index;
5964 // process ship weapon state info
5965 for(idx=0; idx<ship_count; idx++){
5966 // get the object's net signature
5967 GET_USHORT(net_sig);
5969 // attempt to get the object
5970 objp = multi_get_network_object(net_sig);
5972 // make sure we found a ship
5973 SDL_assert((objp != NULL) && (objp->type == OBJ_SHIP));
5975 // get a pointer to the ship
5976 shipp = &Ships[objp->instance];
5978 // get number of primary and secondary banks;
5980 SDL_assert( b != -1 );
5981 shipp->weapons.num_primary_banks = (int)b;
5984 SDL_assert( b != -1 );
5985 shipp->weapons.num_secondary_banks = (int)b;
5987 // get bank selection info
5992 shipp->weapons.current_primary_bank = (int)b;
5998 shipp->weapons.current_secondary_bank = (int)b;
6000 // primary weapon info
6002 shipp->weapons.primary_bank_weapons[0] = (int)b;
6005 shipp->weapons.primary_bank_weapons[1] = (int)b;
6007 // secondary weapon info
6009 shipp->weapons.secondary_bank_weapons[0] = (int)b;
6010 GET_SHORT(val_short);
6011 shipp->weapons.secondary_bank_ammo[0] = (int)val_short;
6014 shipp->weapons.secondary_bank_weapons[1] = (int)b;
6015 GET_SHORT(val_short);
6016 shipp->weapons.secondary_bank_ammo[1] = (int)val_short;
6019 shipp->weapons.secondary_bank_weapons[2] = (int)b;
6020 GET_SHORT(val_short);
6021 shipp->weapons.secondary_bank_ammo[2] = (int)val_short;
6028 shipp->flags |= SF_PRIMARY_LINKED;
6031 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
6033 Objects[shipp->objnum].flags &= ~(OF_PLAYER_SHIP);
6034 Objects[shipp->objnum].flags &= ~(OF_COULD_BE_PLAYER);
6036 Objects[shipp->objnum].flags |= OF_PLAYER_SHIP;
6038 obj_set_flags( &Objects[shipp->objnum], Objects[shipp->objnum].flags | OF_COULD_BE_PLAYER );
6042 GET_USHORT(ship_ets);
6044 shipp->shield_recharge_index = ((ship_ets & 0x0f00) >> 8);
6046 shipp->weapon_recharge_index = ((ship_ets & 0x00f0) >> 4);
6048 shipp->engine_recharge_index = (ship_ets & 0x000f);
6053 Net_player->state = NETPLAYER_STATE_POST_DATA_ACK;
6054 send_netplayer_update_packet();
6056 // the standalone server will receive this packet from the host of the game, to be applied locally and
6057 // also to be rebroadcast.
6058 if(Game_mode & GM_STANDALONE_SERVER){
6059 // update player ets settings
6060 for(idx=0;idx<MAX_PLAYERS;idx++){
6061 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player->objnum != -1)){
6062 multi_server_update_player_weapons(&Net_players[idx],&Ships[Objects[Net_players[idx].player->objnum].instance]);
6066 send_post_sync_data_packet(NULL,0);
6070 void send_wss_slots_data_packet(int team_num,int final,net_player *p,int std_request)
6072 ubyte data[MAX_PACKET_SIZE],val;
6075 int packet_size = 0;
6078 BUILD_HEADER(WSS_SLOTS_DATA);
6080 // some header information for standalone packet routing purposes
6081 val = (ubyte)std_request;
6085 val = (ubyte)team_num;
6088 // add whether this is the final packet or not
6092 // the standalone has two situations
6093 // 1.) sending a request to the host to distribute this block of data
6094 // 2.) having recevied this block of data from the host, it redistributes it
6095 if((Game_mode & GM_STANDALONE_SERVER) && std_request){
6096 // case 1, send the request
6097 multi_io_send_reliable(Netgame.host, data, packet_size);
6100 // case 2 for the standalone is below (as normal)
6102 // add all the slots
6103 for(idx=0;idx<MULTI_TS_NUM_SHIP_SLOTS;idx++){
6104 // add the ship class
6105 val = (ubyte)Wss_slots_teams[team_num][idx].ship_class;
6109 for(i = 0;i<MAX_WL_WEAPONS;i++){
6110 val = (ubyte)Wss_slots_teams[team_num][idx].wep[i];
6114 // add the weapon counts
6115 for(i = 0;i<MAX_WL_WEAPONS;i++){
6116 val_short = (short)Wss_slots_teams[team_num][idx].wep_count[i];
6117 ADD_SHORT(val_short);
6121 // 2 cases, if I'm the host on a standalone, I should be sending this to the standalone only
6122 // or if I'm the server as well as the host, I should be sending this to all players
6123 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
6124 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
6127 multi_io_send_to_all_reliable(data, packet_size);
6129 // send to a specific player
6131 multi_io_send_reliable(p, data, packet_size);
6134 multi_io_send_reliable(Net_player, data, packet_size);
6141 multi_io_send_to_all_reliable(data, packet_size);
6143 // send to a specific player
6145 multi_io_send_reliable(p, data, packet_size);
6150 void process_wss_slots_data_packet(ubyte *data, header *hinfo)
6152 ubyte val,team_num,final;
6154 int offset = HEADER_LENGTH;
6157 // packet routing information
6163 // get whether this is the final packet or not
6166 // 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
6167 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) && val){
6170 // send to the standalone through my socket
6171 send_wss_slots_data_packet((int)team_num,(int)final,Net_player);
6175 // read in all the slot data
6176 for(idx=0;idx<MULTI_TS_NUM_SHIP_SLOTS;idx++){
6177 memset(&Wss_slots_teams[team_num][idx],0,sizeof(wss_unit));
6179 // get the ship class
6181 Wss_slots_teams[team_num][idx].ship_class = (int)val;
6184 for(i = 0;i<MAX_WL_WEAPONS;i++){
6186 Wss_slots_teams[team_num][idx].wep[i] = (int)val;
6188 // check for signed/unsigned problems
6189 if(Wss_slots_teams[team_num][idx].wep[i] == 255){
6190 Wss_slots_teams[team_num][idx].wep[i] = -1;
6194 // get the weapon counts
6195 for(i = 0;i<MAX_WL_WEAPONS;i++){
6196 GET_SHORT(val_short);
6197 Wss_slots_teams[team_num][idx].wep_count[i] = (int)val_short;
6202 // update my netplayer state if this is the final packet
6204 Net_player->state = NETPLAYER_STATE_WSS_ACK;
6205 send_netplayer_update_packet();
6208 // the standalone server will receive this packet from the host of the game, to be applied locally and
6209 // also to be rebroadcast.
6210 if(Game_mode & GM_STANDALONE_SERVER){
6211 send_wss_slots_data_packet((int)team_num,(int)final,NULL,0);
6213 // add some mission sync text
6214 multi_common_add_text(XSTR("Weapon slots packet\n",735),1);
6218 #define OBJ_VISIBILITY_DOT 0.6f
6220 // send and receive packets for shield explosion information
6221 void send_shield_explosion_packet( int objnum, int tri_num, vector hit_pos )
6224 ubyte data[MAX_PACKET_SIZE], utri_num;
6227 // SDL_assert(!(Netgame.debug_flags & NETD_FLAG_CLIENT_NODAMAGE));
6229 SDL_assert( tri_num < UCHAR_MAX );
6230 utri_num = (ubyte)tri_num;
6232 // for each player, determine if this object is behind the player -- if so, don't
6234 for ( i = 0; i < MAX_PLAYERS; i++ ) {
6235 if ( MULTI_CONNECTED(Net_players[i]) && (&Net_players[i] != Net_player) ) {
6237 vector eye_to_obj_vec, diff, eye_pos;
6240 eye_pos = Net_players[i].s_info.eye_pos;
6241 eye_orient = Net_players[i].s_info.eye_orient;
6243 // check for same vectors
6244 vm_vec_sub(&diff, &Objects[objnum].pos, &eye_pos);
6245 if ( vm_vec_mag_quick(&diff) < 0.01 ){
6249 vm_vec_normalized_dir(&eye_to_obj_vec, &Objects[objnum].pos, &eye_pos);
6250 dot = vm_vec_dot(&eye_orient.v.fvec, &eye_to_obj_vec);
6252 if ( dot < OBJ_VISIBILITY_DOT ){
6256 BUILD_HEADER(SHIELD_EXPLOSION);
6258 ADD_USHORT( Objects[objnum].net_signature );
6261 multi_io_send(&Net_players[i], data, packet_size);
6266 void add_shield_point_multi(int objnum, int tri_num, vector *hit_pos);
6268 void process_shield_explosion_packet( ubyte *data, header *hinfo)
6275 // get the shield hit data
6276 offset = HEADER_LENGTH;
6277 GET_USHORT(signature);
6279 //GET_DATA(hit_pos);
6282 // find the object with this signature. If found, then do a shield explosion
6283 objp = multi_get_network_object( signature );
6286 shield_info *shieldp;
6291 // given the tri num, find the local position which is the average of the
6292 // three vertices of the triangle affected. Use this average point as the hit
6294 // SDL_assert( objp->type == OBJ_SHIP );
6295 if(objp->type != OBJ_SHIP){
6299 pm = model_get(Ships[objp->instance].modelnum);
6300 shieldp = &pm->shield;
6301 SDL_assert( utri_num < shieldp->ntris );
6302 stri = shieldp->tris[utri_num];
6303 vm_vec_zero(&hit_pos);
6304 for ( i = 0; i < 3; i++ ) {
6305 vm_vec_add2( &hit_pos, &(shieldp->verts[stri.verts[i]].pos) );
6307 vm_vec_scale( &hit_pos, 1.0f/3.0f );
6308 add_shield_point_multi( OBJ_INDEX(objp), utri_num, &hit_pos );
6312 void send_player_stats_block_packet(net_player *pl, int stats_code, net_player *target)
6315 ubyte data[MAX_PACKET_SIZE], val;
6317 int packet_size = 0;
6322 sc = &pl->player->stats;
6325 BUILD_HEADER(PLAYER_STATS);
6327 // add the player id
6328 ADD_SHORT(pl->player_id);
6330 // add the byte indicating whether these stats are all-time or not
6331 val = (ubyte)stats_code;
6334 // kill information - alltime
6338 for(idx=0;idx<MAX_SHIP_TYPES;idx++){
6339 u_tmp = sc->kills[idx];
6342 // medal information
6343 for(idx=0;idx<NUM_MEDALS;idx++){
6344 i_tmp = sc->medals[idx];
6350 ADD_INT(sc->assists);
6351 ADD_INT(sc->kill_count);
6352 ADD_INT(sc->kill_count_ok);
6353 ADD_UINT(sc->p_shots_fired);
6354 ADD_UINT(sc->s_shots_fired);
6355 ADD_UINT(sc->p_shots_hit);
6356 ADD_UINT(sc->s_shots_hit);
6357 ADD_UINT(sc->p_bonehead_hits);
6358 ADD_UINT(sc->s_bonehead_hits);
6359 ADD_INT(sc->bonehead_kills);
6361 ADD_UINT(sc->missions_flown);
6362 ADD_UINT(sc->flight_time);
6363 ADD_INT(sc->last_flown);
6364 ADD_INT(sc->last_backup);
6369 for(idx=0;idx<MAX_SHIP_TYPES;idx++){
6370 u_tmp = sc->m_okKills[idx];
6374 ADD_INT(sc->m_score);
6375 ADD_INT(sc->m_assists);
6376 ADD_INT(sc->m_kill_count);
6377 ADD_INT(sc->m_kill_count_ok);
6378 ADD_UINT(sc->mp_shots_fired);
6379 ADD_UINT(sc->ms_shots_fired);
6380 ADD_UINT(sc->mp_shots_hit);
6381 ADD_UINT(sc->ms_shots_hit);
6382 ADD_UINT(sc->mp_bonehead_hits);
6383 ADD_UINT(sc->ms_bonehead_hits);
6384 ADD_INT(sc->m_bonehead_kills);
6385 ADD_INT(sc->m_player_deaths);
6386 ADD_INT(sc->m_medal_earned);
6389 case STATS_MISSION_KILLS:
6390 ADD_INT(sc->m_kill_count);
6391 ADD_INT(sc->m_kill_count_ok);
6392 ADD_INT(sc->m_assists);
6395 case STATS_DOGFIGHT_KILLS:
6396 for(idx=0; idx<MAX_PLAYERS; idx++){
6398 u_tmp = sc->m_dogfight_kills[idx];
6404 ADD_INT(sc->m_kill_count);
6405 ADD_INT(sc->m_kill_count_ok);
6406 ADD_INT(sc->m_assists);
6410 SDL_assert(packet_size < MAX_PACKET_SIZE);
6412 // if we're a client, always send the data to the server
6413 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
6414 multi_io_send_reliable(Net_player, data, packet_size);
6416 // otherwise do server specific stuff
6418 // send to a specific target
6420 multi_io_send_reliable(target, data, packet_size);
6422 // otherwise, send to everyone
6424 multi_io_send_to_all_reliable(data, packet_size);
6429 void process_player_stats_block_packet(ubyte *data, header *hinfo)
6433 scoring_struct *sc,bogus;
6435 int offset = HEADER_LENGTH;
6439 // nprintf(("Network","----------++++++++++********RECEIVED STATS***********+++++++++----------\n"));
6441 // get the player who these stats are for
6442 GET_SHORT(player_id);
6443 player_num = find_player_id(player_id);
6444 if (player_num == -1) {
6445 nprintf(("Network", "Couldn't find player for stats update!\n"));
6446 ml_string("Couldn't find player for stats update!\n");
6451 sc = &Net_players[player_num].player->stats;
6454 // get the stats code
6458 ml_string("Received STATS_ALLTIME\n");
6461 for (idx=0; idx<MAX_SHIP_TYPES; idx++) {
6463 sc->kills[idx] = u_tmp;
6466 // read in the stats
6467 for (idx=0; idx<NUM_MEDALS; idx++) {
6469 sc->medals[idx] = i_tmp;
6474 GET_INT(sc->assists);
6475 GET_INT(sc->kill_count);
6476 GET_INT(sc->kill_count_ok);
6477 GET_UINT(sc->p_shots_fired);
6478 GET_UINT(sc->s_shots_fired);
6479 GET_UINT(sc->p_shots_hit);
6480 GET_UINT(sc->s_shots_hit);
6481 GET_UINT(sc->p_bonehead_hits);
6482 GET_UINT(sc->s_bonehead_hits);
6483 GET_INT(sc->bonehead_kills);
6485 GET_UINT(sc->missions_flown);
6486 GET_UINT(sc->flight_time);
6487 GET_INT(sc->last_flown);
6488 GET_INT(sc->last_backup);
6492 ml_string("Received STATS_MISSION\n");
6494 // kills - mission OK
6495 for (idx=0; idx<MAX_SHIP_TYPES; idx++) {
6497 sc->m_okKills[idx] = u_tmp;
6500 GET_INT(sc->m_score);
6501 GET_INT(sc->m_assists);
6502 GET_INT(sc->m_kill_count);
6503 GET_INT(sc->m_kill_count_ok);
6504 GET_UINT(sc->mp_shots_fired);
6505 GET_UINT(sc->ms_shots_fired);
6506 GET_UINT(sc->mp_shots_hit);
6507 GET_UINT(sc->ms_shots_hit);
6508 GET_UINT(sc->mp_bonehead_hits);
6509 GET_UINT(sc->ms_bonehead_hits);
6510 GET_INT(sc->m_bonehead_kills);
6511 GET_INT(sc->m_player_deaths);
6512 GET_INT(sc->m_medal_earned);
6515 case STATS_MISSION_KILLS:
6516 ml_string("Received STATS_MISSION_KILLS\n");
6518 GET_INT(sc->m_kill_count);
6519 GET_INT(sc->m_kill_count_ok);
6520 GET_INT(sc->m_assists);
6523 case STATS_DOGFIGHT_KILLS:
6524 ml_string("Received STATS_DOGFIGHT_KILLS\n");
6525 if(player_num >= 0){
6526 ml_printf("Dogfight stats for %s", Net_players[player_num].player->callsign);
6528 for(idx=0; idx<MAX_PLAYERS; idx++){
6531 sc->m_dogfight_kills[idx] = u_tmp;
6532 if(player_num >= 0){
6533 ml_printf("%d", Net_players[player_num].player->stats.m_dogfight_kills[idx]);
6537 GET_INT(sc->m_kill_count);
6538 GET_INT(sc->m_kill_count_ok);
6539 GET_INT(sc->m_assists);
6544 // if I'm the server of the game, I should always rebroadcast these stats
6545 if ((Net_player->flags & NETINFO_FLAG_AM_MASTER) && (sc != &bogus)) {
6546 // make sure these are alltime stats
6547 SDL_assert(val == STATS_ALLTIME);
6549 multi_broadcast_stats(STATS_ALLTIME);
6553 // called to create asteroid stuff
6554 void send_asteroid_create( object *new_objp, object *parent_objp, int asteroid_type, vector *relvec )
6557 ubyte data[MAX_PACKET_SIZE];
6558 ubyte packet_type, atype;
6562 if (relvec != NULL )
6565 BUILD_HEADER( ASTEROID_INFO );
6566 packet_type = ASTEROID_CREATE;
6568 SDL_assert( asteroid_type < UCHAR_MAX );
6569 atype = (ubyte)asteroid_type;
6571 ADD_DATA( packet_type );
6572 ADD_USHORT( parent_objp->net_signature );
6573 ADD_USHORT( new_objp->net_signature );
6576 add_vector_data( data, &packet_size, vec );
6578 multi_io_send_to_all(data, packet_size);
6581 void send_asteroid_throw( object *objp )
6584 ubyte data[MAX_PACKET_SIZE], packet_type;
6586 BUILD_HEADER( ASTEROID_INFO );
6588 // this packet type is an asteroid throw
6589 packet_type = ASTEROID_THROW;
6590 ADD_DATA( packet_type );
6591 ADD_USHORT( objp->net_signature );
6592 //ADD_DATA( objp->pos );
6593 add_vector_data( data, &packet_size, objp->pos );
6594 //ADD_DATA( objp->phys_info.vel );
6595 add_vector_data( data, &packet_size, objp->phys_info.vel );
6597 multi_io_send_to_all(data, packet_size);
6600 void send_asteroid_hit( object *objp, object *other_objp, vector *hitpos, float damage )
6603 ubyte data[MAX_PACKET_SIZE], packet_type;
6607 if ( hitpos != NULL )
6610 // build up an asteroid hit packet
6611 BUILD_HEADER( ASTEROID_INFO );
6612 packet_type = ASTEROID_HIT;
6613 ADD_DATA( packet_type );
6614 ADD_USHORT( objp->net_signature );
6616 if(other_objp == NULL){
6617 ushort invalid_sig = 0xffff;
6618 ADD_USHORT(invalid_sig);
6620 ADD_USHORT( other_objp->net_signature );
6623 add_vector_data( data, &packet_size, vec );
6624 ADD_FLOAT( damage );
6626 multi_io_send_to_all(data, packet_size);
6629 void process_asteroid_info( ubyte *data, header *hinfo )
6634 offset = HEADER_LENGTH;
6635 GET_DATA( packet_type );
6637 // based on the packet type, do something interesting with an asteroid!
6638 switch( packet_type ) {
6640 case ASTEROID_CREATE: {
6641 ushort psignature, signature;
6643 vector relvec = ZERO_VECTOR;
6644 object *parent_objp;
6646 GET_USHORT( psignature );
6647 GET_USHORT( signature );
6649 //GET_DATA( relvec );
6650 get_vector_data( data, &offset, relvec );
6652 // after getting the values, set the next network signature, and call the create sub function
6653 multi_set_network_signature( signature, MULTI_SIG_ASTEROID );
6654 parent_objp = multi_get_network_object( psignature );
6655 if ( parent_objp ) {
6656 asteroid_sub_create( parent_objp, atype, &relvec );
6658 nprintf(("Network", "Couldn't create asteroid because parent wasn't found!!!\n"));
6665 // asteroid throw packet -- asteroid has wrapped bounds
6666 case ASTEROID_THROW: {
6668 vector pos = ZERO_VECTOR, vel = ZERO_VECTOR;
6671 GET_USHORT( signature );
6673 get_vector_data( data, &offset, pos );
6675 get_vector_data( data, &offset, vel );
6676 objp = multi_get_network_object( signature );
6678 nprintf(("Network", "Couldn't throw asteroid because couldn't find it\n"));
6682 objp->phys_info.vel = vel;
6683 objp->phys_info.desired_vel = vel;
6687 case ASTEROID_HIT: {
6688 ushort signature, osignature;
6689 object *objp, *other_objp;
6690 vector hitpos = ZERO_VECTOR;
6693 GET_USHORT( signature );
6694 GET_USHORT( osignature );
6695 //GET_DATA( hitpos );
6696 get_vector_data( data, &offset, hitpos );
6697 GET_FLOAT( damage );
6699 objp = multi_get_network_object( signature );
6700 if(osignature == 0xffff){
6703 other_objp = multi_get_network_object( osignature );
6706 nprintf(("Network", "Cannot hit asteroid because signature isn't found\n"));
6710 if ( IS_VEC_NULL(&hitpos) ){
6711 asteroid_hit( objp, other_objp, NULL, damage );
6713 asteroid_hit( objp, other_objp, &hitpos, damage );
6716 // if we know the other object is a weapon, then do a weapon hit to kill the weapon
6717 if ( other_objp && (other_objp->type == OBJ_WEAPON) ){
6718 weapon_hit( other_objp, objp, &hitpos );
6731 void send_host_restr_packet(const char *callsign, int code, int mode)
6733 ubyte data[MAX_PACKET_SIZE],val;
6734 int packet_size = 0;
6736 // build the header and add the opcode
6737 BUILD_HEADER(HOST_RESTR_QUERY);
6744 ADD_STRING(callsign);
6746 // if I'm the standalone server, I should be sending this to the game host
6747 if((Game_mode & GM_STANDALONE_SERVER) && (Netgame.host != NULL)){
6748 multi_io_send_reliable(Netgame.host, data, packet_size);
6750 // otherwise if I'm the host, I should be sending a reply back to the standalone server
6752 SDL_assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
6753 multi_io_send_reliable(Net_player, data, packet_size);
6757 void process_host_restr_packet(ubyte *data, header *hinfo)
6761 int offset = HEADER_LENGTH;
6763 // get the opcode and the callsign
6766 GET_STRING(callsign);
6769 // do code specific operations
6771 // query to the host from standalone
6773 SDL_assert((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER));
6775 // set the join mode
6776 Multi_join_restr_mode = mode;
6778 // set the timestamp
6779 Multi_restr_query_timestamp = timestamp(MULTI_QUERY_RESTR_STAMP);
6781 // notify the host of the event
6782 gamesnd_play_iface(SND_BRIEF_STAGE_CHG_FAIL);
6783 HUD_printf(XSTR("Player %s has tried to join - allow (y/n) ?",736),callsign);
6786 // affirmative reply from the host to the standalone
6788 SDL_assert(Game_mode & GM_STANDALONE_SERVER);
6790 // let the player join if the timestamp has not already elapsed on the server
6791 if(Multi_restr_query_timestamp != -1){
6792 multi_process_valid_join_request(&Multi_restr_join_request,&Multi_restr_addr,(int)mode);
6798 SDL_assert(Game_mode & GM_STANDALONE_SERVER);
6799 Netgame.flags &= ~(NG_FLAG_INGAME_JOINING);
6800 Multi_restr_query_timestamp = -1;
6805 void send_netgame_end_error_packet(int notify_code,int err_code)
6809 int packet_size = 0;
6811 // only the server should ever be here - although this might change if for some reason the host wants to end the game
6812 SDL_assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
6814 // build the header and add the notification and error codes
6815 BUILD_HEADER(NETGAME_END_ERROR);
6816 code = (char)notify_code;
6818 code = (char)err_code;
6822 multi_io_send_to_all_reliable(data, packet_size);
6825 void process_netgame_end_error_packet(ubyte *data, header *hinfo)
6827 int offset = HEADER_LENGTH;
6828 char notify_code,error_code;
6830 // get the error and notification codes
6831 GET_DATA(notify_code);
6832 GET_DATA(error_code);
6836 multi_quit_game(PROMPT_NONE,notify_code,error_code);
6839 // sends info that a countermeasure succeeded.
6840 void send_countermeasure_success_packet( int objnum )
6842 int pnum, packet_size;
6843 ubyte data[MAX_PACKET_SIZE];
6845 pnum = multi_find_player_by_object( &Objects[objnum] );
6847 nprintf(("Network", "Coulnd't find player for countermeasure success packet\n"));
6851 BUILD_HEADER(COUNTERMEASURE_SUCCESS);
6852 multi_io_send(&Net_players[pnum], data, packet_size);
6855 // start the flashing of my hud gauge
6856 void process_countermeasure_success_packet( ubyte *data, header *hinfo )
6860 offset = HEADER_LENGTH;
6863 hud_start_text_flash(XSTR("Evaded", 1430), 800);
6864 snd_play(&Snds[SND_MISSILE_EVADED_POPUP]);
6867 #define UPDATE_IS_PAUSED (1<<0)
6868 #define UPDATE_HULL_INFO (1<<1)
6870 void send_client_update_packet(net_player *pl)
6872 ubyte data[MAX_PACKET_SIZE],val;
6873 int packet_size = 0;
6876 BUILD_HEADER(CLIENT_UPDATE);
6880 // add the pause status
6881 if ( Multi_pause_status ) {
6882 val |= UPDATE_IS_PAUSED;
6883 } 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) ) {
6884 val |= UPDATE_HULL_INFO;
6885 SDL_assert( Player_ship ); // I"d better have one of these!!!!
6890 // if paused, add the net address of the guy who paused
6891 if(val & UPDATE_IS_PAUSED){
6892 SDL_assert(Multi_pause_pauser != NULL);
6893 ADD_SHORT(Multi_pause_pauser->player_id);
6896 // when not paused, send hull/shield/subsystem updates to all clients (except for ingame joiners)
6897 if ( val & UPDATE_HULL_INFO ) {
6899 ubyte percent, ns, threats;
6902 ship_subsys *subsysp;
6905 // get the object for the player
6906 SDL_assert( pl->player->objnum != -1 );
6908 objp = &Objects[pl->player->objnum];
6910 SDL_assert ( objp->type == OBJ_SHIP );
6911 shipp = &Ships[objp->instance];
6912 sip = &Ship_info[shipp->ship_info_index];
6914 // hull strength and sheild mesh information are floats (as a percentage). Pass the integer
6915 // percentage value since that should be close enough
6916 float temp = (objp->hull_strength / sip->initial_hull_strength * 100.0f);
6920 percent = (ubyte)temp;
6922 ADD_DATA( percent );
6924 for (i = 0; i < MAX_SHIELD_SECTIONS; i++ ) {
6925 percent = (ubyte)(objp->shields[i] / (sip->shields / MAX_SHIELD_SECTIONS) * 100.0f);
6926 ADD_DATA( percent );
6929 // add the data for the subsystem hits. We can assume that the lists will be the same side of
6930 // both machines. Added as percent since that number <= 100
6932 // also write out the number of subsystems. We do this because the client might not know
6933 // about the object he is getting data for. (i.e. he killed the object already).
6934 ns = (ubyte)sip->n_subsystems;
6937 // now the subsystems.
6938 for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) {
6939 percent = (ubyte)(subsysp->current_hits / subsysp->system_info->max_hits * 100.0f);
6940 ADD_DATA( percent );
6943 // compute the threats for this player. Only compute the threats if this player is actually
6944 // playing (i.e. he has a ship)
6945 hud_update_reticle( pl->player );
6946 threats = (ubyte)pl->player->threat_flags;
6947 ADD_DATA( threats );
6949 // add his energy level for guns
6950 ADD_FLOAT(shipp->weapon_energy);
6952 // add his secondary bank ammo
6953 ADD_INT(shipp->weapons.num_secondary_banks);
6954 for(i=0; i<shipp->weapons.num_secondary_banks; i++){
6955 ADD_INT(shipp->weapons.secondary_bank_ammo[i]);
6960 ADD_INT(pl->sv_last_pl);
6962 // send the packet reliably to the player
6963 multi_io_send(pl, data, packet_size);
6966 void process_client_update_packet(ubyte *data, header *hinfo)
6971 int is_paused, have_hull_info;
6974 float weapon_energy;
6975 int offset = HEADER_LENGTH;
6977 // get the header byte containing useful information
6980 is_paused = (val & UPDATE_IS_PAUSED)?1:0;
6981 have_hull_info = (val & UPDATE_HULL_INFO)?1:0;
6983 // if we are paused, get who paused
6986 player_index = find_player_id(pauser);
6987 if(player_index != -1){
6988 Multi_pause_pauser = &Net_players[player_index];
6990 Multi_pause_pauser = NULL;
6994 // if we have hull information, then read it in.
6995 if ( have_hull_info ) {
6999 ubyte hull_percent, shield_percent[MAX_SHIELD_SECTIONS], n_subsystems, subsystem_percent[MAX_MODEL_SUBSYSTEMS], threats;
7001 ship_subsys *subsysp;
7005 // hull strength and sheild mesh information are floats (as a percentage). Pass the integer
7006 // percentage value since that should be close enough
7007 GET_DATA( hull_percent );
7009 for (i = 0; i < MAX_SHIELD_SECTIONS; i++ ){
7011 shield_percent[i] = ub_tmp;
7014 // get the data for the subsystems
7015 GET_DATA( n_subsystems );
7016 for ( i = 0; i < n_subsystems; i++ ){
7018 subsystem_percent[i] = ub_tmp;
7021 GET_DATA( threats );
7023 // add his energy level for guns
7024 GET_FLOAT(weapon_energy);
7026 // add his secondary bank ammo
7027 GET_INT(ammo_count);
7028 for(i=0; i<ammo_count; i++){
7032 // assign the above information to my ship, assuming that I can find it! Ingame joiners might get this
7033 // packet because of delay between reliable packet acknowledging my ingame ship and the start of these
7034 // UDP client update packets. Only read this info if I have a ship.
7035 if ( !(Net_player->flags & NETINFO_FLAG_INGAME_JOIN) && (Player_ship != NULL) && (Player_obj != NULL) && (Net_player != NULL)) {
7036 shipp = Player_ship;
7038 sip = &Ship_info[shipp->ship_info_index];
7040 fval = hull_percent * sip->initial_hull_strength / 100.0f;
7041 objp->hull_strength = fval;
7043 for ( i = 0; i < MAX_SHIELD_SECTIONS; i++ ) {
7044 fval = (shield_percent[i] * sip->shields / 100.0f) / MAX_SHIELD_SECTIONS;
7045 objp->shields[i] = fval;
7048 // for sanity, be sure that the number of susbystems that I read in matches the player. If not,
7049 // then don't read these in.
7050 if ( n_subsystems == sip->n_subsystems ) {
7052 n_subsystems = 0; // reuse this variable
7053 for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) {
7056 fval = subsystem_percent[n_subsystems] * subsysp->system_info->max_hits / 100.0f;
7057 subsysp->current_hits = fval;
7059 // add the value just generated (it was zero'ed above) into the array of generic system types
7060 subsys_type = subsysp->system_info->type; // this is the generic type of subsystem
7061 SDL_assert ( subsys_type < SUBSYSTEM_MAX );
7062 shipp->subsys_info[subsys_type].current_hits += fval;
7066 ship_recalc_subsys_strength( shipp );
7068 shipp->weapon_energy = weapon_energy;
7069 for(i=0; i<ammo_count; i++){
7070 shipp->weapons.secondary_bank_ammo[i] = ammo[i];
7073 // update my threat flags.
7074 // temporarily commented out until tested.
7075 Net_player->player->threat_flags = threats;
7082 if(Net_player != NULL){
7083 Net_player->cl_last_pl = pl;
7087 // note, if we're already paused or unpaused, calling these will have no effect, so it is safe to do so
7088 if(!popup_active() && !(Net_player->flags & NETINFO_FLAG_RESPAWNING) && !(Net_player->flags & NETINFO_FLAG_LIMBO)){
7090 multi_pause_pause();
7092 multi_pause_unpause();
7097 void send_countdown_packet(int time)
7101 int packet_size = 0;
7103 // build the header and add the time
7104 BUILD_HEADER(COUNTDOWN);
7108 // if we're the server, we should broadcast to everyone
7109 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
7110 multi_io_send_to_all_reliable(data, packet_size);
7112 // otherwise we'de better be a host sending to the standalone
7114 SDL_assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
7115 multi_io_send_reliable(Net_player, data, packet_size);
7119 void process_countdown_packet(ubyte *data, header *hinfo)
7121 int offset = HEADER_LENGTH;
7128 // if we're not in the post sync data screen, ignore it
7129 if(gameseq_get_state() != GS_STATE_MULTI_MISSION_SYNC){
7133 // if we're the standalone, this should be a -1 telling us to start the countdown
7134 if(Game_mode & GM_STANDALONE_SERVER){
7135 SDL_assert((int)time == -1);
7137 // start the countdown
7138 multi_sync_start_countdown();
7140 // otherwise if we're clients, just bash the countdown
7142 Multi_sync_countdown = (int)time;
7146 // packets for debriefing information
7147 void send_debrief_info( int stage_count[], int *stages[] )
7149 ubyte data[MAX_PACKET_SIZE];
7150 int packet_size, i, j;
7153 BUILD_HEADER(DEBRIEF_INFO);
7155 // add the data for the teams
7156 for ( i = 0; i < Num_teams; i++ ) {
7159 count = stage_count[i];
7161 for ( j = 0; j < count; j++ ) {
7162 i_tmp = stages[i][j];
7167 multi_io_send_to_all_reliable(data, packet_size);
7170 // process a debrief info packet from the server
7171 void process_debrief_info( ubyte *data, header *hinfo )
7174 int stage_counts[MAX_TEAMS], active_stages[MAX_TEAMS][MAX_DEBRIEF_STAGES], *stages[MAX_TEAMS];
7177 offset = HEADER_LENGTH;
7178 for ( i = 0; i < Num_teams; i++ ) {
7182 stage_counts[i] = count;
7183 stages[i] = active_stages[i];
7184 for ( j = 0; j < count; j++ ) {
7186 active_stages[i][j] = i_tmp;
7191 // now that we have the stage data for the debriefing stages, call debrief function with the
7192 // data so that clients can now see the debriefing stuff. Do it only for my team.
7193 SDL_assert( (Net_player->p_info.team >= 0) && (Net_player->p_info.team < Num_teams) );
7194 debrief_set_multi_clients( stage_counts[Net_player->p_info.team], stages[Net_player->p_info.team] );
7197 // sends homing information to all clients. We only need signature and num_missiles (because of hornets).
7198 // sends homing_object and homing_subsystem to all clients.
7199 void send_homing_weapon_info( int weapon_num )
7201 ubyte data[MAX_PACKET_SIZE];
7204 object *homing_object;
7205 ushort homing_signature;
7208 wp = &Weapons[weapon_num];
7210 // be sure that this weapon object is a homing object.
7211 if ( !(Weapon_info[wp->weapon_info_index].wi_flags & WIF_HOMING) )
7214 // get the homing signature. If this weapon isn't homing on anything, then sent 0 as the
7215 // homing signature.
7216 homing_signature = 0;
7217 homing_object = wp->homing_object;
7218 if ( homing_object != NULL ) {
7219 homing_signature = homing_object->net_signature;
7221 // get the subsystem index.
7223 if ( (homing_object->type == OBJ_SHIP) && (wp->homing_subsys != NULL) ) {
7226 s_index = ship_get_index_from_subsys( wp->homing_subsys, OBJ_INDEX(homing_object), 1 );
7227 SDL_assert( s_index < CHAR_MAX ); // better be less than this!!!!
7228 t_subsys = (char)s_index;
7232 BUILD_HEADER(HOMING_WEAPON_UPDATE);
7233 ADD_USHORT( Objects[wp->objnum].net_signature );
7234 ADD_USHORT( homing_signature );
7235 ADD_DATA( t_subsys );
7237 multi_io_send_to_all(data, packet_size);
7240 // process a homing weapon info change packet. multiple_missiles parameter specifies is this
7241 // packet contains information for multiple weapons (like hornets).
7242 void process_homing_weapon_info( ubyte *data, header *hinfo )
7245 ushort weapon_signature, homing_signature;
7247 object *homing_object, *weapon_objp;
7250 offset = HEADER_LENGTH;
7252 // get the data for the packet
7253 GET_USHORT( weapon_signature );
7254 GET_USHORT( homing_signature );
7255 GET_DATA( h_subsys );
7258 // deal with changing this weapons homing information
7259 weapon_objp = multi_get_network_object( weapon_signature );
7260 if ( weapon_objp == NULL ) {
7261 nprintf(("Network", "Couldn't find weapon object for homing update -- skipping update\n"));
7264 SDL_assert( weapon_objp->type == OBJ_WEAPON );
7265 wp = &Weapons[weapon_objp->instance];
7267 // be sure that we can find these weapons and
7268 homing_object = multi_get_network_object( homing_signature );
7269 if ( homing_object == NULL ) {
7270 nprintf(("Network", "Couldn't find homing object for homing update\n"));
7274 if ( homing_object->type == OBJ_WEAPON ) {
7275 SDL_assert(Weapon_info[Weapons[homing_object->instance].weapon_info_index].wi_flags & WIF_BOMB);
7278 wp->homing_object = homing_object;
7279 wp->homing_subsys = NULL;
7280 wp->target_num = OBJ_INDEX(homing_object);
7281 wp->target_sig = homing_object->signature;
7282 if ( h_subsys != -1 ) {
7283 SDL_assert( homing_object->type == OBJ_SHIP );
7284 wp->homing_subsys = ship_get_indexed_subsys( &Ships[homing_object->instance], h_subsys);
7287 if ( homing_object->type == OBJ_SHIP ) {
7288 nprintf(("Network", "Updating homing information for weapon -- homing on %s\n", Ships[homing_object->instance].ship_name));
7292 void send_emp_effect(ushort net_sig, float intensity, float time)
7297 SDL_assert(MULTIPLAYER_MASTER);
7299 // build the packet and add the opcode
7300 BUILD_HEADER(EMP_EFFECT);
7301 ADD_USHORT(net_sig);
7302 ADD_FLOAT(intensity);
7305 // send it to the player
7306 multi_io_send_to_all(data, packet_size);
7309 void process_emp_effect(ubyte *data, header *hinfo)
7311 float intensity, time;
7314 int offset = HEADER_LENGTH;
7316 // read in the EMP effect data
7317 GET_USHORT(net_sig);
7318 GET_FLOAT(intensity);
7322 // try and find the object
7323 objp = multi_get_network_object(net_sig);
7324 if((objp != NULL) && (objp->type == OBJ_SHIP)){
7325 // if i'm not an observer and I have a valid ship, play the EMP effect
7326 if(!(Net_player->flags & NETINFO_FLAG_OBSERVER) && (Player_obj != NULL) && (Player_obj->type == OBJ_SHIP) && (Player_obj == objp)){
7327 emp_start_local(intensity, time);
7330 // start the effect for the ship itself
7331 emp_start_ship(objp, intensity, time);
7335 // tells whether or not reinforcements are available
7336 void send_reinforcement_avail( int rnum )
7341 BUILD_HEADER(REINFORCEMENT_AVAIL);
7343 multi_io_send_to_all_reliable(data, packet_size);
7346 void process_reinforcement_avail( ubyte *data, header *hinfo )
7351 offset = HEADER_LENGTH;
7355 // sanity check for a valid reinforcement number
7356 if ( (rnum >= 0) && (rnum < Num_reinforcements) ) {
7357 Reinforcements[rnum].flags |= RF_IS_AVAILABLE;
7361 void send_change_iff_packet(ushort net_signature, int new_team)
7363 ubyte data[MAX_PACKET_SIZE];
7364 int packet_size = 0;
7366 if(Net_player == NULL){
7369 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
7373 // build the packet and add the data
7374 BUILD_HEADER(CHANGE_IFF);
7375 ADD_USHORT(net_signature);
7378 // send to all players
7379 multi_io_send_to_all_reliable(data, packet_size);
7382 void process_change_iff_packet( ubyte *data, header *hinfo)
7384 int offset = HEADER_LENGTH;
7385 ushort net_signature;
7390 GET_USHORT(net_signature);
7394 // lookup the object
7395 objp = multi_get_network_object(net_signature);
7396 if((objp != NULL) && (objp->type == OBJ_SHIP) && (objp->instance >=0)){
7397 Ships[objp->instance].team = new_team;
7401 void send_NEW_primary_fired_packet(ship *shipp, int banks_fired)
7403 int packet_size, objnum;
7404 ubyte data[MAX_PACKET_SIZE]; // ubanks_fired, current_bank;
7407 net_player *ignore = NULL;
7409 // sanity checking for now
7410 SDL_assert ( banks_fired <= 3 );
7412 // get an object pointer for this ship.
7413 objnum = shipp->objnum;
7414 objp = &Objects[objnum];
7416 // if i'm a multiplayer client, I should never send primary fired packets for anyone except me
7417 if(MULTIPLAYER_CLIENT && (Player_obj != objp)){
7421 // just in case nothing got fired
7422 if(banks_fired <= 0){
7426 // ubanks_fired = (ubyte)banks_fired;
7427 // current_bank = (ubyte)shipp->weapons.current_primary_bank;
7428 // SDL_assert( current_bank <= 3 );
7430 // insert the current primary bank into this byte
7431 // ubanks_fired |= (current_bank << CURRENT_BANK_BIT);
7433 // append the SF_PRIMARY_LINKED flag on the top nibble of the banks_fired
7434 // if ( shipp->flags & SF_PRIMARY_LINKED ){
7435 // ubanks_fired |= (1<<7);
7438 // determine if its a player ship and don't send to him if we're in "client firing" mode
7439 // if((Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING) && MULTIPLAYER_MASTER){
7440 if(MULTIPLAYER_MASTER){
7441 np_index = multi_find_player_by_net_signature(objp->net_signature);
7442 if((np_index >= 0) && (np_index < MAX_PLAYERS)){
7443 ignore = &Net_players[np_index];
7447 // build up the standard weapon fired packet. This packet will get sent to all players if an AI
7448 // ship fired the primary weapons. If a player fired the weaspon, then this packet will get sent
7449 // to every player but the guy who actullly fired the weapon. This method is used to help keep client
7450 // and server in sync w.r.t. weapon energy for player ship
7451 BUILD_HEADER( PRIMARY_FIRED_NEW );
7452 ADD_USHORT(objp->net_signature);
7453 // ADD_DATA(ubanks_fired);
7455 // if I'm a server, broadcast to all players
7456 if(MULTIPLAYER_MASTER){
7457 multi_io_send_to_all(data, packet_size, ignore);
7460 multi_rate_add(1, "wfi", packet_size);
7462 // otherwise just send to the server
7464 multi_io_send(Net_player, data, packet_size);
7468 void process_NEW_primary_fired_packet(ubyte *data, header *hinfo)
7470 int offset; // linked;
7471 // ubyte banks_fired, current_bank;
7476 // read all packet info
7477 offset = HEADER_LENGTH;
7478 GET_USHORT(shooter_sig);
7479 // GET_DATA(banks_fired);
7482 // find the object this fired packet is operating on
7483 objp = multi_get_network_object( shooter_sig );
7484 if ( objp == NULL ) {
7485 nprintf(("Network", "Could not find ship for fire primary packet NEW!"));
7488 // if this object is not actually a valid ship, don't do anything
7489 if(objp->type != OBJ_SHIP){
7492 if(objp->instance < 0){
7495 // shipp = &Ships[objp->instance];
7497 // get the link status of the primary banks
7499 // if ( banks_fired & (1<<7) ) {
7501 // banks_fired ^= (1<<7);
7504 // get the current primary bank
7505 // current_bank = (ubyte)(banks_fired >> CURRENT_BANK_BIT);
7506 // current_bank &= 0x3;
7507 // SDL_assert( (current_bank >= 0) && (current_bank < MAX_PRIMARY_BANKS) );
7508 // shipp->weapons.current_primary_bank = current_bank;
7510 // strip off all remaining bits and just keep which banks were actually fired.
7511 // banks_fired &= 0x3;
7513 // set the link status of the ship if not the player. If it is the player, we will do sanity checking
7516 // shipp->flags &= ~SF_PRIMARY_LINKED;
7518 // shipp->flags |= SF_PRIMARY_LINKED;
7521 // if we're in client firing mode, ignore ones for myself
7522 if((Player_obj != NULL) && (Player_obj == objp)){
7526 ship_fire_primary( objp, 0, 1 );
7529 void send_NEW_countermeasure_fired_packet(object *objp, int cmeasure_count, int rand_val)
7531 ubyte data[MAX_PACKET_SIZE];
7534 net_player *ignore = NULL;
7536 // if i'm a multiplayer client, I should never send primary fired packets for anyone except me
7537 if(MULTIPLAYER_CLIENT && (Player_obj != objp)){
7541 SDL_assert ( cmeasure_count < UCHAR_MAX );
7542 BUILD_HEADER(COUNTERMEASURE_NEW);
7543 ADD_USHORT( objp->net_signature );
7544 ADD_INT( rand_val );
7546 nprintf(("Network","Sending NEW countermeasure packet!\n"));
7548 // determine if its a player ship and don't send to him if we're in "client firing" mode
7549 // if((Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING) && MULTIPLAYER_MASTER){
7550 if(MULTIPLAYER_MASTER){
7551 np_index = multi_find_player_by_net_signature(objp->net_signature);
7552 if((np_index >= 0) && (np_index < MAX_PLAYERS)){
7553 ignore = &Net_players[np_index];
7557 // if I'm the server, send to all players
7558 if(MULTIPLAYER_MASTER){
7559 multi_io_send_to_all(data, packet_size, ignore);
7561 // otherwise send to the server
7563 multi_io_send(Net_player, data, packet_size);
7567 void process_NEW_countermeasure_fired_packet(ubyte *data, header *hinfo)
7574 offset = HEADER_LENGTH;
7575 GET_USHORT( signature );
7576 GET_INT( rand_val );
7579 objp = multi_get_network_object( signature );
7580 if ( objp == NULL ) {
7581 nprintf(("network", "Could find object whose countermeasures are being launched!!!\n"));
7584 if(objp->type != OBJ_SHIP){
7588 // if we're in client firing mode, ignore ones for myself
7589 // if((Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING) && (Player_obj != NULL) && (Player_obj == objp)){
7590 if((Player_obj != NULL) && (Player_obj == objp)){
7594 // make it so ship can fire right away!
7595 Ships[objp->instance].cmeasure_fire_stamp = timestamp(0);
7596 if ( objp == Player_obj ){
7597 nprintf(("network", "firing countermeasure from my ship\n"));
7599 ship_launch_countermeasure( objp, rand_val );
7602 void send_beam_fired_packet(object *shooter, ship_subsys *turret, object *target, int beam_info_index, beam_info *override)
7604 ubyte data[MAX_PACKET_SIZE];
7605 int packet_size = 0;
7610 // only the server should ever be doing this
7611 SDL_assert(MULTIPLAYER_MASTER);
7613 // setup outgoing data
7614 SDL_assert(shooter != NULL);
7615 SDL_assert(turret != NULL);
7616 SDL_assert(target != NULL);
7617 SDL_assert(override != NULL);
7618 if((shooter == NULL) || (turret == NULL) || (target == NULL) || (override == NULL)){
7621 u_beam_info = (ubyte)beam_info_index;
7622 subsys_index = (char)ship_get_index_from_subsys(turret, OBJ_INDEX(shooter));
7623 SDL_assert(subsys_index >= 0);
7624 if(subsys_index < 0){
7628 // swap the beam_info override info into little endian byte order
7629 b_info.dir_a.xyz.x = INTEL_FLOAT(override->dir_a.xyz.x);
7630 b_info.dir_a.xyz.y = INTEL_FLOAT(override->dir_a.xyz.y);
7631 b_info.dir_a.xyz.z = INTEL_FLOAT(override->dir_a.xyz.z);
7633 b_info.dir_b.xyz.x = INTEL_FLOAT(override->dir_b.xyz.x);
7634 b_info.dir_b.xyz.y = INTEL_FLOAT(override->dir_b.xyz.y);
7635 b_info.dir_b.xyz.z = INTEL_FLOAT(override->dir_b.xyz.z);
7637 b_info.delta_ang = INTEL_FLOAT(override->delta_ang);
7638 b_info.shot_count = override->shot_count;
7640 for (int i = 0; i < b_info.shot_count; i++) {
7641 b_info.shot_aim[i] = INTEL_FLOAT(override->shot_aim[i]);
7645 BUILD_HEADER(BEAM_FIRED);
7646 ADD_USHORT(shooter->net_signature);
7647 ADD_DATA(subsys_index);
7648 ADD_USHORT(target->net_signature);
7649 ADD_DATA(u_beam_info);
7650 ADD_DATA(b_info); // FIXME: This is still wrong, we shouldn't be sending an entire struct over the wire - taylor
7652 // send to all clients
7653 multi_io_send_to_all_reliable(data, packet_size);
7656 void process_beam_fired_packet(ubyte *data, header *hinfo)
7659 ushort shooter_sig, target_sig;
7663 beam_fire_info fire_info;
7665 // only clients should ever get this
7666 SDL_assert(MULTIPLAYER_CLIENT);
7668 // read in packet data
7669 offset = HEADER_LENGTH;
7670 GET_USHORT(shooter_sig);
7671 GET_DATA(subsys_index);
7672 GET_USHORT(target_sig);
7673 GET_DATA(u_beam_info);
7678 // swap the beam_info override info into native byte order
7679 b_info.dir_a.xyz.x = INTEL_FLOAT( b_info.dir_a.xyz.x );
7680 b_info.dir_a.xyz.y = INTEL_FLOAT( b_info.dir_a.xyz.y );
7681 b_info.dir_a.xyz.z = INTEL_FLOAT( b_info.dir_a.xyz.z );
7682 b_info.dir_b.xyz.x = INTEL_FLOAT( b_info.dir_b.xyz.x );
7683 b_info.dir_b.xyz.y = INTEL_FLOAT( b_info.dir_b.xyz.y );
7684 b_info.dir_b.xyz.z = INTEL_FLOAT( b_info.dir_b.xyz.z );
7685 b_info.delta_ang = INTEL_FLOAT( b_info.delta_ang );
7687 for (i = 0; i < MAX_BEAM_SHOTS; i++) {
7688 b_info.shot_aim[i] = INTEL_FLOAT(b_info.shot_aim[i]);
7691 // lookup all relevant data
7692 fire_info.beam_info_index = (int)u_beam_info;
7693 fire_info.shooter = NULL;
7694 fire_info.target = NULL;
7695 fire_info.turret = NULL;
7696 fire_info.target_subsys = NULL;
7697 fire_info.beam_info_override = NULL;
7698 fire_info.shooter = multi_get_network_object(shooter_sig);
7699 fire_info.target = multi_get_network_object(target_sig);
7700 fire_info.beam_info_override = &b_info;
7701 fire_info.accuracy = 1.0f;
7702 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)){
7703 nprintf(("Network", "Couldn't get shooter/target info for BEAM weapon!\n"));
7706 fire_info.turret = ship_get_indexed_subsys( &Ships[fire_info.shooter->instance], (int)subsys_index);
7707 if(fire_info.turret == NULL){
7708 nprintf(("Network", "Couldn't get turret for BEAM weapon!\n"));
7713 beam_fire(&fire_info);
7716 void send_sw_query_packet(ubyte code, char *txt)
7718 ubyte data[MAX_PACKET_SIZE];
7719 int packet_size = 0;
7721 // build the packet and add the code
7722 BUILD_HEADER(SW_STD_QUERY);
7724 if((code == SW_STD_START) || (code == SW_STD_BAD)){
7728 // if I'm the host, send to standalone
7729 if(MULTIPLAYER_HOST){
7730 SDL_assert(!MULTIPLAYER_MASTER);
7731 SDL_assert(code == SW_STD_START);
7732 multi_io_send_reliable(Net_player, data, packet_size);
7734 // otherwise standalone sends back to host
7736 SDL_assert(Game_mode & GM_STANDALONE_SERVER);
7737 SDL_assert(code != SW_STD_START);
7738 SDL_assert(Netgame.host != NULL);
7739 if(Netgame.host != NULL){
7740 multi_io_send_reliable(Netgame.host, data, packet_size);
7745 void process_sw_query_packet(ubyte *data, header *hinfo)
7749 void send_event_update_packet(int event)
7751 ubyte data[MAX_PACKET_SIZE];
7752 ushort u_event = (ushort)event;
7753 int packet_size = 0;
7755 // build the header and add the event
7756 BUILD_HEADER(EVENT_UPDATE);
7757 ADD_USHORT(u_event);
7758 ADD_INT(Mission_events[event].flags);
7759 ADD_INT(Mission_events[event].formula);
7760 ADD_INT(Mission_events[event].result);
7761 ADD_INT(Mission_events[event].count);
7763 // send to all players
7764 multi_io_send_to_all_reliable(data, packet_size);
7767 void process_event_update_packet(ubyte *data, header *hinfo)
7769 int offset = HEADER_LENGTH;
7774 GET_USHORT(u_event);
7775 store_flags = Mission_events[u_event].flags;
7776 GET_INT(Mission_events[u_event].flags);
7777 GET_INT(Mission_events[u_event].formula);
7778 GET_INT(Mission_events[u_event].result);
7779 GET_INT(Mission_events[u_event].count);
7782 // went from non directive special to directive special
7783 if(!(store_flags & MEF_DIRECTIVE_SPECIAL) && (Mission_events[u_event].flags & MEF_DIRECTIVE_SPECIAL)){
7784 mission_event_set_directive_special(u_event);
7786 // if we went directive special to non directive special
7787 else if((store_flags & MEF_DIRECTIVE_SPECIAL) & !(Mission_events[u_event].flags & MEF_DIRECTIVE_SPECIAL)){
7788 mission_event_unset_directive_special(u_event);
7792 // weapon detonate packet
7793 void send_weapon_detonate_packet(object *objp)
7795 ubyte data[MAX_PACKET_SIZE];
7796 int packet_size = 0;
7799 SDL_assert(MULTIPLAYER_MASTER);
7800 if(!MULTIPLAYER_MASTER){
7803 SDL_assert(objp != NULL);
7808 // build the header and add the data
7809 BUILD_HEADER(WEAPON_DET);
7810 ADD_USHORT(objp->net_signature);
7812 // send to all players
7813 multi_io_send_to_all(data, packet_size);
7816 void process_weapon_detonate_packet(ubyte *data, header *hinfo)
7819 int offset = HEADER_LENGTH;
7820 object *objp = NULL;
7822 // get the weapon signature
7823 GET_USHORT(net_sig);
7826 // lookup the weapon
7827 objp = multi_get_network_object(net_sig);
7828 if((objp != NULL) && (objp->type == OBJ_WEAPON) && (objp->instance >= 0)){
7829 weapon_detonate(objp);
7833 // flak fired packet
7834 void send_flak_fired_packet(int ship_objnum, int subsys_index, int weapon_objnum, float flak_range)
7837 ushort pnet_signature;
7838 ubyte data[MAX_PACKET_SIZE], cindex;
7844 if((weapon_objnum < 0) || (Objects[weapon_objnum].type != OBJ_WEAPON) || (Objects[weapon_objnum].instance < 0) || (Weapons[Objects[weapon_objnum].instance].weapon_info_index < 0)){
7848 // local setup -- be sure we are actually passing a weapon!!!!
7849 objp = &Objects[weapon_objnum];
7850 SDL_assert ( objp->type == OBJ_WEAPON );
7851 pnet_signature = Objects[ship_objnum].net_signature;
7853 SDL_assert( subsys_index < UCHAR_MAX );
7854 cindex = (ubyte)subsys_index;
7856 ssp = ship_get_indexed_subsys( &Ships[Objects[ship_objnum].instance], subsys_index, NULL );
7861 // build the fire turret packet.
7862 BUILD_HEADER(FLAK_FIRED);
7863 packet_size += multi_pack_unpack_position(1, data + packet_size, &objp->orient.v.fvec);
7864 ADD_USHORT( pnet_signature );
7866 val = (short)ssp->submodel_info_1.angs.h;
7868 val = (short)ssp->submodel_info_2.angs.p;
7870 ADD_FLOAT( flak_range );
7872 multi_io_send_to_all(data, packet_size);
7874 multi_rate_add(1, "flk", packet_size);
7877 void process_flak_fired_packet(ubyte *data, header *hinfo)
7879 int offset, weapon_objnum, wid;
7880 ushort pnet_signature;
7888 short pitch, heading;
7891 // get the data for the turret fired packet
7892 offset = HEADER_LENGTH;
7893 offset += multi_pack_unpack_position(0, data + offset, &o_fvec);
7894 GET_USHORT( pnet_signature );
7895 GET_DATA( turret_index );
7896 GET_SHORT( heading );
7898 GET_FLOAT( flak_range );
7899 PACKET_SET_SIZE(); // move our counter forward the number of bytes we have read
7902 objp = multi_get_network_object( pnet_signature );
7903 if ( objp == NULL ) {
7904 nprintf(("network", "could find parent object with net signature %d for flak firing\n", pnet_signature));
7908 // if this isn't a ship, do nothing
7909 if ( objp->type != OBJ_SHIP ){
7913 // make an orientation matrix from the o_fvec
7914 vm_vector_2_matrix(&orient, &o_fvec, NULL, NULL);
7916 // find this turret, and set the position of the turret that just fired to be where it fired. Quite a
7917 // hack, but should be suitable.
7918 shipp = &Ships[objp->instance];
7919 ssp = ship_get_indexed_subsys( shipp, turret_index, NULL );
7923 wid = ssp->system_info->turret_weapon_type;
7924 if((wid < 0) || !(Weapon_info[wid].wi_flags & WIF_FLAK)){
7928 // bash the position and orientation of the turret
7929 ssp->submodel_info_1.angs.h = (float)heading;
7930 ssp->submodel_info_2.angs.p = (float)pitch;
7932 // get the world position of the weapon
7933 ship_get_global_turret_info(objp, ssp->system_info, &pos, &dir);
7935 // create the weapon object
7936 weapon_objnum = weapon_create( &pos, &orient, wid, OBJ_INDEX(objp), 0, -1, 1);
7937 if (weapon_objnum != -1) {
7938 if ( Weapon_info[wid].launch_snd != -1 ) {
7939 snd_play_3d( &Snds[Weapon_info[wid].launch_snd], &pos, &View_position );
7942 // create a muzzle flash from a flak gun based upon firing position and weapon type
7943 flak_muzzle_flash(&pos, &dir, wid);
7945 // set its range explicitly - make it long enough so that it's guaranteed to still exist when the server tells us it blew up
7946 flak_set_range(&Objects[weapon_objnum], &pos, (float)flak_range);
7950 #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);
7951 #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);
7953 // player pain packet
7954 void send_player_pain_packet(net_player *pl, int weapon_info_index, float damage, vector *force, vector *hitpos)
7956 ubyte data[MAX_PACKET_SIZE];
7959 int packet_size = 0;
7961 SDL_assert(MULTIPLAYER_MASTER);
7962 if(!MULTIPLAYER_MASTER){
7965 SDL_assert(pl != NULL);
7970 // build the packet and add the code
7971 BUILD_HEADER(NETPLAYER_PAIN);
7972 windex = (ubyte)weapon_info_index;
7974 udamage = (ushort)damage;
7975 ADD_USHORT(udamage);
7976 //ADD_DATA((*force));
7977 add_vector_data( data, &packet_size, *force );
7978 //ADD_DATA((*hitpos));
7979 add_vector_data( data, &packet_size, *hitpos );
7981 // send to the player
7982 multi_io_send(pl, data, packet_size);
7984 multi_rate_add(1, "pai", packet_size);
7987 void process_player_pain_packet(ubyte *data, header *hinfo)
7992 vector force = ZERO_VECTOR;
7993 vector local_hit_pos = ZERO_VECTOR;
7996 // get the data for the pain packet
7997 offset = HEADER_LENGTH;
7999 GET_USHORT(udamage);
8001 get_vector_data( data, &offset, force );
8002 //GET_DATA(local_hit_pos);
8003 get_vector_data( data, &offset, local_hit_pos );
8006 mprintf(("PAIN!\n"));
8008 // get weapon info pointer
8009 //SDL_assert((windex >= 0) && (windex < Num_weapon_types) && (Weapon_info[windex].subtype == WP_LASER)); // always true
8010 if(! ((windex != 255) && (windex < Num_weapon_types) && (Weapon_info[windex].subtype == WP_LASER)) ){
8013 wip = &Weapon_info[windex];
8015 // play the weapon hit sound
8016 SDL_assert(Player_obj != NULL);
8017 if(Player_obj == NULL){
8020 weapon_hit_do_sound(Player_obj, wip, &Player_obj->pos);
8022 // we need to do 3 things here. player pain (game flash), weapon hit sound, ship_apply_whack()
8023 ship_hit_pain((float)udamage);
8026 ship_apply_whack(&force, &local_hit_pos, Player_obj);
8030 void send_lightning_packet(int bolt_type, vector *start, vector *strike)
8032 ubyte data[MAX_PACKET_SIZE];
8034 int packet_size = 0;
8036 // build the header and add the data
8037 BUILD_HEADER(LIGHTNING_PACKET);
8038 val = (char)bolt_type;
8040 //ADD_DATA((*start));
8041 add_vector_data( data, &packet_size, *start );
8042 //ADD_DATA((*strike));
8043 add_vector_data( data, &packet_size, *strike );
8045 // send to everyone unreliable for now
8046 multi_io_send_to_all(data, packet_size);
8049 void process_lightning_packet(ubyte *data, header *hinfo)
8053 vector start = ZERO_VECTOR, strike = ZERO_VECTOR;
8056 offset = HEADER_LENGTH;
8057 GET_DATA(bolt_type);
8059 get_vector_data(data, &offset, start);
8060 // GET_DATA(strike);
8061 get_vector_data(data, &offset, strike);
8070 nebl_bolt(bolt_type, &start, &strike);
8073 void send_bytes_recvd_packet(net_player *pl)
8075 // only clients should ever be doing this
8080 ubyte data[MAX_PACKET_SIZE];
8081 int packet_size = 0;
8082 BUILD_HEADER(BYTES_SENT);
8083 ADD_INT(pl->cl_bytes_recvd);
8085 // send to the server
8086 multi_io_send_reliable(pl, data, packet_size);
8089 void process_bytes_recvd_packet(ubyte *data, header *hinfo)
8093 net_player *pl = NULL;
8094 int offset = HEADER_LENGTH;
8100 if(Net_player == NULL){
8103 if(!MULTIPLAYER_MASTER){
8107 // make sure we know what player sent this
8108 pid = find_player_id(hinfo->id);
8109 if((pid < 0) || (pid >= MAX_PLAYERS)){
8112 pl = &Net_players[pid];
8115 pl->cl_bytes_recvd = bytes;
8119 pl->sv_last_pl = (int)(100.0f * (1.0f - ((float)pl->cl_bytes_recvd / (float)pl->sv_bytes_sent)));
8122 pl->sv_bytes_sent = 0;
8126 void send_host_captain_change_packet(short player_id, int captain_change)
8128 ubyte data[MAX_PACKET_SIZE];
8129 int packet_size = 0;
8132 BUILD_HEADER(TRANSFER_HOST);
8133 ADD_SHORT(player_id);
8134 ADD_INT(captain_change);
8137 multi_io_send_to_all_reliable(data, packet_size);
8140 void process_host_captain_change_packet(ubyte *data, header *hinfo)
8142 int offset = HEADER_LENGTH;
8143 int idx, found_player, captain_change;
8146 // get the player id
8147 GET_SHORT(player_id);
8148 GET_INT(captain_change);
8154 for(idx=0; idx<MAX_PLAYERS; idx++){
8155 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].player_id == player_id)){
8156 HUD_printf("%s is the new captain of team %d", Net_players[idx].player->callsign, Net_players[idx].p_info.team + 1);
8161 // unflag all old players
8162 for(idx=0; idx<MAX_PLAYERS; idx++){
8163 Net_players[idx].flags &= ~NETINFO_FLAG_GAME_HOST;
8168 for(idx=0; idx<MAX_PLAYERS; idx++){
8169 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].player_id == player_id)){
8170 Net_players[idx].flags |= NETINFO_FLAG_GAME_HOST;
8172 // spew to the HUD config
8173 if(Net_players[idx].player != NULL){
8174 HUD_printf("%s is the new game host", Net_players[idx].player->callsign);
8184 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_HOST_LEFT);
8189 void send_self_destruct_packet()
8191 ubyte data[MAX_PACKET_SIZE];
8192 int packet_size = 0;
8195 if(Net_player == NULL){
8199 // if i'm the server, I shouldn't be here
8200 SDL_assert(!(Net_player->flags & NETINFO_FLAG_AM_MASTER));
8201 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8205 // only if this is valid
8206 if(MULTI_OBSERVER(Net_players[MY_NET_PLAYER_NUM])){
8211 if((Player_ship == NULL) || (Player_obj == NULL)){
8216 BUILD_HEADER(SELF_DESTRUCT);
8217 ADD_USHORT(Player_obj->net_signature);
8219 // send to the server
8220 multi_io_send_reliable(Net_player, data, packet_size);
8223 void process_self_destruct_packet(ubyte *data, header *hinfo)
8225 int offset = HEADER_LENGTH;
8229 // get the net signature
8230 GET_USHORT(net_sig);
8234 np_index = find_player_id(hinfo->id);
8238 if(MULTI_OBSERVER(Net_players[np_index])){
8241 if(Net_players[np_index].player == NULL){
8244 if((Net_players[np_index].player->objnum < 0) || (Net_players[np_index].player->objnum >= MAX_OBJECTS)){
8247 if(Objects[Net_players[np_index].player->objnum].net_signature != net_sig){
8250 if(Objects[Net_players[np_index].player->objnum].type != OBJ_SHIP){
8253 if((Objects[Net_players[np_index].player->objnum].instance < 0) || (Objects[Net_players[np_index].player->objnum].instance >= MAX_SHIPS)){
8258 ship_self_destruct(&Objects[Net_players[np_index].player->objnum]);