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_gui.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"
439 // #define _MULTI_SUPER_WACKY_COMPRESSION
441 #ifdef _MULTI_SUPER_WACKY_COMPRESSION
443 #define MAX_CODE ( ( 1 << BITS ) - 1 )
444 #define TABLE_SIZE 35023L
445 #define END_OF_STREAM 256
446 #define BUMP_CODE 257
447 #define FLUSH_CODE 258
448 #define FIRST_CODE 259
457 static DICTIONARY dict[TABLE_SIZE];
458 static char decode_stack[TABLE_SIZE];
459 static uint next_code;
460 static int current_code_bits;
461 static uint next_bump_code;
463 typedef struct BitBuf {
469 void output_bits( BitBuf *bitbuf, uint code, int count )
473 mask = 1L << ( count - 1 );
476 bitbuf->rack |= bitbuf->mask;
478 if ( bitbuf->mask == 0 ) {
479 *bitbuf->data++=(ubyte)bitbuf->rack;
487 uint input_bits( BitBuf *bitbuf, int bit_count )
492 mask = 1L << ( bit_count - 1 );
495 if ( bitbuf->mask == 0x80 ) {
496 bitbuf->rack = *bitbuf->data++;
497 if ( bitbuf->rack == EOF )
498 return END_OF_STREAM;
500 if ( bitbuf->rack & bitbuf->mask )
501 return_value |= mask;
504 if ( bitbuf->mask == 0 )
507 return( return_value );
511 static void InitializeDictionary()
515 for ( i = 0 ; i < TABLE_SIZE ; i++ )
516 dict[i].code_value = UNUSED;
518 next_code = FIRST_CODE;
519 current_code_bits = 9;
520 next_bump_code = 511;
524 static uint find_child_node( int parent_code, int child_character )
529 index = ( child_character << ( BITS - 8 ) ) ^ parent_code;
533 offset = TABLE_SIZE - index;
535 if ( dict[ index ].code_value == UNUSED )
536 return( (uint) index );
537 if ( dict[ index ].parent_code == parent_code &&
538 dict[ index ].character == (char) child_character )
540 if ( (int) index >= offset )
543 index += TABLE_SIZE - offset;
548 static uint decode_string( uint count, uint code )
550 while ( code > 255 ) {
551 decode_stack[ count++ ] = dict[ code ].character;
552 code = dict[ code ].parent_code;
554 decode_stack[ count++ ] = (char) code;
558 int lzw_compress( ubyte *outputbuf, ubyte *inputbuf, int input_size )
566 // Init output bit buffer
569 output.data = outputbuf;
571 InitializeDictionary();
573 string_code = *inputbuf++;
575 for ( i=1 ; i<input_size ; i++ ) {
576 character = *inputbuf++;
577 index = find_child_node( string_code, character );
578 if ( dict[ index ].code_value != - 1 )
579 string_code = dict[ index ].code_value;
581 dict[ index ].code_value = next_code++;
582 dict[ index ].parent_code = string_code;
583 dict[ index ].character = (char) character;
584 output_bits( &output, (unsigned long) string_code, current_code_bits );
585 string_code = character;
586 if ( next_code > MAX_CODE ) {
587 output_bits( &output, (unsigned long) FLUSH_CODE, current_code_bits );
588 InitializeDictionary();
589 } else if ( next_code > next_bump_code ) {
590 output_bits( &output, (unsigned long) BUMP_CODE, current_code_bits );
592 next_bump_code <<= 1;
597 output_bits( &output, (unsigned long) string_code, current_code_bits );
598 output_bits( &output, (unsigned long) END_OF_STREAM, current_code_bits);
600 if ( output.mask != 0x80 )
601 *output.data++ = (ubyte)output.rack;
603 return output.data-outputbuf;
607 int lzw_expand( ubyte *outputbuf, ubyte *inputbuf )
618 input.data = inputbuf;
622 InitializeDictionary();
623 old_code = (uint) input_bits( &input, current_code_bits );
624 if ( old_code == END_OF_STREAM )
626 character = old_code;
627 outputbuf[counter++] = ( ubyte )old_code;
629 new_code = (uint) input_bits( &input, current_code_bits );
630 if ( new_code == END_OF_STREAM )
632 if ( new_code == FLUSH_CODE )
634 if ( new_code == BUMP_CODE ) {
638 if ( new_code >= next_code ) {
639 decode_stack[ 0 ] = (char) character;
640 count = decode_string( 1, old_code );
642 count = decode_string( 0, new_code );
644 character = decode_stack[ count - 1 ];
646 outputbuf[counter++] = ( ubyte )decode_stack[ --count ];
647 dict[ next_code ].parent_code = old_code;
648 dict[ next_code ].character = (char) character;
656 // process a join request packet add
657 void add_join_request(ubyte *data, int *size, join_request *jr)
659 int packet_size = *size;
660 join_request *jr_tmp = jr;
662 jr_tmp->tracker_id = INTEL_INT(jr->tracker_id);
663 jr_tmp->player_options.flags = INTEL_INT(jr->player_options.flags);
664 jr_tmp->player_options.obj_update_level = INTEL_INT(jr->player_options.obj_update_level);
671 // process a join request packet get
672 void get_join_request(ubyte *data, int *size, join_request jr)
678 jr.tracker_id = INTEL_INT(jr.tracker_id);
679 jr.player_options.flags = INTEL_INT(jr.player_options.flags);
680 jr.player_options.obj_update_level = INTEL_INT(jr.player_options.obj_update_level);
685 void add_net_addr(ubyte *data, int *size, net_addr addr)
687 int packet_size = *size;
688 net_addr addr_tmp = addr;
690 addr_tmp.type = INTEL_INT(addr.type);
691 addr_tmp.port = INTEL_SHORT(addr.port);
698 void get_net_addr(ubyte *data, int *size, net_addr addr)
704 addr.type = INTEL_INT(addr.type);
705 addr.port = INTEL_SHORT(addr.port);
710 void add_vector_data(ubyte *data, int *size, vector vec)
712 int packet_size = *size;
714 ADD_FLOAT(vec.xyz.x);
715 ADD_FLOAT(vec.xyz.y);
716 ADD_FLOAT(vec.xyz.z);
721 void get_vector_data(ubyte *data, int *size, vector vec)
725 GET_FLOAT(vec.xyz.x);
726 GET_FLOAT(vec.xyz.y);
727 GET_FLOAT(vec.xyz.z);
732 // send the specified data packet to all players
733 void multi_io_send(net_player *pl, ubyte *data, int len)
736 if((pl == NULL) || (NET_PLAYER_NUM(pl) >= MAX_PLAYERS)){
740 // don't do it for single player
741 if(!(Game_mode & GM_MULTIPLAYER)){
746 if(MULTIPLAYER_CLIENT){
747 // SDL_assert(pl == Net_player);
748 if(pl != Net_player){
752 // SDL_assert(pl != Net_player);
753 if(pl == Net_player){
758 // If this packet will push the buffer over MAX_PACKET_SIZE, send the current send_buffer
759 if ((pl->s_info.unreliable_buffer_size + len) > MAX_PACKET_SIZE) {
760 multi_io_send_force(pl);
761 pl->s_info.unreliable_buffer_size = 0;
764 SDL_assert((pl->s_info.unreliable_buffer_size + len) <= MAX_PACKET_SIZE);
766 memcpy(pl->s_info.unreliable_buffer + pl->s_info.unreliable_buffer_size, data, len);
767 pl->s_info.unreliable_buffer_size += len;
770 void multi_io_send_to_all(ubyte *data, int length, net_player *ignore)
773 SDL_assert(MULTIPLAYER_MASTER);
775 // need to check for i > 1, hmmm... and connected. I don't know.
776 for (i = 0; i < MAX_PLAYERS; i++ ) {
777 if ( !MULTI_CONNECTED(Net_players[i]) || (Net_player == &Net_players[i])){
781 // maybe ignore a player
782 if((ignore != NULL) && (&Net_players[i] == ignore)){
786 // ingame joiners not waiting to select a ship doesn't get any packets
787 if ( (Net_players[i].flags & NETINFO_FLAG_INGAME_JOIN) && !(Net_players[i].flags & INGAME_JOIN_FLAG_PICK_SHIP) ){
792 multi_io_send(&Net_players[i], data, length);
796 void multi_io_send_force(net_player *pl)
799 if((pl == NULL) || (NET_PLAYER_NUM(pl) >= MAX_PLAYERS)){
803 // don't do it for single player
804 if(!(Game_mode & GM_MULTIPLAYER)){
808 // send everything in
809 if (MULTIPLAYER_MASTER) {
810 psnet_send(&pl->p_info.addr, pl->s_info.unreliable_buffer, pl->s_info.unreliable_buffer_size, NET_PLAYER_NUM(pl));
812 // add the bytes sent to this player
813 pl->sv_bytes_sent += pl->s_info.unreliable_buffer_size;
815 psnet_send(&Netgame.server_addr, pl->s_info.unreliable_buffer, pl->s_info.unreliable_buffer_size, NET_PLAYER_NUM(pl));
817 pl->s_info.unreliable_buffer_size = 0;
820 // send the data packet to all players via their reliable sockets
821 void multi_io_send_reliable(net_player *pl, ubyte *data, int len)
824 if((pl == NULL) || (NET_PLAYER_NUM(pl) >= MAX_PLAYERS)){
828 // don't do it for single player
829 if(!(Game_mode & GM_MULTIPLAYER)){
834 if(MULTIPLAYER_CLIENT){
835 // SDL_assert(pl == Net_player);
836 if(pl != Net_player){
840 // SDL_assert(pl != Net_player);
841 if(pl == Net_player){
846 // If this packet will push the buffer over MAX_PACKET_SIZE, send the current send_buffer
847 if ((pl->s_info.reliable_buffer_size + len) > MAX_PACKET_SIZE) {
848 multi_io_send_reliable_force(pl);
849 pl->s_info.reliable_buffer_size = 0;
852 SDL_assert((pl->s_info.reliable_buffer_size + len) <= MAX_PACKET_SIZE);
854 memcpy(pl->s_info.reliable_buffer + pl->s_info.reliable_buffer_size, data, len);
855 pl->s_info.reliable_buffer_size += len;
858 void multi_io_send_to_all_reliable(ubyte* data, int length, net_player *ignore)
861 SDL_assert(MULTIPLAYER_MASTER);
863 // need to check for i > 1, hmmm... and connected. I don't know.
864 for (i = 0; i < MAX_PLAYERS; i++ ) {
865 if ( !MULTI_CONNECTED(Net_players[i]) || (Net_player == &Net_players[i])){
869 // maybe ignore a player
870 if((ignore != NULL) && (&Net_players[i] == ignore)){
874 // ingame joiners not waiting to select a ship doesn't get any packets
875 if ( (Net_players[i].flags & NETINFO_FLAG_INGAME_JOIN) && !(Net_players[i].flags & INGAME_JOIN_FLAG_PICK_SHIP) ){
880 multi_io_send_reliable(&Net_players[i], data, length);
884 void multi_io_send_reliable_force(net_player *pl)
887 if((pl == NULL) || (NET_PLAYER_NUM(pl) >= MAX_PLAYERS)){
891 // don't do it for single player
892 if(!(Game_mode & GM_MULTIPLAYER)){
896 // send everything in
897 if(MULTIPLAYER_MASTER) {
898 psnet_rel_send(pl->reliable_socket, pl->s_info.reliable_buffer, pl->s_info.reliable_buffer_size, NET_PLAYER_NUM(pl));
899 } else if(Net_player != NULL){
900 psnet_rel_send(Net_player->reliable_socket, pl->s_info.reliable_buffer, pl->s_info.reliable_buffer_size, NET_PLAYER_NUM(pl));
902 pl->s_info.reliable_buffer_size = 0;
905 // send all buffered packets
906 void multi_io_send_buffered_packets()
910 // don't do it for single player
911 if(!(Game_mode & GM_MULTIPLAYER)){
916 if(MULTIPLAYER_MASTER){
917 for(idx=0; idx<MAX_PLAYERS; idx++){
918 if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx])){
919 // force unreliable data
920 if(Net_players[idx].s_info.unreliable_buffer_size > 0){
921 multi_io_send_force(&Net_players[idx]);
922 Net_players[idx].s_info.unreliable_buffer_size = 0;
925 // force reliable data
926 if(Net_players[idx].s_info.reliable_buffer_size > 0){
927 multi_io_send_reliable_force(&Net_players[idx]);
928 Net_players[idx].s_info.reliable_buffer_size = 0;
934 else if(Net_player != NULL){
935 // force unreliable data
936 if(Net_player->s_info.unreliable_buffer_size > 0){
937 multi_io_send_force(Net_player);
938 Net_player->s_info.unreliable_buffer_size = 0;
941 // force reliable data
942 if(Net_player->s_info.reliable_buffer_size > 0){
943 multi_io_send_reliable_force(Net_player);
944 Net_player->s_info.reliable_buffer_size = 0;
949 // 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)
950 void send_game_chat_packet(net_player *from, const char *msg, int msg_mode, net_player *to, const char *expr, int server_msg)
952 ubyte data[MAX_PACKET_SIZE],mode;
955 BUILD_HEADER(GAME_CHAT);
958 ADD_SHORT(from->player_id);
960 // add the message mode and if in MSG_TARGET mode, add who the target is
962 mode = (ubyte)msg_mode;
965 case MULTI_MSG_TARGET:
966 SDL_assert(to != NULL);
967 ADD_SHORT(to->player_id);
970 SDL_assert(expr != NULL);
974 // add the message itself
977 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
979 // message all players
981 for(idx=0;idx<MAX_PLAYERS;idx++){
982 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (&Net_players[idx] != from)){
983 multi_io_send_reliable(&Net_players[idx], data, packet_size);
988 // message only friendly players
989 case MULTI_MSG_FRIENDLY:
990 for(idx=0;idx<MAX_PLAYERS;idx++){
991 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)){
992 multi_io_send_reliable(&Net_players[idx], data, packet_size);
997 // message only hostile players
998 case MULTI_MSG_HOSTILE:
999 for(idx=0;idx<MAX_PLAYERS;idx++){
1000 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)){
1001 multi_io_send_reliable(&Net_players[idx], data, packet_size);
1006 // message the player's target
1007 case MULTI_MSG_TARGET:
1008 SDL_assert(to != NULL);
1009 if(MULTI_CONNECTED((*to)) && !MULTI_STANDALONE((*to))){
1010 multi_io_send_reliable(to, data, packet_size);
1014 // message all players who match the expression string
1015 case MULTI_MSG_EXPR:
1016 SDL_assert(expr != NULL);
1017 for(idx=0;idx<MAX_PLAYERS;idx++){
1018 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (&Net_players[idx] != from) && multi_msg_matches_expr(&Net_players[idx],expr) ){
1019 multi_io_send_reliable(&Net_players[idx], data, packet_size);
1025 // send to the server, who will take care of routing it
1027 multi_io_send_reliable(Net_player, data, packet_size);
1031 // process a general game chat packet, if we're the standalone we should rebroadcast
1032 void process_game_chat_packet( ubyte *data, header *hinfo )
1036 int color_index,player_index,to_player_index,should_display,server_msg;
1037 char msg[MULTI_MSG_MAX_TEXT_LEN+CALLSIGN_LEN+2];
1041 offset = HEADER_LENGTH;
1043 // get the id of the sender
1046 // determine if this is a server message
1047 GET_INT(server_msg);
1052 // if targeting a specific player, get the address
1055 case MULTI_MSG_TARGET:
1058 case MULTI_MSG_EXPR:
1062 // get the message itself
1066 // get the index of the sending player
1067 color_index = find_player_id(from);
1068 player_index = color_index;
1070 // if we couldn't find the player - bail
1071 if(player_index == -1){
1072 nprintf(("Network","Could not find player for processing game chat packet!\n"));
1078 // if we're the server, determine what to do with the packet here
1079 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
1080 // if he's targeting a specific player, find out who it is
1081 if(mode == MULTI_MSG_TARGET){
1082 to_player_index = find_player_id(to);
1084 to_player_index = -1;
1087 // if we couldn't find who sent the message or who should be getting the message, the bail
1088 if(((to_player_index == -1) && (mode == MULTI_MSG_TARGET)) || (player_index == -1)){
1092 // determine if _I_ should be seeing the text
1093 if(Game_mode & GM_STANDALONE_SERVER){
1096 // check against myself for several specific cases
1098 if((mode == MULTI_MSG_ALL) ||
1099 ((mode == MULTI_MSG_FRIENDLY) && (Net_player->p_info.team == Net_players[player_index].p_info.team)) ||
1100 ((mode == MULTI_MSG_HOSTILE) && (Net_player->p_info.team != Net_players[player_index].p_info.team)) ||
1101 ((mode == MULTI_MSG_TARGET) && (MY_NET_PLAYER_NUM == to_player_index)) ||
1102 ((mode == MULTI_MSG_EXPR) && multi_msg_matches_expr(Net_player,expr)) ){
1107 // if we're the server of a game, we need to rebroadcast to all other players
1109 // individual target mission
1110 case MULTI_MSG_TARGET:
1111 // if I was the inteneded target, or we couldn't find the intended target, don't rebroadcast
1112 if(to_player_index != MY_NET_PLAYER_NUM){
1113 send_game_chat_packet(&Net_players[player_index], msg, (int)mode, &Net_players[to_player_index], NULL, server_msg);
1117 case MULTI_MSG_EXPR:
1118 send_game_chat_packet(&Net_players[player_index], msg, (int)mode, NULL, expr, server_msg);
1122 send_game_chat_packet(&Net_players[player_index], msg, (int)mode, NULL, NULL, server_msg);
1126 // if a client receives this packet, its always ok for him to display it
1131 // if we're not on a standalone
1133 if(server_msg == 2){
1136 multi_display_chat_msg(msg, player_index, !server_msg);
1141 // broadcast a hud message to all players
1142 void send_hud_msg_to_all( char* msg )
1144 ubyte data[MAX_PACKET_SIZE];
1147 // only the server should be sending this packet
1148 BUILD_HEADER(HUD_MSG);
1152 multi_io_send_to_all( data, packet_size );
1155 // process an incoming hud message packet
1156 void process_hud_message(ubyte* data, header* hinfo)
1159 char msg_buffer[255];
1161 offset = HEADER_LENGTH;
1163 GET_STRING(msg_buffer);
1166 // this is the only safe place to do this since only in the mission is the HUD guaranteed to be inited
1167 if(Game_mode & GM_IN_MISSION){
1168 HUD_printf(msg_buffer);
1172 // send a join packet request to the specified address (should be a server)
1173 void send_join_packet(net_addr* addr,join_request *jr)
1175 ubyte data[MAX_PACKET_SIZE];
1178 // build the header and add the request
1181 add_join_request(data, &packet_size, jr);
1183 psnet_send(addr, data, packet_size);
1186 // process an incoming join request packet
1187 void process_join_packet(ubyte* data, header* hinfo)
1192 int host_restr_mode;
1193 // int team0_avail,team1_avail;
1194 char join_string[255];
1197 // only the server of the game should ever receive this packet
1198 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) )
1201 offset = HEADER_LENGTH;
1203 // read in the request info
1204 memset(&jr,0,sizeof(join_request));
1207 jr.tracker_id = INTEL_INT(jr.tracker_id);
1211 // fill in the address information of where this came from
1212 fill_net_addr(&addr, hinfo->addr, hinfo->net_id, hinfo->port);
1214 // determine if we should accept this guy, or return a reason we should reject him
1215 // see the DENY_* codes in multi.h
1216 ret_code = multi_eval_join_request(&jr,&addr);
1218 // evaluate the return code
1220 // he should be accepted
1224 // we have to query the host because this is a restricted game
1225 case JOIN_QUERY_RESTRICTED :
1226 if(!(Game_mode & GM_STANDALONE_SERVER)){
1227 // notify the host of the event
1228 snd_play(&Snds[SND_CUE_VOICE]);
1231 // set the query timestamp
1232 Multi_restr_query_timestamp = timestamp(MULTI_QUERY_RESTR_STAMP);
1233 Netgame.flags |= NG_FLAG_INGAME_JOINING;
1235 // determine what mode we're in
1236 host_restr_mode = -1;
1237 memset(join_string,0,255);
1238 // if(Netgame.type == NG_TYPE_TEAM){
1239 // multi_player_ships_available(&team0_avail,&team1_avail);
1241 // if(team0_avail && team1_avail){
1242 // host_restr_mode = MULTI_JOIN_RESTR_MODE_4;
1243 // sprintf(join_string,"Player %s has tried to join. Accept on team 1 or 2 ?",jr.callsign);
1244 // } else if(team0_avail && !team1_avail){
1245 // host_restr_mode = MULTI_JOIN_RESTR_MODE_2;
1246 // sprintf(join_string,"Player %s has tried to join team 0, accept y/n ? ?",jr.callsign);
1247 // } else if(!team0_avail && team1_avail){
1248 // host_restr_mode = MULTI_JOIN_RESTR_MODE_3;
1249 // sprintf(join_string,"Player %s has tried to join team 1, accept y/n ?",jr.callsign);
1251 // } else if(Netgame.mode == NG_MODE_RESTRICTED){
1252 host_restr_mode = MULTI_JOIN_RESTR_MODE_1;
1253 sprintf(join_string,XSTR("Player %s has tried to join, accept y/n ?",715),jr.callsign);
1255 SDL_assert(host_restr_mode != -1);
1257 // store the request info
1258 memcpy(&Multi_restr_join_request,&jr,sizeof(join_request));
1259 memcpy(&Multi_restr_addr,&addr,sizeof(net_addr));
1260 Multi_join_restr_mode = host_restr_mode;
1262 // if i'm the standalone server, I need to send a query to the host
1263 if(Game_mode & GM_STANDALONE_SERVER){
1264 send_host_restr_packet(jr.callsign,0,Multi_join_restr_mode);
1266 HUD_printf(join_string);
1270 ml_printf(NOX("Receive restricted join request from %s"), jr.callsign);
1274 // he'e being denied for some reason
1276 // send him the reason he is being denied
1277 send_deny_packet(&addr,ret_code);
1281 // process the rest of the request
1282 multi_process_valid_join_request(&jr,&addr);
1285 // send a notification that a new player has joined the game (if target != NULL, broadcast the packet)
1286 void send_new_player_packet(int new_player_num,net_player *target)
1288 ubyte data[MAX_PACKET_SIZE], val;
1289 int packet_size = 0;
1291 BUILD_HEADER( NOTIFY_NEW_PLAYER );
1293 // add the new player's info
1294 ADD_INT(new_player_num);
1295 // ADD_DATA(Net_players[new_player_num].p_info.addr);
1297 add_net_addr(data, &packet_size, Net_players[new_player_num].p_info.addr);
1299 ADD_SHORT(Net_players[new_player_num].player_id);
1300 ADD_INT(Net_players[new_player_num].flags);
1301 ADD_STRING(Net_players[new_player_num].player->callsign);
1302 ADD_STRING(Net_players[new_player_num].player->image_filename);
1303 ADD_STRING(Net_players[new_player_num].player->squad_filename);
1304 ADD_STRING(Net_players[new_player_num].p_info.pxo_squad_name);
1306 val = (ubyte)Net_players[new_player_num].p_info.team;
1309 // broadcast the data
1311 multi_io_send_reliable(target, data, packet_size);
1313 multi_io_send_to_all_reliable(data, packet_size);
1317 // process a notification for a new player who has joined the game
1318 void process_new_player_packet(ubyte* data, header* hinfo)
1320 int already_in_game = 0;
1321 int offset, new_player_num,player_num,new_flags;
1323 char new_player_name[CALLSIGN_LEN+2] = "";
1324 char new_player_image[MAX_FILENAME_LEN+1] = "";
1325 char new_player_squad[MAX_FILENAME_LEN+1] = "";
1326 char new_player_pxo_squad[LOGIN_LEN+1] = "";
1327 char notify_string[256];
1331 offset = HEADER_LENGTH;
1333 // get the new players information
1334 GET_INT(new_player_num);
1335 get_net_addr(data, &offset, new_addr);
1339 GET_STRING(new_player_name);
1340 GET_STRING(new_player_image);
1341 GET_STRING(new_player_squad);
1342 GET_STRING(new_player_pxo_squad);
1346 player_num = multi_find_open_player_slot();
1347 SDL_assert(player_num != -1);
1349 // note that this new code does not check for duplicate IPs. It merely checks to see if
1350 // the slot referenced by new_player_num is already occupied by a connected player
1351 if(MULTI_CONNECTED(Net_players[new_player_num])){
1355 // if he's not alreayd in the game for one reason or another
1356 if ( !already_in_game ) {
1357 if ( Game_mode & GM_IN_MISSION ){
1358 HUD_sourced_printf(HUD_SOURCE_COMPUTER, XSTR("%s has entered the game\n",716), new_player_name);
1361 // create the player
1362 memcpy(new_addr.net_id, Psnet_my_addr.net_id, 4);
1364 if(new_flags & NETINFO_FLAG_OBSERVER){
1365 multi_obs_create_player(new_player_num,new_player_name,&new_addr,&Players[player_num]);
1366 Net_players[new_player_num].flags |= new_flags;
1368 multi_create_player( new_player_num, &Players[player_num],new_player_name, &new_addr, -1, new_id );
1369 Net_players[new_player_num].flags |= new_flags;
1372 // copy in the filename
1373 if(strlen(new_player_image) > 0){
1374 strcpy(Net_players[new_player_num].player->image_filename, new_player_image);
1376 strcpy(Net_players[new_player_num].player->image_filename, "");
1378 // copy his pilot squad filename
1379 Net_players[new_player_num].player->insignia_texture = -1;
1380 player_set_squad_bitmap(Net_players[new_player_num].player, new_player_squad);
1382 // copy in his pxo squad name
1383 strcpy(Net_players[new_player_num].p_info.pxo_squad_name, new_player_pxo_squad);
1385 // since we just created the player, set the last_heard_time here.
1386 Net_players[new_player_num].last_heard_time = timer_get_fixed_seconds();
1388 Net_players[new_player_num].p_info.team = team;
1390 Net_players[new_player_num].player_id = new_id;
1392 // zero out this players ping
1393 multi_ping_reset(&Net_players[new_player_num].s_info.ping);
1395 // add a chat message
1396 if(Net_players[new_player_num].player->callsign != NULL){
1397 sprintf(notify_string,XSTR("<%s has joined>",717),Net_players[new_player_num].player->callsign);
1398 multi_display_chat_msg(notify_string,0,0);
1403 ml_printf(NOX("Received notification of new player %s"), Net_players[new_player_num].player->callsign);
1405 // let the current ui screen know someone joined
1406 switch(gameseq_get_state()){
1407 case GS_STATE_MULTI_HOST_SETUP :
1408 multi_create_handle_join(&Net_players[new_player_num]);
1410 case GS_STATE_MULTI_CLIENT_SETUP :
1411 multi_jw_handle_join(&Net_players[new_player_num]);
1416 #define PLAYER_DATA_SLOP 100
1418 void send_accept_player_data( net_player *npp, int is_ingame )
1422 ubyte data[MAX_PACKET_SIZE], stop;
1424 BUILD_HEADER(ACCEPT_PLAYER_DATA);
1426 // add in the netplayer data for all players
1428 for (i=0; i<MAX_PLAYERS; i++) {
1429 // skip non connected players
1430 if ( !MULTI_CONNECTED(Net_players[i]) ){
1434 // skip this new player's entry
1435 if ( npp->player_id == Net_players[i].player_id ){
1439 // add the stop byte
1442 // add the player's number
1445 // add the player's address
1446 // ADD_DATA(Net_players[i].p_info.addr);
1447 add_net_addr(data, &packet_size, Net_players[i].p_info.addr);
1450 ADD_SHORT(Net_players[i].player_id);
1453 ADD_STRING(Net_players[i].player->callsign);
1455 // add his image filename
1456 ADD_STRING(Net_players[i].player->image_filename);
1458 // add his squad filename
1459 ADD_STRING(Net_players[i].player->squad_filename);
1461 // add his PXO squad name
1462 ADD_STRING(Net_players[i].p_info.pxo_squad_name);
1465 ADD_INT(Net_players[i].flags);
1467 // add his object's net sig
1469 ADD_USHORT( Objects[Net_players[i].player->objnum].net_signature );
1472 if ( (packet_size + PLAYER_DATA_SLOP) > MAX_PACKET_SIZE ) {
1473 stop = APD_END_PACKET;
1475 multi_io_send_reliable( npp, data, packet_size );
1476 BUILD_HEADER(ACCEPT_PLAYER_DATA);
1482 // add the stop byte
1483 stop = APD_END_DATA;
1485 multi_io_send_reliable(npp, data, packet_size);
1488 // send an accept packet to a client in response to a request to join the game
1489 void send_accept_packet(int new_player_num, int code, int ingame_join_team)
1492 ubyte data[MAX_PACKET_SIZE],val;
1493 char notify_string[256];
1496 SDL_assert(new_player_num >= 0);
1498 // setup his "reliable" socket
1499 Net_players[new_player_num].last_heard_time = timer_get_fixed_seconds();
1501 // build the packet header
1503 BUILD_HEADER(ACCEPT);
1505 // add the accept code
1508 // add code specific accept data
1509 if (code & ACCEPT_INGAME) {
1510 // the game filename
1511 ADD_STRING(Game_current_mission_filename);
1513 // if he is joining on a specific team, mark it here
1514 if(ingame_join_team != -1){
1517 val = (ubyte)ingame_join_team;
1525 if (code & ACCEPT_OBSERVER) {
1526 SDL_assert(!(code & (ACCEPT_CLIENT | ACCEPT_HOST)));
1529 if (code & ACCEPT_HOST) {
1530 SDL_assert(!(code & (ACCEPT_CLIENT | ACCEPT_OBSERVER | ACCEPT_INGAME)));
1533 if (code & ACCEPT_CLIENT) {
1534 SDL_assert(!(code & (ACCEPT_HOST | ACCEPT_OBSERVER | ACCEPT_INGAME)));
1537 // add the current skill level setting on the host
1538 ADD_INT(Game_skill_level);
1540 // add this guys player num
1541 ADD_INT(new_player_num);
1543 // add his player id
1544 ADD_SHORT(Net_players[new_player_num].player_id);
1546 // add netgame type flags
1547 ADD_INT(Netgame.type_flags);
1550 // char buffer[100];
1551 // nprintf(("Network", "About to send accept packet to %s on port %d\n", get_text_address(buffer, addr->addr), addr->port ));
1554 // actually send the packet
1555 psnet_send(&Net_players[new_player_num].p_info.addr, data, packet_size);
1557 // if he's not an observer, inform all the other players in the game about him
1558 // inform the other players in the game about this new player
1559 for (i=0; i<MAX_PLAYERS; i++) {
1560 // skip unconnected players as well as this new guy himself
1561 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])) {
1565 // send the new packet
1566 send_new_player_packet(new_player_num,&Net_players[i]);
1569 // add a chat message
1570 if(Net_players[new_player_num].player->callsign != NULL){
1571 sprintf(notify_string,XSTR("<%s has joined>",717), Net_players[new_player_num].player->callsign);
1572 multi_display_chat_msg(notify_string, 0, 0);
1575 // handle any team vs. team details
1576 if (!(code & ACCEPT_OBSERVER)) {
1577 multi_team_handle_join(&Net_players[new_player_num]);
1581 if(Net_players[new_player_num].tracker_player_id >= 0){
1582 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);
1584 ml_printf(NOX("Server accepted %s as new client"), Net_players[new_player_num].player->callsign);
1589 // process the player data from the server
1590 void process_accept_player_data( ubyte *data, header *hinfo )
1592 int offset, player_num, player_slot_num, new_flags;
1593 char name[CALLSIGN_LEN + 1] = "";
1594 char image_name[MAX_FILENAME_LEN + 1] = "";
1595 char squad_name[MAX_FILENAME_LEN + 1] = "";
1596 char pxo_squad_name[LOGIN_LEN+1] = "";
1600 ushort ig_signature;
1602 offset = HEADER_LENGTH;
1605 while ( stop == APD_NEXT ) {
1606 player_slot_num = multi_find_open_player_slot();
1607 SDL_assert(player_slot_num != -1);
1609 // get the player's number
1610 GET_INT(player_num);
1612 // add the player's address
1613 get_net_addr(data, &offset, addr);
1615 // get the player's id#
1616 GET_SHORT(player_id);
1621 // add his image filename
1622 GET_STRING(image_name);
1624 // get his squad logo filename
1625 GET_STRING(squad_name);
1627 // get his PXO squad name
1628 GET_STRING(pxo_squad_name);
1633 if (Net_players[player_num].flags & NETINFO_FLAG_OBSERVER) {
1634 if (!multi_obs_create_player(player_num, name, &addr, &Players[player_slot_num])) {
1639 // the error handling here is less than stellar. We should probably put up a popup and go
1640 // back to the main menu. But then again, this should never ever happen!
1641 if ( !multi_create_player(player_num, &Players[player_slot_num],name, &addr, -1, player_id) ) {
1646 // copy his image filename
1647 strcpy(Net_players[player_num].player->image_filename, image_name);
1649 // copy his pilot squad filename
1650 Net_players[player_num].player->insignia_texture = -1;
1651 player_set_squad_bitmap(Net_players[player_num].player, squad_name);
1653 // copy his pxo squad name
1654 strcpy(Net_players[player_num].p_info.pxo_squad_name, pxo_squad_name);
1656 // set his player id#
1657 Net_players[player_num].player_id = player_id;
1659 // mark him as being connected
1660 Net_players[player_num].flags |= NETINFO_FLAG_CONNECTED;
1661 Net_players[player_num].flags |= new_flags;
1663 // set the server pointer
1664 if ( Net_players[player_num].flags & NETINFO_FLAG_AM_MASTER ) {
1665 Netgame.server = &Net_players[player_num];
1666 Netgame.server->last_heard_time = timer_get_fixed_seconds();
1668 // also - always set the server address to be where this data came from, NOT from
1669 // the data in the packet
1670 fill_net_addr(&Net_players[player_num].p_info.addr, hinfo->addr, hinfo->net_id, hinfo->port);
1673 // set the host pointer
1674 if ( Net_players[player_num].flags & NETINFO_FLAG_GAME_HOST ) {
1675 Netgame.host = &Net_players[player_num];
1678 // read in the player's object net signature and store as his objnum for now
1679 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_INGAME ) {
1680 GET_USHORT( ig_signature );
1681 Net_players[player_num].player->objnum = ig_signature;
1684 // get the stop byte
1689 if ( stop == APD_END_DATA ) {
1690 // if joining a game automatically, set the connect address to NULl so we don't try and
1691 // do this next time we enter a game
1692 if (Cmdline_connect_addr != NULL) {
1693 Cmdline_connect_addr = NULL;
1696 // send my stats to the server if I'm not in observer mode
1697 if (!(Net_player->flags & NETINFO_FLAG_ACCEPT_OBSERVER)) {
1698 send_player_stats_block_packet(Net_player, STATS_ALLTIME);
1701 // if i'm being accepted as a host, then move into the host setup state
1702 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_HOST) {
1703 // set my permission bits
1704 Net_player->flags |= NETINFO_FLAG_GAME_HOST;
1705 Net_player->state = NETPLAYER_STATE_STD_HOST_SETUP;
1707 gameseq_post_event(GS_EVENT_MULTI_START_GAME);
1710 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_OBSERVER) {
1711 Net_player->flags |= NETINFO_FLAG_OBSERVER;
1713 // since observers can join 1 of 2 ways, only do this if we're not doing an ingame observer join
1714 if ( !(Net_player->flags & NETINFO_FLAG_ACCEPT_INGAME) ) {
1715 gameseq_post_event(GS_EVENT_MULTI_CLIENT_SETUP);
1719 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_CLIENT) {
1720 gameseq_post_event(GS_EVENT_MULTI_CLIENT_SETUP);
1723 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_INGAME) {
1724 // flag myself as being an ingame joiner
1725 Net_player->flags |= NETINFO_FLAG_INGAME_JOIN;
1727 // move myself into the ingame join mission sync state
1728 Multi_sync_mode = MULTI_SYNC_INGAME;
1729 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
1732 // update my options on the server
1733 multi_options_update_local();
1735 // if we're in PXO mode, mark it down in our player struct
1736 if(MULTI_IS_TRACKER_GAME){
1737 Player->flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
1738 Player->save_flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
1743 // process an accept packet from the server
1744 extern int Select_default_ship;
1746 void process_accept_packet(ubyte* data, header* hinfo)
1748 int code, my_player_num, offset;
1752 // get the accept code
1753 offset = HEADER_LENGTH;
1757 // read in the accept code specific data
1759 if (code & ACCEPT_INGAME) {
1760 // the game filename
1761 GET_STRING(Game_current_mission_filename);
1762 Select_default_ship = 0;
1764 // determine if I'm being placed on a team
1771 if (code & ACCEPT_OBSERVER) {
1772 SDL_assert(!(code & (ACCEPT_CLIENT | ACCEPT_HOST)));
1775 if (code & ACCEPT_HOST) {
1776 SDL_assert(!(code & (ACCEPT_CLIENT | ACCEPT_OBSERVER | ACCEPT_INGAME)));
1779 if (code & ACCEPT_CLIENT) {
1780 SDL_assert(!(code & (ACCEPT_HOST | ACCEPT_OBSERVER | ACCEPT_INGAME)));
1783 // fill in the netgame server address
1784 fill_net_addr( &Netgame.server_addr, hinfo->addr, hinfo->net_id, hinfo->port );
1786 // get the skill level setting
1787 GET_INT(Game_skill_level);
1789 // get my netplayer number
1790 GET_INT(my_player_num);
1793 GET_SHORT(player_id);
1795 // get netgame type flags
1796 GET_INT(Netgame.type_flags);
1798 // setup the Net_players structure for myself first
1799 Net_player = &Net_players[my_player_num];
1800 Net_player->flags = 0;
1801 Net_player->tracker_player_id = Multi_tracker_id;
1802 Net_player->player_id = player_id;
1803 Net_player->s_info.xfer_handle = -1;
1804 // stuff_netplayer_info( Net_player, &Psnet_my_addr, Ships[Objects[Player->objnum].instance].ship_info_index, Player );
1805 stuff_netplayer_info( Net_player, &Psnet_my_addr, 0, Player );
1806 multi_options_local_load(&Net_player->p_info.options, Net_player);
1808 Net_player->p_info.team = team;
1811 // determine if I have a CD
1813 Net_player->flags |= NETINFO_FLAG_HAS_CD;
1816 // set accept code in netplayer for this guy
1817 if ( code & ACCEPT_INGAME ){
1818 Net_player->flags |= NETINFO_FLAG_ACCEPT_INGAME;
1820 if ( code & ACCEPT_OBSERVER ){
1821 Net_player->flags |= NETINFO_FLAG_ACCEPT_OBSERVER;
1823 if ( code & ACCEPT_HOST ){
1824 Net_player->flags |= NETINFO_FLAG_ACCEPT_HOST;
1826 if ( code & ACCEPT_CLIENT ){
1827 Net_player->flags |= NETINFO_FLAG_ACCEPT_CLIENT;
1830 // if I have hacked data
1831 if(game_hacked_data()){
1832 Net_player->flags |= NETINFO_FLAG_HAXOR;
1835 // if we're supposed to flush our local data cache, do so now
1836 if(Net_player->p_info.options.flags & MLO_FLAG_FLUSH_CACHE){
1837 multi_flush_multidata_cache();
1840 Net_player->sv_bytes_sent = 0;
1841 Net_player->sv_last_pl = -1;
1842 Net_player->cl_bytes_recvd = 0;
1843 Net_player->cl_last_pl = -1;
1845 // intiialize endgame stuff
1846 multi_endgame_init();
1850 // make a call to psnet to initialize and try to connect with the server.
1851 psnet_rel_connect_to_server( &Net_player->reliable_socket, &Netgame.server_addr );
1852 if ( Net_player->reliable_socket == INVALID_SOCKET ) {
1853 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_CONNECT_FAIL);
1857 // send a notice that the player at net_addr is leaving (if target is NULL, the broadcast the packet)
1858 void send_leave_game_packet(short player_id, int kicked_reason, net_player *target)
1860 ubyte data[MAX_PACKET_SIZE];
1862 int packet_size = 0;
1864 BUILD_HEADER(LEAVE_GAME);
1866 // add a flag indicating whether he was kicked or not
1867 val = (char)kicked_reason;
1870 if (player_id < 0) {
1871 ADD_SHORT(Net_player->player_id);
1873 // inform the host that we are leaving the game
1874 if (Net_player->flags & NETINFO_FLAG_AM_MASTER) {
1875 multi_io_send_to_all_reliable(data, packet_size);
1877 multi_io_send_reliable(Net_player, data, packet_size);
1880 // this is the case where to server is tossing a player (or indicating a respawned player has quit or become an observer)
1881 // so he has to tell everyone that this guy left
1883 nprintf(("Network","Sending a leave game packet to all players (server)\n"));
1885 // a couple of important checks
1886 SDL_assert(player_id != Net_player->player_id);
1887 SDL_assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
1889 // add the id of the guy to be kicked
1890 ADD_SHORT(player_id);
1892 // broadcast to everyone
1893 if (target == NULL) {
1894 multi_io_send_to_all_reliable(data, packet_size);
1896 multi_io_send_reliable(target, data, packet_size);
1901 // process a notification the a player has left the game
1902 void process_leave_game_packet(ubyte* data, header* hinfo)
1910 offset = HEADER_LENGTH;
1912 // get whether he was kicked
1913 GET_DATA(kicked_reason);
1915 // get the address of the guy who is to leave
1916 GET_SHORT(deader_id);
1919 // determine who is dropping and printf out a notification
1920 player_num = find_player_id(deader_id);
1921 if (player_num == -1) {
1922 nprintf(("Network", "Received leave game packet for unknown player, ignoring\n"));
1926 nprintf(("Network", "Received a leave game notice for %s\n", Net_players[player_num].player->callsign));
1929 // a hook to display that a player was kicked
1930 if (kicked_reason >= 0){
1931 // if it was me that was kicked, leave the game
1932 if((Net_player != NULL) && (Net_player->player_id == deader_id)){
1935 switch(kicked_reason){
1936 case KICK_REASON_BAD_XFER:
1937 notify_code = MULTI_END_NOTIFY_KICKED_BAD_XFER;
1939 case KICK_REASON_CANT_XFER:
1940 notify_code = MULTI_END_NOTIFY_KICKED_CANT_XFER;
1942 case KICK_REASON_INGAME_ENDED:
1943 notify_code = MULTI_END_NOTIFY_KICKED_INGAME_ENDED;
1946 notify_code = MULTI_END_NOTIFY_KICKED;
1950 multi_quit_game(PROMPT_NONE, notify_code);
1953 // otherwise indicate someone was kicked
1955 nprintf(("Network","%s was kicked\n",Net_players[player_num].player->callsign));
1957 // display the result
1958 memset(str, 0, 512);
1959 multi_kick_get_text(&Net_players[player_num], kicked_reason, str);
1960 multi_display_chat_msg(str, player_num, 0);
1964 // first of all, if we're the master, we should be rebroadcasting this packet
1965 if (Net_player->flags & NETINFO_FLAG_AM_MASTER) {
1968 sprintf(msg, XSTR("%s has left the game",719), Net_players[player_num].player->callsign );
1970 if (!(Game_mode & GM_STANDALONE_SERVER)){
1971 HUD_sourced_printf(HUD_SOURCE_HIDDEN, msg);
1974 send_hud_msg_to_all(msg);
1975 multi_io_send_to_all_reliable(data, offset);
1978 // leave the game if the host and/or master has dropped
1980 if (((Net_players[player_num].flags & NETINFO_FLAG_AM_MASTER) || (Net_players[player_num].flags & NETINFO_FLAG_GAME_HOST)) ) {
1981 nprintf(("Network","Host and/or server has left the game - aborting...\n"));
1984 ml_string(NOX("Host and/or server has left the game"));
1986 // if the host leaves in the debriefing state, we should still wait until the player selects accept before we quit
1987 if (gameseq_get_state() != GS_STATE_DEBRIEF) {
1988 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_SERVER_LEFT);
1991 delete_player(player_num);
1994 delete_player(player_num);
1996 // OSAPI GUI stuff (if standalone)
1997 if (Game_mode & GM_STANDALONE_SERVER) {
1998 // returns true if we should reset the standalone
1999 if (std_remove_player(&Net_players[player_num])) {
2000 nprintf(("Network", "Should reset!!\n"));
2004 // update these gui vals
2005 std_connect_set_host_connect_status();
2006 std_connect_set_connect_count();
2010 // send information about this currently active game to the specified address
2011 void send_game_active_packet(net_addr* addr)
2015 ubyte data[MAX_PACKET_SIZE],val;
2017 // build the header and add the data
2018 BUILD_HEADER(GAME_ACTIVE);
2020 // add the server version and compatible version #
2021 val = MULTI_FS_SERVER_VERSION;
2023 val = MULTI_FS_SERVER_COMPATIBLE_VERSION;
2026 ADD_STRING(Netgame.name);
2027 ADD_STRING(Netgame.mission_name);
2028 ADD_STRING(Netgame.title);
2029 val = (ubyte)multi_num_players();
2032 // add the proper flags
2034 if((Netgame.mode == NG_MODE_PASSWORD) || ((Game_mode & GM_STANDALONE_SERVER) && (multi_num_players() == 0) && (std_is_host_passwd()))){
2035 flags |= AG_FLAG_PASSWD;
2038 // proper netgame type flags
2039 if(Netgame.type_flags & NG_TYPE_TEAM){
2040 flags |= AG_FLAG_TEAMS;
2041 } else if(Netgame.type_flags & NG_TYPE_DOGFIGHT){
2042 flags |= AG_FLAG_DOGFIGHT;
2044 flags |= AG_FLAG_COOP;
2047 // proper netgame state flags
2048 switch(Netgame.game_state){
2049 case NETGAME_STATE_FORMING:
2050 flags |= AG_FLAG_FORMING;
2053 case NETGAME_STATE_BRIEFING:
2054 case NETGAME_STATE_MISSION_SYNC:
2055 case NETGAME_STATE_HOST_SETUP:
2056 flags |= AG_FLAG_BRIEFING;
2059 case NETGAME_STATE_IN_MISSION:
2060 flags |= AG_FLAG_IN_MISSION;
2063 case NETGAME_STATE_PAUSED:
2064 flags |= AG_FLAG_PAUSE;
2067 case NETGAME_STATE_ENDGAME:
2068 case NETGAME_STATE_DEBRIEF:
2069 flags |= AG_FLAG_DEBRIEF;
2073 // if this is a standalone
2074 if(Game_mode & GM_STANDALONE_SERVER){
2075 flags |= AG_FLAG_STANDALONE;
2078 // if we're in campaign mode
2079 if(Netgame.campaign_mode == MP_CAMPAIGN){
2080 flags |= AG_FLAG_CAMPAIGN;
2083 // add the data about the connection speed of the host machine
2084 SDL_assert( (Multi_connection_speed >= 0) && (Multi_connection_speed <= 4) );
2085 flags |= (Multi_connection_speed << AG_FLAG_CONNECTION_BIT);
2090 psnet_send(addr, data, packet_size);
2093 // process information about an active game
2094 void process_game_active_packet(ubyte* data, header* hinfo)
2099 int modes_compatible;
2101 fill_net_addr(&ag.server_addr, hinfo->addr, hinfo->net_id, hinfo->port);
2103 // read this game into a temporary structure
2104 offset = HEADER_LENGTH;
2106 // get the server version and compatible version
2107 GET_DATA(ag.version);
2108 GET_DATA(ag.comp_version);
2110 GET_STRING(ag.name);
2111 GET_STRING(ag.mission_name);
2112 GET_STRING(ag.title);
2114 ag.num_players = val;
2115 GET_USHORT(ag.flags);
2119 modes_compatible = 1;
2121 if((ag.flags & AG_FLAG_TRACKER) && !Multi_options_g.pxo){
2122 modes_compatible = 0;
2124 if(!(ag.flags & AG_FLAG_TRACKER) && Multi_options_g.pxo){
2125 modes_compatible = 0;
2129 // if this is a compatible version, and our modes are compatible, register it
2130 if( (ag.version == MULTI_FS_SERVER_VERSION) && modes_compatible ){
2131 multi_update_active_games(&ag);
2135 // send_game_update_packet sends an updated Netgame structure to all players currently connected. The update
2136 // is used to change the current mission, current state, etc.
2137 void send_netgame_update_packet(net_player *pl)
2141 ubyte data[MAX_PACKET_SIZE];
2144 BUILD_HEADER(GAME_UPDATE);
2146 // with new mission description field, this becomes way to large
2147 // so we must add every element piece by piece except the
2148 ADD_STRING(Netgame.name);
2149 ADD_STRING(Netgame.mission_name);
2150 ADD_STRING(Netgame.title);
2151 ADD_STRING(Netgame.campaign_name);
2152 ADD_INT(Netgame.campaign_mode);
2153 ADD_INT(Netgame.max_players);
2154 ADD_INT(Netgame.security);
2155 ADD_UINT(Netgame.respawn);
2156 ADD_INT(Netgame.flags);
2157 ADD_INT(Netgame.type_flags);
2158 ADD_INT(Netgame.version_info);
2159 ADD_DATA(Netgame.debug_flags);
2161 // only the server should ever send the netgame state (standalone situation)
2162 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2163 ADD_INT(Netgame.game_state);
2166 // if we're the host on a standalone, send to the standalone and let him rebroadcast
2167 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2169 multi_io_send_to_all_reliable(data, packet_size);
2171 for(idx=0; idx<MAX_PLAYERS; idx++){
2172 if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx])){
2173 send_netgame_descript_packet(&Net_players[idx].p_info.addr, 1);
2177 multi_io_send_reliable(pl, data, packet_size);
2178 send_netgame_descript_packet( &pl->p_info.addr , 1 );
2181 SDL_assert( pl == NULL ); // I don't think that a host in a standalone game would get here.
2182 multi_io_send_reliable(Net_player, data, packet_size);
2185 // host should always send a netgame options update as well
2186 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
2187 multi_options_update_netgame();
2191 // process information about the netgame sent from the server/host
2192 void process_netgame_update_packet( ubyte *data, header *hinfo )
2194 int offset,old_flags;
2197 SDL_assert(!(Game_mode & GM_STANDALONE_SERVER));
2198 SDL_assert(!(Net_player->flags & NETINFO_FLAG_AM_MASTER));
2200 // read in the netgame information
2201 offset = HEADER_LENGTH;
2202 GET_STRING(Netgame.name);
2203 GET_STRING(Netgame.mission_name);
2204 GET_STRING(Netgame.title);
2205 GET_STRING(Netgame.campaign_name);
2206 GET_INT(Netgame.campaign_mode);
2207 GET_INT(Netgame.max_players); // ignore on the standalone, who keeps track of this himself
2208 GET_INT(Netgame.security);
2209 GET_UINT(Netgame.respawn);
2211 // be sure not to blast the quitting flag because of the "one frame extra" problem
2212 old_flags = Netgame.flags;
2213 GET_INT(Netgame.flags);
2214 GET_INT(Netgame.type_flags);
2215 GET_INT(Netgame.version_info);
2216 GET_DATA(Netgame.debug_flags);
2223 // now compare the passed in game state to our current known state. If it has changed, then maybe
2224 // do something interesting.
2225 // move from the forming or debriefing state to the mission sync state
2226 if ( ng_state == NETGAME_STATE_MISSION_SYNC ){
2227 // if coming from the forming state
2228 if( (Netgame.game_state == NETGAME_STATE_FORMING) ||
2229 ((Netgame.game_state != NETGAME_STATE_FORMING) && ((gameseq_get_state() == GS_STATE_MULTI_HOST_SETUP) || (gameseq_get_state() == GS_STATE_MULTI_CLIENT_SETUP))) ){
2230 // do any special processing for forced state transitions
2231 multi_handle_state_special();
2233 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
2234 strncpy( Game_current_mission_filename, Netgame.mission_name, MAX_FILENAME_LEN );
2235 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
2237 // if coming from the debriefing state
2238 else if( (Netgame.game_state == NETGAME_STATE_DEBRIEF) ||
2239 ((Netgame.game_state != NETGAME_STATE_DEBRIEF) && ((gameseq_get_state() == GS_STATE_DEBRIEF) || (gameseq_get_state() == GS_STATE_MULTI_DOGFIGHT_DEBRIEF)) ) ){
2241 // do any special processing for forced state transitions
2242 multi_handle_state_special();
2244 multi_flush_mission_stuff();
2246 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
2247 strncpy( Game_current_mission_filename, Netgame.mission_name, MAX_FILENAME_LEN );
2248 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
2251 // move from mission sync to team select
2252 else if ( ng_state == NETGAME_STATE_BRIEFING ){
2253 if( (Netgame.game_state == NETGAME_STATE_MISSION_SYNC) ||
2254 ((Netgame.game_state != NETGAME_STATE_MISSION_SYNC) && (gameseq_get_state() == GS_STATE_MULTI_MISSION_SYNC) && (Multi_sync_mode != MULTI_SYNC_POST_BRIEFING)) ){
2256 // do any special processing for forced state transitions
2257 multi_handle_state_special();
2259 strncpy( Game_current_mission_filename, Netgame.mission_name, MAX_FILENAME_LEN );
2260 gameseq_post_event(GS_EVENT_START_BRIEFING);
2263 // move from the debriefing to the create game screen
2264 else if ( ng_state == NETGAME_STATE_FORMING ){
2265 if( (Netgame.game_state == NETGAME_STATE_DEBRIEF) ||
2266 ((Netgame.game_state != NETGAME_STATE_DEBRIEF) && ((gameseq_get_state() == GS_STATE_DEBRIEF) || (gameseq_get_state() == GS_STATE_MULTI_DOGFIGHT_DEBRIEF)) ) ){
2267 // do any special processing for forced state transitions
2268 multi_handle_state_special();
2270 multi_flush_mission_stuff();
2272 // move to the proper screen
2273 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
2274 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2276 gameseq_post_event(GS_EVENT_MULTI_CLIENT_SETUP);
2281 Netgame.game_state = ng_state;
2284 // send a request or a reply for mission description, if code == 0, request, if code == 1, reply
2285 void send_netgame_descript_packet(net_addr *addr, int code)
2287 ubyte data[MAX_PACKET_SIZE],val;
2289 int packet_size = 0;
2292 BUILD_HEADER(UPDATE_DESCRIPT);
2298 // add as much of the description as we dare
2299 len = strlen(The_mission.mission_desc);
2300 if(len > MAX_PACKET_SIZE - 10){
2301 len = MAX_PACKET_SIZE - 10;
2303 memcpy(data+packet_size,The_mission.mission_desc,len);
2306 ADD_STRING(The_mission.mission_desc);
2310 SDL_assert(addr != NULL);
2312 psnet_send(addr, data, packet_size);
2316 // process an incoming netgame description packet
2317 void process_netgame_descript_packet( ubyte *data, header *hinfo )
2321 char mission_desc[MISSION_DESC_LENGTH+2];
2324 fill_net_addr(&addr, hinfo->addr, hinfo->net_id, hinfo->port);
2326 // read this game into a temporary structure
2327 offset = HEADER_LENGTH;
2330 // if this is a request for mission description
2332 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
2337 // send an update to this guy
2338 send_netgame_descript_packet(&addr, 1);
2340 memset(mission_desc,0,MISSION_DESC_LENGTH+2);
2341 GET_STRING(mission_desc);
2343 // only display if we're in the proper state
2344 state = gameseq_get_state();
2346 case GS_STATE_MULTI_JOIN_GAME:
2347 case GS_STATE_MULTI_CLIENT_SETUP:
2348 case GS_STATE_MULTI_HOST_SETUP:
2349 multi_common_set_text(mission_desc);
2357 // broadcast a query for active games. IPX will use net broadcast and TCP will either request from the MT or from the specified list
2358 void broadcast_game_query()
2362 server_item *s_moveup;
2363 ubyte data[MAX_PACKET_SIZE];
2365 BUILD_HEADER(GAME_QUERY);
2367 // go through the server list and query each of those as well
2368 s_moveup = Game_server_head;
2369 if(s_moveup != NULL){
2371 send_server_query(&s_moveup->server_addr);
2372 s_moveup = s_moveup->next;
2373 } while(s_moveup != Game_server_head);
2376 fill_net_addr(&addr, Psnet_my_addr.addr, Psnet_my_addr.net_id, DEFAULT_GAME_PORT);
2378 // send out a broadcast if our options allow us
2379 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
2380 psnet_broadcast( &addr, data, packet_size);
2384 // send an individual query to an address to see if there is an active game
2385 void send_server_query(net_addr *addr)
2388 ubyte data[MAX_PACKET_SIZE];
2390 // build the header and send the data
2391 BUILD_HEADER(GAME_QUERY);
2392 psnet_send(addr, data, packet_size);
2395 // process a query from a client looking for active freespace games
2396 void process_game_query(ubyte* data, header* hinfo)
2401 offset = HEADER_LENGTH;
2405 // check to be sure that we don't capture our own broadcast message
2406 fill_net_addr(&addr, hinfo->addr, hinfo->net_id, hinfo->port);
2407 if ( psnet_same( &addr, &Psnet_my_addr) ){
2411 // if I am not a server of a game, don't send a reply!!!
2412 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) ){
2416 // if the game options are being selected, then ignore the request
2417 // also, if Netgame.max_players == -1, the host has not chosen a mission yet and we should wait
2418 if((Netgame.game_state == NETGAME_STATE_STD_HOST_SETUP) || (Netgame.game_state == NETGAME_STATE_HOST_SETUP) || (Netgame.game_state == 0) || (Netgame.max_players == -1)){
2422 // send information about this active game
2423 send_game_active_packet(&addr);
2426 // sends information about netplayers in the game. if called on the server, broadcasts information about _all_ players
2427 void send_netplayer_update_packet( net_player *pl )
2429 int packet_size,idx;
2430 ubyte data[MAX_PACKET_SIZE],val;
2432 BUILD_HEADER(NETPLAYER_UPDATE);
2434 // if I'm the server of the game, I should send an update for _all_players in the game
2435 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2436 for(idx=0;idx<MAX_PLAYERS;idx++){
2437 // only send info for connected players
2438 if(MULTI_CONNECTED(Net_players[idx])){
2443 // add the net player's information
2444 ADD_SHORT(Net_players[idx].player_id);
2445 ADD_INT(Net_players[idx].state);
2446 ADD_INT(Net_players[idx].p_info.ship_class);
2447 ADD_INT(Net_players[idx].tracker_player_id);
2449 if(Net_players[idx].flags & NETINFO_FLAG_HAS_CD){
2457 // add the final stop byte
2461 // broadcast the packet
2462 if(!(Game_mode & GM_IN_MISSION)){
2464 multi_io_send_to_all_reliable(data, packet_size);
2466 multi_io_send_reliable(pl, data, packet_size);
2470 multi_io_send_to_all(data, packet_size);
2472 multi_io_send(pl, data, packet_size);
2480 // add my current state in the netgame to this packet
2481 ADD_SHORT(Net_player->player_id);
2482 ADD_INT(Net_player->state);
2483 ADD_INT(Net_player->p_info.ship_class);
2484 ADD_INT(Multi_tracker_id);
2486 // add if I have a CD or not
2494 // add a final stop byte
2498 // send the packet to the server
2499 SDL_assert( pl == NULL ); // shouldn't ever be the case that pl is non-null here.
2500 if(!(Game_mode & GM_IN_MISSION)){
2501 multi_io_send_reliable(Net_player, data, packet_size);
2503 multi_io_send(Net_player, data, packet_size);
2508 // process an incoming netplayer state update. if we're the server, we should rebroadcast
2509 void process_netplayer_update_packet( ubyte *data, header *hinfo )
2511 int offset, player_num;
2517 offset = HEADER_LENGTH;
2519 // get the first stop byte
2522 while(stop != 0xff){
2523 // look the player up
2524 GET_SHORT(player_id);
2525 player_num = find_player_id(player_id);
2526 // if we couldn't find him, read in the bogus data
2527 if((player_num == -1) || (Net_player == &Net_players[player_num])){
2528 GET_INT(bogus.state);
2529 GET_INT(bogus.p_info.ship_class);
2530 GET_INT(bogus.tracker_player_id);
2534 // otherwise read in the data correctly
2537 GET_INT(Net_players[player_num].p_info.ship_class);
2538 GET_INT(Net_players[player_num].tracker_player_id);
2541 Net_players[player_num].flags |= NETINFO_FLAG_HAS_CD;
2543 Net_players[player_num].flags &= ~(NETINFO_FLAG_HAS_CD);
2546 // if he's changing state to joined, send a team update
2547 if((Net_players[player_num].state == NETPLAYER_STATE_JOINING) && (new_state == NETPLAYER_STATE_JOINED) && (Netgame.type_flags & NG_TYPE_TEAM)){
2548 multi_team_send_update();
2552 Net_players[player_num].state = new_state;
2555 // get the next stop byte
2561 // if I'm the host or the server of the game, update everyone else so things are synched up as tightly as possible
2562 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2563 send_netplayer_update_packet(NULL);
2566 // if i'm the standalone and this is an update from the host, maybe change some netgame settings
2567 if((Game_mode & GM_STANDALONE_SERVER) && (player_num != -1) && (Net_players[player_num].flags & NETINFO_FLAG_GAME_HOST)){
2568 switch(Net_players[player_num].state){
2569 case NETPLAYER_STATE_STD_HOST_SETUP:
2570 Netgame.game_state = NETGAME_STATE_STD_HOST_SETUP;
2573 case NETPLAYER_STATE_HOST_SETUP:
2574 // check for race conditions
2575 if(Netgame.game_state != NETGAME_STATE_MISSION_SYNC){
2576 Netgame.game_state = NETGAME_STATE_FORMING;
2583 #define EXTRA_DEATH_VAPORIZED (1<<0)
2584 #define EXTRA_DEATH_WASHED (1<<1)
2585 // send a packet indicating a ship has been killed
2586 void send_ship_kill_packet( object *objp, object *other_objp, float percent_killed, int self_destruct )
2588 int packet_size, model;
2589 ubyte data[MAX_PACKET_SIZE], was_player, extra_death_info, vaporized;
2590 ushort debris_signature;
2594 // only sendable from the master
2595 SDL_assert ( Net_player->flags & NETINFO_FLAG_AM_MASTER );
2598 vaporized = ( (Ships[objp->instance].flags & SF_VAPORIZE) > 0 );
2600 extra_death_info = 0;
2602 extra_death_info |= EXTRA_DEATH_VAPORIZED;
2605 if ( Ships[objp->instance].wash_killed ) {
2606 extra_death_info |= EXTRA_DEATH_WASHED;
2609 // find out the next network signature that will be used for the debris pieces.
2610 model = Ships[objp->instance].modelnum;
2611 pm = model_get(model);
2612 debris_signature = 0;
2613 if ( pm && !vaporized ) {
2614 debris_signature = multi_get_next_network_signature( MULTI_SIG_DEBRIS );
2615 multi_set_network_signature( (ushort)(debris_signature + pm->num_debris_objects), MULTI_SIG_DEBRIS );
2616 Ships[objp->instance].arrival_distance = debris_signature;
2619 BUILD_HEADER(SHIP_KILL);
2620 ADD_USHORT(objp->net_signature);
2622 // ships which are initially killed get the rest of the data sent. self destructed ships and
2623 if ( other_objp == NULL ) {
2628 nprintf(("Network","Don't know other_obj for ship kill packet, sending NULL\n"));
2630 ADD_USHORT( other_objp->net_signature );
2633 ADD_USHORT( debris_signature );
2634 ADD_FLOAT( percent_killed );
2635 sd = (ubyte)self_destruct;
2637 ADD_DATA( extra_death_info );
2639 // if the ship who died is a player, then send some extra info, like who killed him, etc.
2641 if ( objp->flags & OF_PLAYER_SHIP ) {
2645 pnum = multi_find_player_by_object( objp );
2648 ADD_DATA( was_player );
2650 SDL_assert(Net_players[pnum].player->killer_objtype < CHAR_MAX);
2651 temp = (char)Net_players[pnum].player->killer_objtype;
2654 SDL_assert(Net_players[pnum].player->killer_species < CHAR_MAX);
2655 temp = (char)Net_players[pnum].player->killer_species;
2658 SDL_assert(Net_players[pnum].player->killer_weapon_index < CHAR_MAX);
2659 temp = (char)Net_players[pnum].player->killer_weapon_index;
2662 ADD_STRING( Net_players[pnum].player->killer_parent_name );
2664 ADD_DATA( was_player );
2667 ADD_DATA( was_player );
2670 // send the packet reliably!!!
2671 multi_io_send_to_all_reliable(data, packet_size);
2674 // process a packet indicating that a ship has been killed
2675 void process_ship_kill_packet( ubyte *data, header *hinfo )
2678 ushort ship_sig, other_sig, debris_sig;
2679 object *sobjp, *oobjp;
2680 float percent_killed;
2681 ubyte was_player, extra_death_info, sd;
2682 char killer_name[NAME_LENGTH], killer_objtype = OBJ_NONE, killer_species = SPECIES_TERRAN, killer_weapon_index = -1;
2684 offset = HEADER_LENGTH;
2685 GET_USHORT(ship_sig);
2687 GET_USHORT( other_sig );
2688 GET_USHORT( debris_sig );
2689 GET_FLOAT( percent_killed );
2691 GET_DATA( extra_death_info );
2692 GET_DATA( was_player );
2695 // pnum is >=0 when the dying ship is a pleyer ship. Get the info about how he died
2696 if ( was_player != 0 ) {
2697 GET_DATA( killer_objtype );
2698 GET_DATA( killer_species );
2699 GET_DATA( killer_weapon_index );
2700 GET_STRING( killer_name );
2705 sobjp = multi_get_network_object( ship_sig );
2707 // if I am unable to find the ship object which was killed, I have to bail and rely on getting
2708 // another message from the server that this happened!
2709 if ( sobjp == NULL ) {
2710 nprintf(("Network", "Couldn't find net signature %d for kill packet\n", ship_sig));
2714 // set this ship's hull value to 0
2715 sobjp->hull_strength = 0.0f;
2717 // maybe set vaporized
2718 if (extra_death_info & EXTRA_DEATH_VAPORIZED) {
2719 Ships[sobjp->instance].flags |= SF_VAPORIZE;
2722 // maybe set wash_killed
2723 if (extra_death_info & EXTRA_DEATH_VAPORIZED) {
2724 Ships[sobjp->instance].wash_killed = 1;
2727 oobjp = multi_get_network_object( other_sig );
2729 if ( was_player != 0 ) {
2732 pnum = multi_find_player_by_object( sobjp );
2734 Net_players[pnum].player->killer_objtype = killer_objtype;
2735 Net_players[pnum].player->killer_species = killer_species;
2736 Net_players[pnum].player->killer_weapon_index = killer_weapon_index;
2737 strcpy( Net_players[pnum].player->killer_parent_name, killer_name );
2741 // check to see if I need to respawn myself
2742 multi_respawn_check(sobjp);
2744 // store the debris signature in the arrival distance which will never get used for player ships
2745 Ships[sobjp->instance].arrival_distance = debris_sig;
2747 // set this bit so that we don't accidentally start switching targets when we die
2748 if(sobjp == Player_obj){
2749 Game_mode |= GM_DEAD_DIED;
2752 nprintf(("Network", "Killing off %s\n", Ships[sobjp->instance].ship_name));
2754 // do the normal thing when not ingame joining. When ingame joining, simply kill off the ship.
2755 if ( !(Net_player->flags & NETINFO_FLAG_INGAME_JOIN) ) {
2756 ship_hit_kill( sobjp, oobjp, percent_killed, sd );
2758 extern void ship_destroyed( int shipnum );
2759 ship_destroyed( sobjp->instance );
2760 sobjp->flags |= OF_SHOULD_BE_DEAD;
2761 obj_delete( OBJ_INDEX(sobjp) );
2765 // send a packet indicating a ship should be created
2766 void send_ship_create_packet( object *objp, int is_support )
2769 ubyte data[MAX_PACKET_SIZE];
2771 // We will pass the ship to create by name.
2772 BUILD_HEADER(SHIP_CREATE);
2773 ADD_USHORT(objp->net_signature);
2774 ADD_INT( is_support );
2776 add_vector_data(data, &packet_size, objp->pos);
2779 // broadcast the packet
2780 multi_io_send_to_all_reliable(data, packet_size);
2783 // process a packet indicating a ship should be created
2784 void process_ship_create_packet( ubyte *data, header *hinfo )
2786 int offset, objnum, is_support;
2789 vector pos = ZERO_VECTOR;
2791 SDL_assert ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
2792 offset = HEADER_LENGTH;
2793 GET_USHORT(signature);
2794 GET_INT( is_support );
2796 get_vector_data(data, &offset, pos);
2801 // find the name of this ship on ship ship arrival list. if found, pass it to parse_object_create
2802 if ( !is_support ) {
2803 objp = mission_parse_get_arrival_ship( signature );
2804 if ( objp != NULL ) {
2805 objnum = parse_create_object(objp);
2807 nprintf(("Network", "Ship with sig %d not found on ship arrival list -- not creating!!\n", signature));
2810 SDL_assert( Arriving_support_ship );
2811 if(Arriving_support_ship == NULL){
2814 Arriving_support_ship->pos = pos;
2815 Arriving_support_ship->net_signature = signature;
2816 objnum = parse_create_object( Arriving_support_ship );
2817 SDL_assert( objnum != -1 );
2819 mission_parse_support_arrived( objnum );
2824 // send a packet indicating a wing of ships should be created
2825 void send_wing_create_packet( wing *wingp, int num_to_create, int pre_create_count )
2827 int packet_size, index, ship_instance;
2828 ubyte data[MAX_PACKET_SIZE];
2832 // for creating wing -- we just send the index into the wing array of this wing.
2833 // all players load the same mission, and so their array's should all match. We also
2834 // need to send the signature of the first ship that was created. We can find this by
2835 // looking num_to_create places back in the ship_index field in the wing structure.
2837 index = WING_INDEX(wingp);
2838 ship_instance = wingp->ship_index[wingp->current_count - num_to_create];
2839 signature = Objects[Ships[ship_instance].objnum].net_signature;
2841 BUILD_HEADER( WING_CREATE );
2843 ADD_INT(num_to_create);
2844 ADD_USHORT(signature);
2845 ADD_INT(pre_create_count);
2846 val = wingp->current_wave - 1;
2849 multi_io_send_to_all_reliable(data, packet_size);
2852 // process a packet saying that a wing should be created
2853 void process_wing_create_packet( ubyte *data, header *hinfo )
2855 int offset, index, num_to_create;
2857 int total_arrived_count, current_wave;
2859 offset = HEADER_LENGTH;
2861 GET_INT(num_to_create);
2862 GET_USHORT(signature);
2863 GET_INT(total_arrived_count);
2864 GET_INT(current_wave);
2868 // do a sanity check on the wing to be sure that we are actually working on a valid wing
2869 if ( (index < 0) || (index >= num_wings) || (Wings[index].num_waves == -1) ) {
2870 nprintf(("Network", "invalid index %d for wing create packet\n"));
2873 if ( (num_to_create <= 0) || (num_to_create > Wings[index].wave_count) ) {
2874 nprintf(("Network", "Invalid number of ships to create (%d) for wing %s\n", num_to_create, Wings[index].name));
2879 Wings[index].current_count = 0;
2880 Wings[index].total_arrived_count = total_arrived_count;
2881 Wings[index].current_wave = current_wave;
2883 // set the network signature that was passed. The client should create ships in the same order
2884 // as the server -- so all ships should get the same sigs as assigned by the server. We also
2885 // need to set some timestamps and cues correctly to be sure that these things get created on
2886 // the clients correctly
2887 multi_set_network_signature( signature, MULTI_SIG_SHIP );
2888 parse_wing_create_ships( &Wings[index], num_to_create, 1 );
2891 // packet indicating a ship is departing
2892 void send_ship_depart_packet( object *objp )
2894 ubyte data[MAX_PACKET_SIZE];
2898 signature = objp->net_signature;
2900 BUILD_HEADER(SHIP_DEPART);
2901 ADD_USHORT( signature );
2903 multi_io_send_to_all_reliable(data, packet_size);
2906 // process a packet indicating a ship is departing
2907 void process_ship_depart_packet( ubyte *data, header *hinfo )
2913 offset = HEADER_LENGTH;
2914 GET_USHORT( signature );
2917 // find the object which is departing
2918 objp = multi_get_network_object( signature );
2919 if ( objp == NULL ) {
2920 nprintf(("network", "Couldn't find object with net signature %d to depart\n", signature ));
2924 // start warping him out
2925 shipfx_warpout_start( objp );
2928 // packet to tell clients cargo of a ship was revealed to all
2929 void send_cargo_revealed_packet( ship *shipp )
2931 ubyte data[MAX_PACKET_SIZE];
2934 // build the header and add the data
2935 BUILD_HEADER(CARGO_REVEALED);
2936 ADD_USHORT( Objects[shipp->objnum].net_signature );
2938 // server sends to all players
2939 if(MULTIPLAYER_MASTER){
2940 multi_io_send_to_all_reliable(data, packet_size);
2942 // clients just send to the server
2944 multi_io_send_reliable(Net_player, data, packet_size);
2948 // process a cargo revealed packet
2949 void process_cargo_revealed_packet( ubyte *data, header *hinfo )
2955 offset = HEADER_LENGTH;
2956 GET_USHORT(signature);
2959 // get a ship pointer and call the ship function to reveal the cargo
2960 objp = multi_get_network_object( signature );
2961 if ( objp == NULL ) {
2962 nprintf(("Network", "Could not find object with net signature %d for cargo revealed\n", signature ));
2966 // SDL_assert( objp->type == OBJ_SHIP );
2967 if((objp->type != OBJ_SHIP) || (objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
2971 // this will take care of re-routing to all other clients
2972 ship_do_cargo_revealed( &Ships[objp->instance], 1);
2974 // server should rebroadcast
2975 if(MULTIPLAYER_MASTER){
2976 send_cargo_revealed_packet(&Ships[objp->instance]);
2980 // defines used for secondary fire packet
2981 #define SFPF_ALLOW_SWARM (1<<7)
2982 #define SFPF_DUAL_FIRE (1<<6)
2983 #define SFPF_TARGET_LOCKED (1<<5)
2985 // send a packet indicating a secondary weapon was fired
2986 void send_secondary_fired_packet( ship *shipp, ushort starting_sig, int starting_count, int num_fired, int allow_swarm )
2988 int packet_size, net_player_num;
2989 ubyte data[MAX_PACKET_SIZE], sinfo, current_bank;
2991 ushort target_signature;
2995 // SDL_assert ( starting_count < UCHAR_MAX );
2997 // get the object for this ship. If it is an AI object, send all the info to all player. Otherwise,
2998 // we might send the info to the other player different than the one who fired
2999 objp = &Objects[shipp->objnum];
3000 if ( !(objp->flags & OF_PLAYER_SHIP) ) {
3001 if ( num_fired == 0 ) {
3006 aip = &Ai_info[shipp->ai_index];
3008 current_bank = (ubyte)shipp->weapons.current_secondary_bank;
3009 //SDL_assert( (current_bank >= 0) && (current_bank < MAX_SECONDARY_BANKS) ); // always true
3011 // build up the header portion
3012 BUILD_HEADER( SECONDARY_FIRED_AI );
3014 ADD_USHORT( Objects[shipp->objnum].net_signature );
3015 ADD_USHORT( starting_sig );
3017 // add a couple of bits for swarm missiles and dual fire secondary weaspons
3020 sinfo = current_bank;
3023 sinfo |= SFPF_ALLOW_SWARM;
3026 if ( shipp->flags & SF_SECONDARY_DUAL_FIRE ){
3027 sinfo |= SFPF_DUAL_FIRE;
3030 if ( aip->current_target_is_locked ){
3031 sinfo |= SFPF_TARGET_LOCKED;
3036 // add the ship's target and any targeted subsystem
3037 target_signature = 0;
3039 if ( aip->target_objnum != -1) {
3040 target_signature = Objects[aip->target_objnum].net_signature;
3041 if ( (Objects[aip->target_objnum].type == OBJ_SHIP) && (aip->targeted_subsys != NULL) ) {
3044 s_index = ship_get_index_from_subsys( aip->targeted_subsys, aip->target_objnum );
3045 SDL_assert( s_index < CHAR_MAX ); // better be less than this!!!!
3046 t_subsys = (char)s_index;
3049 if ( Objects[aip->target_objnum].type == OBJ_WEAPON ) {
3050 SDL_assert(Weapon_info[Weapons[Objects[aip->target_objnum].instance].weapon_info_index].wi_flags & WIF_BOMB);
3055 ADD_USHORT( target_signature );
3056 ADD_DATA( t_subsys );
3058 // just send this packet to everyone, then bail if an AI ship fired.
3059 if ( !(objp->flags & OF_PLAYER_SHIP) ) {
3060 multi_io_send_to_all(data, packet_size);
3064 net_player_num = multi_find_player_by_object( objp );
3066 // getting here means a player fired. Send the current packet to all players except the player
3067 // who fired. If nothing got fired, then don't send to the other players -- we will just send
3068 // a packet to the player who will find out that he didn't fire anything
3069 if ( num_fired > 0 ) {
3070 multi_io_send_to_all_reliable(data, packet_size, &Net_players[net_player_num]);
3073 // if I (the master) fired, then return
3074 if ( Net_players[net_player_num].flags & NETINFO_FLAG_AM_MASTER ){
3078 // now build up the packet to send to the player who actually fired.
3079 BUILD_HEADER( SECONDARY_FIRED_PLR );
3080 ADD_USHORT(starting_sig);
3083 // add the targeting information so that the player's weapons will always home on the correct
3085 ADD_USHORT( target_signature );
3086 ADD_DATA( t_subsys );
3088 multi_io_send_reliable(&Net_players[net_player_num], data, packet_size);
3091 /// process a packet indicating a secondary weapon was fired
3092 void process_secondary_fired_packet(ubyte* data, header* hinfo, int from_player)
3094 int offset, allow_swarm, target_objnum_save;
3095 ushort net_signature, starting_sig, target_signature;
3096 ubyte sinfo, current_bank;
3097 object* objp, *target_objp;
3101 ship_subsys *targeted_subsys_save;
3103 offset = HEADER_LENGTH; // size of the header
3105 // if from_player is false, it means that the secondary weapon info in this packet was
3106 // fired by an ai object (or another player). from_player == 1 means tha me (the person
3107 // receiving this packet) fired the secondary weapon
3108 if ( !from_player ) {
3109 GET_USHORT( net_signature );
3110 GET_USHORT( starting_sig );
3111 GET_DATA( sinfo ); // are we firing swarm missiles
3113 GET_USHORT( target_signature );
3114 GET_DATA( t_subsys );
3118 // find the object (based on network signatures) for the object that fired
3119 objp = multi_get_network_object( net_signature );
3120 if ( objp == NULL ) {
3121 nprintf(("Network", "Could not find ship for fire secondary packet!"));
3125 // set up the ships current secondary bank and that bank's mode. Below, we will set the timeout
3126 // of the next fire time of this bank to 0 so we can fire right away
3127 shipp = &Ships[objp->instance];
3130 GET_USHORT( starting_sig );
3133 GET_USHORT( target_signature );
3134 GET_DATA( t_subsys );
3138 // get the object and ship
3140 shipp = Player_ship;
3143 // check the allow swarm bit
3145 if ( sinfo & SFPF_ALLOW_SWARM ){
3149 // set the dual fire properties of the ship
3150 if ( sinfo & SFPF_DUAL_FIRE ){
3151 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
3153 shipp->flags &= ~SF_SECONDARY_DUAL_FIRE;
3156 // determine whether current target is locked
3157 SDL_assert( shipp->ai_index != -1 );
3158 aip = &Ai_info[shipp->ai_index];
3159 if ( sinfo & SFPF_TARGET_LOCKED ) {
3160 aip->current_target_is_locked = 1;
3162 aip->current_target_is_locked = 0;
3165 // find out the current bank
3166 current_bank = (ubyte)(sinfo & 0x3);
3167 //SDL_assert( (current_bank >= 0) && (current_bank < MAX_SECONDARY_BANKS) ); // always true
3168 shipp->weapons.current_secondary_bank = current_bank;
3170 // make it so we can fire this ship's secondary bank immediately!!!
3171 shipp->weapons.next_secondary_fire_stamp[shipp->weapons.current_secondary_bank] = timestamp(0);
3172 shipp->weapons.detonate_weapon_time = timestamp(5000); // be sure that we don't detonate a remote weapon before it is time.
3174 // set this ship's target and subsystem information. We will save and restore target and
3175 // targeted subsystem so that we do not accidentally change targets for this player or
3176 // any AI ships on his system.
3177 target_objnum_save = aip->target_objnum;
3178 targeted_subsys_save = aip->targeted_subsys;
3180 // reset these variables for accuracy. They will get reassigned at the end of this fuction
3181 aip->target_objnum = -1;
3182 aip->targeted_subsys = NULL;
3184 target_objp = multi_get_network_object( target_signature );
3185 if ( target_objp != NULL ) {
3186 aip->target_objnum = OBJ_INDEX(target_objp);
3188 if ( (t_subsys != -1) && (target_objp->type == OBJ_SHIP) ) {
3189 aip->targeted_subsys = ship_get_indexed_subsys( &Ships[target_objp->instance], t_subsys);
3193 if ( starting_sig != 0 ){
3194 multi_set_network_signature( starting_sig, MULTI_SIG_NON_PERMANENT );
3196 shipp->weapons.detonate_weapon_time = timestamp(0); // signature of -1 say detonate remote weapon
3199 ship_fire_secondary( objp, allow_swarm );
3201 // restore targeted object and targeted subsystem
3202 aip->target_objnum = target_objnum_save;
3203 aip->targeted_subsys = targeted_subsys_save;
3206 // send a packet indicating a countermeasure was fired
3207 void send_countermeasure_fired_packet( object *objp, int cmeasure_count, int rand_val )
3209 ubyte data[MAX_PACKET_SIZE];
3214 SDL_assert ( cmeasure_count < UCHAR_MAX );
3215 BUILD_HEADER(COUNTERMEASURE_FIRED);
3216 ADD_USHORT( objp->net_signature );
3217 ADD_INT( rand_val );
3219 multi_io_send_to_all(data, packet_size);
3222 // process a packet indicating a countermeasure was fired
3223 void process_countermeasure_fired_packet( ubyte *data, header *hinfo )
3225 int offset, rand_val;
3231 offset = HEADER_LENGTH;
3233 GET_USHORT( signature );
3234 GET_INT( rand_val );
3237 objp = multi_get_network_object( signature );
3238 if ( objp == NULL ) {
3239 nprintf(("network", "Could find object whose countermeasures are being launched!!!\n"));
3242 if(objp->type != OBJ_SHIP){
3245 // SDL_assert ( objp->type == OBJ_SHIP );
3247 // make it so ship can fire right away!
3248 Ships[objp->instance].cmeasure_fire_stamp = timestamp(0);
3249 if ( objp == Player_obj ){
3250 nprintf(("network", "firing countermeasure from my ship\n"));
3253 ship_launch_countermeasure( objp, rand_val );
3256 // send a packet indicating that a turret has been fired
3257 void send_turret_fired_packet( int ship_objnum, int subsys_index, int weapon_objnum )
3260 ushort pnet_signature;
3261 ubyte data[MAX_PACKET_SIZE], cindex;
3268 if((weapon_objnum < 0) || (Objects[weapon_objnum].type != OBJ_WEAPON) || (Objects[weapon_objnum].instance < 0) || (Weapons[Objects[weapon_objnum].instance].weapon_info_index < 0)){
3272 // local setup -- be sure we are actually passing a weapon!!!!
3273 objp = &Objects[weapon_objnum];
3274 SDL_assert ( objp->type == OBJ_WEAPON );
3275 if(Weapon_info[Weapons[objp->instance].weapon_info_index].subtype == WP_MISSILE){
3279 pnet_signature = Objects[ship_objnum].net_signature;
3281 SDL_assert( subsys_index < UCHAR_MAX );
3282 cindex = (ubyte)subsys_index;
3284 ssp = ship_get_indexed_subsys( &Ships[Objects[ship_objnum].instance], subsys_index, NULL );
3289 // build the fire turret packet.
3290 BUILD_HEADER(FIRE_TURRET_WEAPON);
3291 packet_size += multi_pack_unpack_position(1, data + packet_size, &objp->orient.v.fvec);
3292 ADD_DATA( has_sig );
3293 ADD_USHORT( pnet_signature );
3295 ADD_USHORT( objp->net_signature );
3298 val = (short)ssp->submodel_info_1.angs.h;
3300 val = (short)ssp->submodel_info_2.angs.p;
3303 multi_io_send_to_all(data, packet_size);
3305 multi_rate_add(1, "tur", packet_size);
3308 // process a packet indicating a turret has been fired
3309 void process_turret_fired_packet( ubyte *data, header *hinfo )
3311 int offset, weapon_objnum, wid;
3312 ushort pnet_signature, wnet_signature;
3321 short pitch, heading;
3323 // get the data for the turret fired packet
3324 offset = HEADER_LENGTH;
3325 offset += multi_pack_unpack_position(0, data + offset, &o_fvec);
3326 GET_DATA( has_sig );
3327 GET_USHORT( pnet_signature );
3329 GET_USHORT( wnet_signature );
3333 GET_DATA( turret_index );
3334 GET_SHORT( heading );
3336 PACKET_SET_SIZE(); // move our counter forward the number of bytes we have read
3339 objp = multi_get_network_object( pnet_signature );
3340 if ( objp == NULL ) {
3341 nprintf(("network", "could find parent object with net signature %d for turret firing\n", pnet_signature));
3345 // if this isn't a ship, do nothing
3346 if ( objp->type != OBJ_SHIP ){
3350 // make an orientation matrix from the o_fvec
3351 vm_vector_2_matrix(&orient, &o_fvec, NULL, NULL);
3353 // find this turret, and set the position of the turret that just fired to be where it fired. Quite a
3354 // hack, but should be suitable.
3355 shipp = &Ships[objp->instance];
3356 ssp = ship_get_indexed_subsys( shipp, turret_index, NULL );
3360 wid = ssp->system_info->turret_weapon_type;
3362 // bash the position and orientation of the turret
3363 ssp->submodel_info_1.angs.h = (float)heading;
3364 ssp->submodel_info_2.angs.p = (float)pitch;
3366 // get the world position of the weapon
3367 ship_get_global_turret_info(objp, ssp->system_info, &pos, &temp);
3369 // create the weapon object
3370 if(wnet_signature != 0){
3371 multi_set_network_signature( wnet_signature, MULTI_SIG_NON_PERMANENT );
3373 weapon_objnum = weapon_create( &pos, &orient, wid, OBJ_INDEX(objp), 0, -1, 1);
3374 if (weapon_objnum != -1) {
3375 if ( Weapon_info[wid].launch_snd != -1 ) {
3376 snd_play_3d( &Snds[Weapon_info[wid].launch_snd], &pos, &View_position );
3381 // send a mission log item packet
3382 void send_mission_log_packet( int num )
3385 ubyte data[MAX_PACKET_SIZE];
3390 SDL_assert ( (Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER) );
3392 // get the data from the log
3393 entry = &log_entries[num];
3394 type = (ubyte)entry->type; // do the type casting thing to save on packet space
3395 sindex = (ushort)entry->index;
3397 BUILD_HEADER(MISSION_LOG_ENTRY);
3399 ADD_INT(entry->flags);
3401 ADD_DATA(entry->timestamp);
3402 ADD_STRING(entry->pname);
3403 ADD_STRING(entry->sname);
3405 // broadcast the packet to all players
3406 multi_io_send_to_all_reliable(data, packet_size);
3409 // process a mission log item packet
3410 void process_mission_log_packet( ubyte *data, header *hinfo )
3415 char pname[NAME_LENGTH], sname[NAME_LENGTH];
3418 SDL_assert ( (Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
3420 offset = HEADER_LENGTH;
3424 GET_DATA(timestamp);
3430 mission_log_add_entry_multi( type, pname, sname, sindex, timestamp, flags );
3433 // send a mission message packet
3434 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)
3437 ubyte data[MAX_PACKET_SIZE], up, us, utime;
3439 SDL_assert ( Net_player->flags & NETINFO_FLAG_AM_MASTER );
3440 SDL_assert ( (priority >= 0) && (priority < UCHAR_MAX) );
3441 SDL_assert ( (timing >= 0) && (timing < UCHAR_MAX) );
3443 up = (ubyte) priority;
3444 us = (ubyte) source;
3445 utime = (ubyte)timing;
3447 BUILD_HEADER(MISSION_MESSAGE);
3449 ADD_STRING(who_from);
3453 ADD_INT(builtin_type);
3454 ADD_INT(multi_team_filter);
3456 if (multi_target == -1){
3457 multi_io_send_to_all_reliable(data, packet_size);
3459 multi_io_send_reliable(&Net_players[multi_target], data, packet_size);
3463 // process a mission message packet
3464 void process_mission_message_packet( ubyte *data, header *hinfo )
3466 int offset, id, builtin_type;
3467 ubyte priority, source, utiming;
3468 char who_from[NAME_LENGTH];
3469 int multi_team_filter;
3471 SDL_assert( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
3473 offset = HEADER_LENGTH;
3475 GET_STRING(who_from);
3479 GET_INT(builtin_type);
3480 GET_INT(multi_team_filter);
3484 // filter out builtin ones in TvT
3485 if((builtin_type >= 0) && (Netgame.type_flags & NG_TYPE_TEAM) && (Net_player != NULL) && (Net_player->p_info.team != multi_team_filter)){
3489 // maybe filter this out
3490 if(!message_filter_multi(id)){
3491 // send the message as if it came from an sexpression
3492 message_queue_message( id, priority, utiming, who_from, source, 0, 0, builtin_type );
3496 // just send them a pong back as fast as possible
3497 void process_ping_packet(ubyte *data, header *hinfo)
3502 offset = HEADER_LENGTH;
3505 // get the address to return the pong to
3506 fill_net_addr(&addr, hinfo->addr, hinfo->net_id, hinfo->port);
3512 // right now it just routes the pong through to the standalone gui, which is the only
3513 // system which uses ping and pong right now.
3514 void process_pong_packet(ubyte *data, header *hinfo)
3520 offset = HEADER_LENGTH;
3522 fill_net_addr(&addr, hinfo->addr, hinfo->net_id, hinfo->port);
3526 // if we're connected , see who sent us this pong
3527 if(Net_player->flags & NETINFO_FLAG_CONNECTED){
3528 lookup = find_player_id(hinfo->id);
3533 p = &Net_players[lookup];
3535 // evaluate the ping
3536 multi_ping_eval_pong(&Net_players[lookup].s_info.ping);
3538 // put in calls to any functions which may want to know about the ping times from
3540 if(Game_mode & GM_STANDALONE_SERVER){
3541 std_update_player_ping(p);
3544 // mark his socket as still alive (extra precaution)
3545 psnet_mark_received(Net_players[lookup].reliable_socket);
3547 // otherwise, do any special processing
3549 // if we're in the join game state, see if this pong came from a server on our
3551 if(gameseq_get_state() == GS_STATE_MULTI_JOIN_GAME){
3552 multi_join_eval_pong(&addr, timer_get_fixed_seconds());
3557 // send a ping packet
3558 void send_ping(net_addr *addr)
3560 unsigned char data[8];
3563 // build the header and send the packet
3564 BUILD_HEADER( PING );
3565 psnet_send(addr, &data[0], packet_size);
3568 // send a pong packet
3569 void send_pong(net_addr *addr)
3571 unsigned char data[8];
3574 // build the header and send the packet
3576 psnet_send(addr, &data[0], packet_size);
3579 // sent from host to master. give me the list of missions you have.
3580 // this will be used only in a standalone mode
3581 void send_mission_list_request( int what )
3583 ubyte data[MAX_PACKET_SIZE];
3586 // build the header and ask for a list of missions or campaigns (depending
3587 // on the 'what' flag).
3588 BUILD_HEADER(MISSION_REQUEST);
3590 multi_io_send_reliable(Net_player, data, packet_size);
3593 // maximum number of bytes that we can send in a mission items packet.
3594 #define MAX_MISSION_ITEMS_BYTES (MAX_PACKET_SIZE - (sizeof(multi_create_info) + 1) )
3596 // defines used to tell what type of packets are being sent
3597 #define MISSION_LIST_ITEMS 1
3598 #define CAMPAIGN_LIST_ITEMS 2
3600 // send an individual mission file item
3601 void send_mission_items( net_player *pl )
3603 ubyte data[MAX_PACKET_SIZE];
3608 BUILD_HEADER(MISSION_ITEM);
3610 // send the list of missions and campaigns avilable on the server. Stop when
3611 // reaching a certain maximum
3612 type = MISSION_LIST_ITEMS;
3614 for (i = 0; i < Multi_create_mission_count; i++ ) {
3618 ADD_STRING( Multi_create_mission_list[i].filename );
3619 ADD_STRING( Multi_create_mission_list[i].name );
3620 ADD_INT( Multi_create_mission_list[i].flags );
3621 ADD_DATA( Multi_create_mission_list[i].max_players );
3622 ADD_UINT( Multi_create_mission_list[i].respawn );
3625 ADD_DATA( Multi_create_mission_list[i].valid_status );
3627 if ( packet_size > (int)MAX_MISSION_ITEMS_BYTES ) {
3630 multi_io_send_reliable(pl, data, packet_size);
3631 BUILD_HEADER( MISSION_ITEM );
3637 multi_io_send_reliable(pl, data, packet_size);
3639 // send the campaign information
3640 type = CAMPAIGN_LIST_ITEMS;
3641 BUILD_HEADER(MISSION_ITEM);
3643 for (i = 0; i < Multi_create_campaign_count; i++ ) {
3647 ADD_STRING( Multi_create_campaign_list[i].filename );
3648 ADD_STRING( Multi_create_campaign_list[i].name );
3649 ADD_INT( Multi_create_campaign_list[i].flags );
3650 ADD_DATA( Multi_create_campaign_list[i].max_players );
3652 if ( packet_size > (int)MAX_MISSION_ITEMS_BYTES ) {
3655 multi_io_send_reliable(pl, data, packet_size);
3656 BUILD_HEADER( MISSION_ITEM );
3662 multi_io_send_reliable(pl, data, packet_size);
3665 // process a request for a list of missions
3666 void process_mission_request_packet(ubyte *data, header *hinfo)
3668 int player_num,offset;
3670 offset = HEADER_LENGTH;
3673 // fill in the address information of where this came from
3674 player_num = find_player_id(hinfo->id);
3675 if(player_num == -1){
3676 nprintf(("Network","Could not find player to send mission list items to!\n"));
3680 send_mission_items( &Net_players[player_num] );
3683 // process an individual mission file item
3684 void process_mission_item_packet(ubyte *data,header *hinfo)
3687 char filename[MAX_FILENAME_LEN], name[NAME_LENGTH], valid_status;
3688 ubyte stop, type,max_players;
3691 SDL_assert(gameseq_get_state() == GS_STATE_MULTI_HOST_SETUP);
3692 offset = HEADER_LENGTH;
3697 GET_STRING( filename );
3700 GET_DATA( max_players );
3702 // missions also have respawns and a crc32 associated with them
3703 if(type == MISSION_LIST_ITEMS){
3707 GET_DATA(valid_status);
3709 if ( Multi_create_mission_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
3710 strcpy(Multi_create_mission_list[Multi_create_mission_count].filename, filename );
3711 strcpy(Multi_create_mission_list[Multi_create_mission_count].name, name );
3712 Multi_create_mission_list[Multi_create_mission_count].flags = flags;
3713 Multi_create_mission_list[Multi_create_mission_count].respawn = respawn;
3714 Multi_create_mission_list[Multi_create_mission_count].max_players = max_players;
3717 Multi_create_mission_list[Multi_create_mission_count].valid_status = valid_status;
3719 Multi_create_mission_count++;
3721 } else if ( type == CAMPAIGN_LIST_ITEMS ) {
3722 if ( Multi_create_campaign_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
3723 strcpy(Multi_create_campaign_list[Multi_create_campaign_count].filename, filename );
3724 strcpy(Multi_create_campaign_list[Multi_create_campaign_count].name, name );
3725 Multi_create_campaign_list[Multi_create_campaign_count].flags = flags;
3726 Multi_create_campaign_list[Multi_create_campaign_count].respawn = 0;
3727 Multi_create_campaign_list[Multi_create_campaign_count].max_players = max_players;
3728 Multi_create_campaign_count++;
3737 // this will cause whatever list to get resorted (although they should be appearing in order)
3738 multi_create_setup_list_data(-1);
3741 // send a request to the server to pause or unpause the game
3742 void send_multi_pause_packet(int pause)
3744 ubyte data[MAX_PACKET_SIZE];
3746 int packet_size = 0;
3748 SDL_assert(!(Net_player->flags & NETINFO_FLAG_AM_MASTER));
3751 BUILD_HEADER(MULTI_PAUSE_REQUEST);
3752 val = (ubyte) pause;
3754 // add the pause info
3757 // send the request to the server
3758 multi_io_send_reliable(Net_player, data, packet_size);
3761 // process a pause update packet (pause, unpause, etc)
3762 void process_multi_pause_packet(ubyte *data, header *hinfo)
3768 offset = HEADER_LENGTH;
3774 // get who sent the packet
3775 player_index = find_player_id(hinfo->id);
3776 // if we don't know who sent the packet, don't do anything
3777 if(player_index == -1){
3781 // if we're the server, we should evaluate whether this guy is allowed to send the packet
3782 multi_pause_server_eval_request(&Net_players[player_index],(int)val);
3785 // send a game information update
3786 void send_game_info_packet()
3789 ubyte data[MAX_PACKET_SIZE], paused;
3791 // set the paused variable
3792 paused = (ubyte)((Netgame.game_state == NETGAME_STATE_PAUSED)?1:0);
3794 BUILD_HEADER(GAME_INFO);
3795 ADD_INT( Missiontime );
3798 multi_io_send_to_all(data, packet_size);
3801 // process a game information update
3802 void process_game_info_packet( ubyte *data, header *hinfo )
3808 offset = HEADER_LENGTH;
3810 // get the mission time -- we should examine our time and the time from the server. If off by some delta
3811 // time, set our time to server time (should take ping time into account!!!)
3812 GET_DATA( mission_time );
3817 // send an ingame nak packet
3818 void send_ingame_nak(int state, net_player *p)
3820 ubyte data[MAX_PACKET_SIZE];
3823 BUILD_HEADER(INGAME_NAK);
3827 multi_io_send_reliable(p, data, packet_size);
3830 // process an ingame nak packet
3831 void process_ingame_nak(ubyte *data, header *hinfo)
3833 int offset,state,pid;
3836 offset = HEADER_LENGTH;
3840 pid = find_player_id(hinfo->id);
3844 pl = &Net_players[pid];
3847 case ACK_FILE_ACCEPTED :
3848 SDL_assert(Net_player->flags & NETINFO_FLAG_INGAME_JOIN);
3849 nprintf(("Network","Mission file rejected by server, aborting...\n"));
3850 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_FILE_REJECTED);
3855 // send a packet telling players to end the mission
3856 void send_endgame_packet(net_player *pl)
3858 ubyte data[MAX_PACKET_SIZE];
3862 BUILD_HEADER(MISSION_END);
3864 // sending to a specific player?
3866 SDL_assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
3867 multi_io_send_reliable(pl, data, packet_size);
3871 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3872 // send all player stats here
3873 multi_broadcast_stats(STATS_MISSION);
3875 // if in dogfight mode, send all dogfight stats as well
3876 ml_string("Before dogfight stats!");
3877 if(Netgame.type_flags & NG_TYPE_DOGFIGHT){
3878 ml_string("Sending dogfight stats!");
3880 multi_broadcast_stats(STATS_DOGFIGHT_KILLS);
3882 ml_string("After dogfight stats!");
3884 // tell everyone to leave the game
3885 multi_io_send_to_all_reliable(data, packet_size);
3887 multi_io_send_reliable(Net_player, data, packet_size);
3891 // process a packet indicating we should end the current mission
3892 void process_endgame_packet(ubyte *data, header *hinfo)
3897 offset = HEADER_LENGTH;
3901 ml_string("Receiving endgame packet");
3903 // if I'm the server, I should evaluate whether the sender is authorized to end the game
3904 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3905 // determine who this came from and make sure he is allowed to end the game
3906 player_num = find_player_id(hinfo->id);
3907 SDL_assert(player_num != -1);
3912 // if the player is allowed to end the mission
3913 if(!multi_can_end_mission(&Net_players[player_num])){
3917 // act as if we hit alt+j locally
3918 multi_handle_end_mission_request();
3920 // all clients process immediately
3922 // ingame joiners should quit when they receive an endgame packet since the game is over
3923 if(Net_player->flags & NETINFO_FLAG_INGAME_JOIN){
3924 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_EARLY_END);
3928 // do any special processing for being in a state other than the gameplay state
3929 multi_handle_state_special();
3931 // make sure we're not already in the debrief state
3932 if((gameseq_get_state() != GS_STATE_DEBRIEF) && (gameseq_get_state() != GS_STATE_MULTI_DOGFIGHT_DEBRIEF)){
3933 multi_warpout_all_players();
3938 // send a position/orientation update for myself (if I'm an observer)
3939 void send_observer_update_packet()
3941 ubyte data[MAX_PACKET_SIZE];
3946 // its possible for the master to be an observer if has run out of respawns. In this case, he doesn't need
3947 // to send any update packets to anyone.
3948 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3952 if((Player_obj == NULL) || (Player_obj->type != OBJ_OBSERVER) || (Net_player == NULL) || !(Net_player->flags & NETINFO_FLAG_OBSERVER)){
3958 BUILD_HEADER(OBSERVER_UPDATE);
3960 ret = multi_pack_unpack_position( 1, data + packet_size, &Player_obj->pos );
3962 ret = multi_pack_unpack_orient( 1, data + packet_size, &Player_obj->orient );
3965 // add targeting infomation
3966 if((Player_ai != NULL) && (Player_ai->target_objnum >= 0)){
3967 target_sig = Objects[Player_ai->target_objnum].net_signature;
3971 ADD_USHORT(target_sig);
3973 multi_io_send(Net_player, data, packet_size);
3976 // process a position/orientation update from an observer
3977 void process_observer_update_packet(ubyte *data, header *hinfo)
3983 physics_info bogus_pi;
3986 offset = HEADER_LENGTH;
3988 obs_num = find_player_id(hinfo->id);
3990 memset(&bogus_pi,0,sizeof(physics_info));
3991 ret = multi_pack_unpack_position( 0, data + offset, &g_vec );
3993 ret = multi_pack_unpack_orient( 0, data + offset, &g_mat );
3996 // targeting information
3997 GET_USHORT(target_sig);
4000 if((obs_num < 0) || (Net_players[obs_num].player->objnum < 0)){
4004 // set targeting info
4005 if(target_sig == 0){
4006 Net_players[obs_num].s_info.target_objnum = -1;
4008 target_obj = multi_get_network_object(target_sig);
4009 Net_players[obs_num].s_info.target_objnum = (target_obj == NULL) ? -1 : OBJ_INDEX(target_obj);
4012 Objects[Net_players[obs_num].player->objnum].pos = g_vec;
4013 Objects[Net_players[obs_num].player->objnum].orient = g_mat;
4014 Net_players[obs_num].s_info.eye_pos = g_vec;
4015 Net_players[obs_num].s_info.eye_orient = g_mat;
4018 void send_netplayer_slot_packet()
4020 ubyte data[MAX_PACKET_SIZE];
4021 int packet_size,idx;
4026 BUILD_HEADER(NETPLAYER_SLOTS_P);
4027 for(idx=0;idx<MAX_PLAYERS;idx++){
4028 if( MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_OBSERVER(Net_players[idx])){
4030 ADD_SHORT(Net_players[idx].player_id);
4031 ADD_USHORT(Objects[Net_players[idx].player->objnum].net_signature);
4032 ADD_INT(Net_players[idx].p_info.ship_class);
4033 ADD_INT(Net_players[idx].p_info.ship_index);
4039 // standalone case or not
4040 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4041 multi_io_send_to_all_reliable(data, packet_size);
4043 multi_io_send_reliable(Net_player, data, packet_size);
4047 void process_netplayer_slot_packet(ubyte *data, header *hinfo)
4050 int player_num,ship_class,ship_index;
4056 offset = HEADER_LENGTH;
4058 // first untag all of the player ships and make them OF_COULD_BE_PLAYER
4059 multi_untag_player_ships();
4063 GET_SHORT(player_id);
4064 GET_USHORT(net_sig);
4065 GET_INT(ship_class);
4066 GET_INT(ship_index);
4067 player_num = find_player_id(player_id);
4069 nprintf(("Network","Error looking up player for object/slot assignment!!\n"));
4071 // call the function in multiutil.cpp to set up the player object stuff
4072 // being careful not to muck with the standalone object
4073 if(!((player_num == 0) && (Game_mode & GM_STANDALONE_SERVER))){
4074 objp = multi_get_network_object(net_sig);
4075 SDL_assert(objp != NULL);
4076 multi_assign_player_ship( player_num, objp, ship_class );
4077 Net_players[player_num].p_info.ship_index = ship_index;
4078 objp->flags &= ~(OF_COULD_BE_PLAYER);
4079 objp->flags |= OF_PLAYER_SHIP;
4086 // standalone should forward the packet and wait for a response
4087 if(Game_mode & GM_STANDALONE_SERVER){
4088 send_netplayer_slot_packet();
4091 Net_player->state = NETPLAYER_STATE_SLOT_ACK;
4092 send_netplayer_update_packet();
4095 // two functions to deal with ships changing their primary/secondary weapon status. 'what' indicates
4096 // if this change is a primary or secondary change. new_bank is the new current primary/secondary
4097 // bank, link_status is whether primaries are linked or not, or secondaries are dual fire or not
4098 void send_ship_weapon_change( ship *shipp, int what, int new_bank, int link_status )
4100 ubyte data[MAX_PACKET_SIZE], utmp;
4103 BUILD_HEADER(SHIP_WSTATE_CHANGE);
4104 ADD_USHORT( Objects[shipp->objnum].net_signature );
4105 utmp = (ubyte)(what);
4107 utmp = (ubyte)(new_bank);
4109 utmp = (ubyte)(link_status);
4112 // Removed the above psnet_send() call - it didn't appear to do anything since it was called only from the server anyway - DB
4113 multi_io_send_to_all_reliable(data, packet_size);
4116 void process_ship_weapon_change( ubyte *data, header *hinfo )
4120 ubyte what, new_bank, link_status;
4124 offset = HEADER_LENGTH;
4125 GET_USHORT( signature );
4127 GET_DATA( new_bank );
4128 GET_DATA( link_status );
4131 objp = multi_get_network_object( signature );
4132 if ( objp == NULL ) {
4133 nprintf(("network", "Unable to locate ship with signature %d for weapon state change\n", signature));
4136 // SDL_assert( objp->type == OBJ_SHIP );
4137 if(objp->type != OBJ_SHIP){
4141 // if this is my data, do nothing since I already have my own data
4142 if ( objp == Player_obj ){
4146 // now, get the ship and set the new bank and link modes based on the 'what' value
4147 shipp = &Ships[objp->instance];
4148 if ( what == MULTI_PRIMARY_CHANGED ) {
4149 shipp->weapons.current_primary_bank = new_bank;
4151 shipp->flags |= SF_PRIMARY_LINKED;
4153 shipp->flags &= ~SF_PRIMARY_LINKED;
4156 shipp->weapons.current_secondary_bank = new_bank;
4158 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
4160 shipp->flags &= ~SF_SECONDARY_DUAL_FIRE;
4165 // ship status change procedure
4166 // 1.) <client> - Client runs through the normal button_function procedure. Any remaining control bits are implied as being
4168 // 2.) <client> - Client puts this button_info item into his last_buttons array and sends a bunch of SHIP_STATUS packets
4169 // for added redundancy.
4170 // 3.) <server> - Receives the packet. Checks to see if the net_player on his side already has this one defined. If so, it
4171 // ignores as a repeat packet. Otherwise it puts it in the last_buttons array for the net_player
4172 // 4.) <server> - Server applies the command on his side (with multi_apply_ship_status(...) and sends the ack (also a SHIP_STATUS)
4173 // back to the client. Also sends multiple times for redundancy
4174 // 5.) <client> - Receives the packet back. Does a lookup into his last_buttons array. If he finds the match, apply the functions
4175 // and remove the item from the list. If no match is found it means that either he has received an ack, has acted
4176 // on it and removed it, or that it has been "timed out" and replaced by a newer button_info.
4178 #define SHIP_STATUS_REPEAT 2
4179 void send_ship_status_packet(net_player *pl, button_info *bi, int id)
4182 ubyte data[MAX_PACKET_SIZE];
4183 int packet_size = 0;
4189 BUILD_HEADER(SHIP_STATUS_CHANGE);
4191 for(idx=0;idx<NUM_BUTTON_FIELDS;idx++){
4192 temp = bi->status[idx];
4196 // server should send reliably (response packet)
4197 if(MULTIPLAYER_MASTER){
4198 multi_io_send_reliable(pl, data, packet_size);
4200 multi_io_send(pl, data, packet_size);
4204 void process_ship_status_packet(ubyte *data, header *hinfo)
4208 int player_num,unique_id;
4212 offset = HEADER_LENGTH;
4214 // zero out the button info structure for good measure
4215 memset(&bi,0,sizeof(button_info));
4217 // read the button-info
4220 for(idx=0;idx<NUM_BUTTON_FIELDS;idx++){
4222 bi.status[idx] = i_tmp;
4227 // this will be handled differently client and server side. Duh.
4228 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){ // SERVER SIDE
4229 // find which net-player has sent us butotn information
4230 player_num = find_player_id(hinfo->id);
4231 SDL_assert(player_num >= 0);
4236 // don't process critical button information for observers
4237 // its a new button_info for this guy. apply and ack
4238 if(!MULTI_OBSERVER(Net_players[player_num]) && !lookup_ship_status(&Net_players[player_num],unique_id)){
4239 // mark that he's pressed this button
4240 // add_net_button_info(&Net_players[player_num], &bi, unique_id);
4242 // send a return packet
4243 send_ship_status_packet(&Net_players[player_num], &bi,unique_id);
4245 // apply the button presses to his ship as normal
4246 multi_apply_ship_status(&Net_players[player_num], &bi, 0);
4248 // else ignore it as a repeat from the same guy
4249 } else { // CLIENT SIDE
4250 // this is the return from the server, so we should now apply them locally
4251 // if(lookup_ship_status(Net_player,unique_id,1)){
4252 multi_apply_ship_status(Net_player, &bi, 1);
4257 // MWA 4/28/9 -- redid this function since message all fighers was really broken
4258 // for clients. Left all details to this function instead of higher level messaging
4260 void send_player_order_packet(int type, int index, int cmd)
4262 ubyte data[MAX_PACKET_SIZE];
4264 ushort target_signature;
4266 int packet_size = 0;
4268 BUILD_HEADER(PLAYER_ORDER_PACKET);
4271 ADD_DATA(val); // ship order or wing order, or message all fighters
4273 // if we are not messaging all ships or wings, add the index, which is the shipnum or wingnum
4274 if ( val != SQUAD_MSG_ALL ){
4275 ADD_INT(index); // net signature of target ship
4278 ADD_INT(cmd); // the command itself
4281 target_signature = 0;
4282 if ( Player_ai->target_objnum != -1 ){
4283 target_signature = Objects[Player_ai->target_objnum].net_signature;
4286 ADD_USHORT( target_signature );
4289 if ( (Player_ai->target_objnum != -1) && (Player_ai->targeted_subsys != NULL) ) {
4292 s_index = ship_get_index_from_subsys( Player_ai->targeted_subsys, Player_ai->target_objnum );
4293 SDL_assert( s_index < CHAR_MAX ); // better be less than this!!!!
4294 t_subsys = (char)s_index;
4298 multi_io_send_reliable(Net_player, data, packet_size);
4301 // brief explanation :
4302 // in either case (wing or ship command), we need to send in a pseudo-ai object. Basically, both command handler
4303 // functions "normally" (non multiplayer) use a couple of the Player_ai fields. So, we just fill in the ones necessary
4304 // (which we can reconstruct from the packet data), and pass this as the default variable ai_info *local
4305 // Its kind of a hack, but it eliminates the need to go in and screw around with quite a bit of code
4306 void process_player_order_packet(ubyte *data, header *hinfo)
4308 int offset, player_num, command, index = 0, tobjnum_save;
4309 ushort target_signature;
4310 char t_subsys, type;
4311 object *objp, *target_objp;
4314 ship_subsys *tsubsys_save, *targeted_subsys;
4316 SDL_assert(MULTIPLAYER_MASTER);
4318 // packet values - its easier to read all of these in first
4320 offset = HEADER_LENGTH;
4323 if ( type != SQUAD_MSG_ALL ){
4328 GET_USHORT( target_signature );
4329 GET_DATA( t_subsys );
4333 player_num = find_player_id(hinfo->id);
4334 if(player_num == -1){
4335 nprintf(("Network","Received player order packet from unknown player\n"));
4339 objp = &Objects[Net_players[player_num].player->objnum];
4340 if ( objp->type != OBJ_SHIP ) {
4341 nprintf(("Network", "not doing player order because object requestting is not a ship\n"));
4345 // HACK HACK HACK HACK HACK HACK
4346 // if the player has sent a rearm-repair me message, we should bail here after evaluating it, since most likely the rest of
4347 // the data is BOGUS. All people should be able to to these things as well.
4348 if(command == REARM_REPAIR_ME_ITEM){
4349 hud_squadmsg_repair_rearm(0,&Objects[Net_players[player_num].player->objnum]);
4351 } else if(command == ABORT_REARM_REPAIR_ITEM){
4352 hud_squadmsg_repair_rearm_abort(0,&Objects[Net_players[player_num].player->objnum]);
4356 // if this player is not allowed to do messaging, quit here
4357 if( !multi_can_message(&Net_players[player_num]) ){
4358 nprintf(("Network","Recieved player order packet from player not allowed to give orders!!\n"));
4362 // check to see if the type of order is a reinforcement call. If so, intercept it, and
4363 // then call them in.
4364 if ( type == SQUAD_MSG_REINFORCEMENT ) {
4365 SDL_assert( (index >= 0) && (index < Num_reinforcements) );
4366 hud_squadmsg_call_reinforcement(index, player_num);
4370 // set the player's ai information here
4371 shipp = &Ships[objp->instance];
4372 aip = &Ai_info[shipp->ai_index];
4374 // get the target objnum and targeted subsystem. Quick out if we don't have an object to act on.
4375 target_objp = multi_get_network_object( target_signature );
4376 if ( target_objp == NULL ) {
4380 targeted_subsys = NULL;
4381 if ( t_subsys != -1 ) {
4382 SDL_assert( target_objp != NULL );
4383 targeted_subsys = ship_get_indexed_subsys( &Ships[target_objp->instance], t_subsys);
4386 // save and restore the target objnum and targeted subsystem so that we don't mess up other things
4388 tobjnum_save = aip->target_objnum;
4389 tsubsys_save = aip->targeted_subsys;
4391 if ( target_objp ) {
4392 aip->target_objnum = OBJ_INDEX(target_objp);
4394 aip->target_objnum = -1;
4397 aip->targeted_subsys = targeted_subsys;
4399 if ( type == SQUAD_MSG_SHIP ) {
4400 hud_squadmsg_send_ship_command(index, command, 1, player_num);
4401 } else if ( type == SQUAD_MSG_WING ) {
4402 hud_squadmsg_send_wing_command(index, command, 1, player_num);
4403 } else if ( type == SQUAD_MSG_ALL ) {
4404 hud_squadmsg_send_to_all_fighters( command, player_num );
4407 SDL_assert(tobjnum_save != Ships[aip->shipnum].objnum); // make sure not targeting self
4408 aip->target_objnum = tobjnum_save;
4409 aip->targeted_subsys = tsubsys_save;
4412 // FILE SIGNATURE stuff :
4413 // there are 2 cases for file signature sending which are handled very differently
4414 // 1.) Pregame. In this case, the host requires that all clients send a filesig packet (when process_file_sig() is called, it
4415 // posts an ACK_FILE_ACCEPTED packet to ack_evaluate, so he thinks they have acked).
4416 // 2.) Ingame join. In this case, the client sends his filesig packet automatically to the server and the _client_ waits for
4417 // the ack, before continuing to join. It would be way too messy to have the server wait on the clients ack, since he
4418 // would have to keep track of up to potentially 14 other ack handles (ouch).
4419 void send_file_sig_packet(ushort sum_sig,int length_sig)
4421 ubyte data[MAX_PACKET_SIZE];
4422 int packet_size = 0;
4424 BUILD_HEADER(FILE_SIG_INFO);
4425 ADD_USHORT(sum_sig);
4426 ADD_SHORT(length_sig);
4428 multi_io_send_reliable(Net_player, data, packet_size);
4431 void process_file_sig_packet(ubyte *data, header *hinfo)
4436 offset = HEADER_LENGTH;
4438 // should only be received on the server-side
4439 SDL_assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
4441 GET_USHORT(sum_sig);
4442 GET_INT(length_sig);
4444 server_verify_filesig(hinfo->id, sum_sig, length_sig);
4447 void send_file_sig_request(char *file_name)
4449 ubyte data[MAX_PACKET_SIZE];
4450 int packet_size = 0;
4452 BUILD_HEADER(FILE_SIG_REQUEST);
4453 ADD_STRING(file_name);
4455 SDL_assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
4457 multi_io_send_to_all_reliable(data, packet_size);
4460 void process_file_sig_request(ubyte *data, header *hinfo)
4462 int offset = HEADER_LENGTH;
4464 // get the mission name
4465 GET_STRING(Netgame.mission_name);
4468 // set the current mission filename
4469 strcpy(Game_current_mission_filename,Netgame.mission_name);
4472 multi_get_mission_checksum(Game_current_mission_filename);
4474 if(!multi_endgame_ending()){
4475 // reply to the server
4476 send_file_sig_packet(Multi_current_file_checksum,Multi_current_file_length);
4480 // functions to deal with subsystems getting whacked
4481 void send_subsystem_destroyed_packet( ship *shipp, int index, vector world_hitpos )
4483 ubyte data[MAX_PACKET_SIZE];
4486 vector tmp, local_hitpos;
4489 SDL_assert ( index < UCHAR_MAX );
4490 uindex = (ubyte)(index);
4492 objp = &Objects[shipp->objnum];
4494 vm_vec_sub(&tmp, &world_hitpos, &objp->pos );
4495 vm_vec_rotate( &local_hitpos, &tmp, &objp->orient );
4497 BUILD_HEADER(SUBSYSTEM_DESTROYED);
4498 ADD_USHORT( Objects[shipp->objnum].net_signature );
4500 // ADD_DATA( local_hitpos );
4501 add_vector_data(data, &packet_size, local_hitpos);
4503 multi_io_send_to_all_reliable(data, packet_size);
4506 void process_subsystem_destroyed_packet( ubyte *data, header *hinfo )
4512 vector local_hit_pos, world_hit_pos;
4514 offset = HEADER_LENGTH;
4516 GET_USHORT( signature );
4518 // GET_DATA( local_hit_pos );
4519 get_vector_data(data, &offset, local_hit_pos);
4521 // get the network object. process it if we find it.
4522 objp = multi_get_network_object( signature );
4523 if ( objp != NULL ) {
4525 ship_subsys *subsysp;
4527 // be sure we have a ship!!!
4528 // SDL_assert ( objp->type == OBJ_SHIP );
4529 if(objp->type != OBJ_SHIP){
4534 shipp = &Ships[objp->instance];
4536 // call to get the pointer to the subsystem we should be working on
4537 subsysp = ship_get_indexed_subsys( shipp, (int)uindex );
4538 vm_vec_unrotate( &world_hit_pos, &local_hit_pos, &objp->orient );
4539 vm_vec_add2( &world_hit_pos, &objp->pos );
4541 do_subobj_destroyed_stuff( shipp, subsysp, &world_hit_pos );
4542 if ( objp == Player_obj ) {
4543 hud_gauge_popup_start(HUD_DAMAGE_GAUGE, 5000);
4551 // packet to tell clients cargo of a ship was revealed to all
4552 void send_subsystem_cargo_revealed_packet( ship *shipp, int index )
4554 ubyte data[MAX_PACKET_SIZE], uindex;
4557 SDL_assert ( index < UCHAR_MAX );
4558 uindex = (ubyte)(index);
4560 // build the header and add the data
4561 BUILD_HEADER(SUBSYS_CARGO_REVEALED);
4562 ADD_USHORT( Objects[shipp->objnum].net_signature );
4565 // server sends to all players
4566 if(MULTIPLAYER_MASTER){
4567 multi_io_send_to_all_reliable(data, packet_size);
4569 // clients just send to the server
4571 multi_io_send_reliable(Net_player, data, packet_size);
4575 // process a subsystem cargo revealed packet
4576 void process_subsystem_cargo_revealed_packet( ubyte *data, header *hinfo )
4583 ship_subsys *subsysp;
4585 offset = HEADER_LENGTH;
4586 GET_USHORT( signature );
4590 // get a ship pointer and call the ship function to reveal the cargo
4591 objp = multi_get_network_object( signature );
4592 if ( objp == NULL ) {
4593 nprintf(("Network", "Could not find object with net signature %d for cargo revealed\n", signature ));
4597 // SDL_assert( objp->type == OBJ_SHIP );
4598 if((objp->type != OBJ_SHIP) || (objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
4602 shipp = &Ships[objp->instance];
4604 // call to get the pointer to the subsystem we should be working on
4605 subsysp = ship_get_indexed_subsys( shipp, (int)uindex );
4606 if (subsysp == NULL) {
4607 nprintf(("Network", "Could not find subsys for ship %s for cargo revealed\n", Ships[objp->instance].ship_name ));
4611 // this will take care of re-routing to all other clients
4612 void ship_do_cap_subsys_cargo_revealed( ship *shipp, ship_subsys *subsys, int from_network );
4613 ship_do_cap_subsys_cargo_revealed( shipp, subsysp, 1 );
4615 // server should rebroadcast
4616 if(MULTIPLAYER_MASTER){
4617 send_subsystem_cargo_revealed_packet(&Ships[objp->instance], (int)uindex);
4621 void send_netplayer_load_packet(net_player *pl)
4623 ubyte data[MAX_PACKET_SIZE];
4624 int packet_size = 0;
4626 BUILD_HEADER(LOAD_MISSION_NOW);
4627 ADD_STRING(Netgame.mission_name);
4630 multi_io_send_to_all_reliable(data, packet_size);
4632 multi_io_send_reliable(pl, data, packet_size);
4636 void process_netplayer_load_packet(ubyte *data, header *hinfo)
4639 int offset = HEADER_LENGTH;
4644 strcpy(Netgame.mission_name,str);
4645 strcpy(Game_current_mission_filename,str);
4646 if(!Multi_mission_loaded){
4648 // MWA 2/3/98 -- ingame join changes!!!
4649 // everyone can go through the same mission loading path here!!!!
4650 nprintf(("Network","Loading mission..."));
4652 // notify everyone that I'm loading the mission
4653 Net_player->state = NETPLAYER_STATE_MISSION_LOADING;
4654 send_netplayer_update_packet();
4656 // do the load itself
4657 game_start_mission();
4659 // ingame joiners need to "untag" all player ships as could_be_players. The ingame joining
4660 // code will remark the correct player ships
4661 if ( Net_player->flags & NETINFO_FLAG_INGAME_JOIN ) {
4662 multi_untag_player_ships();
4665 Net_player->flags |= NETINFO_FLAG_MISSION_OK;
4666 Net_player->state = NETPLAYER_STATE_MISSION_LOADED;
4667 send_netplayer_update_packet();
4669 Multi_mission_loaded = 1;
4670 nprintf(("Network","Finished loading mission\n"));
4674 void send_jump_into_mission_packet(net_player *pl)
4676 ubyte data[MAX_PACKET_SIZE];
4677 int packet_size = 0;
4679 SDL_assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
4681 BUILD_HEADER(JUMP_INTO_GAME);
4683 // ingame joiners will get special data. We need to tell them about the state of the mission, like paused,
4684 // and possible other things.
4686 if ( pl->flags & NETINFO_FLAG_INGAME_JOIN ) {
4687 ADD_INT(Netgame.game_state);
4693 multi_io_send_to_all_reliable(data, packet_size);
4695 // send to a specific player
4697 multi_io_send_reliable(pl, data, packet_size);
4701 void process_jump_into_mission_packet(ubyte *data, header *hinfo)
4703 int offset = HEADER_LENGTH;
4708 // if I am ingame joining, there should be extra data. For now, this data is the netgame state.
4709 // the game could be paused, so ingame joiner needs to deal with it.
4710 if ( Net_player->flags & NETINFO_FLAG_INGAME_JOIN ) {
4712 Netgame.game_state = state;
4717 // handle any special processing for being in a weird substate
4718 multi_handle_state_special();
4720 // if I'm an ingame joiner, go to the ship select screen, or if I'm an observer, jump right in!
4721 if(Net_player->flags & NETINFO_FLAG_INGAME_JOIN){
4722 if(Net_player->flags & NETINFO_FLAG_OBSERVER){
4723 multi_ingame_observer_finish();
4725 gameseq_post_event(GS_EVENT_INGAME_PRE_JOIN);
4726 Net_player->state = NETPLAYER_STATE_INGAME_SHIP_SELECT;
4727 send_netplayer_update_packet();
4730 // start the mission!!
4731 if(!(Game_mode & GM_IN_MISSION) && !(Game_mode & GM_STANDALONE_SERVER)){
4732 Netgame.game_state = NETGAME_STATE_IN_MISSION;
4733 gameseq_post_event(GS_EVENT_ENTER_GAME);
4734 Net_player->state = NETPLAYER_STATE_IN_MISSION;
4735 send_netplayer_update_packet();
4739 extern int Player_multi_died_check;
4740 Player_multi_died_check = -1;
4742 // recalc all object pairs now
4743 extern void obj_reset_all_collisions();
4744 obj_reset_all_collisions();
4746 // display some cool text
4747 multi_common_add_text(XSTR("Received mission start\n",720),1);
4750 ml_string(NOX("Client received mission start from server - entering mission"));
4755 const char *repair_text[] = {
4757 "REPAIR_INFO_BEGIN",
4759 "REPAIR_INFO_UPDATE",
4760 "REPAIR_INFO_QUEUE",
4761 "REPAIR_INFO_ABORT",
4762 "REPAIR_INFO_BROKEN",
4763 "REPAIR_INFO_WARP_ADD",
4764 "REPAIR_INFO_WARP_REMOVE",
4765 "REPAIR_INFO_ONWAY",
4766 "REPAIR_INFO_KILLED",
4767 "REPAIR_INFO_COMPLETE",
4772 // the following two routines deal with updating and sending information regarding players
4773 // rearming and repairing during the game. The process function calls the routines to deal with
4774 // setting flags and other interesting things.
4775 void send_repair_info_packet(object *repaired_objp, object *repair_objp, int code )
4777 int packet_size = 0;
4778 ushort repaired_signature, repair_signature;
4779 ubyte data[MAX_PACKET_SIZE];
4782 // use the network signature of the destination object if there is one, -1 otherwise.
4783 // client will piece it all together
4784 repaired_signature = repaired_objp->net_signature;
4786 // the repair ship may be NULL here since it might have been destroyed
4787 repair_signature = 0;
4789 repair_signature = repair_objp->net_signature;
4792 BUILD_HEADER(CLIENT_REPAIR_INFO);
4795 ADD_USHORT( repaired_signature );
4796 ADD_USHORT( repair_signature );
4798 multi_io_send_to_all_reliable(data, packet_size);
4800 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));
4803 void process_repair_info_packet(ubyte *data, header *hinfo)
4805 int offset = HEADER_LENGTH;
4806 ushort repaired_signature, repair_signature;
4807 object *repaired_objp, *repair_objp;
4811 GET_USHORT( repaired_signature );
4812 GET_USHORT( repair_signature );
4815 repaired_objp = multi_get_network_object( repaired_signature );
4816 repair_objp = multi_get_network_object( repair_signature );
4818 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));
4820 if ( Net_player->flags & NETINFO_FLAG_WARPING_OUT ){
4824 if ( repaired_objp == NULL ) {
4825 Int3(); // Sandeep says this is bad bad bad. No ship to repair.
4829 // the hope is to simply call the routine in the ai code to set/unset flags
4830 // based on the code value and everything else should happen..I hope....
4831 if ( (code != REPAIR_INFO_WARP_ADD) && (code != REPAIR_INFO_WARP_REMOVE ) ) {
4833 ai_do_objects_repairing_stuff( repaired_objp, repair_objp, (int)code );
4835 // set the dock flags when repair begins. Prevents problem in lagging docking
4836 // packet. Also set any other flags/modes which need to be set to prevent Asserts.
4838 if ( (code == REPAIR_INFO_BEGIN) && (repair_objp != NULL) ) {
4839 ai_do_objects_docked_stuff( repaired_objp, repair_objp );
4840 Ai_info[Ships[repair_objp->instance].ai_index].mode = AIM_DOCK;
4843 // if the repair is done (either by abort, or ending), mark the repair ship's goal
4845 if ( ((code == REPAIR_INFO_ABORT) || (code == REPAIR_INFO_END)) && repair_objp ){
4846 ai_mission_goal_complete( &Ai_info[Ships[repair_objp->instance].ai_index] );
4849 if ( code == REPAIR_INFO_WARP_ADD ){
4850 mission_warp_in_support_ship( repaired_objp );
4852 mission_remove_scheduled_repair( repaired_objp );
4857 // sends information updating clients on certain AI information that clients will
4858 // need to know about to keep HUD information up to date. objp is the object that we
4859 // are updating, and what is the type of stuff that we are updating.
4860 void send_ai_info_update_packet( object *objp, char what )
4863 ushort other_signature;
4864 ubyte data[MAX_PACKET_SIZE];
4866 ubyte dock_index, dockee_index;
4868 // SDL_assert( objp->type == OBJ_SHIP );
4869 if(objp->type != OBJ_SHIP){
4872 aip = &Ai_info[Ships[objp->instance].ai_index];
4875 if ( Ships[objp->instance].flags & (SF_DEPARTING | SF_DYING) )
4878 BUILD_HEADER( AI_INFO_UPDATE );
4879 ADD_USHORT( objp->net_signature );
4882 // depending on the "what" value, we will send different information
4886 case AI_UPDATE_DOCK:
4887 // for docking ships, add the signature of the ship that we are docking with.
4888 SDL_assert( aip->dock_objnum != -1 );
4889 other_signature = Objects[aip->dock_objnum].net_signature;
4890 dock_index = (ubyte)(aip->dock_index);
4891 dockee_index = (ubyte)(aip->dockee_index);
4892 ADD_USHORT( other_signature );
4893 ADD_DATA(dock_index);
4894 ADD_DATA(dockee_index);
4897 case AI_UPDATE_UNDOCK:
4898 // for undocking ships, check the dock_objnum since we might or might not have it
4899 // depending on whether or not a ship was destroyed while we were docked.
4900 other_signature = 0;
4901 if ( aip->dock_objnum != -1 )
4902 other_signature = Objects[aip->dock_objnum].net_signature;
4903 ADD_USHORT( other_signature );
4907 case AI_UPDATE_ORDERS: {
4910 // for orders, we only need to send a little bit of information here. Be sure that the
4911 // first order for this ship is active
4912 SDL_assert( (aip->active_goal != AI_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) );
4913 ADD_INT( aip->goals[0].ai_mode );
4914 ADD_INT( aip->goals[0].ai_submode );
4916 if ( aip->goals[0].ship_name != NULL )
4917 shipnum = ship_name_lookup( aip->goals[0].ship_name );
4919 // the ship_name member of the goals structure may or may not contain a real shipname. If we don't
4920 // have a valid shipnum, then don't sweat it since it may not really be a ship.
4921 if ( shipnum != -1 ) {
4922 SDL_assert( Ships[shipnum].objnum != -1 );
4923 other_signature = Objects[Ships[shipnum].objnum].net_signature;
4925 other_signature = 0;
4927 ADD_USHORT( other_signature );
4929 // for docking, add the dock and dockee index
4930 if ( aip->goals[0].ai_mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
4931 SDL_assert( (aip->goals[0].docker.index >= 0) && (aip->goals[0].docker.index < UCHAR_MAX) );
4932 SDL_assert( (aip->goals[0].dockee.index >= 0) && (aip->goals[0].dockee.index < UCHAR_MAX) );
4933 dock_index = (ubyte)aip->goals[0].docker.index;
4934 dockee_index = (ubyte)aip->goals[0].dockee.index;
4935 ADD_DATA( dock_index );
4936 ADD_DATA( dockee_index );
4945 multi_rate_add(1, "aiu", packet_size);
4946 multi_io_send_to_all_reliable(data, packet_size);
4949 // process an ai_info update packet. Docking/undocking, ai orders, etc. are taken care of here. This
4950 // information is mainly used to keep the clients HUD up to date with the appropriate information.
4951 void process_ai_info_update_packet( ubyte *data, header *hinfo)
4953 int offset = HEADER_LENGTH;
4955 ushort net_signature, other_net_signature;
4956 object *objp, *other_objp;
4959 ubyte dock_index = 0, dockee_index = 0;
4961 GET_USHORT( net_signature ); // signature of the object that we are dealing with.
4962 GET_DATA( code ); // code of what we are doing.
4963 objp = multi_get_network_object( net_signature );
4965 nprintf(("Network", "Couldn't find object for ai update\n"));
4968 case AI_UPDATE_DOCK:
4969 GET_USHORT( other_net_signature );
4970 GET_DATA( dock_index );
4971 GET_DATA( dockee_index );
4972 other_objp = multi_get_network_object( other_net_signature );
4974 nprintf(("Network", "Couldn't find other object for ai update on dock\n"));
4976 // if we don't have an object to work with, break out of loop
4977 if ( !objp || !other_objp || (objp->type != OBJ_SHIP) || (other_objp->type != OBJ_SHIP)){
4981 SDL_assert( other_objp->type == OBJ_SHIP );
4982 Ai_info[Ships[objp->instance].ai_index].dock_index = dock_index;
4983 Ai_info[Ships[objp->instance].ai_index].dockee_index = dockee_index;
4985 Ai_info[Ships[other_objp->instance].ai_index].dock_index = dockee_index;
4986 Ai_info[Ships[other_objp->instance].ai_index].dockee_index = dock_index;
4988 ai_do_objects_docked_stuff( objp, other_objp );
4991 case AI_UPDATE_UNDOCK:
4992 GET_USHORT( other_net_signature );
4993 other_objp = multi_get_network_object( other_net_signature );
4995 // if we don't have an object to work with, break out of loop
4999 ai_do_objects_undocked_stuff( objp, other_objp );
5002 case AI_UPDATE_ORDERS:
5005 GET_USHORT( other_net_signature );
5006 if ( mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
5007 GET_DATA(dock_index);
5008 GET_DATA(dockee_index);
5011 // be sure that we have a ship object!!!
5012 if ( !objp || (objp->type != OBJ_SHIP) )
5015 // set up the information in the first goal element of the object in question
5016 aip = &Ai_info[Ships[objp->instance].ai_index];
5017 aip->active_goal = 0;
5018 aip->goals[0].ai_mode = mode;
5019 aip->goals[0].ai_submode = submode;
5021 // for docking, add the dock and dockee index
5022 if ( mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
5023 aip->dock_index = dock_index;
5024 aip->dockee_index = dockee_index;
5027 // get a shipname if we can.
5028 other_objp = multi_get_network_object( other_net_signature );
5029 if ( other_objp && (other_objp->type == OBJ_SHIP) ) {
5030 // get a pointer to the shipname in question. Use the ship_name value in the
5031 // ship. We are only using this for HUD display, so I think that using this
5032 // method will be fine.
5033 aip->goals[0].ship_name = Ships[other_objp->instance].ship_name;
5035 // special case for destroy subsystem -- get the ai_info pointer to our target ship
5036 // so that we can properly set up what subsystem this ship is attacking.
5037 if ( (mode == AI_GOAL_DESTROY_SUBSYSTEM ) && (submode >= 0) )
5038 aip->targeted_subsys = ship_get_indexed_subsys( &Ships[other_objp->instance], submode);
5040 // if docking -- set the dock index and dockee index of this other ship
5041 if ( mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
5042 Ai_info[Ships[other_objp->instance].ai_index].dock_index = dockee_index;
5043 Ai_info[Ships[other_objp->instance].ai_index].dockee_index = dock_index;
5050 Int3(); // this Int3() should be temporary
5051 nprintf(("Network", "Invalid code for ai update: %d\n", code));
5057 // tell the standalone to move into the MISSION_SYNC_STATE
5058 void send_mission_sync_packet(int mode,int start_campaign)
5060 ubyte data[MAX_PACKET_SIZE],is_campaign;
5061 int packet_size = 0;
5063 SDL_assert((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER));
5065 // build the header and add the sync mode (pre or post briefing)
5066 BUILD_HEADER(MISSION_SYNC_DATA);
5069 // if this is a campaign game
5070 if(mode == MULTI_SYNC_PRE_BRIEFING){
5071 if(Game_mode & GM_CAMPAIGN_MODE){
5072 // add a byte indicating campaign mode
5074 ADD_DATA(is_campaign);
5076 // add a byte indicating if we should be starting a campaign or continuing it
5077 is_campaign = (ubyte)start_campaign;
5078 ADD_DATA(is_campaign);
5080 // add the campaign filename
5081 ADD_STRING(Netgame.campaign_name);
5083 // otherwise if this is a single mission
5085 // add a byte indicating single mission mode
5087 ADD_DATA(is_campaign);
5089 // add the mission filename
5090 ADD_STRING(Game_current_mission_filename);
5093 multi_io_send_reliable(Net_player, data, packet_size);
5096 // move into the MISSION_SYNC state when this is received
5097 // this packet is sent only from a game host to a standalone
5098 void process_mission_sync_packet(ubyte *data, header *hinfo)
5101 ubyte campaign_flag;
5102 int offset = HEADER_LENGTH;
5104 SDL_assert(Game_mode & GM_STANDALONE_SERVER);
5106 // if this is a team vs team situation, lock the players send a final team update
5107 if(Netgame.type_flags & NG_TYPE_TEAM){
5108 multi_team_host_lock_all();
5109 multi_team_send_update();
5112 // get the sync mode (pre or post briefing)
5115 if(mode == MULTI_SYNC_PRE_BRIEFING){
5116 // get the flag indicating if this is a single mission or a campaign mode
5117 GET_DATA(campaign_flag);
5119 // get the flag indicating whether we should be starting a new campaign
5120 GET_DATA(campaign_flag);
5122 // get the campaign filename
5123 GET_STRING(Netgame.campaign_name);
5125 // either start a new campaign or continue on to the next mission in the current campaign
5127 multi_campaign_start(Netgame.campaign_name);
5129 multi_campaign_next_mission();
5132 // make sure we remove the campaign mode flag
5133 Game_mode &= ~(GM_CAMPAIGN_MODE);
5135 // get the single mission filename
5136 GET_STRING(Game_current_mission_filename);
5137 strcpy(Netgame.mission_name,Game_current_mission_filename);
5142 // set the correct mode and m ove into the state
5143 Multi_sync_mode = mode;
5144 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
5147 // tell a player to merge his mission stats into his alltime stats
5148 void send_store_stats_packet(int accept)
5151 int packet_size = 0;
5153 BUILD_HEADER(STORE_MISSION_STATS);
5155 // add whether we're accepting or tossing
5156 val = (ubyte)accept;
5159 // if I'm the server, send to everyone, else send to the standalone to be rebroadcasted
5160 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5161 multi_io_send_to_all_reliable(data, packet_size);
5163 multi_io_send_reliable(Net_player, data, packet_size);
5167 void process_store_stats_packet(ubyte *data, header *hinfo)
5169 int offset = HEADER_LENGTH;
5175 // if I'm the standalone, rebroadcast. Otherwise, if I'm a client, merge my mission stats with my alltime stats
5176 if(Game_mode & GM_STANDALONE_SERVER){
5177 // rebroadcast the packet to all others in the game
5178 nprintf(("Network","Standalone received store stats packet - rebroadcasting..\n"));
5179 multi_io_send_to_all_reliable(data, offset);
5182 // all players should mark the stats as being accepted in the debriefing
5183 multi_debrief_stats_accept();
5185 // all players should mark the stats as being "tossed" in the debriefing
5186 multi_debrief_stats_toss();
5191 void send_debris_update_packet(object *objp,int code)
5193 ubyte data[MAX_PACKET_SIZE];
5195 int packet_size = 0;
5197 BUILD_HEADER(DEBRIS_UPDATE);
5198 ADD_USHORT(objp->net_signature);
5202 // add any extra relevant data
5204 case DEBRIS_UPDATE_UPDATE:
5205 // ADD_DATA(objp->pos); // add position
5206 add_vector_data(data, &packet_size, objp->pos);
5207 ADD_ORIENT(objp->orient); // add orientation
5208 // ADD_DATA(objp->phys_info.vel); // add velocity
5209 add_vector_data(data, &packet_size, objp->phys_info.vel);
5210 // ADD_DATA(objp->phys_info.rotvel); // add rotational velocity
5211 add_vector_data(data, &packet_size, objp->phys_info.rotvel);
5214 multi_io_send_to_all(data, packet_size);
5217 void process_debris_update_packet(ubyte *data, header *hinfo)
5221 object bogus_object;
5223 int offset = HEADER_LENGTH;
5225 GET_USHORT(net_sig);
5229 objp = multi_get_network_object(net_sig);
5231 objp = &bogus_object;
5235 // update the object
5236 case DEBRIS_UPDATE_UPDATE:
5237 //GET_DATA(objp->pos);
5238 get_vector_data( data, &offset, objp->pos );
5240 GET_ORIENT(objp->orient);
5241 GET_DATA(objp->phys_info.vel);
5242 GET_DATA(objp->phys_info.rotvel);
5244 // simply remove it (no explosion)
5245 case DEBRIS_UPDATE_REMOVE:
5246 if(objp != &bogus_object){
5247 SDL_assert(objp->type == OBJ_DEBRIS);
5248 obj_delete(OBJ_INDEX(objp));
5252 case DEBRIS_UPDATE_NUKE:
5253 if(objp != &bogus_object)
5254 debris_hit(objp,NULL,&objp->pos,1000000.0f);
5262 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)
5264 ubyte data[MAX_PACKET_SIZE];
5266 int packet_size = 0;
5268 BUILD_HEADER(WSS_REQUEST_PACKET);
5270 // add the request information
5271 ADD_SHORT(player_id);
5273 ADD_INT(from_index);
5276 ADD_INT(wl_ship_slot); // only used in weapons loadout
5277 ADD_INT(ship_class);
5280 // a standard request
5282 multi_io_send_reliable(Net_player, data, packet_size);
5284 // being routed through the standalone to the host of the game
5286 SDL_assert(Game_mode & GM_STANDALONE_SERVER);
5287 multi_io_send_reliable(p, data, packet_size);
5291 void process_wss_request_packet(ubyte *data, header *hinfo)
5293 int offset = HEADER_LENGTH;
5294 int from_slot,from_index;
5295 int to_slot,to_index;
5297 int wl_ship_slot,ship_class;
5301 // determine who this request is from
5302 GET_SHORT(player_id);
5303 player_num = find_player_id(player_id);
5305 // read in the request data
5307 GET_INT(from_index);
5310 GET_INT(wl_ship_slot); // only used in weapons loadout
5311 GET_INT(ship_class); // only used in multi team select
5315 SDL_assert(player_num != -1);
5316 if(player_num == -1){
5320 // if we're the standalone, we have to route this packet to the host of the game
5321 if(Game_mode & GM_STANDALONE_SERVER){
5322 send_wss_request_packet(player_id, from_slot, from_index, to_slot, to_index, wl_ship_slot, ship_class, mode, Netgame.host);
5324 // otherwise we're the host and should process the request
5327 case WSS_WEAPON_SELECT :
5328 wl_drop(from_slot,from_index,to_slot,to_index,wl_ship_slot,player_num);
5330 case WSS_SHIP_SELECT :
5331 multi_ts_drop(from_slot,from_index,to_slot,to_index,ship_class,player_num);
5339 void send_wss_update_packet(int team_num,ubyte *wss_data,int size)
5341 ubyte data[MAX_PACKET_SIZE],team;
5342 int packet_size = 0;
5344 SDL_assert(size <= (MAX_PACKET_SIZE - 10));
5346 BUILD_HEADER(WSS_UPDATE_PACKET);
5348 // add the team/pool # this is for
5349 team = (ubyte)team_num;
5352 // add the data block size
5355 // add the data itself
5356 memcpy(data + packet_size,wss_data,size);
5357 packet_size += size;
5359 // if we're also the master of the game (not on a standalone)
5360 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5361 multi_io_send_to_all_reliable(data, packet_size);
5363 // if we're only the host on the standalone, then send the packet to the standalone to be routed
5365 multi_io_send_reliable(Net_player, data, packet_size);
5369 void process_wss_update_packet(ubyte *data, header *hinfo)
5372 int size,player_index,idx;
5373 int offset = HEADER_LENGTH;
5375 // get the team/pool #
5378 // get the data size
5381 // if we're the standalone, then we should be routing this data to all the other clients
5382 if(Game_mode & GM_STANDALONE_SERVER){
5387 // determine where this came from
5388 player_index = find_player_id(hinfo->id);
5389 SDL_assert(player_index != -1);
5390 if(player_index < 0){
5394 // route the packet (don't resend it to the host)
5395 for(idx=0;idx<MAX_PLAYERS;idx++){
5396 if(MULTI_CONNECTED(Net_players[idx]) && (&Net_players[idx] != Net_player) && (&Net_players[idx] != &Net_players[player_index]) ){
5397 multi_io_send_reliable(&Net_players[idx], data, offset);
5401 // set the proper pool pointers
5402 ss_set_team_pointers((int)team);
5404 // read in the block of data, and apply it to the weapons/ship pools
5405 offset += restore_wss_data(data + offset);
5408 // set the pool pointers back to my own team
5409 ss_set_team_pointers(Net_player->p_info.team);
5411 // sync the interface if this was for my own team
5412 if((int)team == Net_player->p_info.team){
5413 multi_ts_sync_interface();
5420 // function to send firing information from the client to the server once they reach
5421 // the final sync screen.
5422 void send_firing_info_packet()
5424 ubyte data[MAX_PACKET_SIZE];
5426 ubyte plinked, sdual;
5428 SDL_assert( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
5430 BUILD_HEADER(FIRING_INFO);
5431 plinked = (ubyte)((Player_ship->flags & SF_PRIMARY_LINKED)?1:0);
5432 sdual = (ubyte)((Player_ship->flags & SF_SECONDARY_DUAL_FIRE)?1:0);
5433 ADD_DATA( plinked );
5436 multi_io_send_reliable(Net_player, data, packet_size);
5439 void process_firing_info_packet( ubyte *data, header *hinfo )
5441 int offset, player_num;
5442 ubyte plinked, sdual;
5445 // only the master of the game should be dealing with these packets
5446 SDL_assert( Net_player->flags & NETINFO_FLAG_AM_MASTER );
5448 offset = HEADER_LENGTH;
5449 GET_DATA( plinked );
5453 player_num = find_player_id(hinfo->id);
5455 nprintf(("Network","Received firing info packet from unknown player, ignoring\n"));
5459 // get the ship pointer for this player and set the flags accordingly.
5460 shipp = &(Ships[Objects[Net_players[player_num].player->objnum].instance]);
5462 shipp->flags |= SF_PRIMARY_LINKED;
5464 shipp->flags &= ~SF_PRIMARY_LINKED;
5467 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
5469 shipp->flags &= ~SF_SECONDARY_DUAL_FIRE;
5472 // packet to deal with changing status of mission goals. used to be sent every so often from server
5473 // to clients, but with addition of reliable sockets, send when complete, invalid, etc.
5474 // goal_num is the index into mission_goals. new_status means failed, success, etc. -1 if not used.
5475 // valid means goal is changing to invalid(0) or valid(1). only applies if new_status == -1
5476 void send_mission_goal_info_packet( int goal_num, int new_status, int valid )
5478 ubyte data[MAX_PACKET_SIZE];
5481 BUILD_HEADER(MISSION_GOAL_INFO);
5484 ADD_INT(new_status);
5487 multi_io_send_to_all_reliable(data, packet_size);
5490 void process_mission_goal_info_packet( ubyte *data, header *hinfo )
5492 int offset, goal_num, new_status, valid;
5494 offset = HEADER_LENGTH;
5496 GET_INT(new_status);
5500 // if new_status != -1, then this is a change in goal status (i.e. goal failed, or is successful)
5501 if ( new_status != -1 ){
5502 mission_goal_status_change( goal_num, new_status );
5504 mission_goal_validation_change( goal_num, valid );
5508 void send_player_settings_packet(net_player *p)
5510 ubyte data[MAX_PACKET_SIZE];
5513 int packet_size = 0;
5516 BUILD_HEADER(PLAYER_SETTINGS);
5518 // add all the data for all the players
5520 for(idx=0;idx<MAX_PLAYERS;idx++){
5521 if(MULTI_CONNECTED(Net_players[idx])){
5523 ADD_SHORT(Net_players[idx].player_id);
5525 // break the p_info structure by member, so we don't overwrite any absolute pointers
5526 // ADD_DATA(Net_players[idx].p_info);
5527 ADD_INT(Net_players[idx].p_info.team);
5528 ADD_INT(Net_players[idx].p_info.ship_index);
5529 ADD_INT(Net_players[idx].p_info.ship_class);
5532 // add the stop byte
5536 // either broadcast the data or send to a specific player
5538 multi_io_send_to_all_reliable(data, packet_size);
5540 multi_io_send_reliable(p, data, packet_size);
5544 void process_player_settings_packet(ubyte *data, header *hinfo)
5546 int offset,player_num;
5547 net_player_info bogus,*ptr;
5551 offset = HEADER_LENGTH;
5553 // read in the data for all the players
5555 while(stop != 0xff){
5556 // lookup the player
5557 GET_SHORT(player_id);
5558 player_num = find_player_id(player_id);
5560 // make sure this is a valid player
5561 if(player_num == -1){
5564 ptr = &Net_players[player_num].p_info;
5568 GET_INT(ptr->ship_index);
5569 GET_INT(ptr->ship_class);
5576 // update the server with my new state
5577 // MWA -- 3/31/98 -- check for in mission instead of state.
5578 //if ( Netgame.game_state == NETGAME_STATE_MISSION_SYNC) {
5579 if( !(Game_mode & GM_IN_MISSION) ) {
5580 Net_player->state = NETPLAYER_STATE_SETTINGS_ACK;
5581 send_netplayer_update_packet();
5585 // display some cool text
5586 multi_common_add_text(XSTR("Received player settings packet\n",721),1);
5589 void send_deny_packet(net_addr *addr, int code)
5592 int packet_size = 0;
5594 // build the header and add the rejection code
5600 psnet_send(addr, data, packet_size);
5603 void process_deny_packet(ubyte *data, header *hinfo)
5607 // get the denial code
5608 offset = HEADER_LENGTH;
5612 // if there is already a dialog active, do nothing - who cares at this point.
5617 // display the appropriate dialog
5619 case JOIN_DENY_JR_STATE :
5620 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));
5622 case JOIN_DENY_JR_TRACKER_INVAL :
5623 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));
5625 case JOIN_DENY_JR_PASSWD :
5626 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because this is a password protected game",724));
5628 case JOIN_DENY_JR_CLOSED :
5629 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));
5631 case JOIN_DENY_JR_TEMP_CLOSED :
5632 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));
5634 case JOIN_DENY_JR_RANK_HIGH :
5635 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));
5637 case JOIN_DENY_JR_RANK_LOW :
5638 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));
5640 case JOIN_DENY_JR_DUP :
5641 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because there is an identical player already in the game",729));
5643 case JOIN_DENY_JR_FULL :
5644 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because the game is full",730));
5646 case JOIN_DENY_JR_BANNED :
5647 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because you are banned from this server",731));
5649 case JOIN_DENY_JR_NOOBS :
5650 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because this game does not allow observers",732));
5652 case JOIN_DENY_JR_INGAME_JOIN :
5653 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));
5655 case JOIN_DENY_JR_BAD_VERSION :
5656 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));
5658 case JOIN_DENY_JR_TYPE :
5659 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You cannot join a game in progress unless it is a dogfight mission.",1433));
5663 // call this so that the join request timestamp automatically expires when we hear back from the server
5664 multi_join_reset_join_stamp();
5667 // this packet will consist of
5668 // 1.) netplayer ship classes (85 bytes max)
5669 // 2.) ship weapon state data (241 bytes max)
5670 // 3.) player settings et. al. (133 bytes max)
5671 // TOTAL 459 NOTE : keep this in mind when/if adding new data to this packet
5672 void send_post_sync_data_packet(net_player *p, int std_request)
5674 ubyte data[MAX_PACKET_SIZE], val;
5679 ushort sval, ship_ets;
5680 int idx, player_index;
5681 int packet_size = 0;
5685 BUILD_HEADER(POST_SYNC_DATA);
5687 // some header information for standalone packet routing purposes
5688 val = (ubyte)std_request;
5691 // the standalone has two situations
5692 // 1.) sending a request to the host to distribute this block of data
5693 // 2.) having recevied this block of data from the host, it redistributes it
5694 if((Game_mode & GM_STANDALONE_SERVER) && std_request && (Netgame.host != NULL)){
5695 // case 1, send the request
5696 multi_io_send_reliable(Netgame.host, data, packet_size);
5699 // case 2 for the standalone is below (as normal)
5701 // otherwise build the data now
5703 // add all deleted ships
5704 val = (ubyte)Multi_ts_num_deleted;
5706 for(idx=0;idx<Multi_ts_num_deleted;idx++){
5707 sval = (ushort)Objects[Multi_ts_deleted_objnums[idx]].net_signature;
5713 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5714 shipp = &Ships[Objects[so->objnum].instance];
5716 // don't process non player wing ships
5717 if ( !(shipp->flags & SF_FROM_PLAYER_WING ) )
5723 // # of ships - used multiple times in the packet
5724 val = (ubyte)ship_count;
5727 // add ship class information (85 bytes max)
5728 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5729 shipp = &Ships[Objects[so->objnum].instance];
5731 // don't process non player wing ships
5732 if ( !(shipp->flags & SF_FROM_PLAYER_WING ) )
5735 // add the net signature of the object for look up
5736 ADD_USHORT( Objects[so->objnum].net_signature );
5738 // add the ship info index
5739 val = (ubyte)(shipp->ship_info_index);
5742 // add the ships's team select index
5743 val = (ubyte)shipp->ts_index;
5747 // add weapon state information for all starting ships (241 bytes max)
5748 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5749 shipp = &Ships[Objects[so->objnum].instance];
5751 // don't process non player wing ships
5752 if ( !(shipp->flags & SF_FROM_PLAYER_WING ) )
5755 // if this is a ship owned by a player, we should mark down his weapons bank/link settings now if we're the server
5757 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5758 player_index = multi_find_player_by_net_signature(Objects[so->objnum].net_signature);
5759 if(player_index == -1){
5762 pl = &Net_players[player_index];
5766 // add the net signature and other weapon information
5767 ADD_USHORT( Objects[so->objnum].net_signature );
5769 // add number of primary and secondary banks
5770 bval = (char)(shipp->weapons.num_primary_banks);
5772 bval = (char)(shipp->weapons.num_secondary_banks);
5775 // add weapon bank status
5776 bval = (char)(shipp->weapons.current_primary_bank);
5778 pl->s_info.cur_primary_bank = bval;
5780 // SDL_assert(bval != -1);
5783 bval = (char)(shipp->weapons.current_secondary_bank);
5785 pl->s_info.cur_secondary_bank = bval;
5787 // SDL_assert(bval != -1);
5790 // primary weapon info
5791 bval = (char)(shipp->weapons.primary_bank_weapons[0]);
5793 bval = (char)(shipp->weapons.primary_bank_weapons[1]);
5796 // secondary weapon info
5797 bval = (char)(shipp->weapons.secondary_bank_weapons[0]);
5799 val_short = (short)(shipp->weapons.secondary_bank_ammo[0]);
5800 ADD_SHORT(val_short);
5801 bval = (char)(shipp->weapons.secondary_bank_weapons[1]);
5803 val_short = (short)(shipp->weapons.secondary_bank_ammo[1]);
5804 ADD_SHORT(val_short);
5805 bval = (char)(shipp->weapons.secondary_bank_weapons[2]);
5807 val_short = (short)(shipp->weapons.secondary_bank_ammo[2]);
5808 ADD_SHORT(val_short);
5810 // send primary and secondary weapon link status
5812 if(shipp->flags & SF_PRIMARY_LINKED){
5814 pl->s_info.cur_link_status |= (1<<0);
5818 if(shipp->flags & SF_SECONDARY_DUAL_FIRE){
5820 pl->s_info.cur_link_status |= (1<<1);
5824 // if this is a player ship add (1<<2)
5825 if(Objects[shipp->objnum].flags & OF_PLAYER_SHIP){
5830 // add a ship ets value
5833 ship_ets |= ((ushort)shipp->shield_recharge_index << 8);
5835 ship_ets |= ((ushort)shipp->weapon_recharge_index << 4);
5837 ship_ets |= ((ushort)shipp->engine_recharge_index);
5838 ADD_USHORT(ship_ets);
5842 // 2 cases, if I'm the host on a standalone, I should be sending this to the standalone only
5843 // or if I'm the server as well as the host, I should be sending this to all players
5844 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
5845 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5848 multi_io_send_to_all_reliable(data, packet_size);
5850 // send to a specific player
5852 multi_io_send_reliable(p, data, packet_size);
5855 multi_io_send_reliable(Net_player, data, packet_size);
5862 multi_io_send_to_all_reliable(data, packet_size);
5864 // send to a specific player
5866 multi_io_send_reliable(p, data, packet_size);
5871 void process_post_sync_data_packet(ubyte *data, header *hinfo)
5873 ubyte val, sinfo_index, ts_index;
5875 ushort net_sig, ship_ets, sval;
5879 int offset = HEADER_LENGTH;
5883 // packet routing information
5886 // 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
5887 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) && val){
5890 // at this point we want to delete all necessary ships, change all necessary ship classes, and set all weapons up
5891 multi_ts_create_wings();
5893 // send to the standalone through my socket
5894 send_post_sync_data_packet(Net_player);
5900 // add all deleted ships
5902 Multi_ts_num_deleted = (int)val;
5903 for(idx=0;idx<Multi_ts_num_deleted;idx++){
5904 // get the ship's objnum
5907 objp = multi_get_network_object(sval);
5909 // delete the ship appropriately
5910 // mark the object as having been deleted
5911 Multi_ts_deleted_objnums[idx] = OBJ_INDEX(objp);
5914 ship_add_exited_ship(&Ships[objp->instance], SEF_PLAYER_DELETED);
5915 obj_delete(Multi_ts_deleted_objnums[idx]);
5916 ship_wing_cleanup(objp->instance,&Wings[Ships[objp->instance].wingnum]);
5918 Multi_ts_num_deleted--;
5919 nprintf(("Network","Couldn't find object by net signature for ship delete in post sync data packet\n"));
5927 // process ship class information
5928 for(idx=0; idx<ship_count; idx++){
5929 // get the object's net signature
5930 GET_USHORT(net_sig);
5931 GET_DATA(sinfo_index);
5934 // attempt to get the object
5935 objp = multi_get_network_object(net_sig);
5937 // make sure we found a ship
5938 SDL_assert((objp != NULL) && (objp->type == OBJ_SHIP));
5940 // set the ship to be the right class
5941 change_ship_type(objp->instance,(int)sinfo_index);
5943 // set the ship's team select index
5944 Ships[objp->instance].ts_index = (int)ts_index;
5947 // process ship weapon state info
5948 for(idx=0; idx<ship_count; idx++){
5949 // get the object's net signature
5950 GET_USHORT(net_sig);
5952 // attempt to get the object
5953 objp = multi_get_network_object(net_sig);
5955 // make sure we found a ship
5956 SDL_assert((objp != NULL) && (objp->type == OBJ_SHIP));
5958 // get a pointer to the ship
5959 shipp = &Ships[objp->instance];
5961 // get number of primary and secondary banks;
5963 SDL_assert( b != -1 );
5964 shipp->weapons.num_primary_banks = (int)b;
5967 SDL_assert( b != -1 );
5968 shipp->weapons.num_secondary_banks = (int)b;
5970 // get bank selection info
5975 shipp->weapons.current_primary_bank = (int)b;
5981 shipp->weapons.current_secondary_bank = (int)b;
5983 // primary weapon info
5985 shipp->weapons.primary_bank_weapons[0] = (int)b;
5988 shipp->weapons.primary_bank_weapons[1] = (int)b;
5990 // secondary weapon info
5992 shipp->weapons.secondary_bank_weapons[0] = (int)b;
5993 GET_SHORT(val_short);
5994 shipp->weapons.secondary_bank_ammo[0] = (int)val_short;
5997 shipp->weapons.secondary_bank_weapons[1] = (int)b;
5998 GET_SHORT(val_short);
5999 shipp->weapons.secondary_bank_ammo[1] = (int)val_short;
6002 shipp->weapons.secondary_bank_weapons[2] = (int)b;
6003 GET_SHORT(val_short);
6004 shipp->weapons.secondary_bank_ammo[2] = (int)val_short;
6011 shipp->flags |= SF_PRIMARY_LINKED;
6014 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
6016 Objects[shipp->objnum].flags &= ~(OF_PLAYER_SHIP);
6017 Objects[shipp->objnum].flags &= ~(OF_COULD_BE_PLAYER);
6019 Objects[shipp->objnum].flags |= OF_PLAYER_SHIP;
6021 obj_set_flags( &Objects[shipp->objnum], Objects[shipp->objnum].flags | OF_COULD_BE_PLAYER );
6025 GET_USHORT(ship_ets);
6027 shipp->shield_recharge_index = ((ship_ets & 0x0f00) >> 8);
6029 shipp->weapon_recharge_index = ((ship_ets & 0x00f0) >> 4);
6031 shipp->engine_recharge_index = (ship_ets & 0x000f);
6036 Net_player->state = NETPLAYER_STATE_POST_DATA_ACK;
6037 send_netplayer_update_packet();
6039 // the standalone server will receive this packet from the host of the game, to be applied locally and
6040 // also to be rebroadcast.
6041 if(Game_mode & GM_STANDALONE_SERVER){
6042 // update player ets settings
6043 for(idx=0;idx<MAX_PLAYERS;idx++){
6044 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player->objnum != -1)){
6045 multi_server_update_player_weapons(&Net_players[idx],&Ships[Objects[Net_players[idx].player->objnum].instance]);
6049 send_post_sync_data_packet(NULL,0);
6053 void send_wss_slots_data_packet(int team_num,int final,net_player *p,int std_request)
6055 ubyte data[MAX_PACKET_SIZE],val;
6058 int packet_size = 0;
6061 BUILD_HEADER(WSS_SLOTS_DATA);
6063 // some header information for standalone packet routing purposes
6064 val = (ubyte)std_request;
6068 val = (ubyte)team_num;
6071 // add whether this is the final packet or not
6075 // the standalone has two situations
6076 // 1.) sending a request to the host to distribute this block of data
6077 // 2.) having recevied this block of data from the host, it redistributes it
6078 if((Game_mode & GM_STANDALONE_SERVER) && std_request){
6079 // case 1, send the request
6080 multi_io_send_reliable(Netgame.host, data, packet_size);
6083 // case 2 for the standalone is below (as normal)
6085 // add all the slots
6086 for(idx=0;idx<MULTI_TS_NUM_SHIP_SLOTS;idx++){
6087 // add the ship class
6088 val = (ubyte)Wss_slots_teams[team_num][idx].ship_class;
6092 for(i = 0;i<MAX_WL_WEAPONS;i++){
6093 val = (ubyte)Wss_slots_teams[team_num][idx].wep[i];
6097 // add the weapon counts
6098 for(i = 0;i<MAX_WL_WEAPONS;i++){
6099 val_short = (short)Wss_slots_teams[team_num][idx].wep_count[i];
6100 ADD_SHORT(val_short);
6104 // 2 cases, if I'm the host on a standalone, I should be sending this to the standalone only
6105 // or if I'm the server as well as the host, I should be sending this to all players
6106 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
6107 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
6110 multi_io_send_to_all_reliable(data, packet_size);
6112 // send to a specific player
6114 multi_io_send_reliable(p, data, packet_size);
6117 multi_io_send_reliable(Net_player, data, packet_size);
6124 multi_io_send_to_all_reliable(data, packet_size);
6126 // send to a specific player
6128 multi_io_send_reliable(p, data, packet_size);
6133 void process_wss_slots_data_packet(ubyte *data, header *hinfo)
6135 ubyte val,team_num,final;
6137 int offset = HEADER_LENGTH;
6140 // packet routing information
6146 // get whether this is the final packet or not
6149 // 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
6150 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) && val){
6153 // send to the standalone through my socket
6154 send_wss_slots_data_packet((int)team_num,(int)final,Net_player);
6158 // read in all the slot data
6159 for(idx=0;idx<MULTI_TS_NUM_SHIP_SLOTS;idx++){
6160 memset(&Wss_slots_teams[team_num][idx],0,sizeof(wss_unit));
6162 // get the ship class
6164 Wss_slots_teams[team_num][idx].ship_class = (int)val;
6167 for(i = 0;i<MAX_WL_WEAPONS;i++){
6169 Wss_slots_teams[team_num][idx].wep[i] = (int)val;
6171 // check for signed/unsigned problems
6172 if(Wss_slots_teams[team_num][idx].wep[i] == 255){
6173 Wss_slots_teams[team_num][idx].wep[i] = -1;
6177 // get the weapon counts
6178 for(i = 0;i<MAX_WL_WEAPONS;i++){
6179 GET_SHORT(val_short);
6180 Wss_slots_teams[team_num][idx].wep_count[i] = (int)val_short;
6185 // update my netplayer state if this is the final packet
6187 Net_player->state = NETPLAYER_STATE_WSS_ACK;
6188 send_netplayer_update_packet();
6191 // the standalone server will receive this packet from the host of the game, to be applied locally and
6192 // also to be rebroadcast.
6193 if(Game_mode & GM_STANDALONE_SERVER){
6194 send_wss_slots_data_packet((int)team_num,(int)final,NULL,0);
6196 // add some mission sync text
6197 multi_common_add_text(XSTR("Weapon slots packet\n",735),1);
6201 #define OBJ_VISIBILITY_DOT 0.6f
6203 // send and receive packets for shield explosion information
6204 void send_shield_explosion_packet( int objnum, int tri_num, vector hit_pos )
6207 ubyte data[MAX_PACKET_SIZE], utri_num;
6210 // SDL_assert(!(Netgame.debug_flags & NETD_FLAG_CLIENT_NODAMAGE));
6212 SDL_assert( tri_num < UCHAR_MAX );
6213 utri_num = (ubyte)tri_num;
6215 // for each player, determine if this object is behind the player -- if so, don't
6217 for ( i = 0; i < MAX_PLAYERS; i++ ) {
6218 if ( MULTI_CONNECTED(Net_players[i]) && (&Net_players[i] != Net_player) ) {
6220 vector eye_to_obj_vec, diff, eye_pos;
6223 eye_pos = Net_players[i].s_info.eye_pos;
6224 eye_orient = Net_players[i].s_info.eye_orient;
6226 // check for same vectors
6227 vm_vec_sub(&diff, &Objects[objnum].pos, &eye_pos);
6228 if ( vm_vec_mag_quick(&diff) < 0.01 ){
6232 vm_vec_normalized_dir(&eye_to_obj_vec, &Objects[objnum].pos, &eye_pos);
6233 dot = vm_vec_dot(&eye_orient.v.fvec, &eye_to_obj_vec);
6235 if ( dot < OBJ_VISIBILITY_DOT ){
6239 BUILD_HEADER(SHIELD_EXPLOSION);
6241 ADD_USHORT( Objects[objnum].net_signature );
6244 multi_io_send(&Net_players[i], data, packet_size);
6249 void add_shield_point_multi(int objnum, int tri_num, vector *hit_pos);
6251 void process_shield_explosion_packet( ubyte *data, header *hinfo)
6258 // get the shield hit data
6259 offset = HEADER_LENGTH;
6260 GET_USHORT(signature);
6262 //GET_DATA(hit_pos);
6265 // find the object with this signature. If found, then do a shield explosion
6266 objp = multi_get_network_object( signature );
6269 shield_info *shieldp;
6274 // given the tri num, find the local position which is the average of the
6275 // three vertices of the triangle affected. Use this average point as the hit
6277 // SDL_assert( objp->type == OBJ_SHIP );
6278 if(objp->type != OBJ_SHIP){
6282 pm = model_get(Ships[objp->instance].modelnum);
6283 shieldp = &pm->shield;
6284 SDL_assert( utri_num < shieldp->ntris );
6285 stri = shieldp->tris[utri_num];
6286 vm_vec_zero(&hit_pos);
6287 for ( i = 0; i < 3; i++ ) {
6288 vm_vec_add2( &hit_pos, &(shieldp->verts[stri.verts[i]].pos) );
6290 vm_vec_scale( &hit_pos, 1.0f/3.0f );
6291 add_shield_point_multi( OBJ_INDEX(objp), utri_num, &hit_pos );
6295 void send_player_stats_block_packet(net_player *pl, int stats_code, net_player *target)
6298 ubyte data[MAX_PACKET_SIZE], val;
6300 int packet_size = 0;
6305 sc = &pl->player->stats;
6308 BUILD_HEADER(PLAYER_STATS);
6310 // add the player id
6311 ADD_SHORT(pl->player_id);
6313 // add the byte indicating whether these stats are all-time or not
6314 val = (ubyte)stats_code;
6317 // kill information - alltime
6321 for(idx=0;idx<MAX_SHIP_TYPES;idx++){
6322 u_tmp = sc->kills[idx];
6325 // medal information
6326 for(idx=0;idx<NUM_MEDALS;idx++){
6327 i_tmp = sc->medals[idx];
6333 ADD_INT(sc->assists);
6334 ADD_INT(sc->kill_count);
6335 ADD_INT(sc->kill_count_ok);
6336 ADD_UINT(sc->p_shots_fired);
6337 ADD_UINT(sc->s_shots_fired);
6338 ADD_UINT(sc->p_shots_hit);
6339 ADD_UINT(sc->s_shots_hit);
6340 ADD_UINT(sc->p_bonehead_hits);
6341 ADD_UINT(sc->s_bonehead_hits);
6342 ADD_INT(sc->bonehead_kills);
6344 ADD_UINT(sc->missions_flown);
6345 ADD_UINT(sc->flight_time);
6346 ADD_INT(sc->last_flown);
6347 ADD_INT(sc->last_backup);
6352 for(idx=0;idx<MAX_SHIP_TYPES;idx++){
6353 u_tmp = sc->m_okKills[idx];
6357 ADD_INT(sc->m_score);
6358 ADD_INT(sc->m_assists);
6359 ADD_INT(sc->m_kill_count);
6360 ADD_INT(sc->m_kill_count_ok);
6361 ADD_UINT(sc->mp_shots_fired);
6362 ADD_UINT(sc->ms_shots_fired);
6363 ADD_UINT(sc->mp_shots_hit);
6364 ADD_UINT(sc->ms_shots_hit);
6365 ADD_UINT(sc->mp_bonehead_hits);
6366 ADD_UINT(sc->ms_bonehead_hits);
6367 ADD_INT(sc->m_bonehead_kills);
6368 ADD_INT(sc->m_player_deaths);
6369 ADD_INT(sc->m_medal_earned);
6372 case STATS_MISSION_KILLS:
6373 ADD_INT(sc->m_kill_count);
6374 ADD_INT(sc->m_kill_count_ok);
6375 ADD_INT(sc->m_assists);
6378 case STATS_DOGFIGHT_KILLS:
6379 for(idx=0; idx<MAX_PLAYERS; idx++){
6380 u_tmp = sc->m_dogfight_kills[idx];
6383 ADD_INT(sc->m_kill_count);
6384 ADD_INT(sc->m_kill_count_ok);
6385 ADD_INT(sc->m_assists);
6389 SDL_assert(packet_size < MAX_PACKET_SIZE);
6391 // if we're a client, always send the data to the server
6392 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
6393 multi_io_send_reliable(Net_player, data, packet_size);
6395 // otherwise do server specific stuff
6397 // send to a specific target
6399 multi_io_send_reliable(target, data, packet_size);
6401 // otherwise, send to everyone
6403 multi_io_send_to_all_reliable(data, packet_size);
6408 void process_player_stats_block_packet(ubyte *data, header *hinfo)
6412 scoring_struct *sc,bogus;
6414 int offset = HEADER_LENGTH;
6418 // nprintf(("Network","----------++++++++++********RECEIVED STATS***********+++++++++----------\n"));
6420 // get the player who these stats are for
6421 GET_SHORT(player_id);
6422 player_num = find_player_id(player_id);
6423 if (player_num == -1) {
6424 nprintf(("Network", "Couldn't find player for stats update!\n"));
6425 ml_string("Couldn't find player for stats update!\n");
6430 sc = &Net_players[player_num].player->stats;
6433 // get the stats code
6437 ml_string("Received STATS_ALLTIME\n");
6440 for (idx=0; idx<MAX_SHIP_TYPES; idx++) {
6442 sc->kills[idx] = u_tmp;
6445 // read in the stats
6446 for (idx=0; idx<NUM_MEDALS; idx++) {
6448 sc->medals[idx] = i_tmp;
6453 GET_INT(sc->assists);
6454 GET_INT(sc->kill_count);
6455 GET_INT(sc->kill_count_ok);
6456 GET_UINT(sc->p_shots_fired);
6457 GET_UINT(sc->s_shots_fired);
6458 GET_UINT(sc->p_shots_hit);
6459 GET_UINT(sc->s_shots_hit);
6460 GET_UINT(sc->p_bonehead_hits);
6461 GET_UINT(sc->s_bonehead_hits);
6462 GET_INT(sc->bonehead_kills);
6464 GET_UINT(sc->missions_flown);
6465 GET_UINT(sc->flight_time);
6466 GET_INT(sc->last_flown);
6467 GET_INT(sc->last_backup);
6471 ml_string("Received STATS_MISSION\n");
6473 // kills - mission OK
6474 for (idx=0; idx<MAX_SHIP_TYPES; idx++) {
6476 sc->m_okKills[idx] = u_tmp;
6479 GET_INT(sc->m_score);
6480 GET_INT(sc->m_assists);
6481 GET_INT(sc->m_kill_count);
6482 GET_INT(sc->m_kill_count_ok);
6483 GET_UINT(sc->mp_shots_fired);
6484 GET_UINT(sc->ms_shots_fired);
6485 GET_UINT(sc->mp_shots_hit);
6486 GET_UINT(sc->ms_shots_hit);
6487 GET_UINT(sc->mp_bonehead_hits);
6488 GET_UINT(sc->ms_bonehead_hits);
6489 GET_INT(sc->m_bonehead_kills);
6490 GET_INT(sc->m_player_deaths);
6491 GET_INT(sc->m_medal_earned);
6494 case STATS_MISSION_KILLS:
6495 ml_string("Received STATS_MISSION_KILLS\n");
6497 GET_INT(sc->m_kill_count);
6498 GET_INT(sc->m_kill_count_ok);
6499 GET_INT(sc->m_assists);
6502 case STATS_DOGFIGHT_KILLS:
6503 ml_string("Received STATS_DOGFIGHT_KILLS\n");
6504 if(player_num >= 0){
6505 ml_printf("Dogfight stats for %s", Net_players[player_num].player->callsign);
6507 for(idx=0; idx<MAX_PLAYERS; idx++){
6509 sc->m_dogfight_kills[idx] = u_tmp;
6510 if(player_num >= 0){
6511 ml_printf("%d", Net_players[player_num].player->stats.m_dogfight_kills[idx]);
6514 GET_INT(sc->m_kill_count);
6515 GET_INT(sc->m_kill_count_ok);
6516 GET_INT(sc->m_assists);
6521 // if I'm the server of the game, I should always rebroadcast these stats
6522 if ((Net_player->flags & NETINFO_FLAG_AM_MASTER) && (sc != &bogus)) {
6523 // make sure these are alltime stats
6524 SDL_assert(val == STATS_ALLTIME);
6526 multi_broadcast_stats(STATS_ALLTIME);
6530 // called to create asteroid stuff
6531 void send_asteroid_create( object *new_objp, object *parent_objp, int asteroid_type, vector *relvec )
6534 ubyte data[MAX_PACKET_SIZE];
6535 ubyte packet_type, atype;
6539 if (relvec != NULL )
6542 BUILD_HEADER( ASTEROID_INFO );
6543 packet_type = ASTEROID_CREATE;
6545 SDL_assert( asteroid_type < UCHAR_MAX );
6546 atype = (ubyte)asteroid_type;
6548 ADD_DATA( packet_type );
6549 ADD_USHORT( parent_objp->net_signature );
6550 ADD_USHORT( new_objp->net_signature );
6553 add_vector_data( data, &packet_size, vec );
6555 multi_io_send_to_all(data, packet_size);
6558 void send_asteroid_throw( object *objp )
6561 ubyte data[MAX_PACKET_SIZE], packet_type;
6563 BUILD_HEADER( ASTEROID_INFO );
6565 // this packet type is an asteroid throw
6566 packet_type = ASTEROID_THROW;
6567 ADD_DATA( packet_type );
6568 ADD_USHORT( objp->net_signature );
6569 //ADD_DATA( objp->pos );
6570 add_vector_data( data, &packet_size, objp->pos );
6571 //ADD_DATA( objp->phys_info.vel );
6572 add_vector_data( data, &packet_size, objp->phys_info.vel );
6574 multi_io_send_to_all(data, packet_size);
6577 void send_asteroid_hit( object *objp, object *other_objp, vector *hitpos, float damage )
6580 ubyte data[MAX_PACKET_SIZE], packet_type;
6584 if ( hitpos != NULL )
6587 // build up an asteroid hit packet
6588 BUILD_HEADER( ASTEROID_INFO );
6589 packet_type = ASTEROID_HIT;
6590 ADD_DATA( packet_type );
6591 ADD_USHORT( objp->net_signature );
6593 if(other_objp == NULL){
6594 ushort invalid_sig = 0xffff;
6595 ADD_USHORT(invalid_sig);
6597 ADD_USHORT( other_objp->net_signature );
6600 add_vector_data( data, &packet_size, vec );
6601 ADD_FLOAT( damage );
6603 multi_io_send_to_all(data, packet_size);
6606 void process_asteroid_info( ubyte *data, header *hinfo )
6611 offset = HEADER_LENGTH;
6612 GET_DATA( packet_type );
6614 // based on the packet type, do something interesting with an asteroid!
6615 switch( packet_type ) {
6617 case ASTEROID_CREATE: {
6618 ushort psignature, signature;
6621 object *parent_objp;
6623 GET_USHORT( psignature );
6624 GET_USHORT( signature );
6626 //GET_DATA( relvec );
6627 get_vector_data( data, &offset, relvec );
6629 // after getting the values, set the next network signature, and call the create sub function
6630 multi_set_network_signature( signature, MULTI_SIG_ASTEROID );
6631 parent_objp = multi_get_network_object( psignature );
6632 if ( parent_objp ) {
6633 asteroid_sub_create( parent_objp, atype, &relvec );
6635 nprintf(("Network", "Couldn't create asteroid because parent wasn't found!!!\n"));
6642 // asteroid throw packet -- asteroid has wrapped bounds
6643 case ASTEROID_THROW: {
6648 GET_USHORT( signature );
6650 get_vector_data( data, &offset, pos );
6652 get_vector_data( data, &offset, vel );
6653 objp = multi_get_network_object( signature );
6655 nprintf(("Network", "Couldn't throw asteroid because couldn't find it\n"));
6659 objp->phys_info.vel = vel;
6660 objp->phys_info.desired_vel = vel;
6664 case ASTEROID_HIT: {
6665 ushort signature, osignature;
6666 object *objp, *other_objp;
6670 GET_USHORT( signature );
6671 GET_USHORT( osignature );
6672 //GET_DATA( hitpos );
6673 get_vector_data( data, &offset, hitpos );
6674 GET_FLOAT( damage );
6676 objp = multi_get_network_object( signature );
6677 if(osignature == 0xffff){
6680 other_objp = multi_get_network_object( osignature );
6683 nprintf(("Network", "Cannot hit asteroid because signature isn't found\n"));
6687 if ( IS_VEC_NULL(&hitpos) ){
6688 asteroid_hit( objp, other_objp, NULL, damage );
6690 asteroid_hit( objp, other_objp, &hitpos, damage );
6693 // if we know the other object is a weapon, then do a weapon hit to kill the weapon
6694 if ( other_objp && (other_objp->type == OBJ_WEAPON) ){
6695 weapon_hit( other_objp, objp, &hitpos );
6708 void send_host_restr_packet(const char *callsign, int code, int mode)
6710 ubyte data[MAX_PACKET_SIZE],val;
6711 int packet_size = 0;
6713 // build the header and add the opcode
6714 BUILD_HEADER(HOST_RESTR_QUERY);
6721 ADD_STRING(callsign);
6723 // if I'm the standalone server, I should be sending this to the game host
6724 if((Game_mode & GM_STANDALONE_SERVER) && (Netgame.host != NULL)){
6725 multi_io_send_reliable(Netgame.host, data, packet_size);
6727 // otherwise if I'm the host, I should be sending a reply back to the standalone server
6729 SDL_assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
6730 multi_io_send_reliable(Net_player, data, packet_size);
6734 void process_host_restr_packet(ubyte *data, header *hinfo)
6738 int offset = HEADER_LENGTH;
6740 // get the opcode and the callsign
6743 GET_STRING(callsign);
6746 // do code specific operations
6748 // query to the host from standalone
6750 SDL_assert((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER));
6752 // set the join mode
6753 Multi_join_restr_mode = mode;
6755 // set the timestamp
6756 Multi_restr_query_timestamp = timestamp(MULTI_QUERY_RESTR_STAMP);
6758 // notify the host of the event
6759 gamesnd_play_iface(SND_BRIEF_STAGE_CHG_FAIL);
6760 HUD_printf(XSTR("Player %s has tried to join - allow (y/n) ?",736),callsign);
6763 // affirmative reply from the host to the standalone
6765 SDL_assert(Game_mode & GM_STANDALONE_SERVER);
6767 // let the player join if the timestamp has not already elapsed on the server
6768 if(Multi_restr_query_timestamp != -1){
6769 multi_process_valid_join_request(&Multi_restr_join_request,&Multi_restr_addr,(int)mode);
6775 SDL_assert(Game_mode & GM_STANDALONE_SERVER);
6776 Netgame.flags &= ~(NG_FLAG_INGAME_JOINING);
6777 Multi_restr_query_timestamp = -1;
6782 void send_netgame_end_error_packet(int notify_code,int err_code)
6786 int packet_size = 0;
6788 // only the server should ever be here - although this might change if for some reason the host wants to end the game
6789 SDL_assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
6791 // build the header and add the notification and error codes
6792 BUILD_HEADER(NETGAME_END_ERROR);
6793 code = (char)notify_code;
6795 code = (char)err_code;
6799 multi_io_send_to_all_reliable(data, packet_size);
6802 void process_netgame_end_error_packet(ubyte *data, header *hinfo)
6804 int offset = HEADER_LENGTH;
6805 char notify_code,error_code;
6807 // get the error and notification codes
6808 GET_DATA(notify_code);
6809 GET_DATA(error_code);
6813 multi_quit_game(PROMPT_NONE,notify_code,error_code);
6816 // sends info that a countermeasure succeeded.
6817 void send_countermeasure_success_packet( int objnum )
6819 int pnum, packet_size;
6820 ubyte data[MAX_PACKET_SIZE];
6822 pnum = multi_find_player_by_object( &Objects[objnum] );
6824 nprintf(("Network", "Coulnd't find player for countermeasure success packet\n"));
6828 BUILD_HEADER(COUNTERMEASURE_SUCCESS);
6829 multi_io_send(&Net_players[pnum], data, packet_size);
6832 // start the flashing of my hud gauge
6833 void process_countermeasure_success_packet( ubyte *data, header *hinfo )
6837 offset = HEADER_LENGTH;
6840 hud_start_text_flash(XSTR("Evaded", 1430), 800);
6841 snd_play(&Snds[SND_MISSILE_EVADED_POPUP]);
6844 #define UPDATE_IS_PAUSED (1<<0)
6845 #define UPDATE_HULL_INFO (1<<1)
6847 void send_client_update_packet(net_player *pl)
6849 ubyte data[MAX_PACKET_SIZE],val;
6850 int packet_size = 0;
6853 BUILD_HEADER(CLIENT_UPDATE);
6857 // add the pause status
6858 if ( Multi_pause_status ) {
6859 val |= UPDATE_IS_PAUSED;
6860 } 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) ) {
6861 val |= UPDATE_HULL_INFO;
6862 SDL_assert( Player_ship ); // I"d better have one of these!!!!
6867 // if paused, add the net address of the guy who paused
6868 if(val & UPDATE_IS_PAUSED){
6869 SDL_assert(Multi_pause_pauser != NULL);
6870 ADD_SHORT(Multi_pause_pauser->player_id);
6873 // when not paused, send hull/shield/subsystem updates to all clients (except for ingame joiners)
6874 if ( val & UPDATE_HULL_INFO ) {
6876 ubyte percent, ns, threats;
6879 ship_subsys *subsysp;
6882 // get the object for the player
6883 SDL_assert( pl->player->objnum != -1 );
6885 objp = &Objects[pl->player->objnum];
6887 SDL_assert ( objp->type == OBJ_SHIP );
6888 shipp = &Ships[objp->instance];
6889 sip = &Ship_info[shipp->ship_info_index];
6891 // hull strength and sheild mesh information are floats (as a percentage). Pass the integer
6892 // percentage value since that should be close enough
6893 float temp = (objp->hull_strength / sip->initial_hull_strength * 100.0f);
6897 percent = (ubyte)temp;
6899 ADD_DATA( percent );
6901 for (i = 0; i < MAX_SHIELD_SECTIONS; i++ ) {
6902 percent = (ubyte)(objp->shields[i] / (sip->shields / MAX_SHIELD_SECTIONS) * 100.0f);
6903 ADD_DATA( percent );
6906 // add the data for the subsystem hits. We can assume that the lists will be the same side of
6907 // both machines. Added as percent since that number <= 100
6909 // also write out the number of subsystems. We do this because the client might not know
6910 // about the object he is getting data for. (i.e. he killed the object already).
6911 ns = (ubyte)sip->n_subsystems;
6914 // now the subsystems.
6915 for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) {
6916 percent = (ubyte)(subsysp->current_hits / subsysp->system_info->max_hits * 100.0f);
6917 ADD_DATA( percent );
6920 // compute the threats for this player. Only compute the threats if this player is actually
6921 // playing (i.e. he has a ship)
6922 hud_update_reticle( pl->player );
6923 threats = (ubyte)pl->player->threat_flags;
6924 ADD_DATA( threats );
6926 // add his energy level for guns
6927 ADD_FLOAT(shipp->weapon_energy);
6929 // add his secondary bank ammo
6930 ADD_INT(shipp->weapons.num_secondary_banks);
6931 for(i=0; i<shipp->weapons.num_secondary_banks; i++){
6932 ADD_INT(shipp->weapons.secondary_bank_ammo[i]);
6937 ADD_INT(pl->sv_last_pl);
6939 // send the packet reliably to the player
6940 multi_io_send(pl, data, packet_size);
6943 void process_client_update_packet(ubyte *data, header *hinfo)
6948 int is_paused, have_hull_info;
6951 float weapon_energy;
6952 int offset = HEADER_LENGTH;
6954 // get the header byte containing useful information
6957 is_paused = (val & UPDATE_IS_PAUSED)?1:0;
6958 have_hull_info = (val & UPDATE_HULL_INFO)?1:0;
6960 // if we are paused, get who paused
6963 player_index = find_player_id(pauser);
6964 if(player_index != -1){
6965 Multi_pause_pauser = &Net_players[player_index];
6967 Multi_pause_pauser = NULL;
6971 // if we have hull information, then read it in.
6972 if ( have_hull_info ) {
6976 ubyte hull_percent, shield_percent[MAX_SHIELD_SECTIONS], n_subsystems, subsystem_percent[MAX_MODEL_SUBSYSTEMS], threats;
6978 ship_subsys *subsysp;
6982 // hull strength and sheild mesh information are floats (as a percentage). Pass the integer
6983 // percentage value since that should be close enough
6984 GET_DATA( hull_percent );
6986 for (i = 0; i < MAX_SHIELD_SECTIONS; i++ ){
6988 shield_percent[i] = ub_tmp;
6991 // get the data for the subsystems
6992 GET_DATA( n_subsystems );
6993 for ( i = 0; i < n_subsystems; i++ ){
6995 subsystem_percent[i] = ub_tmp;
6998 GET_DATA( threats );
7000 // add his energy level for guns
7001 GET_FLOAT(weapon_energy);
7003 // add his secondary bank ammo
7004 GET_INT(ammo_count);
7005 for(i=0; i<ammo_count; i++){
7009 // assign the above information to my ship, assuming that I can find it! Ingame joiners might get this
7010 // packet because of delay between reliable packet acknowledging my ingame ship and the start of these
7011 // UDP client update packets. Only read this info if I have a ship.
7012 if ( !(Net_player->flags & NETINFO_FLAG_INGAME_JOIN) && (Player_ship != NULL) && (Player_obj != NULL) && (Net_player != NULL)) {
7013 shipp = Player_ship;
7015 sip = &Ship_info[shipp->ship_info_index];
7017 val = hull_percent * sip->initial_hull_strength / 100.0f;
7018 objp->hull_strength = val;
7020 for ( i = 0; i < MAX_SHIELD_SECTIONS; i++ ) {
7021 val = (shield_percent[i] * sip->shields / 100.0f) / MAX_SHIELD_SECTIONS;
7022 objp->shields[i] = val;
7025 // for sanity, be sure that the number of susbystems that I read in matches the player. If not,
7026 // then don't read these in.
7027 if ( n_subsystems == sip->n_subsystems ) {
7029 n_subsystems = 0; // reuse this variable
7030 for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) {
7033 val = subsystem_percent[n_subsystems] * subsysp->system_info->max_hits / 100.0f;
7034 subsysp->current_hits = val;
7036 // add the value just generated (it was zero'ed above) into the array of generic system types
7037 subsys_type = subsysp->system_info->type; // this is the generic type of subsystem
7038 SDL_assert ( subsys_type < SUBSYSTEM_MAX );
7039 shipp->subsys_info[subsys_type].current_hits += val;
7043 ship_recalc_subsys_strength( shipp );
7045 shipp->weapon_energy = weapon_energy;
7046 for(i=0; i<ammo_count; i++){
7047 shipp->weapons.secondary_bank_ammo[i] = ammo[i];
7050 // update my threat flags.
7051 // temporarily commented out until tested.
7052 Net_player->player->threat_flags = threats;
7059 if(Net_player != NULL){
7060 Net_player->cl_last_pl = pl;
7064 // note, if we're already paused or unpaused, calling these will have no effect, so it is safe to do so
7065 if(!popup_active() && !(Net_player->flags & NETINFO_FLAG_RESPAWNING) && !(Net_player->flags & NETINFO_FLAG_LIMBO)){
7067 multi_pause_pause();
7069 multi_pause_unpause();
7074 void send_countdown_packet(int time)
7078 int packet_size = 0;
7080 // build the header and add the time
7081 BUILD_HEADER(COUNTDOWN);
7085 // if we're the server, we should broadcast to everyone
7086 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
7087 multi_io_send_to_all_reliable(data, packet_size);
7089 // otherwise we'de better be a host sending to the standalone
7091 SDL_assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
7092 multi_io_send_reliable(Net_player, data, packet_size);
7096 void process_countdown_packet(ubyte *data, header *hinfo)
7098 int offset = HEADER_LENGTH;
7105 // if we're not in the post sync data screen, ignore it
7106 if(gameseq_get_state() != GS_STATE_MULTI_MISSION_SYNC){
7110 // if we're the standalone, this should be a -1 telling us to start the countdown
7111 if(Game_mode & GM_STANDALONE_SERVER){
7112 SDL_assert((int)time == -1);
7114 // start the countdown
7115 multi_sync_start_countdown();
7117 // otherwise if we're clients, just bash the countdown
7119 Multi_sync_countdown = (int)time;
7123 // packets for debriefing information
7124 void send_debrief_info( int stage_count[], int *stages[] )
7126 ubyte data[MAX_PACKET_SIZE];
7127 int packet_size, i, j;
7130 BUILD_HEADER(DEBRIEF_INFO);
7132 // add the data for the teams
7133 for ( i = 0; i < Num_teams; i++ ) {
7136 count = stage_count[i];
7138 for ( j = 0; j < count; j++ ) {
7139 i_tmp = stages[i][j];
7144 multi_io_send_to_all_reliable(data, packet_size);
7147 // process a debrief info packet from the server
7148 void process_debrief_info( ubyte *data, header *hinfo )
7151 int stage_counts[MAX_TEAMS], active_stages[MAX_TEAMS][MAX_DEBRIEF_STAGES], *stages[MAX_TEAMS];
7154 offset = HEADER_LENGTH;
7155 for ( i = 0; i < Num_teams; i++ ) {
7159 stage_counts[i] = count;
7160 stages[i] = active_stages[i];
7161 for ( j = 0; j < count; j++ ) {
7163 active_stages[i][j] = i_tmp;
7168 // now that we have the stage data for the debriefing stages, call debrief function with the
7169 // data so that clients can now see the debriefing stuff. Do it only for my team.
7170 SDL_assert( (Net_player->p_info.team >= 0) && (Net_player->p_info.team < Num_teams) );
7171 debrief_set_multi_clients( stage_counts[Net_player->p_info.team], stages[Net_player->p_info.team] );
7174 // sends homing information to all clients. We only need signature and num_missiles (because of hornets).
7175 // sends homing_object and homing_subsystem to all clients.
7176 void send_homing_weapon_info( int weapon_num )
7178 ubyte data[MAX_PACKET_SIZE];
7181 object *homing_object;
7182 ushort homing_signature;
7185 wp = &Weapons[weapon_num];
7187 // be sure that this weapon object is a homing object.
7188 if ( !(Weapon_info[wp->weapon_info_index].wi_flags & WIF_HOMING) )
7191 // get the homing signature. If this weapon isn't homing on anything, then sent 0 as the
7192 // homing signature.
7193 homing_signature = 0;
7194 homing_object = wp->homing_object;
7195 if ( homing_object != NULL ) {
7196 homing_signature = homing_object->net_signature;
7198 // get the subsystem index.
7200 if ( (homing_object->type == OBJ_SHIP) && (wp->homing_subsys != NULL) ) {
7203 s_index = ship_get_index_from_subsys( wp->homing_subsys, OBJ_INDEX(homing_object), 1 );
7204 SDL_assert( s_index < CHAR_MAX ); // better be less than this!!!!
7205 t_subsys = (char)s_index;
7209 BUILD_HEADER(HOMING_WEAPON_UPDATE);
7210 ADD_USHORT( Objects[wp->objnum].net_signature );
7211 ADD_USHORT( homing_signature );
7212 ADD_DATA( t_subsys );
7214 multi_io_send_to_all(data, packet_size);
7217 // process a homing weapon info change packet. multiple_missiles parameter specifies is this
7218 // packet contains information for multiple weapons (like hornets).
7219 void process_homing_weapon_info( ubyte *data, header *hinfo )
7222 ushort weapon_signature, homing_signature;
7224 object *homing_object, *weapon_objp;
7227 offset = HEADER_LENGTH;
7229 // get the data for the packet
7230 GET_USHORT( weapon_signature );
7231 GET_USHORT( homing_signature );
7232 GET_DATA( h_subsys );
7235 // deal with changing this weapons homing information
7236 weapon_objp = multi_get_network_object( weapon_signature );
7237 if ( weapon_objp == NULL ) {
7238 nprintf(("Network", "Couldn't find weapon object for homing update -- skipping update\n"));
7241 SDL_assert( weapon_objp->type == OBJ_WEAPON );
7242 wp = &Weapons[weapon_objp->instance];
7244 // be sure that we can find these weapons and
7245 homing_object = multi_get_network_object( homing_signature );
7246 if ( homing_object == NULL ) {
7247 nprintf(("Network", "Couldn't find homing object for homing update\n"));
7251 if ( homing_object->type == OBJ_WEAPON ) {
7252 SDL_assert(Weapon_info[Weapons[homing_object->instance].weapon_info_index].wi_flags & WIF_BOMB);
7255 wp->homing_object = homing_object;
7256 wp->homing_subsys = NULL;
7257 wp->target_num = OBJ_INDEX(homing_object);
7258 wp->target_sig = homing_object->signature;
7259 if ( h_subsys != -1 ) {
7260 SDL_assert( homing_object->type == OBJ_SHIP );
7261 wp->homing_subsys = ship_get_indexed_subsys( &Ships[homing_object->instance], h_subsys);
7264 if ( homing_object->type == OBJ_SHIP ) {
7265 nprintf(("Network", "Updating homing information for weapon -- homing on %s\n", Ships[homing_object->instance].ship_name));
7269 void send_emp_effect(ushort net_sig, float intensity, float time)
7274 SDL_assert(MULTIPLAYER_MASTER);
7276 // build the packet and add the opcode
7277 BUILD_HEADER(EMP_EFFECT);
7278 ADD_USHORT(net_sig);
7279 ADD_FLOAT(intensity);
7282 // send it to the player
7283 multi_io_send_to_all(data, packet_size);
7286 void process_emp_effect(ubyte *data, header *hinfo)
7288 float intensity, time;
7291 int offset = HEADER_LENGTH;
7293 // read in the EMP effect data
7294 GET_USHORT(net_sig);
7295 GET_FLOAT(intensity);
7299 // try and find the object
7300 objp = multi_get_network_object(net_sig);
7301 if((objp != NULL) && (objp->type == OBJ_SHIP)){
7302 // if i'm not an observer and I have a valid ship, play the EMP effect
7303 if(!(Net_player->flags & NETINFO_FLAG_OBSERVER) && (Player_obj != NULL) && (Player_obj->type == OBJ_SHIP) && (Player_obj == objp)){
7304 emp_start_local(intensity, time);
7307 // start the effect for the ship itself
7308 emp_start_ship(objp, intensity, time);
7312 // tells whether or not reinforcements are available
7313 void send_reinforcement_avail( int rnum )
7318 BUILD_HEADER(REINFORCEMENT_AVAIL);
7320 multi_io_send_to_all_reliable(data, packet_size);
7323 void process_reinforcement_avail( ubyte *data, header *hinfo )
7328 offset = HEADER_LENGTH;
7332 // sanity check for a valid reinforcement number
7333 if ( (rnum >= 0) && (rnum < Num_reinforcements) ) {
7334 Reinforcements[rnum].flags |= RF_IS_AVAILABLE;
7338 void send_change_iff_packet(ushort net_signature, int new_team)
7340 ubyte data[MAX_PACKET_SIZE];
7341 int packet_size = 0;
7343 if(Net_player == NULL){
7346 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
7350 // build the packet and add the data
7351 BUILD_HEADER(CHANGE_IFF);
7352 ADD_USHORT(net_signature);
7355 // send to all players
7356 multi_io_send_to_all_reliable(data, packet_size);
7359 void process_change_iff_packet( ubyte *data, header *hinfo)
7361 int offset = HEADER_LENGTH;
7362 ushort net_signature;
7367 GET_USHORT(net_signature);
7371 // lookup the object
7372 objp = multi_get_network_object(net_signature);
7373 if((objp != NULL) && (objp->type == OBJ_SHIP) && (objp->instance >=0)){
7374 Ships[objp->instance].team = new_team;
7378 void send_NEW_primary_fired_packet(ship *shipp, int banks_fired)
7380 int packet_size, objnum;
7381 ubyte data[MAX_PACKET_SIZE]; // ubanks_fired, current_bank;
7384 net_player *ignore = NULL;
7386 // sanity checking for now
7387 SDL_assert ( banks_fired <= 3 );
7389 // get an object pointer for this ship.
7390 objnum = shipp->objnum;
7391 objp = &Objects[objnum];
7393 // if i'm a multiplayer client, I should never send primary fired packets for anyone except me
7394 if(MULTIPLAYER_CLIENT && (Player_obj != objp)){
7398 // just in case nothing got fired
7399 if(banks_fired <= 0){
7403 // ubanks_fired = (ubyte)banks_fired;
7404 // current_bank = (ubyte)shipp->weapons.current_primary_bank;
7405 // SDL_assert( current_bank <= 3 );
7407 // insert the current primary bank into this byte
7408 // ubanks_fired |= (current_bank << CURRENT_BANK_BIT);
7410 // append the SF_PRIMARY_LINKED flag on the top nibble of the banks_fired
7411 // if ( shipp->flags & SF_PRIMARY_LINKED ){
7412 // ubanks_fired |= (1<<7);
7415 // determine if its a player ship and don't send to him if we're in "client firing" mode
7416 // if((Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING) && MULTIPLAYER_MASTER){
7417 if(MULTIPLAYER_MASTER){
7418 np_index = multi_find_player_by_net_signature(objp->net_signature);
7419 if((np_index >= 0) && (np_index < MAX_PLAYERS)){
7420 ignore = &Net_players[np_index];
7424 // build up the standard weapon fired packet. This packet will get sent to all players if an AI
7425 // ship fired the primary weapons. If a player fired the weaspon, then this packet will get sent
7426 // to every player but the guy who actullly fired the weapon. This method is used to help keep client
7427 // and server in sync w.r.t. weapon energy for player ship
7428 BUILD_HEADER( PRIMARY_FIRED_NEW );
7429 ADD_USHORT(objp->net_signature);
7430 // ADD_DATA(ubanks_fired);
7432 // if I'm a server, broadcast to all players
7433 if(MULTIPLAYER_MASTER){
7434 multi_io_send_to_all(data, packet_size, ignore);
7437 multi_rate_add(1, "wfi", packet_size);
7439 // otherwise just send to the server
7441 multi_io_send(Net_player, data, packet_size);
7445 void process_NEW_primary_fired_packet(ubyte *data, header *hinfo)
7447 int offset; // linked;
7448 // ubyte banks_fired, current_bank;
7453 // read all packet info
7454 offset = HEADER_LENGTH;
7455 GET_USHORT(shooter_sig);
7456 // GET_DATA(banks_fired);
7459 // find the object this fired packet is operating on
7460 objp = multi_get_network_object( shooter_sig );
7461 if ( objp == NULL ) {
7462 nprintf(("Network", "Could not find ship for fire primary packet NEW!"));
7465 // if this object is not actually a valid ship, don't do anything
7466 if(objp->type != OBJ_SHIP){
7469 if(objp->instance < 0){
7472 shipp = &Ships[objp->instance];
7474 // get the link status of the primary banks
7476 // if ( banks_fired & (1<<7) ) {
7478 // banks_fired ^= (1<<7);
7481 // get the current primary bank
7482 // current_bank = (ubyte)(banks_fired >> CURRENT_BANK_BIT);
7483 // current_bank &= 0x3;
7484 // SDL_assert( (current_bank >= 0) && (current_bank < MAX_PRIMARY_BANKS) );
7485 // shipp->weapons.current_primary_bank = current_bank;
7487 // strip off all remaining bits and just keep which banks were actually fired.
7488 // banks_fired &= 0x3;
7490 // set the link status of the ship if not the player. If it is the player, we will do sanity checking
7493 // shipp->flags &= ~SF_PRIMARY_LINKED;
7495 // shipp->flags |= SF_PRIMARY_LINKED;
7498 // if we're in client firing mode, ignore ones for myself
7499 if((Player_obj != NULL) && (Player_obj == objp)){
7503 ship_fire_primary( objp, 0, 1 );
7506 void send_NEW_countermeasure_fired_packet(object *objp, int cmeasure_count, int rand_val)
7508 ubyte data[MAX_PACKET_SIZE];
7511 net_player *ignore = NULL;
7513 // if i'm a multiplayer client, I should never send primary fired packets for anyone except me
7514 if(MULTIPLAYER_CLIENT && (Player_obj != objp)){
7518 SDL_assert ( cmeasure_count < UCHAR_MAX );
7519 BUILD_HEADER(COUNTERMEASURE_NEW);
7520 ADD_USHORT( objp->net_signature );
7521 ADD_INT( rand_val );
7523 nprintf(("Network","Sending NEW countermeasure packet!\n"));
7525 // determine if its a player ship and don't send to him if we're in "client firing" mode
7526 // if((Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING) && MULTIPLAYER_MASTER){
7527 if(MULTIPLAYER_MASTER){
7528 np_index = multi_find_player_by_net_signature(objp->net_signature);
7529 if((np_index >= 0) && (np_index < MAX_PLAYERS)){
7530 ignore = &Net_players[np_index];
7534 // if I'm the server, send to all players
7535 if(MULTIPLAYER_MASTER){
7536 multi_io_send_to_all(data, packet_size, ignore);
7538 // otherwise send to the server
7540 multi_io_send(Net_player, data, packet_size);
7544 void process_NEW_countermeasure_fired_packet(ubyte *data, header *hinfo)
7551 offset = HEADER_LENGTH;
7552 GET_USHORT( signature );
7553 GET_INT( rand_val );
7556 objp = multi_get_network_object( signature );
7557 if ( objp == NULL ) {
7558 nprintf(("network", "Could find object whose countermeasures are being launched!!!\n"));
7561 if(objp->type != OBJ_SHIP){
7565 // if we're in client firing mode, ignore ones for myself
7566 // if((Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING) && (Player_obj != NULL) && (Player_obj == objp)){
7567 if((Player_obj != NULL) && (Player_obj == objp)){
7571 // make it so ship can fire right away!
7572 Ships[objp->instance].cmeasure_fire_stamp = timestamp(0);
7573 if ( objp == Player_obj ){
7574 nprintf(("network", "firing countermeasure from my ship\n"));
7576 ship_launch_countermeasure( objp, rand_val );
7579 void send_beam_fired_packet(object *shooter, ship_subsys *turret, object *target, int beam_info_index, beam_info *override)
7581 ubyte data[MAX_PACKET_SIZE];
7582 int packet_size = 0;
7587 // only the server should ever be doing this
7588 SDL_assert(MULTIPLAYER_MASTER);
7590 // setup outgoing data
7591 SDL_assert(shooter != NULL);
7592 SDL_assert(turret != NULL);
7593 SDL_assert(target != NULL);
7594 SDL_assert(override != NULL);
7595 if((shooter == NULL) || (turret == NULL) || (target == NULL) || (override == NULL)){
7598 u_beam_info = (ubyte)beam_info_index;
7599 subsys_index = (char)ship_get_index_from_subsys(turret, OBJ_INDEX(shooter));
7600 SDL_assert(subsys_index >= 0);
7601 if(subsys_index < 0){
7605 // swap the beam_info override info into little endian byte order
7606 b_info.dir_a.xyz.x = INTEL_FLOAT(override->dir_a.xyz.x);
7607 b_info.dir_a.xyz.y = INTEL_FLOAT(override->dir_a.xyz.y);
7608 b_info.dir_a.xyz.z = INTEL_FLOAT(override->dir_a.xyz.z);
7610 b_info.dir_b.xyz.x = INTEL_FLOAT(override->dir_b.xyz.x);
7611 b_info.dir_b.xyz.y = INTEL_FLOAT(override->dir_b.xyz.y);
7612 b_info.dir_b.xyz.z = INTEL_FLOAT(override->dir_b.xyz.z);
7614 b_info.delta_ang = INTEL_FLOAT(override->delta_ang);
7615 b_info.shot_count = override->shot_count;
7617 for (int i = 0; i < b_info.shot_count; i++) {
7618 b_info.shot_aim[i] = INTEL_FLOAT(override->shot_aim[i]);
7622 BUILD_HEADER(BEAM_FIRED);
7623 ADD_USHORT(shooter->net_signature);
7624 ADD_DATA(subsys_index);
7625 ADD_USHORT(target->net_signature);
7626 ADD_DATA(u_beam_info);
7627 ADD_DATA(b_info); // FIXME: This is still wrong, we shouldn't be sending an entire struct over the wire - taylor
7629 // send to all clients
7630 multi_io_send_to_all_reliable(data, packet_size);
7633 void process_beam_fired_packet(ubyte *data, header *hinfo)
7636 ushort shooter_sig, target_sig;
7640 beam_fire_info fire_info;
7642 // only clients should ever get this
7643 SDL_assert(MULTIPLAYER_CLIENT);
7645 // read in packet data
7646 offset = HEADER_LENGTH;
7647 GET_USHORT(shooter_sig);
7648 GET_DATA(subsys_index);
7649 GET_USHORT(target_sig);
7650 GET_DATA(u_beam_info);
7655 // swap the beam_info override info into native byte order
7656 b_info.dir_a.xyz.x = INTEL_FLOAT( b_info.dir_a.xyz.x );
7657 b_info.dir_a.xyz.y = INTEL_FLOAT( b_info.dir_a.xyz.y );
7658 b_info.dir_a.xyz.z = INTEL_FLOAT( b_info.dir_a.xyz.z );
7659 b_info.dir_b.xyz.x = INTEL_FLOAT( b_info.dir_b.xyz.x );
7660 b_info.dir_b.xyz.y = INTEL_FLOAT( b_info.dir_b.xyz.y );
7661 b_info.dir_b.xyz.z = INTEL_FLOAT( b_info.dir_b.xyz.z );
7662 b_info.delta_ang = INTEL_FLOAT( b_info.delta_ang );
7664 for (i = 0; i < MAX_BEAM_SHOTS; i++) {
7665 b_info.shot_aim[i] = INTEL_FLOAT(b_info.shot_aim[i]);
7668 // lookup all relevant data
7669 fire_info.beam_info_index = (int)u_beam_info;
7670 fire_info.shooter = NULL;
7671 fire_info.target = NULL;
7672 fire_info.turret = NULL;
7673 fire_info.target_subsys = NULL;
7674 fire_info.beam_info_override = NULL;
7675 fire_info.shooter = multi_get_network_object(shooter_sig);
7676 fire_info.target = multi_get_network_object(target_sig);
7677 fire_info.beam_info_override = &b_info;
7678 fire_info.accuracy = 1.0f;
7679 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)){
7680 nprintf(("Network", "Couldn't get shooter/target info for BEAM weapon!\n"));
7683 fire_info.turret = ship_get_indexed_subsys( &Ships[fire_info.shooter->instance], (int)subsys_index);
7684 if(fire_info.turret == NULL){
7685 nprintf(("Network", "Couldn't get turret for BEAM weapon!\n"));
7690 beam_fire(&fire_info);
7693 void send_sw_query_packet(ubyte code, char *txt)
7695 ubyte data[MAX_PACKET_SIZE];
7696 int packet_size = 0;
7698 // build the packet and add the code
7699 BUILD_HEADER(SW_STD_QUERY);
7701 if((code == SW_STD_START) || (code == SW_STD_BAD)){
7705 // if I'm the host, send to standalone
7706 if(MULTIPLAYER_HOST){
7707 SDL_assert(!MULTIPLAYER_MASTER);
7708 SDL_assert(code == SW_STD_START);
7709 multi_io_send_reliable(Net_player, data, packet_size);
7711 // otherwise standalone sends back to host
7713 SDL_assert(Game_mode & GM_STANDALONE_SERVER);
7714 SDL_assert(code != SW_STD_START);
7715 SDL_assert(Netgame.host != NULL);
7716 if(Netgame.host != NULL){
7717 multi_io_send_reliable(Netgame.host, data, packet_size);
7722 void process_sw_query_packet(ubyte *data, header *hinfo)
7726 void send_event_update_packet(int event)
7728 ubyte data[MAX_PACKET_SIZE];
7729 ushort u_event = (ushort)event;
7730 int packet_size = 0;
7732 // build the header and add the event
7733 BUILD_HEADER(EVENT_UPDATE);
7734 ADD_USHORT(u_event);
7735 ADD_INT(Mission_events[event].flags);
7736 ADD_INT(Mission_events[event].formula);
7737 ADD_INT(Mission_events[event].result);
7738 ADD_INT(Mission_events[event].count);
7740 // send to all players
7741 multi_io_send_to_all_reliable(data, packet_size);
7744 void process_event_update_packet(ubyte *data, header *hinfo)
7746 int offset = HEADER_LENGTH;
7751 GET_USHORT(u_event);
7752 store_flags = Mission_events[u_event].flags;
7753 GET_INT(Mission_events[u_event].flags);
7754 GET_INT(Mission_events[u_event].formula);
7755 GET_INT(Mission_events[u_event].result);
7756 GET_INT(Mission_events[u_event].count);
7759 // went from non directive special to directive special
7760 if(!(store_flags & MEF_DIRECTIVE_SPECIAL) && (Mission_events[u_event].flags & MEF_DIRECTIVE_SPECIAL)){
7761 mission_event_set_directive_special(u_event);
7763 // if we went directive special to non directive special
7764 else if((store_flags & MEF_DIRECTIVE_SPECIAL) & !(Mission_events[u_event].flags & MEF_DIRECTIVE_SPECIAL)){
7765 mission_event_unset_directive_special(u_event);
7769 // weapon detonate packet
7770 void send_weapon_detonate_packet(object *objp)
7772 ubyte data[MAX_PACKET_SIZE];
7773 int packet_size = 0;
7776 SDL_assert(MULTIPLAYER_MASTER);
7777 if(!MULTIPLAYER_MASTER){
7780 SDL_assert(objp != NULL);
7785 // build the header and add the data
7786 BUILD_HEADER(WEAPON_DET);
7787 ADD_USHORT(objp->net_signature);
7789 // send to all players
7790 multi_io_send_to_all(data, packet_size);
7793 void process_weapon_detonate_packet(ubyte *data, header *hinfo)
7796 int offset = HEADER_LENGTH;
7797 object *objp = NULL;
7799 // get the weapon signature
7800 GET_USHORT(net_sig);
7803 // lookup the weapon
7804 objp = multi_get_network_object(net_sig);
7805 if((objp != NULL) && (objp->type == OBJ_WEAPON) && (objp->instance >= 0)){
7806 weapon_detonate(objp);
7810 // flak fired packet
7811 void send_flak_fired_packet(int ship_objnum, int subsys_index, int weapon_objnum, float flak_range)
7814 ushort pnet_signature;
7815 ubyte data[MAX_PACKET_SIZE], cindex;
7821 if((weapon_objnum < 0) || (Objects[weapon_objnum].type != OBJ_WEAPON) || (Objects[weapon_objnum].instance < 0) || (Weapons[Objects[weapon_objnum].instance].weapon_info_index < 0)){
7825 // local setup -- be sure we are actually passing a weapon!!!!
7826 objp = &Objects[weapon_objnum];
7827 SDL_assert ( objp->type == OBJ_WEAPON );
7828 pnet_signature = Objects[ship_objnum].net_signature;
7830 SDL_assert( subsys_index < UCHAR_MAX );
7831 cindex = (ubyte)subsys_index;
7833 ssp = ship_get_indexed_subsys( &Ships[Objects[ship_objnum].instance], subsys_index, NULL );
7838 // build the fire turret packet.
7839 BUILD_HEADER(FLAK_FIRED);
7840 packet_size += multi_pack_unpack_position(1, data + packet_size, &objp->orient.v.fvec);
7841 ADD_USHORT( pnet_signature );
7843 val = (short)ssp->submodel_info_1.angs.h;
7845 val = (short)ssp->submodel_info_2.angs.p;
7847 ADD_FLOAT( flak_range );
7849 multi_io_send_to_all(data, packet_size);
7851 multi_rate_add(1, "flk", packet_size);
7854 void process_flak_fired_packet(ubyte *data, header *hinfo)
7856 int offset, weapon_objnum, wid;
7857 ushort pnet_signature;
7865 short pitch, heading;
7868 // get the data for the turret fired packet
7869 offset = HEADER_LENGTH;
7870 offset += multi_pack_unpack_position(0, data + offset, &o_fvec);
7871 GET_USHORT( pnet_signature );
7872 GET_DATA( turret_index );
7873 GET_SHORT( heading );
7875 GET_FLOAT( flak_range );
7876 PACKET_SET_SIZE(); // move our counter forward the number of bytes we have read
7879 objp = multi_get_network_object( pnet_signature );
7880 if ( objp == NULL ) {
7881 nprintf(("network", "could find parent object with net signature %d for flak firing\n", pnet_signature));
7885 // if this isn't a ship, do nothing
7886 if ( objp->type != OBJ_SHIP ){
7890 // make an orientation matrix from the o_fvec
7891 vm_vector_2_matrix(&orient, &o_fvec, NULL, NULL);
7893 // find this turret, and set the position of the turret that just fired to be where it fired. Quite a
7894 // hack, but should be suitable.
7895 shipp = &Ships[objp->instance];
7896 ssp = ship_get_indexed_subsys( shipp, turret_index, NULL );
7900 wid = ssp->system_info->turret_weapon_type;
7901 if((wid < 0) || !(Weapon_info[wid].wi_flags & WIF_FLAK)){
7905 // bash the position and orientation of the turret
7906 ssp->submodel_info_1.angs.h = (float)heading;
7907 ssp->submodel_info_2.angs.p = (float)pitch;
7909 // get the world position of the weapon
7910 ship_get_global_turret_info(objp, ssp->system_info, &pos, &dir);
7912 // create the weapon object
7913 weapon_objnum = weapon_create( &pos, &orient, wid, OBJ_INDEX(objp), 0, -1, 1);
7914 if (weapon_objnum != -1) {
7915 if ( Weapon_info[wid].launch_snd != -1 ) {
7916 snd_play_3d( &Snds[Weapon_info[wid].launch_snd], &pos, &View_position );
7919 // create a muzzle flash from a flak gun based upon firing position and weapon type
7920 flak_muzzle_flash(&pos, &dir, wid);
7922 // set its range explicitly - make it long enough so that it's guaranteed to still exist when the server tells us it blew up
7923 flak_set_range(&Objects[weapon_objnum], &pos, (float)flak_range);
7927 #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);
7928 #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);
7930 // player pain packet
7931 void send_player_pain_packet(net_player *pl, int weapon_info_index, float damage, vector *force, vector *hitpos)
7933 ubyte data[MAX_PACKET_SIZE];
7936 int packet_size = 0;
7938 SDL_assert(MULTIPLAYER_MASTER);
7939 if(!MULTIPLAYER_MASTER){
7942 SDL_assert(pl != NULL);
7947 // build the packet and add the code
7948 BUILD_HEADER(NETPLAYER_PAIN);
7949 windex = (ubyte)weapon_info_index;
7951 udamage = (ushort)damage;
7952 ADD_USHORT(udamage);
7953 //ADD_DATA((*force));
7954 add_vector_data( data, &packet_size, *force );
7955 //ADD_DATA((*hitpos));
7956 add_vector_data( data, &packet_size, *hitpos );
7958 // send to the player
7959 multi_io_send(pl, data, packet_size);
7961 multi_rate_add(1, "pai", packet_size);
7964 void process_player_pain_packet(ubyte *data, header *hinfo)
7970 vector local_hit_pos;
7973 // get the data for the pain packet
7974 offset = HEADER_LENGTH;
7976 GET_USHORT(udamage);
7978 get_vector_data( data, &offset, force );
7979 //GET_DATA(local_hit_pos);
7980 get_vector_data( data, &offset, local_hit_pos );
7983 mprintf(("PAIN!\n"));
7985 // get weapon info pointer
7986 //SDL_assert((windex >= 0) && (windex < Num_weapon_types) && (Weapon_info[windex].subtype == WP_LASER)); // always true
7987 if(! ((windex != 255) && (windex < Num_weapon_types) && (Weapon_info[windex].subtype == WP_LASER)) ){
7990 wip = &Weapon_info[windex];
7992 // play the weapon hit sound
7993 SDL_assert(Player_obj != NULL);
7994 if(Player_obj == NULL){
7997 weapon_hit_do_sound(Player_obj, wip, &Player_obj->pos);
7999 // we need to do 3 things here. player pain (game flash), weapon hit sound, ship_apply_whack()
8000 ship_hit_pain((float)udamage);
8003 ship_apply_whack(&force, &local_hit_pos, Player_obj);
8007 void send_lightning_packet(int bolt_type, vector *start, vector *strike)
8009 ubyte data[MAX_PACKET_SIZE];
8011 int packet_size = 0;
8013 // build the header and add the data
8014 BUILD_HEADER(LIGHTNING_PACKET);
8015 val = (char)bolt_type;
8017 //ADD_DATA((*start));
8018 add_vector_data( data, &packet_size, *start );
8019 //ADD_DATA((*strike));
8020 add_vector_data( data, &packet_size, *strike );
8022 // send to everyone unreliable for now
8023 multi_io_send_to_all(data, packet_size);
8026 void process_lightning_packet(ubyte *data, header *hinfo)
8030 vector start, strike;
8033 offset = HEADER_LENGTH;
8034 GET_DATA(bolt_type);
8036 get_vector_data(data, &offset, start);
8037 // GET_DATA(strike);
8038 get_vector_data(data, &offset, strike);
8047 nebl_bolt(bolt_type, &start, &strike);
8050 void send_bytes_recvd_packet(net_player *pl)
8052 // only clients should ever be doing this
8057 ubyte data[MAX_PACKET_SIZE];
8058 int packet_size = 0;
8059 BUILD_HEADER(BYTES_SENT);
8060 ADD_INT(pl->cl_bytes_recvd);
8062 // send to the server
8063 multi_io_send_reliable(pl, data, packet_size);
8066 void process_bytes_recvd_packet(ubyte *data, header *hinfo)
8070 net_player *pl = NULL;
8071 int offset = HEADER_LENGTH;
8077 if(Net_player == NULL){
8080 if(!MULTIPLAYER_MASTER){
8084 // make sure we know what player sent this
8085 pid = find_player_id(hinfo->id);
8086 if((pid < 0) || (pid >= MAX_PLAYERS)){
8089 pl = &Net_players[pid];
8092 pl->cl_bytes_recvd = bytes;
8096 pl->sv_last_pl = (int)(100.0f * (1.0f - ((float)pl->cl_bytes_recvd / (float)pl->sv_bytes_sent)));
8099 pl->sv_bytes_sent = 0;
8103 void send_host_captain_change_packet(short player_id, int captain_change)
8105 ubyte data[MAX_PACKET_SIZE];
8106 int packet_size = 0;
8109 BUILD_HEADER(TRANSFER_HOST);
8110 ADD_SHORT(player_id);
8111 ADD_INT(captain_change);
8114 multi_io_send_to_all_reliable(data, packet_size);
8117 void process_host_captain_change_packet(ubyte *data, header *hinfo)
8119 int offset = HEADER_LENGTH;
8120 int idx, found_player, captain_change;
8123 // get the player id
8124 GET_SHORT(player_id);
8125 GET_INT(captain_change);
8131 for(idx=0; idx<MAX_PLAYERS; idx++){
8132 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].player_id == player_id)){
8133 HUD_printf("%s is the new captain of team %d", Net_players[idx].player->callsign, Net_players[idx].p_info.team + 1);
8138 // unflag all old players
8139 for(idx=0; idx<MAX_PLAYERS; idx++){
8140 Net_players[idx].flags &= ~NETINFO_FLAG_GAME_HOST;
8145 for(idx=0; idx<MAX_PLAYERS; idx++){
8146 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].player_id == player_id)){
8147 Net_players[idx].flags |= NETINFO_FLAG_GAME_HOST;
8149 // spew to the HUD config
8150 if(Net_players[idx].player != NULL){
8151 HUD_printf("%s is the new game host", Net_players[idx].player->callsign);
8161 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_HOST_LEFT);
8166 void send_self_destruct_packet()
8168 ubyte data[MAX_PACKET_SIZE];
8169 int packet_size = 0;
8172 if(Net_player == NULL){
8176 // if i'm the server, I shouldn't be here
8177 SDL_assert(!(Net_player->flags & NETINFO_FLAG_AM_MASTER));
8178 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8182 // only if this is valid
8183 if(MULTI_OBSERVER(Net_players[MY_NET_PLAYER_NUM])){
8188 if((Player_ship == NULL) || (Player_obj == NULL)){
8193 BUILD_HEADER(SELF_DESTRUCT);
8194 ADD_USHORT(Player_obj->net_signature);
8196 // send to the server
8197 multi_io_send_reliable(Net_player, data, packet_size);
8200 void process_self_destruct_packet(ubyte *data, header *hinfo)
8202 int offset = HEADER_LENGTH;
8206 // get the net signature
8207 GET_USHORT(net_sig);
8211 np_index = find_player_id(hinfo->id);
8215 if(MULTI_OBSERVER(Net_players[np_index])){
8218 if(Net_players[np_index].player == NULL){
8221 if((Net_players[np_index].player->objnum < 0) || (Net_players[np_index].player->objnum >= MAX_OBJECTS)){
8224 if(Objects[Net_players[np_index].player->objnum].net_signature != net_sig){
8227 if(Objects[Net_players[np_index].player->objnum].type != OBJ_SHIP){
8230 if((Objects[Net_players[np_index].player->objnum].instance < 0) || (Objects[Net_players[np_index].player->objnum].instance >= MAX_SHIPS)){
8235 ship_self_destruct(&Objects[Net_players[np_index].player->objnum]);