2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
10 * $Logfile: /Freespace2/code/Network/MultiMsgs.cpp $
15 * C file that holds functions for the building and processing of multiplayer packets
18 * Revision 1.11 2006/04/26 19:48:58 taylor
19 * various big-endian fixes, mainly networking support related
21 * Revision 1.10 2005/10/02 09:30:10 taylor
22 * sync up rest of big-endian network changes. it should at least be as good as what's in FS2_Open now, only better :)
24 * Revision 1.9 2005/08/12 08:59:17 taylor
27 * Revision 1.8 2004/09/20 01:31:44 theoddone33
30 * Revision 1.7 2004/06/11 01:49:45 tigital
31 * byte-swapping changes for bigendian systems
33 * Revision 1.6 2003/08/03 16:10:29 taylor
34 * cleanup; compile warning fixes
36 * Revision 1.5 2002/06/17 06:33:10 relnev
37 * ryan's struct patch for gcc 2.95
39 * Revision 1.4 2002/06/09 04:41:24 relnev
40 * added copyright header
42 * Revision 1.3 2002/05/26 20:49:54 theoddone33
45 * Revision 1.2 2002/05/07 03:16:47 theoddone33
46 * The Great Newline Fix
48 * Revision 1.1.1.1 2002/05/03 03:28:10 root
52 * 83 9/14/99 2:21p Dave
53 * Fixed observer mode joining and ingame stuff.
55 * 82 9/14/99 3:26a Dave
56 * Fixed laser fogging problem in nebula (D3D)> Fixed multiplayer
57 * respawn-too-early problem. Made a few crash points safe.
59 * 81 9/13/99 4:52p Dave
62 * 80 9/08/99 10:01p Dave
63 * Make sure game won't run in a drive's root directory. Make sure
64 * standalone routes suqad war messages properly to the host.
66 * 79 8/28/99 4:54p Dave
67 * Fixed directives display for multiplayer clients for wings with
68 * multiple waves. Fixed hud threat indicator rendering color.
70 * 78 8/27/99 12:32a Dave
71 * Allow the user to specify a local port through the launcher.
73 * 77 8/26/99 8:51p Dave
74 * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
76 * 76 8/25/99 4:38p Dave
77 * Updated PXO stuff. Make squad war report stuff much more nicely.
79 * 75 8/24/99 1:50a Dave
80 * Fixed client-side afterburner stuttering. Added checkbox for no version
81 * checking on PXO join. Made button info passing more friendly between
84 * 74 8/22/99 5:53p Dave
85 * Scoring fixes. Added self destruct key. Put callsigns in the logfile
86 * instead of ship designations for multiplayer players.
88 * 73 8/22/99 1:55p Dave
89 * Cleaned up host/team-captain leaving code.
91 * 72 8/22/99 1:19p Dave
92 * Fixed up http proxy code. Cleaned up scoring code. Reverse the order in
93 * which d3d cards are detected.
95 * 71 8/19/99 10:59a Dave
96 * Packet loss detection.
98 * 70 8/17/99 1:12p Dave
99 * Send TvT update when a client has finished joining so he stays nice and
102 * 69 8/16/99 4:05p Dave
103 * Big honking checkin.
105 * 68 8/11/99 5:54p Dave
106 * Fixed collision problem. Fixed standalone ghost problem.
108 * 67 8/06/99 9:46p Dave
109 * Hopefully final changes for the demo.
111 * 66 8/05/99 2:06a Dave
114 * 65 7/30/99 7:01p Dave
115 * Dogfight escort gauge. Fixed up laser rendering in Glide.
117 * 64 7/29/99 5:41p Jefff
118 * Sound hooks for cmeasure success
120 * 63 7/28/99 5:34p Dave
121 * Nailed the missing stats bug to the wall. Problem was optimized build
122 * and using GET_DATA() with array elements. BLECH.
124 * 62 7/26/99 5:50p Dave
125 * Revised ingame join. Better? We'll see....
127 * 61 7/24/99 1:54p Dave
128 * Hud text flash gauge. Reworked dead popup to use 4 buttons in red-alert
131 * 60 7/22/99 7:17p Dave
132 * Fixed excessive whacks in multiplayer.
134 * 59 7/08/99 10:53a Dave
135 * New multiplayer interpolation scheme. Not 100% done yet, but still
136 * better than the old way.
138 * 58 7/03/99 5:50p Dave
139 * Make rotated bitmaps draw properly in padlock views.
141 * 57 7/03/99 4:08p Dave
142 * Fixed wss_slots size issues. Fixed potentially nasty bug in low level
145 * 56 6/21/99 7:24p Dave
146 * netplayer pain packet. Added type E unmoving beams.
148 * 55 6/18/99 5:16p Dave
149 * Added real beam weapon lighting. Fixed beam weapon sounds. Added MOTD
150 * dialog to PXO screen.
152 * 54 6/16/99 4:06p Dave
153 * New pilot info popup. Added new draw-bitmap-as-poly function.
155 * 53 6/16/99 10:20a Dave
156 * Added send-message-list sexpression.
158 * 52 6/04/99 3:52p Anoop
159 * Removed bogus assert.
161 * 51 6/01/99 8:35p Dave
162 * Finished lockarm weapons. Added proper supercap weapons/damage. Added
163 * awacs-set-radius sexpression.
165 * 50 5/21/99 5:03p Andsager
166 * Add code to display engine wash death. Modify ship_kill_packet
168 * 49 5/18/99 1:30p Dave
169 * Added muzzle flash table stuff.
171 * 48 5/14/99 1:59p Andsager
172 * Multiplayer message for subsystem cargo revealed.
174 * 47 5/14/99 12:15p Andsager
175 * Add vaporized to kill packet
177 * 46 5/03/99 8:32p Dave
178 * New version of multi host options screen.
180 * 45 4/30/99 12:18p Dave
181 * Several minor bug fixes.
183 * 44 4/29/99 2:29p Dave
184 * Made flak work much better in multiplayer.
186 * 43 4/28/99 11:13p Dave
187 * Temporary checkin of artillery code.
189 * 42 4/16/99 5:54p Dave
190 * Support for on/off style "stream" weapons. Real early support for
191 * target-painting lasers.
193 * 41 4/12/99 2:22p Dave
194 * More checks for dogfight stats.
196 * 40 4/09/99 2:21p Dave
197 * Multiplayer beta stuff. CD checking.
199 * 39 4/02/99 9:55a Dave
200 * Added a few more options in the weapons.tbl for beam weapons. Attempt
201 * at putting "pain" packets into multiplayer.
203 * 38 4/01/99 3:41p Anoop
204 * Removed bogus Int3().
206 * 37 3/19/99 9:51a Dave
207 * Checkin to repair massive source safe crash. Also added support for
208 * pof-style nebulae, and some new weapons code.
210 * 38 3/12/99 2:32p Anoop
211 * Removed bogus asserts.
213 * 37 3/11/99 11:41a Neilk
214 * Don't do multi_io_* operations in single-player
216 * 36 3/10/99 6:50p Dave
217 * Changed the way we buffer packets for all clients. Optimized turret
218 * fired packets. Did some weapon firing optimizations.
220 * 35 3/09/99 6:24p Dave
221 * More work on object update revamping. Identified several sources of
222 * unnecessary bandwidth.
224 * 34 3/08/99 7:03p Dave
225 * First run of new object update system. Looks very promising.
227 * 33 3/04/99 6:09p Dave
228 * Added in sexpressions for firing beams and checking for if a ship is
231 * 32 3/01/99 10:00a Dave
232 * Fxied several dogfight related stats bugs.
234 * 31 2/24/99 2:25p Dave
235 * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn
236 * bug for dogfight more.
238 * 30 2/23/99 2:29p Dave
239 * First run of oldschool dogfight mode.
241 * 29 2/21/99 6:01p Dave
242 * Fixed standalone WSS packets.
244 * 28 2/21/99 1:48p Dave
245 * Some code for monitoring datarate for multiplayer in detail.
247 * 27 2/17/99 2:11p Dave
248 * First full run of squad war. All freespace and tracker side stuff
251 * 26 2/12/99 6:16p Dave
252 * Pre-mission Squad War code is 95% done.
254 * 25 2/11/99 3:08p Dave
255 * PXO refresh button. Very preliminary squad war support.
257 * 24 1/29/99 5:07p Dave
258 * Fixed multiplayer stuff. Put in multiplayer support for rapid fire
261 * 23 1/27/99 9:56a Dave
262 * Temporary checkin of beam weapons for Dan to make cool sounds.
264 * 22 1/26/99 6:33p Anoop
265 * Fixed multiplayer slot switching problem (be sure to remember that
266 * hinfo->id is player id# _not_ player index #)
268 * 21 1/24/99 11:37p Dave
269 * First full rev of beam weapons. Very customizable. Removed some bogus
270 * Int3()'s in low level net code.
272 * 20 1/15/99 4:37p Dave
273 * Potential fix for weapon pair problem.
275 * 19 1/14/99 6:06p Dave
276 * 100% full squad logo support for single player and multiplayer.
278 * 18 1/14/99 12:48a Dave
279 * Todo list bug fixes. Made a pass at putting briefing icons back into
280 * FRED. Sort of works :(
282 * 17 1/12/99 5:45p Dave
283 * Moved weapon pipeline in multiplayer to almost exclusively client side.
284 * Very good results. Bandwidth goes down, playability goes up for crappy
285 * connections. Fixed object update problem for ship subsystems.
287 * 16 1/08/99 4:56p Anoop
288 * Fixed a problem with wss request packets.
290 * 15 12/18/98 12:24p Markm
291 * Fixed a dumb bug where player image_filenames were not being passed
292 * properly in new players packet.
294 * 14 12/14/98 12:13p Dave
295 * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
298 * 13 11/30/98 1:07p Dave
299 * 16 bit conversion, first run.
301 * 12 11/20/98 4:08p Dave
302 * Fixed flak effect in multiplayer.
304 * 11 11/19/98 4:19p Dave
305 * Put IPX sockets back in psnet. Consolidated all multiplayer config
308 * 10 11/19/98 8:04a Dave
309 * Full support for D3-style reliable sockets. Revamped packet lag/loss
310 * system, made it receiver side and at the lowest possible level.
312 * 9 11/17/98 11:12a Dave
313 * Removed player identification by address. Now assign explicit id #'s.
315 * 8 11/12/98 11:50a Dave
316 * Multiplayer clients set flak range to be very long.
318 * 7 11/12/98 12:13a Dave
319 * Tidied code up for multiplayer test. Put in network support for flak
322 * 6 11/05/98 5:55p Dave
323 * Big pass at reducing #includes
325 * 5 10/20/98 1:39p Andsager
326 * Make so sparks follow animated ship submodels. Modify
327 * ship_weapon_do_hit_stuff() and ship_apply_local_damage() to add
328 * submodel_num. Add submodel_num to multiplayer hit packet.
330 * 4 10/13/98 9:29a Dave
331 * Started neatening up freespace.h. Many variables renamed and
332 * reorganized. Added AlphaColors.[h,cpp]
334 * 3 10/07/98 6:27p Dave
335 * Globalized mission and campaign file extensions. Removed Silent Threat
336 * special code. Moved \cache \players and \multidata into the \data
339 * 2 10/07/98 10:53a Dave
342 * 1 10/07/98 10:50a Dave
344 * 506 10/02/98 3:22p Allender
345 * fix up the -connect option and fix the -port option
347 * 505 10/02/98 11:45a Dave
348 * Fixed stupid chat message bug.
350 * 504 9/29/98 1:33p Dave
351 * Remove standalone only conditional compiles for pre 1.04 stuff.
353 * 503 9/28/98 1:54p Dave
354 * Make sure French and German don't xfer builtin files they don't have
357 * 502 9/20/98 7:19p Dave
358 * Added CHANGE_IFF packet.
360 * 501 9/17/98 3:08p Dave
361 * PXO to non-pxo game warning popup. Player icon stuff in create and join
362 * game screens. Upped server count refresh time in PXO to 35 secs (from
371 #include <io.h> // for findfirst/findnext, etc
374 #include "multimsgs.h"
375 #include "multiutil.h"
378 #include "multiteamselect.h"
379 #include "linklist.h"
380 #include "gamesequence.h"
381 #include "hudmessage.h"
382 #include "hudsquadmsg.h"
383 #include "freespace.h"
387 #include "missiongoals.h"
388 #include "missionparse.h"
389 #include "missionlog.h"
390 #include "missionmessage.h"
391 #include "missionbrief.h"
393 #include "cmeasure.h"
394 #include "model.h" // for some limits
395 #include "afterburner.h"
396 #include "stand_server.h"
397 #include "multi_xfer.h"
403 #include "managepilot.h"
404 #include "hudsquadmsg.h"
406 #include "missionweaponchoice.h"
407 #include "missionshipchoice.h"
408 #include "fireballs.h"
411 #include "multi_ingame.h"
412 #include "multiteamselect.h"
414 #include "multi_campaign.h"
415 #include "multi_team.h"
416 #include "multi_respawn.h"
417 #include "multi_observer.h"
418 #include "multi_voice.h"
419 #include "asteroid.h"
420 #include "multi_pmsg.h"
421 #include "multi_data.h"
422 #include "multi_options.h"
423 #include "objcollide.h"
424 #include "hudreticle.h"
425 #include "multi_pause.h"
426 #include "multi_endgame.h"
427 #include "missiondebrief.h"
428 #include "multi_obj.h"
429 #include "multi_log.h"
431 #include "multi_kick.h"
435 #include "multi_rate.h"
436 #include "neblightning.h"
437 #include "hudescort.h"
438 #include "multi_fstracker.h"
439 #include "multi_sw.h"
441 // #define _MULTI_SUPER_WACKY_COMPRESSION
443 #ifdef _MULTI_SUPER_WACKY_COMPRESSION
445 #define MAX_CODE ( ( 1 << BITS ) - 1 )
446 #define TABLE_SIZE 35023L
447 #define END_OF_STREAM 256
448 #define BUMP_CODE 257
449 #define FLUSH_CODE 258
450 #define FIRST_CODE 259
459 static DICTIONARY dict[TABLE_SIZE];
460 static char decode_stack[TABLE_SIZE];
461 static uint next_code;
462 static int current_code_bits;
463 static uint next_bump_code;
465 typedef struct BitBuf {
471 void output_bits( BitBuf *bitbuf, uint code, int count )
475 mask = 1L << ( count - 1 );
478 bitbuf->rack |= bitbuf->mask;
480 if ( bitbuf->mask == 0 ) {
481 *bitbuf->data++=(ubyte)bitbuf->rack;
489 uint input_bits( BitBuf *bitbuf, int bit_count )
494 mask = 1L << ( bit_count - 1 );
497 if ( bitbuf->mask == 0x80 ) {
498 bitbuf->rack = *bitbuf->data++;
499 if ( bitbuf->rack == EOF )
500 return END_OF_STREAM;
502 if ( bitbuf->rack & bitbuf->mask )
503 return_value |= mask;
506 if ( bitbuf->mask == 0 )
509 return( return_value );
513 static void InitializeDictionary()
517 for ( i = 0 ; i < TABLE_SIZE ; i++ )
518 dict[i].code_value = UNUSED;
520 next_code = FIRST_CODE;
521 current_code_bits = 9;
522 next_bump_code = 511;
526 static uint find_child_node( int parent_code, int child_character )
531 index = ( child_character << ( BITS - 8 ) ) ^ parent_code;
535 offset = TABLE_SIZE - index;
537 if ( dict[ index ].code_value == UNUSED )
538 return( (uint) index );
539 if ( dict[ index ].parent_code == parent_code &&
540 dict[ index ].character == (char) child_character )
542 if ( (int) index >= offset )
545 index += TABLE_SIZE - offset;
550 static uint decode_string( uint count, uint code )
552 while ( code > 255 ) {
553 decode_stack[ count++ ] = dict[ code ].character;
554 code = dict[ code ].parent_code;
556 decode_stack[ count++ ] = (char) code;
560 int lzw_compress( ubyte *outputbuf, ubyte *inputbuf, int input_size )
568 // Init output bit buffer
571 output.data = outputbuf;
573 InitializeDictionary();
575 string_code = *inputbuf++;
577 for ( i=1 ; i<input_size ; i++ ) {
578 character = *inputbuf++;
579 index = find_child_node( string_code, character );
580 if ( dict[ index ].code_value != - 1 )
581 string_code = dict[ index ].code_value;
583 dict[ index ].code_value = next_code++;
584 dict[ index ].parent_code = string_code;
585 dict[ index ].character = (char) character;
586 output_bits( &output, (unsigned long) string_code, current_code_bits );
587 string_code = character;
588 if ( next_code > MAX_CODE ) {
589 output_bits( &output, (unsigned long) FLUSH_CODE, current_code_bits );
590 InitializeDictionary();
591 } else if ( next_code > next_bump_code ) {
592 output_bits( &output, (unsigned long) BUMP_CODE, current_code_bits );
594 next_bump_code <<= 1;
599 output_bits( &output, (unsigned long) string_code, current_code_bits );
600 output_bits( &output, (unsigned long) END_OF_STREAM, current_code_bits);
602 if ( output.mask != 0x80 )
603 *output.data++ = (ubyte)output.rack;
605 return output.data-outputbuf;
609 int lzw_expand( ubyte *outputbuf, ubyte *inputbuf )
620 input.data = inputbuf;
624 InitializeDictionary();
625 old_code = (uint) input_bits( &input, current_code_bits );
626 if ( old_code == END_OF_STREAM )
628 character = old_code;
629 outputbuf[counter++] = ( ubyte )old_code;
631 new_code = (uint) input_bits( &input, current_code_bits );
632 if ( new_code == END_OF_STREAM )
634 if ( new_code == FLUSH_CODE )
636 if ( new_code == BUMP_CODE ) {
640 if ( new_code >= next_code ) {
641 decode_stack[ 0 ] = (char) character;
642 count = decode_string( 1, old_code );
644 count = decode_string( 0, new_code );
646 character = decode_stack[ count - 1 ];
648 outputbuf[counter++] = ( ubyte )decode_stack[ --count ];
649 dict[ next_code ].parent_code = old_code;
650 dict[ next_code ].character = (char) character;
658 // process a join request packet add
659 void add_join_request(ubyte *data, int *size, join_request *jr)
661 int packet_size = *size;
662 join_request *jr_tmp = jr;
664 jr_tmp->tracker_id = INTEL_INT(jr->tracker_id);
665 jr_tmp->player_options.flags = INTEL_INT(jr->player_options.flags);
666 jr_tmp->player_options.obj_update_level = INTEL_INT(jr->player_options.obj_update_level);
673 // process a join request packet get
674 void get_join_request(ubyte *data, int *size, join_request jr)
680 jr.tracker_id = INTEL_INT(jr.tracker_id);
681 jr.player_options.flags = INTEL_INT(jr.player_options.flags);
682 jr.player_options.obj_update_level = INTEL_INT(jr.player_options.obj_update_level);
687 struct net_addr_compat {
694 SDL_COMPILE_TIME_ASSERT(net_addr_compat, sizeof(net_addr_compat) == 16);
696 void add_net_addr(ubyte *data, int &size, const net_addr *addr)
698 int packet_size = size;
699 net_addr_compat addr_c;
703 addr_c.type = INTEL_INT(addr->type);
704 addr_c.port = INTEL_SHORT(addr->port);
705 memcpy(&addr_c.addr, &addr->addr, IP_ADDRESS_LENGTH);
712 void get_net_addr(const ubyte *data, int &size, net_addr &addr)
715 net_addr_compat addr_c;
721 addr.type = INTEL_INT(addr_c.type);
722 addr.port = INTEL_SHORT(addr_c.port);
723 memcpy(&addr.addr, &addr_c.addr, IP_ADDRESS_LENGTH);
728 void add_vector_data(ubyte *data, int *size, vector vec)
730 int packet_size = *size;
732 ADD_FLOAT(vec.xyz.x);
733 ADD_FLOAT(vec.xyz.y);
734 ADD_FLOAT(vec.xyz.z);
739 void get_vector_data(ubyte *data, int *size, vector vec)
743 GET_FLOAT(vec.xyz.x);
744 GET_FLOAT(vec.xyz.y);
745 GET_FLOAT(vec.xyz.z);
750 // send the specified data packet to all players
751 void multi_io_send(net_player *pl, ubyte *data, int len)
754 if((pl == NULL) || (NET_PLAYER_NUM(pl) >= MAX_PLAYERS)){
758 // don't do it for single player
759 if(!(Game_mode & GM_MULTIPLAYER)){
764 if(MULTIPLAYER_CLIENT){
765 // SDL_assert(pl == Net_player);
766 if(pl != Net_player){
770 // SDL_assert(pl != Net_player);
771 if(pl == Net_player){
776 // If this packet will push the buffer over MAX_PACKET_SIZE, send the current send_buffer
777 if ((pl->s_info.unreliable_buffer_size + len) > MAX_PACKET_SIZE) {
778 multi_io_send_force(pl);
779 pl->s_info.unreliable_buffer_size = 0;
782 SDL_assert((pl->s_info.unreliable_buffer_size + len) <= MAX_PACKET_SIZE);
784 memcpy(pl->s_info.unreliable_buffer + pl->s_info.unreliable_buffer_size, data, len);
785 pl->s_info.unreliable_buffer_size += len;
788 void multi_io_send_to_all(ubyte *data, int length, net_player *ignore)
791 SDL_assert(MULTIPLAYER_MASTER);
793 // need to check for i > 1, hmmm... and connected. I don't know.
794 for (i = 0; i < MAX_PLAYERS; i++ ) {
795 if ( !MULTI_CONNECTED(Net_players[i]) || (Net_player == &Net_players[i])){
799 // maybe ignore a player
800 if((ignore != NULL) && (&Net_players[i] == ignore)){
804 // ingame joiners not waiting to select a ship doesn't get any packets
805 if ( (Net_players[i].flags & NETINFO_FLAG_INGAME_JOIN) && !(Net_players[i].flags & INGAME_JOIN_FLAG_PICK_SHIP) ){
810 multi_io_send(&Net_players[i], data, length);
814 void multi_io_send_force(net_player *pl)
817 if((pl == NULL) || (NET_PLAYER_NUM(pl) >= MAX_PLAYERS)){
821 // don't do it for single player
822 if(!(Game_mode & GM_MULTIPLAYER)){
826 // send everything in
827 if (MULTIPLAYER_MASTER) {
828 psnet_send(&pl->p_info.addr, pl->s_info.unreliable_buffer, pl->s_info.unreliable_buffer_size, NET_PLAYER_NUM(pl));
830 // add the bytes sent to this player
831 pl->sv_bytes_sent += pl->s_info.unreliable_buffer_size;
833 psnet_send(&Netgame.server_addr, pl->s_info.unreliable_buffer, pl->s_info.unreliable_buffer_size, NET_PLAYER_NUM(pl));
835 pl->s_info.unreliable_buffer_size = 0;
838 // send the data packet to all players via their reliable sockets
839 void multi_io_send_reliable(net_player *pl, ubyte *data, int len)
842 if((pl == NULL) || (NET_PLAYER_NUM(pl) >= MAX_PLAYERS)){
846 // don't do it for single player
847 if(!(Game_mode & GM_MULTIPLAYER)){
852 if(MULTIPLAYER_CLIENT){
853 // SDL_assert(pl == Net_player);
854 if(pl != Net_player){
858 // SDL_assert(pl != Net_player);
859 if(pl == Net_player){
864 // If this packet will push the buffer over MAX_PACKET_SIZE, send the current send_buffer
865 if ((pl->s_info.reliable_buffer_size + len) > MAX_PACKET_SIZE) {
866 multi_io_send_reliable_force(pl);
867 pl->s_info.reliable_buffer_size = 0;
870 SDL_assert((pl->s_info.reliable_buffer_size + len) <= MAX_PACKET_SIZE);
872 memcpy(pl->s_info.reliable_buffer + pl->s_info.reliable_buffer_size, data, len);
873 pl->s_info.reliable_buffer_size += len;
876 void multi_io_send_to_all_reliable(ubyte* data, int length, net_player *ignore)
879 SDL_assert(MULTIPLAYER_MASTER);
881 // need to check for i > 1, hmmm... and connected. I don't know.
882 for (i = 0; i < MAX_PLAYERS; i++ ) {
883 if ( !MULTI_CONNECTED(Net_players[i]) || (Net_player == &Net_players[i])){
887 // maybe ignore a player
888 if((ignore != NULL) && (&Net_players[i] == ignore)){
892 // ingame joiners not waiting to select a ship doesn't get any packets
893 if ( (Net_players[i].flags & NETINFO_FLAG_INGAME_JOIN) && !(Net_players[i].flags & INGAME_JOIN_FLAG_PICK_SHIP) ){
898 multi_io_send_reliable(&Net_players[i], data, length);
902 void multi_io_send_reliable_force(net_player *pl)
905 if((pl == NULL) || (NET_PLAYER_NUM(pl) >= MAX_PLAYERS)){
909 // don't do it for single player
910 if(!(Game_mode & GM_MULTIPLAYER)){
914 // send everything in
915 if(MULTIPLAYER_MASTER) {
916 psnet_rel_send(pl->reliable_socket, pl->s_info.reliable_buffer, pl->s_info.reliable_buffer_size, NET_PLAYER_NUM(pl));
917 } else if(Net_player != NULL){
918 psnet_rel_send(Net_player->reliable_socket, pl->s_info.reliable_buffer, pl->s_info.reliable_buffer_size, NET_PLAYER_NUM(pl));
920 pl->s_info.reliable_buffer_size = 0;
923 // send all buffered packets
924 void multi_io_send_buffered_packets()
928 // don't do it for single player
929 if(!(Game_mode & GM_MULTIPLAYER)){
934 if(MULTIPLAYER_MASTER){
935 for(idx=0; idx<MAX_PLAYERS; idx++){
936 if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx])){
937 // force unreliable data
938 if(Net_players[idx].s_info.unreliable_buffer_size > 0){
939 multi_io_send_force(&Net_players[idx]);
940 Net_players[idx].s_info.unreliable_buffer_size = 0;
943 // force reliable data
944 if(Net_players[idx].s_info.reliable_buffer_size > 0){
945 multi_io_send_reliable_force(&Net_players[idx]);
946 Net_players[idx].s_info.reliable_buffer_size = 0;
952 else if(Net_player != NULL){
953 // force unreliable data
954 if(Net_player->s_info.unreliable_buffer_size > 0){
955 multi_io_send_force(Net_player);
956 Net_player->s_info.unreliable_buffer_size = 0;
959 // force reliable data
960 if(Net_player->s_info.reliable_buffer_size > 0){
961 multi_io_send_reliable_force(Net_player);
962 Net_player->s_info.reliable_buffer_size = 0;
967 // 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)
968 void send_game_chat_packet(net_player *from, const char *msg, int msg_mode, net_player *to, const char *expr, int server_msg)
970 ubyte data[MAX_PACKET_SIZE],mode;
973 BUILD_HEADER(GAME_CHAT);
976 ADD_SHORT(from->player_id);
978 // add the message mode and if in MSG_TARGET mode, add who the target is
980 mode = (ubyte)msg_mode;
983 case MULTI_MSG_TARGET:
984 SDL_assert(to != NULL);
985 ADD_SHORT(to->player_id);
988 SDL_assert(expr != NULL);
992 // add the message itself
995 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
997 // message all players
999 for(idx=0;idx<MAX_PLAYERS;idx++){
1000 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (&Net_players[idx] != from)){
1001 multi_io_send_reliable(&Net_players[idx], data, packet_size);
1006 // message only friendly players
1007 case MULTI_MSG_FRIENDLY:
1008 for(idx=0;idx<MAX_PLAYERS;idx++){
1009 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)){
1010 multi_io_send_reliable(&Net_players[idx], data, packet_size);
1015 // message only hostile players
1016 case MULTI_MSG_HOSTILE:
1017 for(idx=0;idx<MAX_PLAYERS;idx++){
1018 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)){
1019 multi_io_send_reliable(&Net_players[idx], data, packet_size);
1024 // message the player's target
1025 case MULTI_MSG_TARGET:
1026 SDL_assert(to != NULL);
1027 if(MULTI_CONNECTED((*to)) && !MULTI_STANDALONE((*to))){
1028 multi_io_send_reliable(to, data, packet_size);
1032 // message all players who match the expression string
1033 case MULTI_MSG_EXPR:
1034 SDL_assert(expr != NULL);
1035 for(idx=0;idx<MAX_PLAYERS;idx++){
1036 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (&Net_players[idx] != from) && multi_msg_matches_expr(&Net_players[idx],expr) ){
1037 multi_io_send_reliable(&Net_players[idx], data, packet_size);
1043 // send to the server, who will take care of routing it
1045 multi_io_send_reliable(Net_player, data, packet_size);
1049 // process a general game chat packet, if we're the standalone we should rebroadcast
1050 void process_game_chat_packet( ubyte *data, header *hinfo )
1054 int color_index,player_index,to_player_index,should_display,server_msg;
1055 char msg[MULTI_MSG_MAX_TEXT_LEN+CALLSIGN_LEN+2];
1059 offset = HEADER_LENGTH;
1061 // get the id of the sender
1064 // determine if this is a server message
1065 GET_INT(server_msg);
1070 // if targeting a specific player, get the address
1073 case MULTI_MSG_TARGET:
1076 case MULTI_MSG_EXPR:
1080 // get the message itself
1084 // get the index of the sending player
1085 color_index = find_player_id(from);
1086 player_index = color_index;
1088 // if we couldn't find the player - bail
1089 if(player_index == -1){
1090 nprintf(("Network","Could not find player for processing game chat packet!\n"));
1096 // if we're the server, determine what to do with the packet here
1097 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
1098 // if he's targeting a specific player, find out who it is
1099 if(mode == MULTI_MSG_TARGET){
1100 to_player_index = find_player_id(to);
1102 to_player_index = -1;
1105 // if we couldn't find who sent the message or who should be getting the message, the bail
1106 if(((to_player_index == -1) && (mode == MULTI_MSG_TARGET)) || (player_index == -1)){
1110 // determine if _I_ should be seeing the text
1111 if(Game_mode & GM_STANDALONE_SERVER){
1114 // check against myself for several specific cases
1116 if((mode == MULTI_MSG_ALL) ||
1117 ((mode == MULTI_MSG_FRIENDLY) && (Net_player->p_info.team == Net_players[player_index].p_info.team)) ||
1118 ((mode == MULTI_MSG_HOSTILE) && (Net_player->p_info.team != Net_players[player_index].p_info.team)) ||
1119 ((mode == MULTI_MSG_TARGET) && (MY_NET_PLAYER_NUM == to_player_index)) ||
1120 ((mode == MULTI_MSG_EXPR) && multi_msg_matches_expr(Net_player,expr)) ){
1125 // if we're the server of a game, we need to rebroadcast to all other players
1127 // individual target mission
1128 case MULTI_MSG_TARGET:
1129 // if I was the inteneded target, or we couldn't find the intended target, don't rebroadcast
1130 if(to_player_index != MY_NET_PLAYER_NUM){
1131 send_game_chat_packet(&Net_players[player_index], msg, (int)mode, &Net_players[to_player_index], NULL, server_msg);
1135 case MULTI_MSG_EXPR:
1136 send_game_chat_packet(&Net_players[player_index], msg, (int)mode, NULL, expr, server_msg);
1140 send_game_chat_packet(&Net_players[player_index], msg, (int)mode, NULL, NULL, server_msg);
1144 // if a client receives this packet, its always ok for him to display it
1149 // if we're not on a standalone
1151 if(server_msg == 2){
1154 multi_display_chat_msg(msg, player_index, !server_msg);
1159 // broadcast a hud message to all players
1160 void send_hud_msg_to_all( char* msg )
1162 ubyte data[MAX_PACKET_SIZE];
1165 // only the server should be sending this packet
1166 BUILD_HEADER(HUD_MSG);
1170 multi_io_send_to_all( data, packet_size );
1173 // process an incoming hud message packet
1174 void process_hud_message(ubyte* data, header* hinfo)
1177 char msg_buffer[255];
1179 offset = HEADER_LENGTH;
1181 GET_STRING(msg_buffer);
1184 // this is the only safe place to do this since only in the mission is the HUD guaranteed to be inited
1185 if(Game_mode & GM_IN_MISSION){
1186 HUD_printf(msg_buffer);
1190 // send a join packet request to the specified address (should be a server)
1191 void send_join_packet(net_addr* addr,join_request *jr)
1193 ubyte data[MAX_PACKET_SIZE];
1196 // build the header and add the request
1199 add_join_request(data, &packet_size, jr);
1201 psnet_send(addr, data, packet_size);
1204 // process an incoming join request packet
1205 void process_join_packet(ubyte* data, header* hinfo)
1210 int host_restr_mode;
1211 // int team0_avail,team1_avail;
1212 char join_string[255];
1215 // only the server of the game should ever receive this packet
1216 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) )
1219 offset = HEADER_LENGTH;
1221 // read in the request info
1222 memset(&jr,0,sizeof(join_request));
1225 jr.tracker_id = INTEL_INT(jr.tracker_id);
1229 // fill in the address information of where this came from
1230 fill_net_addr(&addr, hinfo->addr, hinfo->port);
1232 // determine if we should accept this guy, or return a reason we should reject him
1233 // see the DENY_* codes in multi.h
1234 ret_code = multi_eval_join_request(&jr,&addr);
1236 // evaluate the return code
1238 // he should be accepted
1242 // we have to query the host because this is a restricted game
1243 case JOIN_QUERY_RESTRICTED :
1244 if(!(Game_mode & GM_STANDALONE_SERVER)){
1245 // notify the host of the event
1246 snd_play(&Snds[SND_CUE_VOICE]);
1249 // set the query timestamp
1250 Multi_restr_query_timestamp = timestamp(MULTI_QUERY_RESTR_STAMP);
1251 Netgame.flags |= NG_FLAG_INGAME_JOINING;
1253 // determine what mode we're in
1254 // host_restr_mode = -1;
1255 memset(join_string,0,255);
1256 // if(Netgame.type == NG_TYPE_TEAM){
1257 // multi_player_ships_available(&team0_avail,&team1_avail);
1259 // if(team0_avail && team1_avail){
1260 // host_restr_mode = MULTI_JOIN_RESTR_MODE_4;
1261 // sprintf(join_string,"Player %s has tried to join. Accept on team 1 or 2 ?",jr.callsign);
1262 // } else if(team0_avail && !team1_avail){
1263 // host_restr_mode = MULTI_JOIN_RESTR_MODE_2;
1264 // sprintf(join_string,"Player %s has tried to join team 0, accept y/n ? ?",jr.callsign);
1265 // } else if(!team0_avail && team1_avail){
1266 // host_restr_mode = MULTI_JOIN_RESTR_MODE_3;
1267 // sprintf(join_string,"Player %s has tried to join team 1, accept y/n ?",jr.callsign);
1269 // } else if(Netgame.mode == NG_MODE_RESTRICTED){
1270 host_restr_mode = MULTI_JOIN_RESTR_MODE_1;
1271 SDL_snprintf(join_string,SDL_arraysize(join_string),XSTR("Player %s has tried to join, accept y/n ?",715),jr.callsign);
1273 SDL_assert(host_restr_mode != -1);
1275 // store the request info
1276 memcpy(&Multi_restr_join_request,&jr,sizeof(join_request));
1277 memcpy(&Multi_restr_addr,&addr,sizeof(net_addr));
1278 Multi_join_restr_mode = host_restr_mode;
1280 // if i'm the standalone server, I need to send a query to the host
1281 if(Game_mode & GM_STANDALONE_SERVER){
1282 send_host_restr_packet(jr.callsign,0,Multi_join_restr_mode);
1284 HUD_printf(join_string);
1288 ml_printf(NOX("Receive restricted join request from %s"), jr.callsign);
1292 // he'e being denied for some reason
1294 // send him the reason he is being denied
1295 send_deny_packet(&addr,ret_code);
1299 // process the rest of the request
1300 multi_process_valid_join_request(&jr,&addr);
1303 // send a notification that a new player has joined the game (if target != NULL, broadcast the packet)
1304 void send_new_player_packet(int new_player_num,net_player *target)
1306 ubyte data[MAX_PACKET_SIZE], val;
1307 int packet_size = 0;
1309 BUILD_HEADER( NOTIFY_NEW_PLAYER );
1311 // add the new player's info
1312 ADD_INT(new_player_num);
1313 // ADD_DATA(Net_players[new_player_num].p_info.addr);
1315 add_net_addr(data, packet_size, &Net_players[new_player_num].p_info.addr);
1317 ADD_SHORT(Net_players[new_player_num].player_id);
1318 ADD_INT(Net_players[new_player_num].flags);
1319 ADD_STRING(Net_players[new_player_num].player->callsign);
1320 ADD_STRING(Net_players[new_player_num].player->image_filename);
1321 ADD_STRING(Net_players[new_player_num].player->squad_filename);
1322 ADD_STRING(Net_players[new_player_num].p_info.pxo_squad_name);
1324 val = (ubyte)Net_players[new_player_num].p_info.team;
1327 // broadcast the data
1329 multi_io_send_reliable(target, data, packet_size);
1331 multi_io_send_to_all_reliable(data, packet_size);
1335 // process a notification for a new player who has joined the game
1336 void process_new_player_packet(ubyte* data, header* hinfo)
1338 int already_in_game = 0;
1339 int offset, new_player_num,player_num,new_flags;
1341 char new_player_name[CALLSIGN_LEN+2] = "";
1342 char new_player_image[MAX_FILENAME_LEN+1] = "";
1343 char new_player_squad[MAX_FILENAME_LEN+1] = "";
1344 char new_player_pxo_squad[LOGIN_LEN+1] = "";
1345 char notify_string[256];
1349 offset = HEADER_LENGTH;
1351 // get the new players information
1352 GET_INT(new_player_num);
1353 memset(&new_addr, 0, sizeof(net_addr));
1354 get_net_addr(data, offset, new_addr);
1358 GET_STRING(new_player_name);
1359 GET_STRING(new_player_image);
1360 GET_STRING(new_player_squad);
1361 GET_STRING(new_player_pxo_squad);
1365 player_num = multi_find_open_player_slot();
1366 SDL_assert(player_num != -1);
1368 // note that this new code does not check for duplicate IPs. It merely checks to see if
1369 // the slot referenced by new_player_num is already occupied by a connected player
1370 if(MULTI_CONNECTED(Net_players[new_player_num])){
1374 // if he's not alreayd in the game for one reason or another
1375 if ( !already_in_game ) {
1376 if ( Game_mode & GM_IN_MISSION ){
1377 HUD_sourced_printf(HUD_SOURCE_COMPUTER, XSTR("%s has entered the game\n",716), new_player_name);
1380 // create the player
1381 if(new_flags & NETINFO_FLAG_OBSERVER){
1382 multi_obs_create_player(new_player_num,new_player_name,&new_addr,&Players[player_num]);
1383 Net_players[new_player_num].flags |= new_flags;
1385 multi_create_player( new_player_num, &Players[player_num],new_player_name, &new_addr, -1, new_id );
1386 Net_players[new_player_num].flags |= new_flags;
1389 // copy in the filename
1390 if(strlen(new_player_image) > 0){
1391 SDL_strlcpy(Net_players[new_player_num].player->image_filename, new_player_image, MAX_FILENAME_LEN);
1393 SDL_strlcpy(Net_players[new_player_num].player->image_filename, "", MAX_FILENAME_LEN);
1395 // copy his pilot squad filename
1396 Net_players[new_player_num].player->insignia_texture = -1;
1397 player_set_squad_bitmap(Net_players[new_player_num].player, new_player_squad);
1399 // copy in his pxo squad name
1400 SDL_strlcpy(Net_players[new_player_num].p_info.pxo_squad_name, new_player_pxo_squad, LOGIN_LEN);
1402 // since we just created the player, set the last_heard_time here.
1403 Net_players[new_player_num].last_heard_time = timer_get_fixed_seconds();
1405 Net_players[new_player_num].p_info.team = team;
1407 Net_players[new_player_num].player_id = new_id;
1409 // zero out this players ping
1410 multi_ping_reset(&Net_players[new_player_num].s_info.ping);
1412 // add a chat message
1413 if(Net_players[new_player_num].player->callsign[0]){
1414 SDL_snprintf(notify_string,SDL_arraysize(notify_string),XSTR("<%s has joined>",717),Net_players[new_player_num].player->callsign);
1415 multi_display_chat_msg(notify_string,0,0);
1420 ml_printf(NOX("Received notification of new player %s"), Net_players[new_player_num].player->callsign);
1422 // let the current ui screen know someone joined
1423 switch(gameseq_get_state()){
1424 case GS_STATE_MULTI_HOST_SETUP :
1425 multi_create_handle_join(&Net_players[new_player_num]);
1427 case GS_STATE_MULTI_CLIENT_SETUP :
1428 multi_jw_handle_join(&Net_players[new_player_num]);
1433 #define PLAYER_DATA_SLOP 100
1435 void send_accept_player_data( net_player *npp, int is_ingame )
1439 ubyte data[MAX_PACKET_SIZE], stop;
1441 BUILD_HEADER(ACCEPT_PLAYER_DATA);
1443 // add in the netplayer data for all players
1445 for (i=0; i<MAX_PLAYERS; i++) {
1446 // skip non connected players
1447 if ( !MULTI_CONNECTED(Net_players[i]) ){
1451 // skip this new player's entry
1452 if ( npp->player_id == Net_players[i].player_id ){
1456 // add the stop byte
1459 // add the player's number
1462 // add the player's address
1463 // ADD_DATA(Net_players[i].p_info.addr);
1464 add_net_addr(data, packet_size, &Net_players[i].p_info.addr);
1467 ADD_SHORT(Net_players[i].player_id);
1470 ADD_STRING(Net_players[i].player->callsign);
1472 // add his image filename
1473 ADD_STRING(Net_players[i].player->image_filename);
1475 // add his squad filename
1476 ADD_STRING(Net_players[i].player->squad_filename);
1478 // add his PXO squad name
1479 ADD_STRING(Net_players[i].p_info.pxo_squad_name);
1482 ADD_INT(Net_players[i].flags);
1484 // add his object's net sig
1486 ADD_USHORT( Objects[Net_players[i].player->objnum].net_signature );
1489 if ( (packet_size + PLAYER_DATA_SLOP) > MAX_PACKET_SIZE ) {
1490 stop = APD_END_PACKET;
1492 multi_io_send_reliable( npp, data, packet_size );
1493 BUILD_HEADER(ACCEPT_PLAYER_DATA);
1499 // add the stop byte
1500 stop = APD_END_DATA;
1502 multi_io_send_reliable(npp, data, packet_size);
1505 // send an accept packet to a client in response to a request to join the game
1506 void send_accept_packet(int new_player_num, int code, int ingame_join_team)
1508 int packet_size = 0, i;
1509 ubyte data[MAX_PACKET_SIZE],val;
1510 char notify_string[256];
1513 SDL_assert(new_player_num >= 0);
1515 // setup his "reliable" socket
1516 Net_players[new_player_num].last_heard_time = timer_get_fixed_seconds();
1518 // build the packet header
1519 BUILD_HEADER(ACCEPT);
1521 // add the accept code
1524 // add code specific accept data
1525 if (code & ACCEPT_INGAME) {
1526 // the game filename
1527 ADD_STRING(Game_current_mission_filename);
1529 // if he is joining on a specific team, mark it here
1530 if(ingame_join_team != -1){
1533 val = (ubyte)ingame_join_team;
1541 if (code & ACCEPT_OBSERVER) {
1542 SDL_assert(!(code & (ACCEPT_CLIENT | ACCEPT_HOST)));
1545 if (code & ACCEPT_HOST) {
1546 SDL_assert(!(code & (ACCEPT_CLIENT | ACCEPT_OBSERVER | ACCEPT_INGAME)));
1549 if (code & ACCEPT_CLIENT) {
1550 SDL_assert(!(code & (ACCEPT_HOST | ACCEPT_OBSERVER | ACCEPT_INGAME)));
1553 // add the current skill level setting on the host
1554 ADD_INT(Game_skill_level);
1556 // add this guys player num
1557 ADD_INT(new_player_num);
1559 // add his player id
1560 ADD_SHORT(Net_players[new_player_num].player_id);
1562 // add netgame type flags
1563 ADD_INT(Netgame.type_flags);
1566 // char buffer[100];
1567 // nprintf(("Network", "About to send accept packet to %s on port %d\n", get_text_address(buffer, addr->addr), addr->port ));
1570 // actually send the packet
1571 psnet_send(&Net_players[new_player_num].p_info.addr, data, packet_size);
1573 // if he's not an observer, inform all the other players in the game about him
1574 // inform the other players in the game about this new player
1575 for (i=0; i<MAX_PLAYERS; i++) {
1576 // skip unconnected players as well as this new guy himself
1577 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])) {
1581 // send the new packet
1582 send_new_player_packet(new_player_num,&Net_players[i]);
1585 // add a chat message
1586 if(Net_players[new_player_num].player->callsign[0]){
1587 SDL_snprintf(notify_string,SDL_arraysize(notify_string),XSTR("<%s has joined>",717), Net_players[new_player_num].player->callsign);
1588 multi_display_chat_msg(notify_string, 0, 0);
1591 // handle any team vs. team details
1592 if (!(code & ACCEPT_OBSERVER)) {
1593 multi_team_handle_join(&Net_players[new_player_num]);
1597 if(Net_players[new_player_num].tracker_player_id >= 0){
1598 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);
1600 ml_printf(NOX("Server accepted %s as new client"), Net_players[new_player_num].player->callsign);
1605 // process the player data from the server
1606 void process_accept_player_data( ubyte *data, header *hinfo )
1608 int offset, player_num, player_slot_num, new_flags;
1609 char name[CALLSIGN_LEN + 1] = "";
1610 char image_name[MAX_FILENAME_LEN + 1] = "";
1611 char squad_name[MAX_FILENAME_LEN + 1] = "";
1612 char pxo_squad_name[LOGIN_LEN+1] = "";
1616 ushort ig_signature;
1618 offset = HEADER_LENGTH;
1621 while ( stop == APD_NEXT ) {
1622 player_slot_num = multi_find_open_player_slot();
1623 SDL_assert(player_slot_num != -1);
1625 // get the player's number
1626 GET_INT(player_num);
1628 // add the player's address
1629 memset(&addr, 0, sizeof(net_addr));
1630 get_net_addr(data, offset, addr);
1632 // get the player's id#
1633 GET_SHORT(player_id);
1638 // add his image filename
1639 GET_STRING(image_name);
1641 // get his squad logo filename
1642 GET_STRING(squad_name);
1644 // get his PXO squad name
1645 GET_STRING(pxo_squad_name);
1650 if (Net_players[player_num].flags & NETINFO_FLAG_OBSERVER) {
1651 if (!multi_obs_create_player(player_num, name, &addr, &Players[player_slot_num])) {
1656 // the error handling here is less than stellar. We should probably put up a popup and go
1657 // back to the main menu. But then again, this should never ever happen!
1658 if ( !multi_create_player(player_num, &Players[player_slot_num],name, &addr, -1, player_id) ) {
1663 // copy his image filename
1664 SDL_strlcpy(Net_players[player_num].player->image_filename, image_name, MAX_FILENAME_LEN);
1666 // copy his pilot squad filename
1667 Net_players[player_num].player->insignia_texture = -1;
1668 player_set_squad_bitmap(Net_players[player_num].player, squad_name);
1670 // copy his pxo squad name
1671 SDL_strlcpy(Net_players[player_num].p_info.pxo_squad_name, pxo_squad_name, LOGIN_LEN);
1673 // set his player id#
1674 Net_players[player_num].player_id = player_id;
1676 // mark him as being connected
1677 Net_players[player_num].flags |= NETINFO_FLAG_CONNECTED;
1678 Net_players[player_num].flags |= new_flags;
1680 // set the server pointer
1681 if ( Net_players[player_num].flags & NETINFO_FLAG_AM_MASTER ) {
1682 Netgame.server = &Net_players[player_num];
1683 Netgame.server->last_heard_time = timer_get_fixed_seconds();
1685 // also - always set the server address to be where this data came from, NOT from
1686 // the data in the packet
1687 fill_net_addr(&Net_players[player_num].p_info.addr, hinfo->addr, hinfo->port);
1690 // set the host pointer
1691 if ( Net_players[player_num].flags & NETINFO_FLAG_GAME_HOST ) {
1692 Netgame.host = &Net_players[player_num];
1695 // read in the player's object net signature and store as his objnum for now
1696 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_INGAME ) {
1697 GET_USHORT( ig_signature );
1698 Net_players[player_num].player->objnum = ig_signature;
1701 // get the stop byte
1706 if ( stop == APD_END_DATA ) {
1707 // if joining a game automatically, set the connect address to NULl so we don't try and
1708 // do this next time we enter a game
1709 if (Cmdline_connect_addr != NULL) {
1710 Cmdline_connect_addr = NULL;
1713 // send my stats to the server if I'm not in observer mode
1714 if (!(Net_player->flags & NETINFO_FLAG_ACCEPT_OBSERVER)) {
1715 send_player_stats_block_packet(Net_player, STATS_ALLTIME);
1718 // if i'm being accepted as a host, then move into the host setup state
1719 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_HOST) {
1720 // set my permission bits
1721 Net_player->flags |= NETINFO_FLAG_GAME_HOST;
1722 Net_player->state = NETPLAYER_STATE_STD_HOST_SETUP;
1724 gameseq_post_event(GS_EVENT_MULTI_START_GAME);
1727 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_OBSERVER) {
1728 Net_player->flags |= NETINFO_FLAG_OBSERVER;
1730 // since observers can join 1 of 2 ways, only do this if we're not doing an ingame observer join
1731 if ( !(Net_player->flags & NETINFO_FLAG_ACCEPT_INGAME) ) {
1732 gameseq_post_event(GS_EVENT_MULTI_CLIENT_SETUP);
1736 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_CLIENT) {
1737 gameseq_post_event(GS_EVENT_MULTI_CLIENT_SETUP);
1740 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_INGAME) {
1741 // flag myself as being an ingame joiner
1742 Net_player->flags |= NETINFO_FLAG_INGAME_JOIN;
1744 // move myself into the ingame join mission sync state
1745 Multi_sync_mode = MULTI_SYNC_INGAME;
1746 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
1749 // update my options on the server
1750 multi_options_update_local();
1752 // if we're in PXO mode, mark it down in our player struct
1753 if(MULTI_IS_TRACKER_GAME){
1754 Player->flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
1755 Player->save_flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
1760 // process an accept packet from the server
1761 extern int Select_default_ship;
1763 void process_accept_packet(ubyte* data, header* hinfo)
1765 int code, my_player_num, offset;
1769 // get the accept code
1770 offset = HEADER_LENGTH;
1774 // read in the accept code specific data
1776 if (code & ACCEPT_INGAME) {
1777 // the game filename
1778 GET_STRING(Game_current_mission_filename);
1779 Select_default_ship = 0;
1781 // determine if I'm being placed on a team
1788 if (code & ACCEPT_OBSERVER) {
1789 SDL_assert(!(code & (ACCEPT_CLIENT | ACCEPT_HOST)));
1792 if (code & ACCEPT_HOST) {
1793 SDL_assert(!(code & (ACCEPT_CLIENT | ACCEPT_OBSERVER | ACCEPT_INGAME)));
1796 if (code & ACCEPT_CLIENT) {
1797 SDL_assert(!(code & (ACCEPT_HOST | ACCEPT_OBSERVER | ACCEPT_INGAME)));
1800 // fill in the netgame server address
1801 fill_net_addr( &Netgame.server_addr, hinfo->addr, hinfo->port );
1803 // get the skill level setting
1804 GET_INT(Game_skill_level);
1806 // get my netplayer number
1807 GET_INT(my_player_num);
1810 GET_SHORT(player_id);
1812 // get netgame type flags
1813 GET_INT(Netgame.type_flags);
1815 // setup the Net_players structure for myself first
1816 Net_player = &Net_players[my_player_num];
1817 Net_player->flags = 0;
1818 Net_player->tracker_player_id = Multi_tracker_id;
1819 Net_player->player_id = player_id;
1820 Net_player->s_info.xfer_handle = -1;
1821 // stuff_netplayer_info( Net_player, &Psnet_my_addr, Ships[Objects[Player->objnum].instance].ship_info_index, Player );
1822 stuff_netplayer_info( Net_player, &Psnet_my_addr, 0, Player );
1823 multi_options_local_load(&Net_player->p_info.options, Net_player);
1825 Net_player->p_info.team = team;
1828 // determine if I have a CD
1830 Net_player->flags |= NETINFO_FLAG_HAS_CD;
1833 // set accept code in netplayer for this guy
1834 if ( code & ACCEPT_INGAME ){
1835 Net_player->flags |= NETINFO_FLAG_ACCEPT_INGAME;
1837 if ( code & ACCEPT_OBSERVER ){
1838 Net_player->flags |= NETINFO_FLAG_ACCEPT_OBSERVER;
1840 if ( code & ACCEPT_HOST ){
1841 Net_player->flags |= NETINFO_FLAG_ACCEPT_HOST;
1843 if ( code & ACCEPT_CLIENT ){
1844 Net_player->flags |= NETINFO_FLAG_ACCEPT_CLIENT;
1847 // if I have hacked data
1848 if(game_hacked_data()){
1849 Net_player->flags |= NETINFO_FLAG_HAXOR;
1852 // if we're supposed to flush our local data cache, do so now
1853 if(Net_player->p_info.options.flags & MLO_FLAG_FLUSH_CACHE){
1854 multi_flush_multidata_cache();
1857 Net_player->sv_bytes_sent = 0;
1858 Net_player->sv_last_pl = -1;
1859 Net_player->cl_bytes_recvd = 0;
1860 Net_player->cl_last_pl = -1;
1862 // intiialize endgame stuff
1863 multi_endgame_init();
1867 // make a call to psnet to initialize and try to connect with the server.
1868 psnet_rel_connect_to_server( &Net_player->reliable_socket, &Netgame.server_addr );
1869 if ( Net_player->reliable_socket == INVALID_SOCKET ) {
1870 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_CONNECT_FAIL);
1874 // send a notice that the player at net_addr is leaving (if target is NULL, the broadcast the packet)
1875 void send_leave_game_packet(short player_id, int kicked_reason, net_player *target)
1877 ubyte data[MAX_PACKET_SIZE];
1879 int packet_size = 0;
1881 BUILD_HEADER(LEAVE_GAME);
1883 // add a flag indicating whether he was kicked or not
1884 val = (char)kicked_reason;
1887 if (player_id < 0) {
1888 ADD_SHORT(Net_player->player_id);
1890 // inform the host that we are leaving the game
1891 if (Net_player->flags & NETINFO_FLAG_AM_MASTER) {
1892 multi_io_send_to_all_reliable(data, packet_size);
1894 multi_io_send_reliable(Net_player, data, packet_size);
1897 // this is the case where to server is tossing a player (or indicating a respawned player has quit or become an observer)
1898 // so he has to tell everyone that this guy left
1900 nprintf(("Network","Sending a leave game packet to all players (server)\n"));
1902 // a couple of important checks
1903 SDL_assert(player_id != Net_player->player_id);
1904 SDL_assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
1906 // add the id of the guy to be kicked
1907 ADD_SHORT(player_id);
1909 // broadcast to everyone
1910 if (target == NULL) {
1911 multi_io_send_to_all_reliable(data, packet_size);
1913 multi_io_send_reliable(target, data, packet_size);
1918 // process a notification the a player has left the game
1919 void process_leave_game_packet(ubyte* data, header* hinfo)
1927 offset = HEADER_LENGTH;
1929 // get whether he was kicked
1930 GET_DATA(kicked_reason);
1932 // get the address of the guy who is to leave
1933 GET_SHORT(deader_id);
1936 // determine who is dropping and printf out a notification
1937 player_num = find_player_id(deader_id);
1938 if (player_num == -1) {
1939 nprintf(("Network", "Received leave game packet for unknown player, ignoring\n"));
1943 nprintf(("Network", "Received a leave game notice for %s\n", Net_players[player_num].player->callsign));
1946 // a hook to display that a player was kicked
1947 if (kicked_reason >= 0){
1948 // if it was me that was kicked, leave the game
1949 if((Net_player != NULL) && (Net_player->player_id == deader_id)){
1952 switch(kicked_reason){
1953 case KICK_REASON_BAD_XFER:
1954 notify_code = MULTI_END_NOTIFY_KICKED_BAD_XFER;
1956 case KICK_REASON_CANT_XFER:
1957 notify_code = MULTI_END_NOTIFY_KICKED_CANT_XFER;
1959 case KICK_REASON_INGAME_ENDED:
1960 notify_code = MULTI_END_NOTIFY_KICKED_INGAME_ENDED;
1963 notify_code = MULTI_END_NOTIFY_KICKED;
1967 multi_quit_game(PROMPT_NONE, notify_code);
1970 // otherwise indicate someone was kicked
1972 nprintf(("Network","%s was kicked\n",Net_players[player_num].player->callsign));
1974 // display the result
1975 memset(str, 0, 512);
1976 multi_kick_get_text(&Net_players[player_num], kicked_reason, str, SDL_arraysize(str));
1977 multi_display_chat_msg(str, player_num, 0);
1981 // first of all, if we're the master, we should be rebroadcasting this packet
1982 if (Net_player->flags & NETINFO_FLAG_AM_MASTER) {
1985 SDL_snprintf(msg, SDL_arraysize(msg), XSTR("%s has left the game",719), Net_players[player_num].player->callsign );
1987 if (!(Game_mode & GM_STANDALONE_SERVER)){
1988 HUD_sourced_printf(HUD_SOURCE_HIDDEN, msg);
1991 send_hud_msg_to_all(msg);
1992 multi_io_send_to_all_reliable(data, offset);
1995 // leave the game if the host and/or master has dropped
1997 if (((Net_players[player_num].flags & NETINFO_FLAG_AM_MASTER) || (Net_players[player_num].flags & NETINFO_FLAG_GAME_HOST)) ) {
1998 nprintf(("Network","Host and/or server has left the game - aborting...\n"));
2001 ml_string(NOX("Host and/or server has left the game"));
2003 // if the host leaves in the debriefing state, we should still wait until the player selects accept before we quit
2004 if (gameseq_get_state() != GS_STATE_DEBRIEF) {
2005 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_SERVER_LEFT);
2008 delete_player(player_num);
2011 delete_player(player_num);
2013 // OSAPI GUI stuff (if standalone)
2014 if (Game_mode & GM_STANDALONE_SERVER) {
2015 // returns true if we should reset the standalone
2016 if (std_remove_player(&Net_players[player_num])) {
2017 nprintf(("Network", "Should reset!!\n"));
2021 // update these gui vals
2022 std_connect_set_host_connect_status();
2023 std_connect_set_connect_count();
2027 // send information about this currently active game to the specified address
2028 void send_game_active_packet(net_addr* addr)
2032 ubyte data[MAX_PACKET_SIZE],val;
2034 // build the header and add the data
2035 BUILD_HEADER(GAME_ACTIVE);
2037 // add the server version and compatible version #
2038 val = MULTI_FS_SERVER_VERSION;
2040 val = MULTI_FS_SERVER_COMPATIBLE_VERSION;
2043 ADD_STRING(Netgame.name);
2044 ADD_STRING(Netgame.mission_name);
2045 ADD_STRING(Netgame.title);
2046 val = (ubyte)multi_num_players();
2049 // add the proper flags
2051 if((Netgame.mode == NG_MODE_PASSWORD) || ((Game_mode & GM_STANDALONE_SERVER) && (multi_num_players() == 0) && (std_is_host_passwd()))){
2052 flags |= AG_FLAG_PASSWD;
2055 // proper netgame type flags
2056 if(Netgame.type_flags & NG_TYPE_TEAM){
2057 flags |= AG_FLAG_TEAMS;
2058 } else if(Netgame.type_flags & NG_TYPE_DOGFIGHT){
2059 flags |= AG_FLAG_DOGFIGHT;
2061 flags |= AG_FLAG_COOP;
2064 // proper netgame state flags
2065 switch(Netgame.game_state){
2066 case NETGAME_STATE_FORMING:
2067 flags |= AG_FLAG_FORMING;
2070 case NETGAME_STATE_BRIEFING:
2071 case NETGAME_STATE_MISSION_SYNC:
2072 case NETGAME_STATE_HOST_SETUP:
2073 flags |= AG_FLAG_BRIEFING;
2076 case NETGAME_STATE_IN_MISSION:
2077 flags |= AG_FLAG_IN_MISSION;
2080 case NETGAME_STATE_PAUSED:
2081 flags |= AG_FLAG_PAUSE;
2084 case NETGAME_STATE_ENDGAME:
2085 case NETGAME_STATE_DEBRIEF:
2086 flags |= AG_FLAG_DEBRIEF;
2090 // if this is a standalone
2091 if(Game_mode & GM_STANDALONE_SERVER){
2092 flags |= AG_FLAG_STANDALONE;
2095 // if we're in campaign mode
2096 if(Netgame.campaign_mode == MP_CAMPAIGN){
2097 flags |= AG_FLAG_CAMPAIGN;
2100 // add the data about the connection speed of the host machine
2101 SDL_assert( (Multi_connection_speed >= 0) && (Multi_connection_speed <= 4) );
2102 flags |= (Multi_connection_speed << AG_FLAG_CONNECTION_BIT);
2107 psnet_send(addr, data, packet_size);
2110 // process information about an active game
2111 void process_game_active_packet(ubyte* data, header* hinfo)
2116 int modes_compatible;
2118 fill_net_addr(&ag.server_addr, hinfo->addr, hinfo->port);
2120 // read this game into a temporary structure
2121 offset = HEADER_LENGTH;
2123 // get the server version and compatible version
2124 GET_DATA(ag.version);
2125 GET_DATA(ag.comp_version);
2127 GET_STRING(ag.name);
2128 GET_STRING(ag.mission_name);
2129 GET_STRING(ag.title);
2131 ag.num_players = val;
2132 GET_USHORT(ag.flags);
2136 modes_compatible = 1;
2138 if((ag.flags & AG_FLAG_TRACKER) && !Multi_options_g.pxo){
2139 modes_compatible = 0;
2141 if(!(ag.flags & AG_FLAG_TRACKER) && Multi_options_g.pxo){
2142 modes_compatible = 0;
2146 // if this is a compatible version, and our modes are compatible, register it
2147 if( (ag.version == MULTI_FS_SERVER_VERSION) && modes_compatible ){
2148 multi_update_active_games(&ag);
2152 // send_game_update_packet sends an updated Netgame structure to all players currently connected. The update
2153 // is used to change the current mission, current state, etc.
2154 void send_netgame_update_packet(net_player *pl)
2156 int packet_size = 0;
2158 ubyte data[MAX_PACKET_SIZE];
2160 BUILD_HEADER(GAME_UPDATE);
2162 // with new mission description field, this becomes way to large
2163 // so we must add every element piece by piece except the
2164 ADD_STRING(Netgame.name);
2165 ADD_STRING(Netgame.mission_name);
2166 ADD_STRING(Netgame.title);
2167 ADD_STRING(Netgame.campaign_name);
2168 ADD_INT(Netgame.campaign_mode);
2169 ADD_INT(Netgame.max_players);
2170 ADD_INT(Netgame.security);
2171 ADD_UINT(Netgame.respawn);
2172 ADD_INT(Netgame.flags);
2173 ADD_INT(Netgame.type_flags);
2174 ADD_INT(Netgame.version_info);
2175 ADD_DATA(Netgame.debug_flags);
2177 // only the server should ever send the netgame state (standalone situation)
2178 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2179 ADD_INT(Netgame.game_state);
2182 // if we're the host on a standalone, send to the standalone and let him rebroadcast
2183 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2185 multi_io_send_to_all_reliable(data, packet_size);
2187 for(idx=0; idx<MAX_PLAYERS; idx++){
2188 if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx])){
2189 send_netgame_descript_packet(&Net_players[idx].p_info.addr, 1);
2193 multi_io_send_reliable(pl, data, packet_size);
2194 send_netgame_descript_packet( &pl->p_info.addr , 1 );
2197 SDL_assert( pl == NULL ); // I don't think that a host in a standalone game would get here.
2198 multi_io_send_reliable(Net_player, data, packet_size);
2201 // host should always send a netgame options update as well
2202 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
2203 multi_options_update_netgame();
2207 // process information about the netgame sent from the server/host
2208 void process_netgame_update_packet( ubyte *data, header *hinfo )
2210 int offset;//,old_flags;
2213 SDL_assert(!(Game_mode & GM_STANDALONE_SERVER));
2214 SDL_assert(!(Net_player->flags & NETINFO_FLAG_AM_MASTER));
2216 // read in the netgame information
2217 offset = HEADER_LENGTH;
2218 GET_STRING(Netgame.name);
2219 GET_STRING(Netgame.mission_name);
2220 GET_STRING(Netgame.title);
2221 GET_STRING(Netgame.campaign_name);
2222 GET_INT(Netgame.campaign_mode);
2223 GET_INT(Netgame.max_players); // ignore on the standalone, who keeps track of this himself
2224 GET_INT(Netgame.security);
2225 GET_UINT(Netgame.respawn);
2227 // be sure not to blast the quitting flag because of the "one frame extra" problem
2228 // old_flags = Netgame.flags;
2229 GET_INT(Netgame.flags);
2230 GET_INT(Netgame.type_flags);
2231 GET_INT(Netgame.version_info);
2232 GET_DATA(Netgame.debug_flags);
2239 // now compare the passed in game state to our current known state. If it has changed, then maybe
2240 // do something interesting.
2241 // move from the forming or debriefing state to the mission sync state
2242 if ( ng_state == NETGAME_STATE_MISSION_SYNC ){
2243 // if coming from the forming state
2244 if( (Netgame.game_state == NETGAME_STATE_FORMING) ||
2245 ((Netgame.game_state != NETGAME_STATE_FORMING) && ((gameseq_get_state() == GS_STATE_MULTI_HOST_SETUP) || (gameseq_get_state() == GS_STATE_MULTI_CLIENT_SETUP))) ){
2246 // do any special processing for forced state transitions
2247 multi_handle_state_special();
2249 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
2250 SDL_strlcpy( Game_current_mission_filename, Netgame.mission_name, MAX_FILENAME_LEN );
2251 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
2253 // if coming from the debriefing state
2254 else if( (Netgame.game_state == NETGAME_STATE_DEBRIEF) ||
2255 ((Netgame.game_state != NETGAME_STATE_DEBRIEF) && ((gameseq_get_state() == GS_STATE_DEBRIEF) || (gameseq_get_state() == GS_STATE_MULTI_DOGFIGHT_DEBRIEF)) ) ){
2257 // do any special processing for forced state transitions
2258 multi_handle_state_special();
2260 multi_flush_mission_stuff();
2262 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
2263 SDL_strlcpy( Game_current_mission_filename, Netgame.mission_name, MAX_FILENAME_LEN );
2264 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
2267 // move from mission sync to team select
2268 else if ( ng_state == NETGAME_STATE_BRIEFING ){
2269 if( (Netgame.game_state == NETGAME_STATE_MISSION_SYNC) ||
2270 ((Netgame.game_state != NETGAME_STATE_MISSION_SYNC) && (gameseq_get_state() == GS_STATE_MULTI_MISSION_SYNC) && (Multi_sync_mode != MULTI_SYNC_POST_BRIEFING)) ){
2272 // do any special processing for forced state transitions
2273 multi_handle_state_special();
2275 SDL_strlcpy( Game_current_mission_filename, Netgame.mission_name, MAX_FILENAME_LEN );
2276 gameseq_post_event(GS_EVENT_START_BRIEFING);
2279 // move from the debriefing to the create game screen
2280 else if ( ng_state == NETGAME_STATE_FORMING ){
2281 if( (Netgame.game_state == NETGAME_STATE_DEBRIEF) ||
2282 ((Netgame.game_state != NETGAME_STATE_DEBRIEF) && ((gameseq_get_state() == GS_STATE_DEBRIEF) || (gameseq_get_state() == GS_STATE_MULTI_DOGFIGHT_DEBRIEF)) ) ){
2283 // do any special processing for forced state transitions
2284 multi_handle_state_special();
2286 multi_flush_mission_stuff();
2288 // move to the proper screen
2289 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
2290 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2292 gameseq_post_event(GS_EVENT_MULTI_CLIENT_SETUP);
2297 Netgame.game_state = ng_state;
2300 // send a request or a reply for mission description, if code == 0, request, if code == 1, reply
2301 void send_netgame_descript_packet(net_addr *addr, int code)
2303 ubyte data[MAX_PACKET_SIZE],val;
2305 int packet_size = 0;
2308 BUILD_HEADER(UPDATE_DESCRIPT);
2314 // add as much of the description as we dare
2315 length = strlen(The_mission.mission_desc);
2316 if(length > MAX_PACKET_SIZE - 10){
2317 length = MAX_PACKET_SIZE - 10;
2319 memcpy(data+packet_size,The_mission.mission_desc,length);
2320 packet_size += length;
2322 ADD_STRING(The_mission.mission_desc);
2326 SDL_assert(addr != NULL);
2328 psnet_send(addr, data, packet_size);
2332 // process an incoming netgame description packet
2333 void process_netgame_descript_packet( ubyte *data, header *hinfo )
2337 char mission_desc[MISSION_DESC_LENGTH+2];
2340 fill_net_addr(&addr, hinfo->addr, hinfo->port);
2342 // read this game into a temporary structure
2343 offset = HEADER_LENGTH;
2346 // if this is a request for mission description
2348 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
2353 // send an update to this guy
2354 send_netgame_descript_packet(&addr, 1);
2356 memset(mission_desc,0,MISSION_DESC_LENGTH+2);
2357 GET_STRING(mission_desc);
2359 // only display if we're in the proper state
2360 state = gameseq_get_state();
2362 case GS_STATE_MULTI_JOIN_GAME:
2363 case GS_STATE_MULTI_CLIENT_SETUP:
2364 case GS_STATE_MULTI_HOST_SETUP:
2365 multi_common_set_text(mission_desc);
2373 // broadcast a query for active games. IPX will use net broadcast and TCP will either request from the MT or from the specified list
2374 void broadcast_game_query()
2378 server_item *s_moveup;
2379 ubyte data[MAX_PACKET_SIZE];
2381 BUILD_HEADER(GAME_QUERY);
2383 if (Multi_options_g.pxo) {
2385 multi_fs_tracker_send_game_request();
2388 // go through the server list and query each of those as well
2389 s_moveup = Game_server_head;
2390 if(s_moveup != NULL){
2392 send_server_query(&s_moveup->server_addr);
2393 s_moveup = s_moveup->next;
2394 } while(s_moveup != Game_server_head);
2398 fill_net_addr(&addr, Psnet_my_addr.addr, DEFAULT_GAME_PORT);
2400 // send out a broadcast if our options allow us
2401 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
2402 psnet_broadcast( &addr, data, packet_size);
2406 // send an individual query to an address to see if there is an active game
2407 void send_server_query(net_addr *addr)
2410 ubyte data[MAX_PACKET_SIZE];
2412 // build the header and send the data
2413 BUILD_HEADER(GAME_QUERY);
2414 psnet_send(addr, data, packet_size);
2417 // process a query from a client looking for active freespace games
2418 void process_game_query(ubyte* data, header* hinfo)
2423 offset = HEADER_LENGTH;
2427 // check to be sure that we don't capture our own broadcast message
2428 fill_net_addr(&addr, hinfo->addr, hinfo->port);
2429 if ( psnet_same( &addr, &Psnet_my_addr) ){
2433 // if I am not a server of a game, don't send a reply!!!
2434 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) ){
2438 // if the game options are being selected, then ignore the request
2439 // also, if Netgame.max_players == -1, the host has not chosen a mission yet and we should wait
2440 if((Netgame.game_state == NETGAME_STATE_STD_HOST_SETUP) || (Netgame.game_state == NETGAME_STATE_HOST_SETUP) || (Netgame.game_state == 0) || (Netgame.max_players == -1)){
2444 // send information about this active game
2445 send_game_active_packet(&addr);
2448 // sends information about netplayers in the game. if called on the server, broadcasts information about _all_ players
2449 void send_netplayer_update_packet( net_player *pl )
2451 int packet_size,idx;
2452 ubyte data[MAX_PACKET_SIZE],val;
2454 BUILD_HEADER(NETPLAYER_UPDATE);
2456 // if I'm the server of the game, I should send an update for _all_players in the game
2457 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2458 for(idx=0;idx<MAX_PLAYERS;idx++){
2459 // only send info for connected players
2460 if(MULTI_CONNECTED(Net_players[idx])){
2465 // add the net player's information
2466 ADD_SHORT(Net_players[idx].player_id);
2467 ADD_INT(Net_players[idx].state);
2468 ADD_INT(Net_players[idx].p_info.ship_class);
2469 ADD_INT(Net_players[idx].tracker_player_id);
2471 if(Net_players[idx].flags & NETINFO_FLAG_HAS_CD){
2479 // add the final stop byte
2483 // broadcast the packet
2484 if(!(Game_mode & GM_IN_MISSION)){
2486 multi_io_send_to_all_reliable(data, packet_size);
2488 multi_io_send_reliable(pl, data, packet_size);
2492 multi_io_send_to_all(data, packet_size);
2494 multi_io_send(pl, data, packet_size);
2502 // add my current state in the netgame to this packet
2503 ADD_SHORT(Net_player->player_id);
2504 ADD_INT(Net_player->state);
2505 ADD_INT(Net_player->p_info.ship_class);
2506 ADD_INT(Multi_tracker_id);
2508 // add if I have a CD or not
2516 // add a final stop byte
2520 // send the packet to the server
2521 SDL_assert( pl == NULL ); // shouldn't ever be the case that pl is non-null here.
2522 if(!(Game_mode & GM_IN_MISSION)){
2523 multi_io_send_reliable(Net_player, data, packet_size);
2525 multi_io_send(Net_player, data, packet_size);
2530 // process an incoming netplayer state update. if we're the server, we should rebroadcast
2531 void process_netplayer_update_packet( ubyte *data, header *hinfo )
2533 int offset, player_num;
2539 offset = HEADER_LENGTH;
2541 // get the first stop byte
2544 while(stop != 0xff){
2545 // look the player up
2546 GET_SHORT(player_id);
2547 player_num = find_player_id(player_id);
2548 // if we couldn't find him, read in the bogus data
2549 if((player_num == -1) || (Net_player == &Net_players[player_num])){
2550 GET_INT(bogus.state);
2551 GET_INT(bogus.p_info.ship_class);
2552 GET_INT(bogus.tracker_player_id);
2556 // otherwise read in the data correctly
2559 GET_INT(Net_players[player_num].p_info.ship_class);
2560 GET_INT(Net_players[player_num].tracker_player_id);
2563 Net_players[player_num].flags |= NETINFO_FLAG_HAS_CD;
2565 Net_players[player_num].flags &= ~(NETINFO_FLAG_HAS_CD);
2568 // if he's changing state to joined, send a team update
2569 if((Net_players[player_num].state == NETPLAYER_STATE_JOINING) && (new_state == NETPLAYER_STATE_JOINED) && (Netgame.type_flags & NG_TYPE_TEAM)){
2570 multi_team_send_update();
2574 Net_players[player_num].state = new_state;
2577 // get the next stop byte
2583 // if I'm the host or the server of the game, update everyone else so things are synched up as tightly as possible
2584 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2585 send_netplayer_update_packet(NULL);
2588 // if i'm the standalone and this is an update from the host, maybe change some netgame settings
2589 if((Game_mode & GM_STANDALONE_SERVER) && (player_num != -1) && (Net_players[player_num].flags & NETINFO_FLAG_GAME_HOST)){
2590 switch(Net_players[player_num].state){
2591 case NETPLAYER_STATE_STD_HOST_SETUP:
2592 Netgame.game_state = NETGAME_STATE_STD_HOST_SETUP;
2595 case NETPLAYER_STATE_HOST_SETUP:
2596 // check for race conditions
2597 if(Netgame.game_state != NETGAME_STATE_MISSION_SYNC){
2598 Netgame.game_state = NETGAME_STATE_FORMING;
2605 #define EXTRA_DEATH_VAPORIZED (1<<0)
2606 #define EXTRA_DEATH_WASHED (1<<1)
2607 // send a packet indicating a ship has been killed
2608 void send_ship_kill_packet( object *objp, object *other_objp, float percent_killed, int self_destruct )
2610 int packet_size, model;
2611 ubyte data[MAX_PACKET_SIZE], was_player, extra_death_info, vaporized;
2612 ushort debris_signature;
2616 // only sendable from the master
2617 SDL_assert ( Net_player->flags & NETINFO_FLAG_AM_MASTER );
2620 vaporized = ( (Ships[objp->instance].flags & SF_VAPORIZE) > 0 );
2622 extra_death_info = 0;
2624 extra_death_info |= EXTRA_DEATH_VAPORIZED;
2627 if ( Ships[objp->instance].wash_killed ) {
2628 extra_death_info |= EXTRA_DEATH_WASHED;
2631 // find out the next network signature that will be used for the debris pieces.
2632 model = Ships[objp->instance].modelnum;
2633 pm = model_get(model);
2634 debris_signature = 0;
2635 if ( pm && !vaporized ) {
2636 debris_signature = multi_get_next_network_signature( MULTI_SIG_DEBRIS );
2637 multi_set_network_signature( (ushort)(debris_signature + pm->num_debris_objects), MULTI_SIG_DEBRIS );
2638 Ships[objp->instance].arrival_distance = debris_signature;
2641 BUILD_HEADER(SHIP_KILL);
2642 ADD_USHORT(objp->net_signature);
2644 // ships which are initially killed get the rest of the data sent. self destructed ships and
2645 if ( other_objp == NULL ) {
2650 nprintf(("Network","Don't know other_obj for ship kill packet, sending NULL\n"));
2652 ADD_USHORT( other_objp->net_signature );
2655 ADD_USHORT( debris_signature );
2656 ADD_FLOAT( percent_killed );
2657 sd = (ubyte)self_destruct;
2659 ADD_DATA( extra_death_info );
2661 // if the ship who died is a player, then send some extra info, like who killed him, etc.
2663 if ( objp->flags & OF_PLAYER_SHIP ) {
2667 pnum = multi_find_player_by_object( objp );
2670 ADD_DATA( was_player );
2672 SDL_assert(Net_players[pnum].player->killer_objtype < CHAR_MAX);
2673 temp = (char)Net_players[pnum].player->killer_objtype;
2676 SDL_assert(Net_players[pnum].player->killer_species < CHAR_MAX);
2677 temp = (char)Net_players[pnum].player->killer_species;
2680 SDL_assert(Net_players[pnum].player->killer_weapon_index < CHAR_MAX);
2681 temp = (char)Net_players[pnum].player->killer_weapon_index;
2684 ADD_STRING( Net_players[pnum].player->killer_parent_name );
2686 ADD_DATA( was_player );
2689 ADD_DATA( was_player );
2692 // send the packet reliably!!!
2693 multi_io_send_to_all_reliable(data, packet_size);
2696 // process a packet indicating that a ship has been killed
2697 void process_ship_kill_packet( ubyte *data, header *hinfo )
2700 ushort ship_sig, other_sig, debris_sig;
2701 object *sobjp, *oobjp;
2702 float percent_killed;
2703 ubyte was_player, extra_death_info, sd;
2704 char killer_name[NAME_LENGTH], killer_objtype = OBJ_NONE, killer_species = SPECIES_TERRAN, killer_weapon_index = -1;
2706 offset = HEADER_LENGTH;
2707 GET_USHORT(ship_sig);
2709 GET_USHORT( other_sig );
2710 GET_USHORT( debris_sig );
2711 GET_FLOAT( percent_killed );
2713 GET_DATA( extra_death_info );
2714 GET_DATA( was_player );
2717 // pnum is >=0 when the dying ship is a pleyer ship. Get the info about how he died
2718 if ( was_player != 0 ) {
2719 GET_DATA( killer_objtype );
2720 GET_DATA( killer_species );
2721 GET_DATA( killer_weapon_index );
2722 GET_STRING( killer_name );
2727 sobjp = multi_get_network_object( ship_sig );
2729 // if I am unable to find the ship object which was killed, I have to bail and rely on getting
2730 // another message from the server that this happened!
2731 if ( sobjp == NULL ) {
2732 nprintf(("Network", "Couldn't find net signature %d for kill packet\n", ship_sig));
2736 // set this ship's hull value to 0
2737 sobjp->hull_strength = 0.0f;
2739 // maybe set vaporized
2740 if (extra_death_info & EXTRA_DEATH_VAPORIZED) {
2741 Ships[sobjp->instance].flags |= SF_VAPORIZE;
2744 // maybe set wash_killed
2745 if (extra_death_info & EXTRA_DEATH_VAPORIZED) {
2746 Ships[sobjp->instance].wash_killed = 1;
2749 oobjp = multi_get_network_object( other_sig );
2751 if ( was_player != 0 ) {
2754 pnum = multi_find_player_by_object( sobjp );
2756 Net_players[pnum].player->killer_objtype = killer_objtype;
2757 Net_players[pnum].player->killer_species = killer_species;
2758 Net_players[pnum].player->killer_weapon_index = killer_weapon_index;
2759 SDL_strlcpy( Net_players[pnum].player->killer_parent_name, killer_name, NAME_LENGTH );
2763 // check to see if I need to respawn myself
2764 multi_respawn_check(sobjp);
2766 // store the debris signature in the arrival distance which will never get used for player ships
2767 Ships[sobjp->instance].arrival_distance = debris_sig;
2769 // set this bit so that we don't accidentally start switching targets when we die
2770 if(sobjp == Player_obj){
2771 Game_mode |= GM_DEAD_DIED;
2774 nprintf(("Network", "Killing off %s\n", Ships[sobjp->instance].ship_name));
2776 // do the normal thing when not ingame joining. When ingame joining, simply kill off the ship.
2777 if ( !(Net_player->flags & NETINFO_FLAG_INGAME_JOIN) ) {
2778 ship_hit_kill( sobjp, oobjp, percent_killed, sd );
2780 extern void ship_destroyed( int shipnum );
2781 ship_destroyed( sobjp->instance );
2782 sobjp->flags |= OF_SHOULD_BE_DEAD;
2783 obj_delete( OBJ_INDEX(sobjp) );
2787 // send a packet indicating a ship should be created
2788 void send_ship_create_packet( object *objp, int is_support )
2791 ubyte data[MAX_PACKET_SIZE];
2793 // We will pass the ship to create by name.
2794 BUILD_HEADER(SHIP_CREATE);
2795 ADD_USHORT(objp->net_signature);
2796 ADD_INT( is_support );
2798 add_vector_data(data, &packet_size, objp->pos);
2801 // broadcast the packet
2802 multi_io_send_to_all_reliable(data, packet_size);
2805 // process a packet indicating a ship should be created
2806 void process_ship_create_packet( ubyte *data, header *hinfo )
2808 int offset, objnum, is_support;
2811 vector pos = ZERO_VECTOR;
2813 SDL_assert ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
2814 offset = HEADER_LENGTH;
2815 GET_USHORT(signature);
2816 GET_INT( is_support );
2818 get_vector_data(data, &offset, pos);
2823 // find the name of this ship on ship ship arrival list. if found, pass it to parse_object_create
2824 if ( !is_support ) {
2825 objp = mission_parse_get_arrival_ship( signature );
2826 if ( objp != NULL ) {
2827 parse_create_object(objp);
2829 nprintf(("Network", "Ship with sig %d not found on ship arrival list -- not creating!!\n", signature));
2832 SDL_assert( Arriving_support_ship );
2833 if(Arriving_support_ship == NULL){
2836 Arriving_support_ship->pos = pos;
2837 Arriving_support_ship->net_signature = signature;
2838 objnum = parse_create_object( Arriving_support_ship );
2839 SDL_assert( objnum != -1 );
2841 mission_parse_support_arrived( objnum );
2846 // send a packet indicating a wing of ships should be created
2847 void send_wing_create_packet( wing *wingp, int num_to_create, int pre_create_count )
2849 int packet_size, index, ship_instance;
2850 ubyte data[MAX_PACKET_SIZE];
2854 // for creating wing -- we just send the index into the wing array of this wing.
2855 // all players load the same mission, and so their array's should all match. We also
2856 // need to send the signature of the first ship that was created. We can find this by
2857 // looking num_to_create places back in the ship_index field in the wing structure.
2859 index = WING_INDEX(wingp);
2860 ship_instance = wingp->ship_index[wingp->current_count - num_to_create];
2861 signature = Objects[Ships[ship_instance].objnum].net_signature;
2863 BUILD_HEADER( WING_CREATE );
2865 ADD_INT(num_to_create);
2866 ADD_USHORT(signature);
2867 ADD_INT(pre_create_count);
2868 val = wingp->current_wave - 1;
2871 multi_io_send_to_all_reliable(data, packet_size);
2874 // process a packet saying that a wing should be created
2875 void process_wing_create_packet( ubyte *data, header *hinfo )
2877 int offset, index, num_to_create;
2879 int total_arrived_count, current_wave;
2881 offset = HEADER_LENGTH;
2883 GET_INT(num_to_create);
2884 GET_USHORT(signature);
2885 GET_INT(total_arrived_count);
2886 GET_INT(current_wave);
2890 // do a sanity check on the wing to be sure that we are actually working on a valid wing
2891 if ( (index < 0) || (index >= num_wings) || (Wings[index].num_waves == -1) ) {
2892 nprintf(("Network", "invalid index %d for wing create packet\n"));
2895 if ( (num_to_create <= 0) || (num_to_create > Wings[index].wave_count) ) {
2896 nprintf(("Network", "Invalid number of ships to create (%d) for wing %s\n", num_to_create, Wings[index].name));
2901 Wings[index].current_count = 0;
2902 Wings[index].total_arrived_count = total_arrived_count;
2903 Wings[index].current_wave = current_wave;
2905 // set the network signature that was passed. The client should create ships in the same order
2906 // as the server -- so all ships should get the same sigs as assigned by the server. We also
2907 // need to set some timestamps and cues correctly to be sure that these things get created on
2908 // the clients correctly
2909 multi_set_network_signature( signature, MULTI_SIG_SHIP );
2910 parse_wing_create_ships( &Wings[index], num_to_create, 1 );
2913 // packet indicating a ship is departing
2914 void send_ship_depart_packet( object *objp )
2916 ubyte data[MAX_PACKET_SIZE];
2920 signature = objp->net_signature;
2922 BUILD_HEADER(SHIP_DEPART);
2923 ADD_USHORT( signature );
2925 multi_io_send_to_all_reliable(data, packet_size);
2928 // process a packet indicating a ship is departing
2929 void process_ship_depart_packet( ubyte *data, header *hinfo )
2935 offset = HEADER_LENGTH;
2936 GET_USHORT( signature );
2939 // find the object which is departing
2940 objp = multi_get_network_object( signature );
2941 if ( objp == NULL ) {
2942 nprintf(("network", "Couldn't find object with net signature %d to depart\n", signature ));
2946 // start warping him out
2947 shipfx_warpout_start( objp );
2950 // packet to tell clients cargo of a ship was revealed to all
2951 void send_cargo_revealed_packet( ship *shipp )
2953 ubyte data[MAX_PACKET_SIZE];
2956 // build the header and add the data
2957 BUILD_HEADER(CARGO_REVEALED);
2958 ADD_USHORT( Objects[shipp->objnum].net_signature );
2960 // server sends to all players
2961 if(MULTIPLAYER_MASTER){
2962 multi_io_send_to_all_reliable(data, packet_size);
2964 // clients just send to the server
2966 multi_io_send_reliable(Net_player, data, packet_size);
2970 // process a cargo revealed packet
2971 void process_cargo_revealed_packet( ubyte *data, header *hinfo )
2977 offset = HEADER_LENGTH;
2978 GET_USHORT(signature);
2981 // get a ship pointer and call the ship function to reveal the cargo
2982 objp = multi_get_network_object( signature );
2983 if ( objp == NULL ) {
2984 nprintf(("Network", "Could not find object with net signature %d for cargo revealed\n", signature ));
2988 // SDL_assert( objp->type == OBJ_SHIP );
2989 if((objp->type != OBJ_SHIP) || (objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
2993 // this will take care of re-routing to all other clients
2994 ship_do_cargo_revealed( &Ships[objp->instance], 1);
2996 // server should rebroadcast
2997 if(MULTIPLAYER_MASTER){
2998 send_cargo_revealed_packet(&Ships[objp->instance]);
3002 // defines used for secondary fire packet
3003 #define SFPF_ALLOW_SWARM (1<<7)
3004 #define SFPF_DUAL_FIRE (1<<6)
3005 #define SFPF_TARGET_LOCKED (1<<5)
3007 // send a packet indicating a secondary weapon was fired
3008 void send_secondary_fired_packet( ship *shipp, ushort starting_sig, int starting_count, int num_fired, int allow_swarm )
3010 int packet_size, net_player_num;
3011 ubyte data[MAX_PACKET_SIZE], sinfo, current_bank;
3013 ushort target_signature;
3017 // SDL_assert ( starting_count < UCHAR_MAX );
3019 // get the object for this ship. If it is an AI object, send all the info to all player. Otherwise,
3020 // we might send the info to the other player different than the one who fired
3021 objp = &Objects[shipp->objnum];
3022 if ( !(objp->flags & OF_PLAYER_SHIP) ) {
3023 if ( num_fired == 0 ) {
3028 aip = &Ai_info[shipp->ai_index];
3030 current_bank = (ubyte)shipp->weapons.current_secondary_bank;
3031 //SDL_assert( (current_bank >= 0) && (current_bank < MAX_SECONDARY_BANKS) ); // always true
3033 // build up the header portion
3034 BUILD_HEADER( SECONDARY_FIRED_AI );
3036 ADD_USHORT( Objects[shipp->objnum].net_signature );
3037 ADD_USHORT( starting_sig );
3039 // add a couple of bits for swarm missiles and dual fire secondary weaspons
3042 sinfo = current_bank;
3045 sinfo |= SFPF_ALLOW_SWARM;
3048 if ( shipp->flags & SF_SECONDARY_DUAL_FIRE ){
3049 sinfo |= SFPF_DUAL_FIRE;
3052 if ( aip->current_target_is_locked ){
3053 sinfo |= SFPF_TARGET_LOCKED;
3058 // add the ship's target and any targeted subsystem
3059 target_signature = 0;
3061 if ( aip->target_objnum != -1) {
3062 target_signature = Objects[aip->target_objnum].net_signature;
3063 if ( (Objects[aip->target_objnum].type == OBJ_SHIP) && (aip->targeted_subsys != NULL) ) {
3066 s_index = ship_get_index_from_subsys( aip->targeted_subsys, aip->target_objnum );
3067 SDL_assert( s_index < CHAR_MAX ); // better be less than this!!!!
3068 t_subsys = (char)s_index;
3071 if ( Objects[aip->target_objnum].type == OBJ_WEAPON ) {
3072 SDL_assert(Weapon_info[Weapons[Objects[aip->target_objnum].instance].weapon_info_index].wi_flags & WIF_BOMB);
3077 ADD_USHORT( target_signature );
3078 ADD_DATA( t_subsys );
3080 // just send this packet to everyone, then bail if an AI ship fired.
3081 if ( !(objp->flags & OF_PLAYER_SHIP) ) {
3082 multi_io_send_to_all(data, packet_size);
3086 net_player_num = multi_find_player_by_object( objp );
3088 // getting here means a player fired. Send the current packet to all players except the player
3089 // who fired. If nothing got fired, then don't send to the other players -- we will just send
3090 // a packet to the player who will find out that he didn't fire anything
3091 if ( num_fired > 0 ) {
3092 multi_io_send_to_all_reliable(data, packet_size, &Net_players[net_player_num]);
3095 // if I (the master) fired, then return
3096 if ( Net_players[net_player_num].flags & NETINFO_FLAG_AM_MASTER ){
3100 // now build up the packet to send to the player who actually fired.
3101 BUILD_HEADER( SECONDARY_FIRED_PLR );
3102 ADD_USHORT(starting_sig);
3105 // add the targeting information so that the player's weapons will always home on the correct
3107 ADD_USHORT( target_signature );
3108 ADD_DATA( t_subsys );
3110 multi_io_send_reliable(&Net_players[net_player_num], data, packet_size);
3113 /// process a packet indicating a secondary weapon was fired
3114 void process_secondary_fired_packet(ubyte* data, header* hinfo, int from_player)
3116 int offset, allow_swarm, target_objnum_save;
3117 ushort net_signature, starting_sig, target_signature;
3118 ubyte sinfo, current_bank;
3119 object* objp, *target_objp;
3123 ship_subsys *targeted_subsys_save;
3125 offset = HEADER_LENGTH; // size of the header
3127 // if from_player is false, it means that the secondary weapon info in this packet was
3128 // fired by an ai object (or another player). from_player == 1 means tha me (the person
3129 // receiving this packet) fired the secondary weapon
3130 if ( !from_player ) {
3131 GET_USHORT( net_signature );
3132 GET_USHORT( starting_sig );
3133 GET_DATA( sinfo ); // are we firing swarm missiles
3135 GET_USHORT( target_signature );
3136 GET_DATA( t_subsys );
3140 // find the object (based on network signatures) for the object that fired
3141 objp = multi_get_network_object( net_signature );
3142 if ( objp == NULL ) {
3143 nprintf(("Network", "Could not find ship for fire secondary packet!"));
3147 // set up the ships current secondary bank and that bank's mode. Below, we will set the timeout
3148 // of the next fire time of this bank to 0 so we can fire right away
3149 shipp = &Ships[objp->instance];
3152 GET_USHORT( starting_sig );
3155 GET_USHORT( target_signature );
3156 GET_DATA( t_subsys );
3160 // get the object and ship
3162 shipp = Player_ship;
3165 // check the allow swarm bit
3167 if ( sinfo & SFPF_ALLOW_SWARM ){
3171 // set the dual fire properties of the ship
3172 if ( sinfo & SFPF_DUAL_FIRE ){
3173 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
3175 shipp->flags &= ~SF_SECONDARY_DUAL_FIRE;
3178 // determine whether current target is locked
3179 SDL_assert( shipp->ai_index != -1 );
3180 aip = &Ai_info[shipp->ai_index];
3181 if ( sinfo & SFPF_TARGET_LOCKED ) {
3182 aip->current_target_is_locked = 1;
3184 aip->current_target_is_locked = 0;
3187 // find out the current bank
3188 current_bank = (ubyte)(sinfo & 0x3);
3189 //SDL_assert( (current_bank >= 0) && (current_bank < MAX_SECONDARY_BANKS) ); // always true
3190 shipp->weapons.current_secondary_bank = current_bank;
3192 // make it so we can fire this ship's secondary bank immediately!!!
3193 shipp->weapons.next_secondary_fire_stamp[shipp->weapons.current_secondary_bank] = timestamp(0);
3194 shipp->weapons.detonate_weapon_time = timestamp(5000); // be sure that we don't detonate a remote weapon before it is time.
3196 // set this ship's target and subsystem information. We will save and restore target and
3197 // targeted subsystem so that we do not accidentally change targets for this player or
3198 // any AI ships on his system.
3199 target_objnum_save = aip->target_objnum;
3200 targeted_subsys_save = aip->targeted_subsys;
3202 // reset these variables for accuracy. They will get reassigned at the end of this fuction
3203 aip->target_objnum = -1;
3204 aip->targeted_subsys = NULL;
3206 target_objp = multi_get_network_object( target_signature );
3207 if ( target_objp != NULL ) {
3208 aip->target_objnum = OBJ_INDEX(target_objp);
3210 if ( (t_subsys != -1) && (target_objp->type == OBJ_SHIP) ) {
3211 aip->targeted_subsys = ship_get_indexed_subsys( &Ships[target_objp->instance], t_subsys);
3215 if ( starting_sig != 0 ){
3216 multi_set_network_signature( starting_sig, MULTI_SIG_NON_PERMANENT );
3218 shipp->weapons.detonate_weapon_time = timestamp(0); // signature of -1 say detonate remote weapon
3221 ship_fire_secondary( objp, allow_swarm );
3223 // restore targeted object and targeted subsystem
3224 aip->target_objnum = target_objnum_save;
3225 aip->targeted_subsys = targeted_subsys_save;
3228 // send a packet indicating a countermeasure was fired
3229 void send_countermeasure_fired_packet( object *objp, int cmeasure_count, int rand_val )
3231 ubyte data[MAX_PACKET_SIZE];
3236 SDL_assert ( cmeasure_count < UCHAR_MAX );
3237 BUILD_HEADER(COUNTERMEASURE_FIRED);
3238 ADD_USHORT( objp->net_signature );
3239 ADD_INT( rand_val );
3241 multi_io_send_to_all(data, packet_size);
3244 // process a packet indicating a countermeasure was fired
3245 void process_countermeasure_fired_packet( ubyte *data, header *hinfo )
3247 int offset, rand_val;
3253 offset = HEADER_LENGTH;
3255 GET_USHORT( signature );
3256 GET_INT( rand_val );
3259 objp = multi_get_network_object( signature );
3260 if ( objp == NULL ) {
3261 nprintf(("network", "Could find object whose countermeasures are being launched!!!\n"));
3264 if(objp->type != OBJ_SHIP){
3267 // SDL_assert ( objp->type == OBJ_SHIP );
3269 // make it so ship can fire right away!
3270 Ships[objp->instance].cmeasure_fire_stamp = timestamp(0);
3271 if ( objp == Player_obj ){
3272 nprintf(("network", "firing countermeasure from my ship\n"));
3275 ship_launch_countermeasure( objp, rand_val );
3278 // send a packet indicating that a turret has been fired
3279 void send_turret_fired_packet( int ship_objnum, int subsys_index, int weapon_objnum )
3282 ushort pnet_signature;
3283 ubyte data[MAX_PACKET_SIZE], cindex;
3290 if((weapon_objnum < 0) || (Objects[weapon_objnum].type != OBJ_WEAPON) || (Objects[weapon_objnum].instance < 0) || (Weapons[Objects[weapon_objnum].instance].weapon_info_index < 0)){
3294 // local setup -- be sure we are actually passing a weapon!!!!
3295 objp = &Objects[weapon_objnum];
3296 SDL_assert ( objp->type == OBJ_WEAPON );
3297 if(Weapon_info[Weapons[objp->instance].weapon_info_index].subtype == WP_MISSILE){
3301 pnet_signature = Objects[ship_objnum].net_signature;
3303 SDL_assert( subsys_index < UCHAR_MAX );
3304 cindex = (ubyte)subsys_index;
3306 ssp = ship_get_indexed_subsys( &Ships[Objects[ship_objnum].instance], subsys_index, NULL );
3311 // build the fire turret packet.
3312 BUILD_HEADER(FIRE_TURRET_WEAPON);
3313 packet_size += multi_pack_unpack_position(1, data + packet_size, &objp->orient.v.fvec);
3314 ADD_DATA( has_sig );
3315 ADD_USHORT( pnet_signature );
3317 ADD_USHORT( objp->net_signature );
3320 val = (short)ssp->submodel_info_1.angs.h;
3322 val = (short)ssp->submodel_info_2.angs.p;
3325 multi_io_send_to_all(data, packet_size);
3327 multi_rate_add(1, "tur", packet_size);
3330 // process a packet indicating a turret has been fired
3331 void process_turret_fired_packet( ubyte *data, header *hinfo )
3333 int offset, weapon_objnum, wid;
3334 ushort pnet_signature, wnet_signature;
3343 short pitch, heading;
3345 // get the data for the turret fired packet
3346 offset = HEADER_LENGTH;
3347 offset += multi_pack_unpack_position(0, data + offset, &o_fvec);
3348 GET_DATA( has_sig );
3349 GET_USHORT( pnet_signature );
3351 GET_USHORT( wnet_signature );
3355 GET_DATA( turret_index );
3356 GET_SHORT( heading );
3358 PACKET_SET_SIZE(); // move our counter forward the number of bytes we have read
3361 objp = multi_get_network_object( pnet_signature );
3362 if ( objp == NULL ) {
3363 nprintf(("network", "could find parent object with net signature %d for turret firing\n", pnet_signature));
3367 // if this isn't a ship, do nothing
3368 if ( objp->type != OBJ_SHIP ){
3372 // make an orientation matrix from the o_fvec
3373 vm_vector_2_matrix(&orient, &o_fvec, NULL, NULL);
3375 // find this turret, and set the position of the turret that just fired to be where it fired. Quite a
3376 // hack, but should be suitable.
3377 shipp = &Ships[objp->instance];
3378 ssp = ship_get_indexed_subsys( shipp, turret_index, NULL );
3382 wid = ssp->system_info->turret_weapon_type;
3384 // bash the position and orientation of the turret
3385 ssp->submodel_info_1.angs.h = (float)heading;
3386 ssp->submodel_info_2.angs.p = (float)pitch;
3388 // get the world position of the weapon
3389 ship_get_global_turret_info(objp, ssp->system_info, &pos, &temp);
3391 // create the weapon object
3392 if(wnet_signature != 0){
3393 multi_set_network_signature( wnet_signature, MULTI_SIG_NON_PERMANENT );
3395 weapon_objnum = weapon_create( &pos, &orient, wid, OBJ_INDEX(objp), 0, -1, 1);
3396 if (weapon_objnum != -1) {
3397 if ( Weapon_info[wid].launch_snd != -1 ) {
3398 snd_play_3d( &Snds[Weapon_info[wid].launch_snd], &pos, &View_position );
3403 // send a mission log item packet
3404 void send_mission_log_packet( int num )
3407 ubyte data[MAX_PACKET_SIZE];
3412 SDL_assert ( (Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER) );
3414 // get the data from the log
3415 entry = &log_entries[num];
3416 type = (ubyte)entry->type; // do the type casting thing to save on packet space
3417 sindex = (ushort)entry->index;
3419 BUILD_HEADER(MISSION_LOG_ENTRY);
3421 ADD_INT(entry->flags);
3423 ADD_DATA(entry->timestamp);
3424 ADD_STRING(entry->pname);
3425 ADD_STRING(entry->sname);
3427 // broadcast the packet to all players
3428 multi_io_send_to_all_reliable(data, packet_size);
3431 // process a mission log item packet
3432 void process_mission_log_packet( ubyte *data, header *hinfo )
3437 char pname[NAME_LENGTH], sname[NAME_LENGTH];
3440 SDL_assert ( (Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
3442 offset = HEADER_LENGTH;
3446 GET_DATA(timestamp);
3452 mission_log_add_entry_multi( type, pname, sname, sindex, timestamp, flags );
3455 // send a mission message packet
3456 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)
3459 ubyte data[MAX_PACKET_SIZE], up, us, utime;
3461 SDL_assert ( Net_player->flags & NETINFO_FLAG_AM_MASTER );
3462 SDL_assert ( (priority >= 0) && (priority < UCHAR_MAX) );
3463 SDL_assert ( (timing >= 0) && (timing < UCHAR_MAX) );
3465 up = (ubyte) priority;
3466 us = (ubyte) source;
3467 utime = (ubyte)timing;
3469 BUILD_HEADER(MISSION_MESSAGE);
3471 ADD_STRING(who_from);
3475 ADD_INT(builtin_type);
3476 ADD_INT(multi_team_filter);
3478 if (multi_target == -1){
3479 multi_io_send_to_all_reliable(data, packet_size);
3481 multi_io_send_reliable(&Net_players[multi_target], data, packet_size);
3485 // process a mission message packet
3486 void process_mission_message_packet( ubyte *data, header *hinfo )
3488 int offset, id, builtin_type;
3489 ubyte priority, source, utiming;
3490 char who_from[NAME_LENGTH];
3491 int multi_team_filter;
3493 SDL_assert( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
3495 offset = HEADER_LENGTH;
3497 GET_STRING(who_from);
3501 GET_INT(builtin_type);
3502 GET_INT(multi_team_filter);
3506 // filter out builtin ones in TvT
3507 if((builtin_type >= 0) && (Netgame.type_flags & NG_TYPE_TEAM) && (Net_player != NULL) && (Net_player->p_info.team != multi_team_filter)){
3511 // maybe filter this out
3512 if(!message_filter_multi(id)){
3513 // send the message as if it came from an sexpression
3514 message_queue_message( id, priority, utiming, who_from, source, 0, 0, builtin_type );
3518 // just send them a pong back as fast as possible
3519 void process_ping_packet(ubyte *data, header *hinfo)
3524 offset = HEADER_LENGTH;
3527 // get the address to return the pong to
3528 fill_net_addr(&addr, hinfo->addr, hinfo->port);
3534 // right now it just routes the pong through to the standalone gui, which is the only
3535 // system which uses ping and pong right now.
3536 void process_pong_packet(ubyte *data, header *hinfo)
3542 offset = HEADER_LENGTH;
3544 fill_net_addr(&addr, hinfo->addr, hinfo->port);
3548 // if we're connected , see who sent us this pong
3549 if(Net_player->flags & NETINFO_FLAG_CONNECTED){
3550 lookup = find_player_id(hinfo->id);
3555 p = &Net_players[lookup];
3557 // evaluate the ping
3558 multi_ping_eval_pong(&Net_players[lookup].s_info.ping);
3560 // put in calls to any functions which may want to know about the ping times from
3562 if(Game_mode & GM_STANDALONE_SERVER){
3563 std_update_player_ping(p);
3567 // mark his socket as still alive (extra precaution)
3568 psnet_mark_received(Net_players[lookup].reliable_socket);
3571 // otherwise, do any special processing
3573 // if we're in the join game state, see if this pong came from a server on our
3575 if(gameseq_get_state() == GS_STATE_MULTI_JOIN_GAME){
3576 multi_join_eval_pong(&addr, timer_get_fixed_seconds());
3581 // send a ping packet
3582 void send_ping(net_addr *addr)
3584 unsigned char data[8];
3587 // build the header and send the packet
3588 BUILD_HEADER( PING );
3589 psnet_send(addr, &data[0], packet_size);
3592 // send a pong packet
3593 void send_pong(net_addr *addr)
3595 unsigned char data[8];
3598 // build the header and send the packet
3600 psnet_send(addr, &data[0], packet_size);
3603 // sent from host to master. give me the list of missions you have.
3604 // this will be used only in a standalone mode
3605 void send_mission_list_request( int what )
3607 ubyte data[MAX_PACKET_SIZE];
3610 // build the header and ask for a list of missions or campaigns (depending
3611 // on the 'what' flag).
3612 BUILD_HEADER(MISSION_REQUEST);
3614 multi_io_send_reliable(Net_player, data, packet_size);
3617 // maximum number of bytes that we can send in a mission items packet.
3618 #define MAX_MISSION_ITEMS_BYTES (MAX_PACKET_SIZE - (sizeof(multi_create_info) + 1) )
3620 // defines used to tell what type of packets are being sent
3621 #define MISSION_LIST_ITEMS 1
3622 #define CAMPAIGN_LIST_ITEMS 2
3624 // send an individual mission file item
3625 void send_mission_items( net_player *pl )
3627 ubyte data[MAX_PACKET_SIZE];
3632 BUILD_HEADER(MISSION_ITEM);
3634 // send the list of missions and campaigns avilable on the server. Stop when
3635 // reaching a certain maximum
3636 type = MISSION_LIST_ITEMS;
3638 for (i = 0; i < Multi_create_mission_count; i++ ) {
3642 ADD_STRING( Multi_create_mission_list[i].filename );
3643 ADD_STRING( Multi_create_mission_list[i].name );
3644 ADD_INT( Multi_create_mission_list[i].flags );
3645 ADD_DATA( Multi_create_mission_list[i].max_players );
3646 ADD_UINT( Multi_create_mission_list[i].respawn );
3649 ADD_DATA( Multi_create_mission_list[i].valid_status );
3651 if ( packet_size > (int)MAX_MISSION_ITEMS_BYTES ) {
3654 multi_io_send_reliable(pl, data, packet_size);
3655 BUILD_HEADER( MISSION_ITEM );
3661 multi_io_send_reliable(pl, data, packet_size);
3663 // send the campaign information
3664 type = CAMPAIGN_LIST_ITEMS;
3665 BUILD_HEADER(MISSION_ITEM);
3667 for (i = 0; i < Multi_create_campaign_count; i++ ) {
3671 ADD_STRING( Multi_create_campaign_list[i].filename );
3672 ADD_STRING( Multi_create_campaign_list[i].name );
3673 ADD_INT( Multi_create_campaign_list[i].flags );
3674 ADD_DATA( Multi_create_campaign_list[i].max_players );
3676 if ( packet_size > (int)MAX_MISSION_ITEMS_BYTES ) {
3679 multi_io_send_reliable(pl, data, packet_size);
3680 BUILD_HEADER( MISSION_ITEM );
3686 multi_io_send_reliable(pl, data, packet_size);
3689 // process a request for a list of missions
3690 void process_mission_request_packet(ubyte *data, header *hinfo)
3692 int player_num,offset;
3694 offset = HEADER_LENGTH;
3697 // fill in the address information of where this came from
3698 player_num = find_player_id(hinfo->id);
3699 if(player_num == -1){
3700 nprintf(("Network","Could not find player to send mission list items to!\n"));
3704 send_mission_items( &Net_players[player_num] );
3707 // process an individual mission file item
3708 void process_mission_item_packet(ubyte *data,header *hinfo)
3711 char filename[MAX_FILENAME_LEN], name[NAME_LENGTH], valid_status;
3712 ubyte stop, type,max_players;
3715 SDL_assert(gameseq_get_state() == GS_STATE_MULTI_HOST_SETUP);
3716 offset = HEADER_LENGTH;
3721 GET_STRING( filename );
3724 GET_DATA( max_players );
3726 // missions also have respawns and a crc32 associated with them
3727 if(type == MISSION_LIST_ITEMS){
3731 GET_DATA(valid_status);
3733 if ( Multi_create_mission_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
3734 SDL_strlcpy(Multi_create_mission_list[Multi_create_mission_count].filename, filename, MAX_FILENAME_LEN );
3735 SDL_strlcpy(Multi_create_mission_list[Multi_create_mission_count].name, name, NAME_LENGTH );
3736 Multi_create_mission_list[Multi_create_mission_count].flags = flags;
3737 Multi_create_mission_list[Multi_create_mission_count].respawn = respawn;
3738 Multi_create_mission_list[Multi_create_mission_count].max_players = max_players;
3741 Multi_create_mission_list[Multi_create_mission_count].valid_status = valid_status;
3743 Multi_create_mission_count++;
3745 } else if ( type == CAMPAIGN_LIST_ITEMS ) {
3746 if ( Multi_create_campaign_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
3747 SDL_strlcpy(Multi_create_campaign_list[Multi_create_campaign_count].filename, filename, MAX_FILENAME_LEN );
3748 SDL_strlcpy(Multi_create_campaign_list[Multi_create_campaign_count].name, name, NAME_LENGTH );
3749 Multi_create_campaign_list[Multi_create_campaign_count].flags = flags;
3750 Multi_create_campaign_list[Multi_create_campaign_count].respawn = 0;
3751 Multi_create_campaign_list[Multi_create_campaign_count].max_players = max_players;
3752 Multi_create_campaign_count++;
3761 // this will cause whatever list to get resorted (although they should be appearing in order)
3762 multi_create_setup_list_data(-1);
3765 // send a request to the server to pause or unpause the game
3766 void send_multi_pause_packet(int pause)
3768 ubyte data[MAX_PACKET_SIZE];
3770 int packet_size = 0;
3772 SDL_assert(!(Net_player->flags & NETINFO_FLAG_AM_MASTER));
3775 BUILD_HEADER(MULTI_PAUSE_REQUEST);
3776 val = (ubyte) pause;
3778 // add the pause info
3781 // send the request to the server
3782 multi_io_send_reliable(Net_player, data, packet_size);
3785 // process a pause update packet (pause, unpause, etc)
3786 void process_multi_pause_packet(ubyte *data, header *hinfo)
3792 offset = HEADER_LENGTH;
3798 // get who sent the packet
3799 player_index = find_player_id(hinfo->id);
3800 // if we don't know who sent the packet, don't do anything
3801 if(player_index == -1){
3805 // if we're the server, we should evaluate whether this guy is allowed to send the packet
3806 multi_pause_server_eval_request(&Net_players[player_index],(int)val);
3809 // send a game information update
3810 void send_game_info_packet()
3813 ubyte data[MAX_PACKET_SIZE], paused;
3815 // set the paused variable
3816 paused = (ubyte)((Netgame.game_state == NETGAME_STATE_PAUSED)?1:0);
3818 BUILD_HEADER(GAME_INFO);
3819 ADD_INT( Missiontime );
3822 multi_io_send_to_all(data, packet_size);
3825 // process a game information update
3826 void process_game_info_packet( ubyte *data, header *hinfo )
3832 offset = HEADER_LENGTH;
3834 // get the mission time -- we should examine our time and the time from the server. If off by some delta
3835 // time, set our time to server time (should take ping time into account!!!)
3836 GET_DATA( mission_time );
3841 // send an ingame nak packet
3842 void send_ingame_nak(int state, net_player *p)
3844 ubyte data[MAX_PACKET_SIZE];
3845 int packet_size = 0;
3847 BUILD_HEADER(INGAME_NAK);
3851 multi_io_send_reliable(p, data, packet_size);
3854 // process an ingame nak packet
3855 void process_ingame_nak(ubyte *data, header *hinfo)
3857 int offset,state,pid;
3859 offset = HEADER_LENGTH;
3863 pid = find_player_id(hinfo->id);
3869 case ACK_FILE_ACCEPTED :
3870 SDL_assert(Net_players[pid].flags & NETINFO_FLAG_INGAME_JOIN);
3871 nprintf(("Network","Mission file rejected by server, aborting...\n"));
3872 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_FILE_REJECTED);
3877 // send a packet telling players to end the mission
3878 void send_endgame_packet(net_player *pl)
3880 ubyte data[MAX_PACKET_SIZE];
3881 int packet_size = 0;
3883 BUILD_HEADER(MISSION_END);
3885 // sending to a specific player?
3887 SDL_assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
3888 multi_io_send_reliable(pl, data, packet_size);
3892 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3893 // send all player stats here
3894 multi_broadcast_stats(STATS_MISSION);
3896 // if in dogfight mode, send all dogfight stats as well
3897 ml_string("Before dogfight stats!");
3898 if(Netgame.type_flags & NG_TYPE_DOGFIGHT){
3899 ml_string("Sending dogfight stats!");
3901 multi_broadcast_stats(STATS_DOGFIGHT_KILLS);
3903 ml_string("After dogfight stats!");
3905 // tell everyone to leave the game
3906 multi_io_send_to_all_reliable(data, packet_size);
3908 multi_io_send_reliable(Net_player, data, packet_size);
3912 // process a packet indicating we should end the current mission
3913 void process_endgame_packet(ubyte *data, header *hinfo)
3918 offset = HEADER_LENGTH;
3922 ml_string("Receiving endgame packet");
3924 // if I'm the server, I should evaluate whether the sender is authorized to end the game
3925 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3926 // determine who this came from and make sure he is allowed to end the game
3927 player_num = find_player_id(hinfo->id);
3928 SDL_assert(player_num != -1);
3933 // if the player is allowed to end the mission
3934 if(!multi_can_end_mission(&Net_players[player_num])){
3938 // act as if we hit alt+j locally
3939 multi_handle_end_mission_request();
3941 // all clients process immediately
3943 // ingame joiners should quit when they receive an endgame packet since the game is over
3944 if(Net_player->flags & NETINFO_FLAG_INGAME_JOIN){
3945 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_EARLY_END);
3949 // do any special processing for being in a state other than the gameplay state
3950 multi_handle_state_special();
3952 // make sure we're not already in the debrief state
3953 if((gameseq_get_state() != GS_STATE_DEBRIEF) && (gameseq_get_state() != GS_STATE_MULTI_DOGFIGHT_DEBRIEF)){
3954 multi_warpout_all_players();
3959 // send a position/orientation update for myself (if I'm an observer)
3960 void send_observer_update_packet()
3962 ubyte data[MAX_PACKET_SIZE];
3963 int packet_size = 0;
3967 // its possible for the master to be an observer if has run out of respawns. In this case, he doesn't need
3968 // to send any update packets to anyone.
3969 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3973 if((Player_obj == NULL) || (Player_obj->type != OBJ_OBSERVER) || (Net_player == NULL) || !(Net_player->flags & NETINFO_FLAG_OBSERVER)){
3977 BUILD_HEADER(OBSERVER_UPDATE);
3979 ret = multi_pack_unpack_position( 1, data + packet_size, &Player_obj->pos );
3981 ret = multi_pack_unpack_orient( 1, data + packet_size, &Player_obj->orient );
3984 // add targeting infomation
3985 if((Player_ai != NULL) && (Player_ai->target_objnum >= 0)){
3986 target_sig = Objects[Player_ai->target_objnum].net_signature;
3990 ADD_USHORT(target_sig);
3992 multi_io_send(Net_player, data, packet_size);
3995 // process a position/orientation update from an observer
3996 void process_observer_update_packet(ubyte *data, header *hinfo)
4002 physics_info bogus_pi;
4005 offset = HEADER_LENGTH;
4007 obs_num = find_player_id(hinfo->id);
4009 memset(&bogus_pi,0,sizeof(physics_info));
4010 ret = multi_pack_unpack_position( 0, data + offset, &g_vec );
4012 ret = multi_pack_unpack_orient( 0, data + offset, &g_mat );
4015 // targeting information
4016 GET_USHORT(target_sig);
4019 if((obs_num < 0) || (Net_players[obs_num].player->objnum < 0)){
4023 // set targeting info
4024 if(target_sig == 0){
4025 Net_players[obs_num].s_info.target_objnum = -1;
4027 target_obj = multi_get_network_object(target_sig);
4028 Net_players[obs_num].s_info.target_objnum = (target_obj == NULL) ? -1 : OBJ_INDEX(target_obj);
4031 Objects[Net_players[obs_num].player->objnum].pos = g_vec;
4032 Objects[Net_players[obs_num].player->objnum].orient = g_mat;
4033 Net_players[obs_num].s_info.eye_pos = g_vec;
4034 Net_players[obs_num].s_info.eye_orient = g_mat;
4037 void send_netplayer_slot_packet()
4039 ubyte data[MAX_PACKET_SIZE];
4040 int packet_size = 0, idx;
4045 BUILD_HEADER(NETPLAYER_SLOTS_P);
4046 for(idx=0;idx<MAX_PLAYERS;idx++){
4047 if( MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_OBSERVER(Net_players[idx])){
4049 ADD_SHORT(Net_players[idx].player_id);
4050 ADD_USHORT(Objects[Net_players[idx].player->objnum].net_signature);
4051 ADD_INT(Net_players[idx].p_info.ship_class);
4052 ADD_INT(Net_players[idx].p_info.ship_index);
4058 // standalone case or not
4059 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4060 multi_io_send_to_all_reliable(data, packet_size);
4062 multi_io_send_reliable(Net_player, data, packet_size);
4066 void process_netplayer_slot_packet(ubyte *data, header *hinfo)
4069 int player_num,ship_class,ship_index;
4075 offset = HEADER_LENGTH;
4077 // first untag all of the player ships and make them OF_COULD_BE_PLAYER
4078 multi_untag_player_ships();
4082 GET_SHORT(player_id);
4083 GET_USHORT(net_sig);
4084 GET_INT(ship_class);
4085 GET_INT(ship_index);
4086 player_num = find_player_id(player_id);
4088 nprintf(("Network","Error looking up player for object/slot assignment!!\n"));
4090 // call the function in multiutil.cpp to set up the player object stuff
4091 // being careful not to muck with the standalone object
4092 if(!((player_num == 0) && (Game_mode & GM_STANDALONE_SERVER))){
4093 objp = multi_get_network_object(net_sig);
4094 SDL_assert(objp != NULL);
4095 multi_assign_player_ship( player_num, objp, ship_class );
4096 Net_players[player_num].p_info.ship_index = ship_index;
4097 objp->flags &= ~(OF_COULD_BE_PLAYER);
4098 objp->flags |= OF_PLAYER_SHIP;
4105 // standalone should forward the packet and wait for a response
4106 if(Game_mode & GM_STANDALONE_SERVER){
4107 send_netplayer_slot_packet();
4110 Net_player->state = NETPLAYER_STATE_SLOT_ACK;
4111 send_netplayer_update_packet();
4114 // two functions to deal with ships changing their primary/secondary weapon status. 'what' indicates
4115 // if this change is a primary or secondary change. new_bank is the new current primary/secondary
4116 // bank, link_status is whether primaries are linked or not, or secondaries are dual fire or not
4117 void send_ship_weapon_change( ship *shipp, int what, int new_bank, int link_status )
4119 ubyte data[MAX_PACKET_SIZE], utmp;
4122 BUILD_HEADER(SHIP_WSTATE_CHANGE);
4123 ADD_USHORT( Objects[shipp->objnum].net_signature );
4124 utmp = (ubyte)(what);
4126 utmp = (ubyte)(new_bank);
4128 utmp = (ubyte)(link_status);
4131 // Removed the above psnet_send() call - it didn't appear to do anything since it was called only from the server anyway - DB
4132 multi_io_send_to_all_reliable(data, packet_size);
4135 void process_ship_weapon_change( ubyte *data, header *hinfo )
4139 ubyte what, new_bank, link_status;
4143 offset = HEADER_LENGTH;
4144 GET_USHORT( signature );
4146 GET_DATA( new_bank );
4147 GET_DATA( link_status );
4150 objp = multi_get_network_object( signature );
4151 if ( objp == NULL ) {
4152 nprintf(("network", "Unable to locate ship with signature %d for weapon state change\n", signature));
4155 // SDL_assert( objp->type == OBJ_SHIP );
4156 if(objp->type != OBJ_SHIP){
4160 // if this is my data, do nothing since I already have my own data
4161 if ( objp == Player_obj ){
4165 // now, get the ship and set the new bank and link modes based on the 'what' value
4166 shipp = &Ships[objp->instance];
4167 if ( what == MULTI_PRIMARY_CHANGED ) {
4168 shipp->weapons.current_primary_bank = new_bank;
4170 shipp->flags |= SF_PRIMARY_LINKED;
4172 shipp->flags &= ~SF_PRIMARY_LINKED;
4175 shipp->weapons.current_secondary_bank = new_bank;
4177 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
4179 shipp->flags &= ~SF_SECONDARY_DUAL_FIRE;
4184 // ship status change procedure
4185 // 1.) <client> - Client runs through the normal button_function procedure. Any remaining control bits are implied as being
4187 // 2.) <client> - Client puts this button_info item into his last_buttons array and sends a bunch of SHIP_STATUS packets
4188 // for added redundancy.
4189 // 3.) <server> - Receives the packet. Checks to see if the net_player on his side already has this one defined. If so, it
4190 // ignores as a repeat packet. Otherwise it puts it in the last_buttons array for the net_player
4191 // 4.) <server> - Server applies the command on his side (with multi_apply_ship_status(...) and sends the ack (also a SHIP_STATUS)
4192 // back to the client. Also sends multiple times for redundancy
4193 // 5.) <client> - Receives the packet back. Does a lookup into his last_buttons array. If he finds the match, apply the functions
4194 // and remove the item from the list. If no match is found it means that either he has received an ack, has acted
4195 // on it and removed it, or that it has been "timed out" and replaced by a newer button_info.
4197 #define SHIP_STATUS_REPEAT 2
4198 void send_ship_status_packet(net_player *pl, button_info *bi, int id)
4201 ubyte data[MAX_PACKET_SIZE];
4202 int packet_size = 0;
4208 BUILD_HEADER(SHIP_STATUS_CHANGE);
4210 for(idx=0;idx<NUM_BUTTON_FIELDS;idx++){
4211 temp = bi->status[idx];
4215 // server should send reliably (response packet)
4216 if(MULTIPLAYER_MASTER){
4217 multi_io_send_reliable(pl, data, packet_size);
4219 multi_io_send(pl, data, packet_size);
4223 void process_ship_status_packet(ubyte *data, header *hinfo)
4227 int player_num,unique_id;
4231 offset = HEADER_LENGTH;
4233 // zero out the button info structure for good measure
4234 memset(&bi,0,sizeof(button_info));
4236 // read the button-info
4239 for(idx=0;idx<NUM_BUTTON_FIELDS;idx++){
4241 bi.status[idx] = i_tmp;
4246 // this will be handled differently client and server side. Duh.
4247 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){ // SERVER SIDE
4248 // find which net-player has sent us butotn information
4249 player_num = find_player_id(hinfo->id);
4250 SDL_assert(player_num >= 0);
4255 // don't process critical button information for observers
4256 // its a new button_info for this guy. apply and ack
4257 if(!MULTI_OBSERVER(Net_players[player_num]) && !lookup_ship_status(&Net_players[player_num],unique_id)){
4258 // mark that he's pressed this button
4259 // add_net_button_info(&Net_players[player_num], &bi, unique_id);
4261 // send a return packet
4262 send_ship_status_packet(&Net_players[player_num], &bi,unique_id);
4264 // apply the button presses to his ship as normal
4265 multi_apply_ship_status(&Net_players[player_num], &bi, 0);
4267 // else ignore it as a repeat from the same guy
4268 } else { // CLIENT SIDE
4269 // this is the return from the server, so we should now apply them locally
4270 // if(lookup_ship_status(Net_player,unique_id,1)){
4271 multi_apply_ship_status(Net_player, &bi, 1);
4276 // MWA 4/28/9 -- redid this function since message all fighers was really broken
4277 // for clients. Left all details to this function instead of higher level messaging
4279 void send_player_order_packet(int type, int index, int cmd)
4281 ubyte data[MAX_PACKET_SIZE];
4283 ushort target_signature;
4285 int packet_size = 0;
4287 BUILD_HEADER(PLAYER_ORDER_PACKET);
4290 ADD_DATA(val); // ship order or wing order, or message all fighters
4292 // if we are not messaging all ships or wings, add the index, which is the shipnum or wingnum
4293 if ( val != SQUAD_MSG_ALL ){
4294 ADD_INT(index); // net signature of target ship
4297 ADD_INT(cmd); // the command itself
4300 target_signature = 0;
4301 if ( Player_ai->target_objnum != -1 ){
4302 target_signature = Objects[Player_ai->target_objnum].net_signature;
4305 ADD_USHORT( target_signature );
4308 if ( (Player_ai->target_objnum != -1) && (Player_ai->targeted_subsys != NULL) ) {
4311 s_index = ship_get_index_from_subsys( Player_ai->targeted_subsys, Player_ai->target_objnum );
4312 SDL_assert( s_index < CHAR_MAX ); // better be less than this!!!!
4313 t_subsys = (char)s_index;
4317 multi_io_send_reliable(Net_player, data, packet_size);
4320 // brief explanation :
4321 // in either case (wing or ship command), we need to send in a pseudo-ai object. Basically, both command handler
4322 // functions "normally" (non multiplayer) use a couple of the Player_ai fields. So, we just fill in the ones necessary
4323 // (which we can reconstruct from the packet data), and pass this as the default variable ai_info *local
4324 // Its kind of a hack, but it eliminates the need to go in and screw around with quite a bit of code
4325 void process_player_order_packet(ubyte *data, header *hinfo)
4327 int offset, player_num, command, index = 0, tobjnum_save;
4328 ushort target_signature;
4329 char t_subsys, type;
4330 object *objp, *target_objp;
4333 ship_subsys *tsubsys_save, *targeted_subsys;
4335 SDL_assert(MULTIPLAYER_MASTER);
4337 // packet values - its easier to read all of these in first
4339 offset = HEADER_LENGTH;
4342 if ( type != SQUAD_MSG_ALL ){
4347 GET_USHORT( target_signature );
4348 GET_DATA( t_subsys );
4352 player_num = find_player_id(hinfo->id);
4353 if(player_num == -1){
4354 nprintf(("Network","Received player order packet from unknown player\n"));
4358 objp = &Objects[Net_players[player_num].player->objnum];
4359 if ( objp->type != OBJ_SHIP ) {
4360 nprintf(("Network", "not doing player order because object requestting is not a ship\n"));
4364 // HACK HACK HACK HACK HACK HACK
4365 // if the player has sent a rearm-repair me message, we should bail here after evaluating it, since most likely the rest of
4366 // the data is BOGUS. All people should be able to to these things as well.
4367 if(command == REARM_REPAIR_ME_ITEM){
4368 hud_squadmsg_repair_rearm(0,&Objects[Net_players[player_num].player->objnum]);
4370 } else if(command == ABORT_REARM_REPAIR_ITEM){
4371 hud_squadmsg_repair_rearm_abort(0,&Objects[Net_players[player_num].player->objnum]);
4375 // if this player is not allowed to do messaging, quit here
4376 if( !multi_can_message(&Net_players[player_num]) ){
4377 nprintf(("Network","Recieved player order packet from player not allowed to give orders!!\n"));
4381 // check to see if the type of order is a reinforcement call. If so, intercept it, and
4382 // then call them in.
4383 if ( type == SQUAD_MSG_REINFORCEMENT ) {
4384 SDL_assert( (index >= 0) && (index < Num_reinforcements) );
4385 hud_squadmsg_call_reinforcement(index, player_num);
4389 // set the player's ai information here
4390 shipp = &Ships[objp->instance];
4391 aip = &Ai_info[shipp->ai_index];
4393 // get the target objnum and targeted subsystem. Quick out if we don't have an object to act on.
4394 target_objp = multi_get_network_object( target_signature );
4395 if ( target_objp == NULL ) {
4399 targeted_subsys = NULL;
4400 if ( t_subsys != -1 ) {
4401 SDL_assert( target_objp != NULL );
4402 targeted_subsys = ship_get_indexed_subsys( &Ships[target_objp->instance], t_subsys);
4405 // save and restore the target objnum and targeted subsystem so that we don't mess up other things
4407 tobjnum_save = aip->target_objnum;
4408 tsubsys_save = aip->targeted_subsys;
4410 if ( target_objp ) {
4411 aip->target_objnum = OBJ_INDEX(target_objp);
4413 aip->target_objnum = -1;
4416 aip->targeted_subsys = targeted_subsys;
4418 if ( type == SQUAD_MSG_SHIP ) {
4419 hud_squadmsg_send_ship_command(index, command, 1, player_num);
4420 } else if ( type == SQUAD_MSG_WING ) {
4421 hud_squadmsg_send_wing_command(index, command, 1, player_num);
4422 } else if ( type == SQUAD_MSG_ALL ) {
4423 hud_squadmsg_send_to_all_fighters( command, player_num );
4426 SDL_assert(tobjnum_save != Ships[aip->shipnum].objnum); // make sure not targeting self
4427 aip->target_objnum = tobjnum_save;
4428 aip->targeted_subsys = tsubsys_save;
4431 // FILE SIGNATURE stuff :
4432 // there are 2 cases for file signature sending which are handled very differently
4433 // 1.) Pregame. In this case, the host requires that all clients send a filesig packet (when process_file_sig() is called, it
4434 // posts an ACK_FILE_ACCEPTED packet to ack_evaluate, so he thinks they have acked).
4435 // 2.) Ingame join. In this case, the client sends his filesig packet automatically to the server and the _client_ waits for
4436 // the ack, before continuing to join. It would be way too messy to have the server wait on the clients ack, since he
4437 // would have to keep track of up to potentially 14 other ack handles (ouch).
4438 void send_file_sig_packet(ushort sum_sig,int length_sig)
4440 ubyte data[MAX_PACKET_SIZE];
4441 int packet_size = 0;
4443 BUILD_HEADER(FILE_SIG_INFO);
4444 ADD_USHORT(sum_sig);
4445 ADD_INT(length_sig);
4447 multi_io_send_reliable(Net_player, data, packet_size);
4450 void process_file_sig_packet(ubyte *data, header *hinfo)
4455 offset = HEADER_LENGTH;
4457 // should only be received on the server-side
4458 SDL_assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
4460 GET_USHORT(sum_sig);
4461 GET_INT(length_sig);
4463 server_verify_filesig(hinfo->id, sum_sig, length_sig);
4466 void send_file_sig_request(char *file_name)
4468 ubyte data[MAX_PACKET_SIZE];
4469 int packet_size = 0;
4471 BUILD_HEADER(FILE_SIG_REQUEST);
4472 ADD_STRING(file_name);
4474 SDL_assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
4476 multi_io_send_to_all_reliable(data, packet_size);
4479 void process_file_sig_request(ubyte *data, header *hinfo)
4481 int offset = HEADER_LENGTH;
4483 // get the mission name
4484 GET_STRING(Netgame.mission_name);
4487 // set the current mission filename
4488 SDL_strlcpy(Game_current_mission_filename, Netgame.mission_name, SDL_arraysize(Game_current_mission_filename));
4491 multi_get_mission_checksum(Game_current_mission_filename);
4493 if(!multi_endgame_ending()){
4494 // reply to the server
4495 send_file_sig_packet(Multi_current_file_checksum,Multi_current_file_length);
4499 // functions to deal with subsystems getting whacked
4500 void send_subsystem_destroyed_packet( ship *shipp, int index, vector world_hitpos )
4502 ubyte data[MAX_PACKET_SIZE];
4505 vector tmp, local_hitpos;
4508 SDL_assert ( index < UCHAR_MAX );
4509 uindex = (ubyte)(index);
4511 objp = &Objects[shipp->objnum];
4513 vm_vec_sub(&tmp, &world_hitpos, &objp->pos );
4514 vm_vec_rotate( &local_hitpos, &tmp, &objp->orient );
4516 BUILD_HEADER(SUBSYSTEM_DESTROYED);
4517 ADD_USHORT( Objects[shipp->objnum].net_signature );
4519 // ADD_DATA( local_hitpos );
4520 add_vector_data(data, &packet_size, local_hitpos);
4522 multi_io_send_to_all_reliable(data, packet_size);
4525 void process_subsystem_destroyed_packet( ubyte *data, header *hinfo )
4531 vector local_hit_pos = ZERO_VECTOR, world_hit_pos;
4533 offset = HEADER_LENGTH;
4535 GET_USHORT( signature );
4537 // GET_DATA( local_hit_pos );
4538 get_vector_data(data, &offset, local_hit_pos);
4540 // get the network object. process it if we find it.
4541 objp = multi_get_network_object( signature );
4542 if ( objp != NULL ) {
4544 ship_subsys *subsysp;
4546 // be sure we have a ship!!!
4547 // SDL_assert ( objp->type == OBJ_SHIP );
4548 if(objp->type != OBJ_SHIP){
4553 shipp = &Ships[objp->instance];
4555 // call to get the pointer to the subsystem we should be working on
4556 subsysp = ship_get_indexed_subsys( shipp, (int)uindex );
4557 vm_vec_unrotate( &world_hit_pos, &local_hit_pos, &objp->orient );
4558 vm_vec_add2( &world_hit_pos, &objp->pos );
4560 do_subobj_destroyed_stuff( shipp, subsysp, &world_hit_pos );
4561 if ( objp == Player_obj ) {
4562 hud_gauge_popup_start(HUD_DAMAGE_GAUGE, 5000);
4570 // packet to tell clients cargo of a ship was revealed to all
4571 void send_subsystem_cargo_revealed_packet( ship *shipp, int index )
4573 ubyte data[MAX_PACKET_SIZE], uindex;
4576 SDL_assert ( index < UCHAR_MAX );
4577 uindex = (ubyte)(index);
4579 // build the header and add the data
4580 BUILD_HEADER(SUBSYS_CARGO_REVEALED);
4581 ADD_USHORT( Objects[shipp->objnum].net_signature );
4584 // server sends to all players
4585 if(MULTIPLAYER_MASTER){
4586 multi_io_send_to_all_reliable(data, packet_size);
4588 // clients just send to the server
4590 multi_io_send_reliable(Net_player, data, packet_size);
4594 // process a subsystem cargo revealed packet
4595 void process_subsystem_cargo_revealed_packet( ubyte *data, header *hinfo )
4602 ship_subsys *subsysp;
4604 offset = HEADER_LENGTH;
4605 GET_USHORT( signature );
4609 // get a ship pointer and call the ship function to reveal the cargo
4610 objp = multi_get_network_object( signature );
4611 if ( objp == NULL ) {
4612 nprintf(("Network", "Could not find object with net signature %d for cargo revealed\n", signature ));
4616 // SDL_assert( objp->type == OBJ_SHIP );
4617 if((objp->type != OBJ_SHIP) || (objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
4621 shipp = &Ships[objp->instance];
4623 // call to get the pointer to the subsystem we should be working on
4624 subsysp = ship_get_indexed_subsys( shipp, (int)uindex );
4625 if (subsysp == NULL) {
4626 nprintf(("Network", "Could not find subsys for ship %s for cargo revealed\n", Ships[objp->instance].ship_name ));
4630 // this will take care of re-routing to all other clients
4631 void ship_do_cap_subsys_cargo_revealed( ship *shipp, ship_subsys *subsys, int from_network );
4632 ship_do_cap_subsys_cargo_revealed( shipp, subsysp, 1 );
4634 // server should rebroadcast
4635 if(MULTIPLAYER_MASTER){
4636 send_subsystem_cargo_revealed_packet(&Ships[objp->instance], (int)uindex);
4640 void send_netplayer_load_packet(net_player *pl)
4642 ubyte data[MAX_PACKET_SIZE];
4643 int packet_size = 0;
4645 BUILD_HEADER(LOAD_MISSION_NOW);
4646 ADD_STRING(Netgame.mission_name);
4649 multi_io_send_to_all_reliable(data, packet_size);
4651 multi_io_send_reliable(pl, data, packet_size);
4655 void process_netplayer_load_packet(ubyte *data, header *hinfo)
4658 int offset = HEADER_LENGTH;
4663 SDL_strlcpy(Netgame.mission_name, str, SDL_arraysize(Netgame.mission_name));
4664 SDL_strlcpy(Game_current_mission_filename, str, SDL_arraysize(Game_current_mission_filename));
4665 if(!Multi_mission_loaded){
4667 // MWA 2/3/98 -- ingame join changes!!!
4668 // everyone can go through the same mission loading path here!!!!
4669 nprintf(("Network","Loading mission..."));
4671 // notify everyone that I'm loading the mission
4672 Net_player->state = NETPLAYER_STATE_MISSION_LOADING;
4673 send_netplayer_update_packet();
4675 // do the load itself
4676 game_start_mission();
4678 // ingame joiners need to "untag" all player ships as could_be_players. The ingame joining
4679 // code will remark the correct player ships
4680 if ( Net_player->flags & NETINFO_FLAG_INGAME_JOIN ) {
4681 multi_untag_player_ships();
4684 Net_player->flags |= NETINFO_FLAG_MISSION_OK;
4685 Net_player->state = NETPLAYER_STATE_MISSION_LOADED;
4686 send_netplayer_update_packet();
4688 Multi_mission_loaded = 1;
4689 nprintf(("Network","Finished loading mission\n"));
4693 void send_jump_into_mission_packet(net_player *pl)
4695 ubyte data[MAX_PACKET_SIZE];
4696 int packet_size = 0;
4698 SDL_assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
4700 BUILD_HEADER(JUMP_INTO_GAME);
4702 // ingame joiners will get special data. We need to tell them about the state of the mission, like paused,
4703 // and possible other things.
4705 if ( pl->flags & NETINFO_FLAG_INGAME_JOIN ) {
4706 ADD_INT(Netgame.game_state);
4712 multi_io_send_to_all_reliable(data, packet_size);
4714 // send to a specific player
4716 multi_io_send_reliable(pl, data, packet_size);
4720 void process_jump_into_mission_packet(ubyte *data, header *hinfo)
4722 int offset = HEADER_LENGTH;
4725 // if I am ingame joining, there should be extra data. For now, this data is the netgame state.
4726 // the game could be paused, so ingame joiner needs to deal with it.
4727 if ( Net_player->flags & NETINFO_FLAG_INGAME_JOIN ) {
4729 Netgame.game_state = state;
4734 // handle any special processing for being in a weird substate
4735 multi_handle_state_special();
4737 // if I'm an ingame joiner, go to the ship select screen, or if I'm an observer, jump right in!
4738 if(Net_player->flags & NETINFO_FLAG_INGAME_JOIN){
4739 if(Net_player->flags & NETINFO_FLAG_OBSERVER){
4740 multi_ingame_observer_finish();
4742 gameseq_post_event(GS_EVENT_INGAME_PRE_JOIN);
4743 Net_player->state = NETPLAYER_STATE_INGAME_SHIP_SELECT;
4744 send_netplayer_update_packet();
4747 // start the mission!!
4748 if(!(Game_mode & GM_IN_MISSION) && !(Game_mode & GM_STANDALONE_SERVER)){
4749 Netgame.game_state = NETGAME_STATE_IN_MISSION;
4750 gameseq_post_event(GS_EVENT_ENTER_GAME);
4751 Net_player->state = NETPLAYER_STATE_IN_MISSION;
4752 send_netplayer_update_packet();
4756 extern time_t Player_multi_died_check;
4757 Player_multi_died_check = -1;
4759 // recalc all object pairs now
4760 extern void obj_reset_all_collisions();
4761 obj_reset_all_collisions();
4763 // display some cool text
4764 multi_common_add_text(XSTR("Received mission start\n",720),1);
4767 ml_string(NOX("Client received mission start from server - entering mission"));
4772 const char *repair_text[] = {
4774 "REPAIR_INFO_BEGIN",
4776 "REPAIR_INFO_UPDATE",
4777 "REPAIR_INFO_QUEUE",
4778 "REPAIR_INFO_ABORT",
4779 "REPAIR_INFO_BROKEN",
4780 "REPAIR_INFO_WARP_ADD",
4781 "REPAIR_INFO_WARP_REMOVE",
4782 "REPAIR_INFO_ONWAY",
4783 "REPAIR_INFO_KILLED",
4784 "REPAIR_INFO_COMPLETE",
4789 // the following two routines deal with updating and sending information regarding players
4790 // rearming and repairing during the game. The process function calls the routines to deal with
4791 // setting flags and other interesting things.
4792 void send_repair_info_packet(object *repaired_objp, object *repair_objp, int code )
4794 int packet_size = 0;
4795 ushort repaired_signature, repair_signature;
4796 ubyte data[MAX_PACKET_SIZE];
4799 // use the network signature of the destination object if there is one, -1 otherwise.
4800 // client will piece it all together
4801 repaired_signature = repaired_objp->net_signature;
4803 // the repair ship may be NULL here since it might have been destroyed
4804 repair_signature = 0;
4806 repair_signature = repair_objp->net_signature;
4809 BUILD_HEADER(CLIENT_REPAIR_INFO);
4812 ADD_USHORT( repaired_signature );
4813 ADD_USHORT( repair_signature );
4815 multi_io_send_to_all_reliable(data, packet_size);
4817 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));
4820 void process_repair_info_packet(ubyte *data, header *hinfo)
4822 int offset = HEADER_LENGTH;
4823 ushort repaired_signature, repair_signature;
4824 object *repaired_objp, *repair_objp;
4828 GET_USHORT( repaired_signature );
4829 GET_USHORT( repair_signature );
4832 repaired_objp = multi_get_network_object( repaired_signature );
4833 repair_objp = multi_get_network_object( repair_signature );
4835 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));
4837 if ( Net_player->flags & NETINFO_FLAG_WARPING_OUT ){
4841 if ( repaired_objp == NULL ) {
4842 Int3(); // Sandeep says this is bad bad bad. No ship to repair.
4846 // the hope is to simply call the routine in the ai code to set/unset flags
4847 // based on the code value and everything else should happen..I hope....
4848 if ( (code != REPAIR_INFO_WARP_ADD) && (code != REPAIR_INFO_WARP_REMOVE ) ) {
4850 ai_do_objects_repairing_stuff( repaired_objp, repair_objp, (int)code );
4852 // set the dock flags when repair begins. Prevents problem in lagging docking
4853 // packet. Also set any other flags/modes which need to be set to prevent Asserts.
4855 if ( (code == REPAIR_INFO_BEGIN) && (repair_objp != NULL) ) {
4856 ai_do_objects_docked_stuff( repaired_objp, repair_objp );
4857 Ai_info[Ships[repair_objp->instance].ai_index].mode = AIM_DOCK;
4860 // if the repair is done (either by abort, or ending), mark the repair ship's goal
4862 if ( ((code == REPAIR_INFO_ABORT) || (code == REPAIR_INFO_END)) && repair_objp ){
4863 ai_mission_goal_complete( &Ai_info[Ships[repair_objp->instance].ai_index] );
4866 if ( code == REPAIR_INFO_WARP_ADD ){
4867 mission_warp_in_support_ship( repaired_objp );
4869 mission_remove_scheduled_repair( repaired_objp );
4874 // sends information updating clients on certain AI information that clients will
4875 // need to know about to keep HUD information up to date. objp is the object that we
4876 // are updating, and what is the type of stuff that we are updating.
4877 void send_ai_info_update_packet( object *objp, char what )
4880 ushort other_signature;
4881 ubyte data[MAX_PACKET_SIZE];
4883 ubyte dock_index, dockee_index;
4885 // SDL_assert( objp->type == OBJ_SHIP );
4886 if(objp->type != OBJ_SHIP){
4889 aip = &Ai_info[Ships[objp->instance].ai_index];
4892 if ( Ships[objp->instance].flags & (SF_DEPARTING | SF_DYING) )
4895 BUILD_HEADER( AI_INFO_UPDATE );
4896 ADD_USHORT( objp->net_signature );
4899 // depending on the "what" value, we will send different information
4903 case AI_UPDATE_DOCK:
4904 // for docking ships, add the signature of the ship that we are docking with.
4905 SDL_assert( aip->dock_objnum != -1 );
4906 other_signature = Objects[aip->dock_objnum].net_signature;
4907 dock_index = (ubyte)(aip->dock_index);
4908 dockee_index = (ubyte)(aip->dockee_index);
4909 ADD_USHORT( other_signature );
4910 ADD_DATA(dock_index);
4911 ADD_DATA(dockee_index);
4914 case AI_UPDATE_UNDOCK:
4915 // for undocking ships, check the dock_objnum since we might or might not have it
4916 // depending on whether or not a ship was destroyed while we were docked.
4917 other_signature = 0;
4918 if ( aip->dock_objnum != -1 )
4919 other_signature = Objects[aip->dock_objnum].net_signature;
4920 ADD_USHORT( other_signature );
4924 case AI_UPDATE_ORDERS: {
4927 // for orders, we only need to send a little bit of information here. Be sure that the
4928 // first order for this ship is active
4929 SDL_assert( (aip->active_goal != AI_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) );
4930 ADD_INT( aip->goals[0].ai_mode );
4931 ADD_INT( aip->goals[0].ai_submode );
4933 if ( aip->goals[0].ship_name != NULL )
4934 shipnum = ship_name_lookup( aip->goals[0].ship_name );
4936 // the ship_name member of the goals structure may or may not contain a real shipname. If we don't
4937 // have a valid shipnum, then don't sweat it since it may not really be a ship.
4938 if ( shipnum != -1 ) {
4939 SDL_assert( Ships[shipnum].objnum != -1 );
4940 other_signature = Objects[Ships[shipnum].objnum].net_signature;
4942 other_signature = 0;
4944 ADD_USHORT( other_signature );
4946 // for docking, add the dock and dockee index
4947 if ( aip->goals[0].ai_mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
4948 SDL_assert( (aip->goals[0].docker.index >= 0) && (aip->goals[0].docker.index < UCHAR_MAX) );
4949 SDL_assert( (aip->goals[0].dockee.index >= 0) && (aip->goals[0].dockee.index < UCHAR_MAX) );
4950 dock_index = (ubyte)aip->goals[0].docker.index;
4951 dockee_index = (ubyte)aip->goals[0].dockee.index;
4952 ADD_DATA( dock_index );
4953 ADD_DATA( dockee_index );
4962 multi_rate_add(1, "aiu", packet_size);
4963 multi_io_send_to_all_reliable(data, packet_size);
4966 // process an ai_info update packet. Docking/undocking, ai orders, etc. are taken care of here. This
4967 // information is mainly used to keep the clients HUD up to date with the appropriate information.
4968 void process_ai_info_update_packet( ubyte *data, header *hinfo)
4970 int offset = HEADER_LENGTH;
4972 ushort net_signature, other_net_signature;
4973 object *objp, *other_objp;
4976 ubyte dock_index = 0, dockee_index = 0;
4978 GET_USHORT( net_signature ); // signature of the object that we are dealing with.
4979 GET_DATA( code ); // code of what we are doing.
4980 objp = multi_get_network_object( net_signature );
4982 nprintf(("Network", "Couldn't find object for ai update\n"));
4986 case AI_UPDATE_DOCK:
4987 GET_USHORT( other_net_signature );
4988 GET_DATA( dock_index );
4989 GET_DATA( dockee_index );
4990 other_objp = multi_get_network_object( other_net_signature );
4991 if ( !other_objp ) {
4992 nprintf(("Network", "Couldn't find other object for ai update on dock\n"));
4995 // if we don't have an object to work with, break out of loop
4996 if ( !objp || !other_objp || (objp->type != OBJ_SHIP) || (other_objp->type != OBJ_SHIP)){
5000 SDL_assert( other_objp->type == OBJ_SHIP );
5001 Ai_info[Ships[objp->instance].ai_index].dock_index = dock_index;
5002 Ai_info[Ships[objp->instance].ai_index].dockee_index = dockee_index;
5004 Ai_info[Ships[other_objp->instance].ai_index].dock_index = dockee_index;
5005 Ai_info[Ships[other_objp->instance].ai_index].dockee_index = dock_index;
5007 ai_do_objects_docked_stuff( objp, other_objp );
5010 case AI_UPDATE_UNDOCK:
5011 GET_USHORT( other_net_signature );
5012 other_objp = multi_get_network_object( other_net_signature );
5014 // if we don't have an object to work with, break out of loop
5018 ai_do_objects_undocked_stuff( objp, other_objp );
5021 case AI_UPDATE_ORDERS:
5024 GET_USHORT( other_net_signature );
5025 if ( mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
5026 GET_DATA(dock_index);
5027 GET_DATA(dockee_index);
5030 // be sure that we have a ship object!!!
5031 if ( !objp || (objp->type != OBJ_SHIP) )
5034 // set up the information in the first goal element of the object in question
5035 aip = &Ai_info[Ships[objp->instance].ai_index];
5036 aip->active_goal = 0;
5037 aip->goals[0].ai_mode = mode;
5038 aip->goals[0].ai_submode = submode;
5040 // for docking, add the dock and dockee index
5041 if ( mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
5042 aip->dock_index = dock_index;
5043 aip->dockee_index = dockee_index;
5046 // get a shipname if we can.
5047 other_objp = multi_get_network_object( other_net_signature );
5048 if ( other_objp && (other_objp->type == OBJ_SHIP) ) {
5049 // get a pointer to the shipname in question. Use the ship_name value in the
5050 // ship. We are only using this for HUD display, so I think that using this
5051 // method will be fine.
5052 aip->goals[0].ship_name = Ships[other_objp->instance].ship_name;
5054 // special case for destroy subsystem -- get the ai_info pointer to our target ship
5055 // so that we can properly set up what subsystem this ship is attacking.
5056 if ( (mode == AI_GOAL_DESTROY_SUBSYSTEM ) && (submode >= 0) )
5057 aip->targeted_subsys = ship_get_indexed_subsys( &Ships[other_objp->instance], submode);
5059 // if docking -- set the dock index and dockee index of this other ship
5060 if ( mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
5061 Ai_info[Ships[other_objp->instance].ai_index].dock_index = dockee_index;
5062 Ai_info[Ships[other_objp->instance].ai_index].dockee_index = dock_index;
5069 Int3(); // this Int3() should be temporary
5070 nprintf(("Network", "Invalid code for ai update: %d\n", code));
5076 // tell the standalone to move into the MISSION_SYNC_STATE
5077 void send_mission_sync_packet(int mode,int start_campaign)
5079 ubyte data[MAX_PACKET_SIZE],is_campaign;
5080 int packet_size = 0;
5082 SDL_assert((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER));
5084 // build the header and add the sync mode (pre or post briefing)
5085 BUILD_HEADER(MISSION_SYNC_DATA);
5088 // if this is a campaign game
5089 if(mode == MULTI_SYNC_PRE_BRIEFING){
5090 if(Game_mode & GM_CAMPAIGN_MODE){
5091 // add a byte indicating campaign mode
5093 ADD_DATA(is_campaign);
5095 // add a byte indicating if we should be starting a campaign or continuing it
5096 is_campaign = (ubyte)start_campaign;
5097 ADD_DATA(is_campaign);
5099 // add the campaign filename
5100 ADD_STRING(Netgame.campaign_name);
5102 // otherwise if this is a single mission
5104 // add a byte indicating single mission mode
5106 ADD_DATA(is_campaign);
5108 // add the mission filename
5109 ADD_STRING(Game_current_mission_filename);
5112 multi_io_send_reliable(Net_player, data, packet_size);
5115 // move into the MISSION_SYNC state when this is received
5116 // this packet is sent only from a game host to a standalone
5117 void process_mission_sync_packet(ubyte *data, header *hinfo)
5120 ubyte campaign_flag;
5121 int offset = HEADER_LENGTH;
5123 SDL_assert(Game_mode & GM_STANDALONE_SERVER);
5125 // if this is a team vs team situation, lock the players send a final team update
5126 if(Netgame.type_flags & NG_TYPE_TEAM){
5127 multi_team_host_lock_all();
5128 multi_team_send_update();
5131 // get the sync mode (pre or post briefing)
5134 if(mode == MULTI_SYNC_PRE_BRIEFING){
5135 // get the flag indicating if this is a single mission or a campaign mode
5136 GET_DATA(campaign_flag);
5138 // get the flag indicating whether we should be starting a new campaign
5139 GET_DATA(campaign_flag);
5141 // get the campaign filename
5142 GET_STRING(Netgame.campaign_name);
5144 // either start a new campaign or continue on to the next mission in the current campaign
5146 multi_campaign_start(Netgame.campaign_name);
5148 multi_campaign_next_mission();
5151 // make sure we remove the campaign mode flag
5152 Game_mode &= ~(GM_CAMPAIGN_MODE);
5154 // get the single mission filename
5155 GET_STRING(Game_current_mission_filename);
5156 SDL_strlcpy(Netgame.mission_name, Game_current_mission_filename, SDL_arraysize(Netgame.mission_name));
5161 // set the correct mode and m ove into the state
5162 Multi_sync_mode = mode;
5163 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
5166 // tell a player to merge his mission stats into his alltime stats
5167 void send_store_stats_packet(int accept)
5170 int packet_size = 0;
5172 BUILD_HEADER(STORE_MISSION_STATS);
5174 // add whether we're accepting or tossing
5175 val = (ubyte)accept;
5178 // if I'm the server, send to everyone, else send to the standalone to be rebroadcasted
5179 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5180 multi_io_send_to_all_reliable(data, packet_size);
5182 multi_io_send_reliable(Net_player, data, packet_size);
5186 void process_store_stats_packet(ubyte *data, header *hinfo)
5188 int offset = HEADER_LENGTH;
5194 // if I'm the standalone, rebroadcast. Otherwise, if I'm a client, merge my mission stats with my alltime stats
5195 if(Game_mode & GM_STANDALONE_SERVER){
5196 // rebroadcast the packet to all others in the game
5197 nprintf(("Network","Standalone received store stats packet - rebroadcasting..\n"));
5198 multi_io_send_to_all_reliable(data, offset);
5201 // all players should mark the stats as being accepted in the debriefing
5202 multi_debrief_stats_accept();
5204 // all players should mark the stats as being "tossed" in the debriefing
5205 multi_debrief_stats_toss();
5210 void send_debris_update_packet(object *objp,int code)
5212 ubyte data[MAX_PACKET_SIZE];
5214 int packet_size = 0;
5216 BUILD_HEADER(DEBRIS_UPDATE);
5217 ADD_USHORT(objp->net_signature);
5221 // add any extra relevant data
5223 case DEBRIS_UPDATE_UPDATE:
5224 // ADD_DATA(objp->pos); // add position
5225 add_vector_data(data, &packet_size, objp->pos);
5226 ADD_ORIENT(objp->orient); // add orientation
5227 // ADD_DATA(objp->phys_info.vel); // add velocity
5228 add_vector_data(data, &packet_size, objp->phys_info.vel);
5229 // ADD_DATA(objp->phys_info.rotvel); // add rotational velocity
5230 add_vector_data(data, &packet_size, objp->phys_info.rotvel);
5233 multi_io_send_to_all(data, packet_size);
5236 void process_debris_update_packet(ubyte *data, header *hinfo)
5240 object bogus_object;
5242 int offset = HEADER_LENGTH;
5244 GET_USHORT(net_sig);
5248 objp = multi_get_network_object(net_sig);
5250 objp = &bogus_object;
5254 // update the object
5255 case DEBRIS_UPDATE_UPDATE:
5256 //GET_DATA(objp->pos);
5257 get_vector_data( data, &offset, objp->pos );
5259 GET_ORIENT(objp->orient);
5260 GET_DATA(objp->phys_info.vel);
5261 GET_DATA(objp->phys_info.rotvel);
5263 // simply remove it (no explosion)
5264 case DEBRIS_UPDATE_REMOVE:
5265 if(objp != &bogus_object){
5266 SDL_assert(objp->type == OBJ_DEBRIS);
5267 obj_delete(OBJ_INDEX(objp));
5271 case DEBRIS_UPDATE_NUKE:
5272 if(objp != &bogus_object)
5273 debris_hit(objp,NULL,&objp->pos,1000000.0f);
5281 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)
5283 ubyte data[MAX_PACKET_SIZE];
5285 int packet_size = 0;
5287 BUILD_HEADER(WSS_REQUEST_PACKET);
5289 // add the request information
5290 ADD_SHORT(player_id);
5292 ADD_INT(from_index);
5295 ADD_INT(wl_ship_slot); // only used in weapons loadout
5296 ADD_INT(ship_class);
5299 // a standard request
5301 multi_io_send_reliable(Net_player, data, packet_size);
5303 // being routed through the standalone to the host of the game
5305 SDL_assert(Game_mode & GM_STANDALONE_SERVER);
5306 multi_io_send_reliable(p, data, packet_size);
5310 void process_wss_request_packet(ubyte *data, header *hinfo)
5312 int offset = HEADER_LENGTH;
5313 int from_slot,from_index;
5314 int to_slot,to_index;
5316 int wl_ship_slot,ship_class;
5320 // determine who this request is from
5321 GET_SHORT(player_id);
5322 player_num = find_player_id(player_id);
5324 // read in the request data
5326 GET_INT(from_index);
5329 GET_INT(wl_ship_slot); // only used in weapons loadout
5330 GET_INT(ship_class); // only used in multi team select
5334 SDL_assert(player_num != -1);
5335 if(player_num == -1){
5339 // if we're the standalone, we have to route this packet to the host of the game
5340 if(Game_mode & GM_STANDALONE_SERVER){
5341 send_wss_request_packet(player_id, from_slot, from_index, to_slot, to_index, wl_ship_slot, ship_class, mode, Netgame.host);
5343 // otherwise we're the host and should process the request
5346 case WSS_WEAPON_SELECT :
5347 wl_drop(from_slot,from_index,to_slot,to_index,wl_ship_slot,player_num);
5349 case WSS_SHIP_SELECT :
5350 multi_ts_drop(from_slot,from_index,to_slot,to_index,ship_class,player_num);
5358 void send_wss_update_packet(int team_num,ubyte *wss_data,int size)
5360 ubyte data[MAX_PACKET_SIZE],team;
5361 int packet_size = 0;
5363 SDL_assert(size <= (MAX_PACKET_SIZE - 10));
5365 BUILD_HEADER(WSS_UPDATE_PACKET);
5367 // add the team/pool # this is for
5368 team = (ubyte)team_num;
5371 // add the data block size
5374 // add the data itself
5375 memcpy(data + packet_size,wss_data,size);
5376 packet_size += size;
5378 // if we're also the master of the game (not on a standalone)
5379 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5380 multi_io_send_to_all_reliable(data, packet_size);
5382 // if we're only the host on the standalone, then send the packet to the standalone to be routed
5384 multi_io_send_reliable(Net_player, data, packet_size);
5388 void process_wss_update_packet(ubyte *data, header *hinfo)
5391 int size,player_index,idx;
5392 int offset = HEADER_LENGTH;
5394 // get the team/pool #
5397 // get the data size
5400 // if we're the standalone, then we should be routing this data to all the other clients
5401 if(Game_mode & GM_STANDALONE_SERVER){
5406 // determine where this came from
5407 player_index = find_player_id(hinfo->id);
5408 SDL_assert(player_index != -1);
5409 if(player_index < 0){
5413 // route the packet (don't resend it to the host)
5414 for(idx=0;idx<MAX_PLAYERS;idx++){
5415 if(MULTI_CONNECTED(Net_players[idx]) && (&Net_players[idx] != Net_player) && (&Net_players[idx] != &Net_players[player_index]) ){
5416 multi_io_send_reliable(&Net_players[idx], data, offset);
5420 // set the proper pool pointers
5421 ss_set_team_pointers((int)team);
5423 // read in the block of data, and apply it to the weapons/ship pools
5424 offset += restore_wss_data(data + offset);
5427 // set the pool pointers back to my own team
5428 ss_set_team_pointers(Net_player->p_info.team);
5430 // sync the interface if this was for my own team
5431 if((int)team == Net_player->p_info.team){
5432 multi_ts_sync_interface();
5439 // function to send firing information from the client to the server once they reach
5440 // the final sync screen.
5441 void send_firing_info_packet()
5443 ubyte data[MAX_PACKET_SIZE];
5445 ubyte plinked, sdual;
5447 SDL_assert( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
5449 BUILD_HEADER(FIRING_INFO);
5450 plinked = (ubyte)((Player_ship->flags & SF_PRIMARY_LINKED)?1:0);
5451 sdual = (ubyte)((Player_ship->flags & SF_SECONDARY_DUAL_FIRE)?1:0);
5452 ADD_DATA( plinked );
5455 multi_io_send_reliable(Net_player, data, packet_size);
5458 void process_firing_info_packet( ubyte *data, header *hinfo )
5460 int offset, player_num;
5461 ubyte plinked, sdual;
5464 // only the master of the game should be dealing with these packets
5465 SDL_assert( Net_player->flags & NETINFO_FLAG_AM_MASTER );
5467 offset = HEADER_LENGTH;
5468 GET_DATA( plinked );
5472 player_num = find_player_id(hinfo->id);
5474 nprintf(("Network","Received firing info packet from unknown player, ignoring\n"));
5478 // get the ship pointer for this player and set the flags accordingly.
5479 shipp = &(Ships[Objects[Net_players[player_num].player->objnum].instance]);
5481 shipp->flags |= SF_PRIMARY_LINKED;
5483 shipp->flags &= ~SF_PRIMARY_LINKED;
5486 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
5488 shipp->flags &= ~SF_SECONDARY_DUAL_FIRE;
5491 // packet to deal with changing status of mission goals. used to be sent every so often from server
5492 // to clients, but with addition of reliable sockets, send when complete, invalid, etc.
5493 // goal_num is the index into mission_goals. new_status means failed, success, etc. -1 if not used.
5494 // valid means goal is changing to invalid(0) or valid(1). only applies if new_status == -1
5495 void send_mission_goal_info_packet( int goal_num, int new_status, int valid )
5497 ubyte data[MAX_PACKET_SIZE];
5500 BUILD_HEADER(MISSION_GOAL_INFO);
5503 ADD_INT(new_status);
5506 multi_io_send_to_all_reliable(data, packet_size);
5509 void process_mission_goal_info_packet( ubyte *data, header *hinfo )
5511 int offset, goal_num, new_status, valid;
5513 offset = HEADER_LENGTH;
5515 GET_INT(new_status);
5519 // if new_status != -1, then this is a change in goal status (i.e. goal failed, or is successful)
5520 if ( new_status != -1 ){
5521 mission_goal_status_change( goal_num, new_status );
5523 mission_goal_validation_change( goal_num, valid );
5527 void send_player_settings_packet(net_player *p)
5529 ubyte data[MAX_PACKET_SIZE];
5532 int packet_size = 0;
5535 BUILD_HEADER(PLAYER_SETTINGS);
5537 // add all the data for all the players
5539 for(idx=0;idx<MAX_PLAYERS;idx++){
5540 if(MULTI_CONNECTED(Net_players[idx])){
5542 ADD_SHORT(Net_players[idx].player_id);
5544 // break the p_info structure by member, so we don't overwrite any absolute pointers
5545 // ADD_DATA(Net_players[idx].p_info);
5546 ADD_INT(Net_players[idx].p_info.team);
5547 ADD_INT(Net_players[idx].p_info.ship_index);
5548 ADD_INT(Net_players[idx].p_info.ship_class);
5551 // add the stop byte
5555 // either broadcast the data or send to a specific player
5557 multi_io_send_to_all_reliable(data, packet_size);
5559 multi_io_send_reliable(p, data, packet_size);
5563 void process_player_settings_packet(ubyte *data, header *hinfo)
5565 int offset,player_num;
5566 net_player_info bogus,*ptr;
5570 offset = HEADER_LENGTH;
5572 // read in the data for all the players
5574 while(stop != 0xff){
5575 // lookup the player
5576 GET_SHORT(player_id);
5577 player_num = find_player_id(player_id);
5579 // make sure this is a valid player
5580 if(player_num == -1){
5583 ptr = &Net_players[player_num].p_info;
5587 GET_INT(ptr->ship_index);
5588 GET_INT(ptr->ship_class);
5595 // update the server with my new state
5596 // MWA -- 3/31/98 -- check for in mission instead of state.
5597 //if ( Netgame.game_state == NETGAME_STATE_MISSION_SYNC) {
5598 if( !(Game_mode & GM_IN_MISSION) ) {
5599 Net_player->state = NETPLAYER_STATE_SETTINGS_ACK;
5600 send_netplayer_update_packet();
5604 // display some cool text
5605 multi_common_add_text(XSTR("Received player settings packet\n",721),1);
5608 void send_deny_packet(net_addr *addr, int code)
5611 int packet_size = 0;
5613 // build the header and add the rejection code
5619 psnet_send(addr, data, packet_size);
5622 void process_deny_packet(ubyte *data, header *hinfo)
5626 // get the denial code
5627 offset = HEADER_LENGTH;
5631 // if there is already a dialog active, do nothing - who cares at this point.
5636 // display the appropriate dialog
5638 case JOIN_DENY_JR_STATE :
5639 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));
5641 case JOIN_DENY_JR_TRACKER_INVAL :
5642 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));
5644 case JOIN_DENY_JR_PASSWD :
5645 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because this is a password protected game",724));
5647 case JOIN_DENY_JR_CLOSED :
5648 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));
5650 case JOIN_DENY_JR_TEMP_CLOSED :
5651 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));
5653 case JOIN_DENY_JR_RANK_HIGH :
5654 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));
5656 case JOIN_DENY_JR_RANK_LOW :
5657 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));
5659 case JOIN_DENY_JR_DUP :
5660 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because there is an identical player already in the game",729));
5662 case JOIN_DENY_JR_FULL :
5663 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because the game is full",730));
5665 case JOIN_DENY_JR_BANNED :
5666 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because you are banned from this server",731));
5668 case JOIN_DENY_JR_NOOBS :
5669 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because this game does not allow observers",732));
5671 case JOIN_DENY_JR_INGAME_JOIN :
5672 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));
5674 case JOIN_DENY_JR_BAD_VERSION :
5675 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));
5677 case JOIN_DENY_JR_TYPE :
5678 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You cannot join a game in progress unless it is a dogfight mission.",1433));
5682 // call this so that the join request timestamp automatically expires when we hear back from the server
5683 multi_join_reset_join_stamp();
5686 // this packet will consist of
5687 // 1.) netplayer ship classes (85 bytes max)
5688 // 2.) ship weapon state data (241 bytes max)
5689 // 3.) player settings et. al. (133 bytes max)
5690 // TOTAL 459 NOTE : keep this in mind when/if adding new data to this packet
5691 void send_post_sync_data_packet(net_player *p, int std_request)
5693 ubyte data[MAX_PACKET_SIZE], val;
5698 ushort sval, ship_ets;
5699 int idx, player_index;
5700 int packet_size = 0;
5704 BUILD_HEADER(POST_SYNC_DATA);
5706 // some header information for standalone packet routing purposes
5707 val = (ubyte)std_request;
5710 // the standalone has two situations
5711 // 1.) sending a request to the host to distribute this block of data
5712 // 2.) having recevied this block of data from the host, it redistributes it
5713 if((Game_mode & GM_STANDALONE_SERVER) && std_request && (Netgame.host != NULL)){
5714 // case 1, send the request
5715 multi_io_send_reliable(Netgame.host, data, packet_size);
5718 // case 2 for the standalone is below (as normal)
5720 // otherwise build the data now
5722 // add all deleted ships
5723 val = (ubyte)Multi_ts_num_deleted;
5725 for(idx=0;idx<Multi_ts_num_deleted;idx++){
5726 sval = (ushort)Objects[Multi_ts_deleted_objnums[idx]].net_signature;
5732 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5733 shipp = &Ships[Objects[so->objnum].instance];
5735 // don't process non player wing ships
5736 if ( !(shipp->flags & SF_FROM_PLAYER_WING ) )
5742 // # of ships - used multiple times in the packet
5743 val = (ubyte)ship_count;
5746 // add ship class information (85 bytes max)
5747 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5748 shipp = &Ships[Objects[so->objnum].instance];
5750 // don't process non player wing ships
5751 if ( !(shipp->flags & SF_FROM_PLAYER_WING ) )
5754 // add the net signature of the object for look up
5755 ADD_USHORT( Objects[so->objnum].net_signature );
5757 // add the ship info index
5758 val = (ubyte)(shipp->ship_info_index);
5761 // add the ships's team select index
5762 val = (ubyte)shipp->ts_index;
5766 // add weapon state information for all starting ships (241 bytes max)
5767 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5768 shipp = &Ships[Objects[so->objnum].instance];
5770 // don't process non player wing ships
5771 if ( !(shipp->flags & SF_FROM_PLAYER_WING ) )
5774 // if this is a ship owned by a player, we should mark down his weapons bank/link settings now if we're the server
5776 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5777 player_index = multi_find_player_by_net_signature(Objects[so->objnum].net_signature);
5778 if(player_index == -1){
5781 pl = &Net_players[player_index];
5785 // add the net signature and other weapon information
5786 ADD_USHORT( Objects[so->objnum].net_signature );
5788 // add number of primary and secondary banks
5789 bval = (char)(shipp->weapons.num_primary_banks);
5791 bval = (char)(shipp->weapons.num_secondary_banks);
5794 // add weapon bank status
5795 bval = (char)(shipp->weapons.current_primary_bank);
5797 pl->s_info.cur_primary_bank = bval;
5799 // SDL_assert(bval != -1);
5802 bval = (char)(shipp->weapons.current_secondary_bank);
5804 pl->s_info.cur_secondary_bank = bval;
5806 // SDL_assert(bval != -1);
5809 // primary weapon info
5810 bval = (char)(shipp->weapons.primary_bank_weapons[0]);
5812 bval = (char)(shipp->weapons.primary_bank_weapons[1]);
5815 // secondary weapon info
5816 bval = (char)(shipp->weapons.secondary_bank_weapons[0]);
5818 val_short = (short)(shipp->weapons.secondary_bank_ammo[0]);
5819 ADD_SHORT(val_short);
5820 bval = (char)(shipp->weapons.secondary_bank_weapons[1]);
5822 val_short = (short)(shipp->weapons.secondary_bank_ammo[1]);
5823 ADD_SHORT(val_short);
5824 bval = (char)(shipp->weapons.secondary_bank_weapons[2]);
5826 val_short = (short)(shipp->weapons.secondary_bank_ammo[2]);
5827 ADD_SHORT(val_short);
5829 // send primary and secondary weapon link status
5831 if(shipp->flags & SF_PRIMARY_LINKED){
5833 pl->s_info.cur_link_status |= (1<<0);
5837 if(shipp->flags & SF_SECONDARY_DUAL_FIRE){
5839 pl->s_info.cur_link_status |= (1<<1);
5843 // if this is a player ship add (1<<2)
5844 if(Objects[shipp->objnum].flags & OF_PLAYER_SHIP){
5849 // add a ship ets value
5852 ship_ets |= ((ushort)shipp->shield_recharge_index << 8);
5854 ship_ets |= ((ushort)shipp->weapon_recharge_index << 4);
5856 ship_ets |= ((ushort)shipp->engine_recharge_index);
5857 ADD_USHORT(ship_ets);
5861 // 2 cases, if I'm the host on a standalone, I should be sending this to the standalone only
5862 // or if I'm the server as well as the host, I should be sending this to all players
5863 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
5864 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5867 multi_io_send_to_all_reliable(data, packet_size);
5869 // send to a specific player
5871 multi_io_send_reliable(p, data, packet_size);
5874 multi_io_send_reliable(Net_player, data, packet_size);
5881 multi_io_send_to_all_reliable(data, packet_size);
5883 // send to a specific player
5885 multi_io_send_reliable(p, data, packet_size);
5890 void process_post_sync_data_packet(ubyte *data, header *hinfo)
5892 ubyte val, sinfo_index, ts_index;
5894 ushort net_sig, ship_ets, sval;
5898 int offset = HEADER_LENGTH;
5902 // packet routing information
5905 // 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
5906 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) && val){
5909 // at this point we want to delete all necessary ships, change all necessary ship classes, and set all weapons up
5910 multi_ts_create_wings();
5912 // send to the standalone through my socket
5913 send_post_sync_data_packet(Net_player);
5919 // add all deleted ships
5921 Multi_ts_num_deleted = (int)val;
5922 for(idx=0;idx<Multi_ts_num_deleted;idx++){
5923 // get the ship's objnum
5926 objp = multi_get_network_object(sval);
5928 // delete the ship appropriately
5929 // mark the object as having been deleted
5930 Multi_ts_deleted_objnums[idx] = OBJ_INDEX(objp);
5933 ship_add_exited_ship(&Ships[objp->instance], SEF_PLAYER_DELETED);
5934 obj_delete(Multi_ts_deleted_objnums[idx]);
5935 ship_wing_cleanup(objp->instance,&Wings[Ships[objp->instance].wingnum]);
5937 Multi_ts_num_deleted--;
5938 nprintf(("Network","Couldn't find object by net signature for ship delete in post sync data packet\n"));
5946 // process ship class information
5947 for(idx=0; idx<ship_count; idx++){
5948 // get the object's net signature
5949 GET_USHORT(net_sig);
5950 GET_DATA(sinfo_index);
5953 // attempt to get the object
5954 objp = multi_get_network_object(net_sig);
5956 // make sure we found a ship
5957 SDL_assert((objp != NULL) && (objp->type == OBJ_SHIP));
5959 // set the ship to be the right class
5960 change_ship_type(objp->instance,(int)sinfo_index);
5962 // set the ship's team select index
5963 Ships[objp->instance].ts_index = (int)ts_index;
5966 // process ship weapon state info
5967 for(idx=0; idx<ship_count; idx++){
5968 // get the object's net signature
5969 GET_USHORT(net_sig);
5971 // attempt to get the object
5972 objp = multi_get_network_object(net_sig);
5974 // make sure we found a ship
5975 SDL_assert((objp != NULL) && (objp->type == OBJ_SHIP));
5977 // get a pointer to the ship
5978 shipp = &Ships[objp->instance];
5980 // get number of primary and secondary banks;
5982 SDL_assert( b != -1 );
5983 shipp->weapons.num_primary_banks = (int)b;
5986 SDL_assert( b != -1 );
5987 shipp->weapons.num_secondary_banks = (int)b;
5989 // get bank selection info
5994 shipp->weapons.current_primary_bank = (int)b;
6000 shipp->weapons.current_secondary_bank = (int)b;
6002 // primary weapon info
6004 shipp->weapons.primary_bank_weapons[0] = (int)b;
6007 shipp->weapons.primary_bank_weapons[1] = (int)b;
6009 // secondary weapon info
6011 shipp->weapons.secondary_bank_weapons[0] = (int)b;
6012 GET_SHORT(val_short);
6013 shipp->weapons.secondary_bank_ammo[0] = (int)val_short;
6016 shipp->weapons.secondary_bank_weapons[1] = (int)b;
6017 GET_SHORT(val_short);
6018 shipp->weapons.secondary_bank_ammo[1] = (int)val_short;
6021 shipp->weapons.secondary_bank_weapons[2] = (int)b;
6022 GET_SHORT(val_short);
6023 shipp->weapons.secondary_bank_ammo[2] = (int)val_short;
6030 shipp->flags |= SF_PRIMARY_LINKED;
6033 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
6035 Objects[shipp->objnum].flags &= ~(OF_PLAYER_SHIP);
6036 Objects[shipp->objnum].flags &= ~(OF_COULD_BE_PLAYER);
6038 Objects[shipp->objnum].flags |= OF_PLAYER_SHIP;
6040 obj_set_flags( &Objects[shipp->objnum], Objects[shipp->objnum].flags | OF_COULD_BE_PLAYER );
6044 GET_USHORT(ship_ets);
6046 shipp->shield_recharge_index = ((ship_ets & 0x0f00) >> 8);
6048 shipp->weapon_recharge_index = ((ship_ets & 0x00f0) >> 4);
6050 shipp->engine_recharge_index = (ship_ets & 0x000f);
6055 Net_player->state = NETPLAYER_STATE_POST_DATA_ACK;
6056 send_netplayer_update_packet();
6058 // the standalone server will receive this packet from the host of the game, to be applied locally and
6059 // also to be rebroadcast.
6060 if(Game_mode & GM_STANDALONE_SERVER){
6061 // update player ets settings
6062 for(idx=0;idx<MAX_PLAYERS;idx++){
6063 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player->objnum != -1)){
6064 multi_server_update_player_weapons(&Net_players[idx],&Ships[Objects[Net_players[idx].player->objnum].instance]);
6068 send_post_sync_data_packet(NULL,0);
6072 void send_wss_slots_data_packet(int team_num,int final,net_player *p,int std_request)
6074 ubyte data[MAX_PACKET_SIZE],val;
6077 int packet_size = 0;
6080 BUILD_HEADER(WSS_SLOTS_DATA);
6082 // some header information for standalone packet routing purposes
6083 val = (ubyte)std_request;
6087 val = (ubyte)team_num;
6090 // add whether this is the final packet or not
6094 // the standalone has two situations
6095 // 1.) sending a request to the host to distribute this block of data
6096 // 2.) having recevied this block of data from the host, it redistributes it
6097 if((Game_mode & GM_STANDALONE_SERVER) && std_request){
6098 // case 1, send the request
6099 multi_io_send_reliable(Netgame.host, data, packet_size);
6102 // case 2 for the standalone is below (as normal)
6104 // add all the slots
6105 for(idx=0;idx<MULTI_TS_NUM_SHIP_SLOTS;idx++){
6106 // add the ship class
6107 val = (ubyte)Wss_slots_teams[team_num][idx].ship_class;
6111 for(i = 0;i<MAX_WL_WEAPONS;i++){
6112 val = (ubyte)Wss_slots_teams[team_num][idx].wep[i];
6116 // add the weapon counts
6117 for(i = 0;i<MAX_WL_WEAPONS;i++){
6118 val_short = (short)Wss_slots_teams[team_num][idx].wep_count[i];
6119 ADD_SHORT(val_short);
6123 // 2 cases, if I'm the host on a standalone, I should be sending this to the standalone only
6124 // or if I'm the server as well as the host, I should be sending this to all players
6125 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
6126 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
6129 multi_io_send_to_all_reliable(data, packet_size);
6131 // send to a specific player
6133 multi_io_send_reliable(p, data, packet_size);
6136 multi_io_send_reliable(Net_player, data, packet_size);
6143 multi_io_send_to_all_reliable(data, packet_size);
6145 // send to a specific player
6147 multi_io_send_reliable(p, data, packet_size);
6152 void process_wss_slots_data_packet(ubyte *data, header *hinfo)
6154 ubyte val,team_num,final;
6156 int offset = HEADER_LENGTH;
6159 // packet routing information
6165 // get whether this is the final packet or not
6168 // 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
6169 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) && val){
6172 // send to the standalone through my socket
6173 send_wss_slots_data_packet((int)team_num,(int)final,Net_player);
6177 // read in all the slot data
6178 for(idx=0;idx<MULTI_TS_NUM_SHIP_SLOTS;idx++){
6179 memset(&Wss_slots_teams[team_num][idx],0,sizeof(wss_unit));
6181 // get the ship class
6183 Wss_slots_teams[team_num][idx].ship_class = (int)val;
6186 for(i = 0;i<MAX_WL_WEAPONS;i++){
6188 Wss_slots_teams[team_num][idx].wep[i] = (int)val;
6190 // check for signed/unsigned problems
6191 if(Wss_slots_teams[team_num][idx].wep[i] == 255){
6192 Wss_slots_teams[team_num][idx].wep[i] = -1;
6196 // get the weapon counts
6197 for(i = 0;i<MAX_WL_WEAPONS;i++){
6198 GET_SHORT(val_short);
6199 Wss_slots_teams[team_num][idx].wep_count[i] = (int)val_short;
6204 // update my netplayer state if this is the final packet
6206 Net_player->state = NETPLAYER_STATE_WSS_ACK;
6207 send_netplayer_update_packet();
6210 // the standalone server will receive this packet from the host of the game, to be applied locally and
6211 // also to be rebroadcast.
6212 if(Game_mode & GM_STANDALONE_SERVER){
6213 send_wss_slots_data_packet((int)team_num,(int)final,NULL,0);
6215 // add some mission sync text
6216 multi_common_add_text(XSTR("Weapon slots packet\n",735),1);
6220 #define OBJ_VISIBILITY_DOT 0.6f
6222 // send and receive packets for shield explosion information
6223 void send_shield_explosion_packet( int objnum, int tri_num, vector hit_pos )
6226 ubyte data[MAX_PACKET_SIZE], utri_num;
6229 // SDL_assert(!(Netgame.debug_flags & NETD_FLAG_CLIENT_NODAMAGE));
6231 SDL_assert( tri_num < UCHAR_MAX );
6232 utri_num = (ubyte)tri_num;
6234 // for each player, determine if this object is behind the player -- if so, don't
6236 for ( i = 0; i < MAX_PLAYERS; i++ ) {
6237 if ( MULTI_CONNECTED(Net_players[i]) && (&Net_players[i] != Net_player) ) {
6239 vector eye_to_obj_vec, diff, eye_pos;
6242 eye_pos = Net_players[i].s_info.eye_pos;
6243 eye_orient = Net_players[i].s_info.eye_orient;
6245 // check for same vectors
6246 vm_vec_sub(&diff, &Objects[objnum].pos, &eye_pos);
6247 if ( vm_vec_mag_quick(&diff) < 0.01 ){
6251 vm_vec_normalized_dir(&eye_to_obj_vec, &Objects[objnum].pos, &eye_pos);
6252 dot = vm_vec_dot(&eye_orient.v.fvec, &eye_to_obj_vec);
6254 if ( dot < OBJ_VISIBILITY_DOT ){
6258 BUILD_HEADER(SHIELD_EXPLOSION);
6260 ADD_USHORT( Objects[objnum].net_signature );
6263 multi_io_send(&Net_players[i], data, packet_size);
6268 void add_shield_point_multi(int objnum, int tri_num, vector *hit_pos);
6270 void process_shield_explosion_packet( ubyte *data, header *hinfo)
6277 // get the shield hit data
6278 offset = HEADER_LENGTH;
6279 GET_USHORT(signature);
6281 //GET_DATA(hit_pos);
6284 // find the object with this signature. If found, then do a shield explosion
6285 objp = multi_get_network_object( signature );
6288 shield_info *shieldp;
6293 // given the tri num, find the local position which is the average of the
6294 // three vertices of the triangle affected. Use this average point as the hit
6296 // SDL_assert( objp->type == OBJ_SHIP );
6297 if(objp->type != OBJ_SHIP){
6301 pm = model_get(Ships[objp->instance].modelnum);
6302 shieldp = &pm->shield;
6303 SDL_assert( utri_num < shieldp->ntris );
6304 stri = shieldp->tris[utri_num];
6305 vm_vec_zero(&hit_pos);
6306 for ( i = 0; i < 3; i++ ) {
6307 vm_vec_add2( &hit_pos, &(shieldp->verts[stri.verts[i]].pos) );
6309 vm_vec_scale( &hit_pos, 1.0f/3.0f );
6310 add_shield_point_multi( OBJ_INDEX(objp), utri_num, &hit_pos );
6314 void send_player_stats_block_packet(net_player *pl, int stats_code, net_player *target)
6317 ubyte data[MAX_PACKET_SIZE], val;
6319 int packet_size = 0;
6324 sc = &pl->player->stats;
6327 BUILD_HEADER(PLAYER_STATS);
6329 // add the player id
6330 ADD_SHORT(pl->player_id);
6332 // add the byte indicating whether these stats are all-time or not
6333 val = (ubyte)stats_code;
6336 // kill information - alltime
6340 for(idx=0;idx<MAX_SHIP_TYPES;idx++){
6341 u_tmp = sc->kills[idx];
6344 // medal information
6345 for(idx=0;idx<NUM_MEDALS;idx++){
6346 i_tmp = sc->medals[idx];
6352 ADD_INT(sc->assists);
6353 ADD_INT(sc->kill_count);
6354 ADD_INT(sc->kill_count_ok);
6355 ADD_UINT(sc->p_shots_fired);
6356 ADD_UINT(sc->s_shots_fired);
6357 ADD_UINT(sc->p_shots_hit);
6358 ADD_UINT(sc->s_shots_hit);
6359 ADD_UINT(sc->p_bonehead_hits);
6360 ADD_UINT(sc->s_bonehead_hits);
6361 ADD_INT(sc->bonehead_kills);
6363 ADD_UINT(sc->missions_flown);
6364 ADD_UINT(sc->flight_time);
6365 ADD_INT(sc->last_flown);
6366 ADD_INT(sc->last_backup);
6371 for(idx=0;idx<MAX_SHIP_TYPES;idx++){
6372 u_tmp = sc->m_okKills[idx];
6376 ADD_INT(sc->m_score);
6377 ADD_INT(sc->m_assists);
6378 ADD_INT(sc->m_kill_count);
6379 ADD_INT(sc->m_kill_count_ok);
6380 ADD_UINT(sc->mp_shots_fired);
6381 ADD_UINT(sc->ms_shots_fired);
6382 ADD_UINT(sc->mp_shots_hit);
6383 ADD_UINT(sc->ms_shots_hit);
6384 ADD_UINT(sc->mp_bonehead_hits);
6385 ADD_UINT(sc->ms_bonehead_hits);
6386 ADD_INT(sc->m_bonehead_kills);
6387 ADD_INT(sc->m_player_deaths);
6388 ADD_INT(sc->m_medal_earned);
6391 case STATS_MISSION_KILLS:
6392 ADD_INT(sc->m_kill_count);
6393 ADD_INT(sc->m_kill_count_ok);
6394 ADD_INT(sc->m_assists);
6397 case STATS_DOGFIGHT_KILLS:
6398 for(idx=0; idx<MAX_PLAYERS; idx++){
6400 u_tmp = sc->m_dogfight_kills[idx];
6406 ADD_INT(sc->m_kill_count);
6407 ADD_INT(sc->m_kill_count_ok);
6408 ADD_INT(sc->m_assists);
6412 SDL_assert(packet_size < MAX_PACKET_SIZE);
6414 // if we're a client, always send the data to the server
6415 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
6416 multi_io_send_reliable(Net_player, data, packet_size);
6418 // otherwise do server specific stuff
6420 // send to a specific target
6422 multi_io_send_reliable(target, data, packet_size);
6424 // otherwise, send to everyone
6426 multi_io_send_to_all_reliable(data, packet_size);
6431 void process_player_stats_block_packet(ubyte *data, header *hinfo)
6435 scoring_struct *sc,bogus;
6437 int offset = HEADER_LENGTH;
6441 // nprintf(("Network","----------++++++++++********RECEIVED STATS***********+++++++++----------\n"));
6443 // get the player who these stats are for
6444 GET_SHORT(player_id);
6445 player_num = find_player_id(player_id);
6446 if (player_num == -1) {
6447 nprintf(("Network", "Couldn't find player for stats update!\n"));
6448 ml_string("Couldn't find player for stats update!");
6453 sc = &Net_players[player_num].player->stats;
6456 // get the stats code
6460 ml_string("Received STATS_ALLTIME");
6463 for (idx=0; idx<MAX_SHIP_TYPES; idx++) {
6465 sc->kills[idx] = u_tmp;
6468 // read in the stats
6469 for (idx=0; idx<NUM_MEDALS; idx++) {
6471 sc->medals[idx] = i_tmp;
6476 GET_INT(sc->assists);
6477 GET_INT(sc->kill_count);
6478 GET_INT(sc->kill_count_ok);
6479 GET_UINT(sc->p_shots_fired);
6480 GET_UINT(sc->s_shots_fired);
6481 GET_UINT(sc->p_shots_hit);
6482 GET_UINT(sc->s_shots_hit);
6483 GET_UINT(sc->p_bonehead_hits);
6484 GET_UINT(sc->s_bonehead_hits);
6485 GET_INT(sc->bonehead_kills);
6487 GET_UINT(sc->missions_flown);
6488 GET_UINT(sc->flight_time);
6489 GET_INT(sc->last_flown);
6490 GET_INT(sc->last_backup);
6494 ml_string("Received STATS_MISSION");
6496 // kills - mission OK
6497 for (idx=0; idx<MAX_SHIP_TYPES; idx++) {
6499 sc->m_okKills[idx] = u_tmp;
6502 GET_INT(sc->m_score);
6503 GET_INT(sc->m_assists);
6504 GET_INT(sc->m_kill_count);
6505 GET_INT(sc->m_kill_count_ok);
6506 GET_UINT(sc->mp_shots_fired);
6507 GET_UINT(sc->ms_shots_fired);
6508 GET_UINT(sc->mp_shots_hit);
6509 GET_UINT(sc->ms_shots_hit);
6510 GET_UINT(sc->mp_bonehead_hits);
6511 GET_UINT(sc->ms_bonehead_hits);
6512 GET_INT(sc->m_bonehead_kills);
6513 GET_INT(sc->m_player_deaths);
6514 GET_INT(sc->m_medal_earned);
6517 case STATS_MISSION_KILLS:
6518 ml_string("Received STATS_MISSION_KILLS");
6520 GET_INT(sc->m_kill_count);
6521 GET_INT(sc->m_kill_count_ok);
6522 GET_INT(sc->m_assists);
6525 case STATS_DOGFIGHT_KILLS:
6526 ml_string("Received STATS_DOGFIGHT_KILLS");
6527 if(player_num >= 0){
6528 ml_printf("Dogfight stats for %s", Net_players[player_num].player->callsign);
6530 for(idx=0; idx<MAX_PLAYERS; idx++){
6533 sc->m_dogfight_kills[idx] = u_tmp;
6534 if(player_num >= 0){
6535 ml_printf("%d", Net_players[player_num].player->stats.m_dogfight_kills[idx]);
6539 GET_INT(sc->m_kill_count);
6540 GET_INT(sc->m_kill_count_ok);
6541 GET_INT(sc->m_assists);
6546 // if I'm the server of the game, I should always rebroadcast these stats
6547 if ((Net_player->flags & NETINFO_FLAG_AM_MASTER) && (sc != &bogus)) {
6548 // make sure these are alltime stats
6549 SDL_assert(val == STATS_ALLTIME);
6551 multi_broadcast_stats(STATS_ALLTIME);
6555 // called to create asteroid stuff
6556 void send_asteroid_create( object *new_objp, object *parent_objp, int asteroid_type, vector *relvec )
6559 ubyte data[MAX_PACKET_SIZE];
6560 ubyte packet_type, atype;
6564 if (relvec != NULL )
6567 BUILD_HEADER( ASTEROID_INFO );
6568 packet_type = ASTEROID_CREATE;
6570 SDL_assert( asteroid_type < UCHAR_MAX );
6571 atype = (ubyte)asteroid_type;
6573 ADD_DATA( packet_type );
6574 ADD_USHORT( parent_objp->net_signature );
6575 ADD_USHORT( new_objp->net_signature );
6578 add_vector_data( data, &packet_size, vec );
6580 multi_io_send_to_all(data, packet_size);
6583 void send_asteroid_throw( object *objp )
6586 ubyte data[MAX_PACKET_SIZE], packet_type;
6588 BUILD_HEADER( ASTEROID_INFO );
6590 // this packet type is an asteroid throw
6591 packet_type = ASTEROID_THROW;
6592 ADD_DATA( packet_type );
6593 ADD_USHORT( objp->net_signature );
6594 //ADD_DATA( objp->pos );
6595 add_vector_data( data, &packet_size, objp->pos );
6596 //ADD_DATA( objp->phys_info.vel );
6597 add_vector_data( data, &packet_size, objp->phys_info.vel );
6599 multi_io_send_to_all(data, packet_size);
6602 void send_asteroid_hit( object *objp, object *other_objp, vector *hitpos, float damage )
6605 ubyte data[MAX_PACKET_SIZE], packet_type;
6609 if ( hitpos != NULL )
6612 // build up an asteroid hit packet
6613 BUILD_HEADER( ASTEROID_INFO );
6614 packet_type = ASTEROID_HIT;
6615 ADD_DATA( packet_type );
6616 ADD_USHORT( objp->net_signature );
6618 if(other_objp == NULL){
6619 ushort invalid_sig = 0xffff;
6620 ADD_USHORT(invalid_sig);
6622 ADD_USHORT( other_objp->net_signature );
6625 add_vector_data( data, &packet_size, vec );
6626 ADD_FLOAT( damage );
6628 multi_io_send_to_all(data, packet_size);
6631 void process_asteroid_info( ubyte *data, header *hinfo )
6636 offset = HEADER_LENGTH;
6637 GET_DATA( packet_type );
6639 // based on the packet type, do something interesting with an asteroid!
6640 switch( packet_type ) {
6642 case ASTEROID_CREATE: {
6643 ushort psignature, signature;
6645 vector relvec = ZERO_VECTOR;
6646 object *parent_objp;
6648 GET_USHORT( psignature );
6649 GET_USHORT( signature );
6651 //GET_DATA( relvec );
6652 get_vector_data( data, &offset, relvec );
6654 // after getting the values, set the next network signature, and call the create sub function
6655 multi_set_network_signature( signature, MULTI_SIG_ASTEROID );
6656 parent_objp = multi_get_network_object( psignature );
6657 if ( parent_objp ) {
6658 asteroid_sub_create( parent_objp, atype, &relvec );
6660 nprintf(("Network", "Couldn't create asteroid because parent wasn't found!!!\n"));
6667 // asteroid throw packet -- asteroid has wrapped bounds
6668 case ASTEROID_THROW: {
6670 vector pos = ZERO_VECTOR, vel = ZERO_VECTOR;
6673 GET_USHORT( signature );
6675 get_vector_data( data, &offset, pos );
6677 get_vector_data( data, &offset, vel );
6678 objp = multi_get_network_object( signature );
6680 nprintf(("Network", "Couldn't throw asteroid because couldn't find it\n"));
6684 objp->phys_info.vel = vel;
6685 objp->phys_info.desired_vel = vel;
6689 case ASTEROID_HIT: {
6690 ushort signature, osignature;
6691 object *objp, *other_objp;
6692 vector hitpos = ZERO_VECTOR;
6695 GET_USHORT( signature );
6696 GET_USHORT( osignature );
6697 //GET_DATA( hitpos );
6698 get_vector_data( data, &offset, hitpos );
6699 GET_FLOAT( damage );
6701 objp = multi_get_network_object( signature );
6702 if(osignature == 0xffff){
6705 other_objp = multi_get_network_object( osignature );
6708 nprintf(("Network", "Cannot hit asteroid because signature isn't found\n"));
6712 if ( IS_VEC_NULL(&hitpos) ){
6713 asteroid_hit( objp, other_objp, NULL, damage );
6715 asteroid_hit( objp, other_objp, &hitpos, damage );
6718 // if we know the other object is a weapon, then do a weapon hit to kill the weapon
6719 if ( other_objp && (other_objp->type == OBJ_WEAPON) ){
6720 weapon_hit( other_objp, objp, &hitpos );
6733 void send_host_restr_packet(const char *callsign, int code, int mode)
6735 ubyte data[MAX_PACKET_SIZE],val;
6736 int packet_size = 0;
6738 // build the header and add the opcode
6739 BUILD_HEADER(HOST_RESTR_QUERY);
6746 ADD_STRING(callsign);
6748 // if I'm the standalone server, I should be sending this to the game host
6749 if((Game_mode & GM_STANDALONE_SERVER) && (Netgame.host != NULL)){
6750 multi_io_send_reliable(Netgame.host, data, packet_size);
6752 // otherwise if I'm the host, I should be sending a reply back to the standalone server
6754 SDL_assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
6755 multi_io_send_reliable(Net_player, data, packet_size);
6759 void process_host_restr_packet(ubyte *data, header *hinfo)
6763 int offset = HEADER_LENGTH;
6765 // get the opcode and the callsign
6768 GET_STRING(callsign);
6771 // do code specific operations
6773 // query to the host from standalone
6775 SDL_assert((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER));
6777 // set the join mode
6778 Multi_join_restr_mode = mode;
6780 // set the timestamp
6781 Multi_restr_query_timestamp = timestamp(MULTI_QUERY_RESTR_STAMP);
6783 // notify the host of the event
6784 gamesnd_play_iface(SND_BRIEF_STAGE_CHG_FAIL);
6785 HUD_printf(XSTR("Player %s has tried to join - allow (y/n) ?",736),callsign);
6788 // affirmative reply from the host to the standalone
6790 SDL_assert(Game_mode & GM_STANDALONE_SERVER);
6792 // let the player join if the timestamp has not already elapsed on the server
6793 if(Multi_restr_query_timestamp != -1){
6794 multi_process_valid_join_request(&Multi_restr_join_request,&Multi_restr_addr,(int)mode);
6800 SDL_assert(Game_mode & GM_STANDALONE_SERVER);
6801 Netgame.flags &= ~(NG_FLAG_INGAME_JOINING);
6802 Multi_restr_query_timestamp = -1;
6807 void send_netgame_end_error_packet(int notify_code,int err_code)
6811 int packet_size = 0;
6813 // only the server should ever be here - although this might change if for some reason the host wants to end the game
6814 SDL_assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
6816 // build the header and add the notification and error codes
6817 BUILD_HEADER(NETGAME_END_ERROR);
6818 code = (char)notify_code;
6820 code = (char)err_code;
6824 multi_io_send_to_all_reliable(data, packet_size);
6827 void process_netgame_end_error_packet(ubyte *data, header *hinfo)
6829 int offset = HEADER_LENGTH;
6830 char notify_code,error_code;
6832 // get the error and notification codes
6833 GET_DATA(notify_code);
6834 GET_DATA(error_code);
6838 multi_quit_game(PROMPT_NONE,notify_code,error_code);
6841 // sends info that a countermeasure succeeded.
6842 void send_countermeasure_success_packet( int objnum )
6844 int pnum, packet_size;
6845 ubyte data[MAX_PACKET_SIZE];
6847 pnum = multi_find_player_by_object( &Objects[objnum] );
6849 nprintf(("Network", "Coulnd't find player for countermeasure success packet\n"));
6853 BUILD_HEADER(COUNTERMEASURE_SUCCESS);
6854 multi_io_send(&Net_players[pnum], data, packet_size);
6857 // start the flashing of my hud gauge
6858 void process_countermeasure_success_packet( ubyte *data, header *hinfo )
6862 offset = HEADER_LENGTH;
6865 hud_start_text_flash(XSTR("Evaded", 1430), 800);
6866 snd_play(&Snds[SND_MISSILE_EVADED_POPUP]);
6869 #define UPDATE_IS_PAUSED (1<<0)
6870 #define UPDATE_HULL_INFO (1<<1)
6872 void send_client_update_packet(net_player *pl)
6874 ubyte data[MAX_PACKET_SIZE],val;
6875 int packet_size = 0;
6878 BUILD_HEADER(CLIENT_UPDATE);
6882 // add the pause status
6883 if ( Multi_pause_status ) {
6884 val |= UPDATE_IS_PAUSED;
6885 } 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) ) {
6886 val |= UPDATE_HULL_INFO;
6887 SDL_assert( Player_ship ); // I"d better have one of these!!!!
6892 // if paused, add the net address of the guy who paused
6893 if(val & UPDATE_IS_PAUSED){
6894 SDL_assert(Multi_pause_pauser != NULL);
6895 ADD_SHORT(Multi_pause_pauser->player_id);
6898 // when not paused, send hull/shield/subsystem updates to all clients (except for ingame joiners)
6899 if ( val & UPDATE_HULL_INFO ) {
6901 ubyte percent, ns, threats;
6904 ship_subsys *subsysp;
6907 // get the object for the player
6908 SDL_assert( pl->player->objnum != -1 );
6910 objp = &Objects[pl->player->objnum];
6912 SDL_assert ( objp->type == OBJ_SHIP );
6913 shipp = &Ships[objp->instance];
6914 sip = &Ship_info[shipp->ship_info_index];
6916 // hull strength and sheild mesh information are floats (as a percentage). Pass the integer
6917 // percentage value since that should be close enough
6918 float temp = (objp->hull_strength / sip->initial_hull_strength * 100.0f);
6922 percent = (ubyte)temp;
6924 ADD_DATA( percent );
6926 for (i = 0; i < MAX_SHIELD_SECTIONS; i++ ) {
6927 percent = (ubyte)(objp->shields[i] / (sip->shields / MAX_SHIELD_SECTIONS) * 100.0f);
6928 ADD_DATA( percent );
6931 // add the data for the subsystem hits. We can assume that the lists will be the same side of
6932 // both machines. Added as percent since that number <= 100
6934 // also write out the number of subsystems. We do this because the client might not know
6935 // about the object he is getting data for. (i.e. he killed the object already).
6936 ns = (ubyte)sip->n_subsystems;
6939 // now the subsystems.
6940 for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) {
6941 percent = (ubyte)(subsysp->current_hits / subsysp->system_info->max_hits * 100.0f);
6942 ADD_DATA( percent );
6945 // compute the threats for this player. Only compute the threats if this player is actually
6946 // playing (i.e. he has a ship)
6947 hud_update_reticle( pl->player );
6948 threats = (ubyte)pl->player->threat_flags;
6949 ADD_DATA( threats );
6951 // add his energy level for guns
6952 ADD_FLOAT(shipp->weapon_energy);
6954 // add his secondary bank ammo
6955 ADD_INT(shipp->weapons.num_secondary_banks);
6956 for(i=0; i<shipp->weapons.num_secondary_banks; i++){
6957 ADD_INT(shipp->weapons.secondary_bank_ammo[i]);
6962 ADD_INT(pl->sv_last_pl);
6964 // send the packet reliably to the player
6965 multi_io_send(pl, data, packet_size);
6968 void process_client_update_packet(ubyte *data, header *hinfo)
6973 int is_paused, have_hull_info;
6976 float weapon_energy;
6977 int offset = HEADER_LENGTH;
6979 // get the header byte containing useful information
6982 is_paused = (val & UPDATE_IS_PAUSED)?1:0;
6983 have_hull_info = (val & UPDATE_HULL_INFO)?1:0;
6985 // if we are paused, get who paused
6988 player_index = find_player_id(pauser);
6989 if(player_index != -1){
6990 Multi_pause_pauser = &Net_players[player_index];
6992 Multi_pause_pauser = NULL;
6996 // if we have hull information, then read it in.
6997 if ( have_hull_info ) {
7001 ubyte hull_percent, shield_percent[MAX_SHIELD_SECTIONS], n_subsystems, subsystem_percent[MAX_MODEL_SUBSYSTEMS], threats;
7003 ship_subsys *subsysp;
7007 // hull strength and sheild mesh information are floats (as a percentage). Pass the integer
7008 // percentage value since that should be close enough
7009 GET_DATA( hull_percent );
7011 for (i = 0; i < MAX_SHIELD_SECTIONS; i++ ){
7013 shield_percent[i] = ub_tmp;
7016 // get the data for the subsystems
7017 GET_DATA( n_subsystems );
7018 for ( i = 0; i < n_subsystems; i++ ){
7020 subsystem_percent[i] = ub_tmp;
7023 GET_DATA( threats );
7025 // add his energy level for guns
7026 GET_FLOAT(weapon_energy);
7028 // add his secondary bank ammo
7029 GET_INT(ammo_count);
7030 for(i=0; i<ammo_count; i++){
7034 // assign the above information to my ship, assuming that I can find it! Ingame joiners might get this
7035 // packet because of delay between reliable packet acknowledging my ingame ship and the start of these
7036 // UDP client update packets. Only read this info if I have a ship.
7037 if ( !(Net_player->flags & NETINFO_FLAG_INGAME_JOIN) && (Player_ship != NULL) && (Player_obj != NULL) && (Net_player != NULL)) {
7038 shipp = Player_ship;
7040 sip = &Ship_info[shipp->ship_info_index];
7042 fval = hull_percent * sip->initial_hull_strength / 100.0f;
7043 objp->hull_strength = fval;
7045 for ( i = 0; i < MAX_SHIELD_SECTIONS; i++ ) {
7046 fval = (shield_percent[i] * sip->shields / 100.0f) / MAX_SHIELD_SECTIONS;
7047 objp->shields[i] = fval;
7050 // for sanity, be sure that the number of susbystems that I read in matches the player. If not,
7051 // then don't read these in.
7052 if ( n_subsystems == sip->n_subsystems ) {
7054 n_subsystems = 0; // reuse this variable
7055 for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) {
7058 fval = subsystem_percent[n_subsystems] * subsysp->system_info->max_hits / 100.0f;
7059 subsysp->current_hits = fval;
7061 // add the value just generated (it was zero'ed above) into the array of generic system types
7062 subsys_type = subsysp->system_info->type; // this is the generic type of subsystem
7063 SDL_assert ( subsys_type < SUBSYSTEM_MAX );
7064 shipp->subsys_info[subsys_type].current_hits += fval;
7068 ship_recalc_subsys_strength( shipp );
7070 shipp->weapon_energy = weapon_energy;
7071 for(i=0; i<ammo_count; i++){
7072 shipp->weapons.secondary_bank_ammo[i] = ammo[i];
7075 // update my threat flags.
7076 // temporarily commented out until tested.
7077 Net_player->player->threat_flags = threats;
7084 if(Net_player != NULL){
7085 Net_player->cl_last_pl = pl;
7089 // note, if we're already paused or unpaused, calling these will have no effect, so it is safe to do so
7090 if(!popup_active() && !(Net_player->flags & NETINFO_FLAG_RESPAWNING) && !(Net_player->flags & NETINFO_FLAG_LIMBO)){
7092 multi_pause_pause();
7094 multi_pause_unpause();
7099 void send_countdown_packet(int time)
7103 int packet_size = 0;
7105 // build the header and add the time
7106 BUILD_HEADER(COUNTDOWN);
7110 // if we're the server, we should broadcast to everyone
7111 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
7112 multi_io_send_to_all_reliable(data, packet_size);
7114 // otherwise we'de better be a host sending to the standalone
7116 SDL_assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
7117 multi_io_send_reliable(Net_player, data, packet_size);
7121 void process_countdown_packet(ubyte *data, header *hinfo)
7123 int offset = HEADER_LENGTH;
7130 // if we're not in the post sync data screen, ignore it
7131 if(gameseq_get_state() != GS_STATE_MULTI_MISSION_SYNC){
7135 // if we're the standalone, this should be a -1 telling us to start the countdown
7136 if(Game_mode & GM_STANDALONE_SERVER){
7137 SDL_assert((int)time == -1);
7139 // start the countdown
7140 multi_sync_start_countdown();
7142 // otherwise if we're clients, just bash the countdown
7144 Multi_sync_countdown = (int)time;
7148 // packets for debriefing information
7149 void send_debrief_info( int stage_count[], int *stages[] )
7151 ubyte data[MAX_PACKET_SIZE];
7152 int packet_size, i, j;
7155 BUILD_HEADER(DEBRIEF_INFO);
7157 // add the data for the teams
7158 for ( i = 0; i < Num_teams; i++ ) {
7161 count = stage_count[i];
7163 for ( j = 0; j < count; j++ ) {
7164 i_tmp = stages[i][j];
7169 multi_io_send_to_all_reliable(data, packet_size);
7172 // process a debrief info packet from the server
7173 void process_debrief_info( ubyte *data, header *hinfo )
7176 int stage_counts[MAX_TEAMS], active_stages[MAX_TEAMS][MAX_DEBRIEF_STAGES], *stages[MAX_TEAMS];
7179 offset = HEADER_LENGTH;
7180 for ( i = 0; i < Num_teams; i++ ) {
7184 stage_counts[i] = count;
7185 stages[i] = active_stages[i];
7186 for ( j = 0; j < count; j++ ) {
7188 active_stages[i][j] = i_tmp;
7193 // now that we have the stage data for the debriefing stages, call debrief function with the
7194 // data so that clients can now see the debriefing stuff. Do it only for my team.
7195 SDL_assert( (Net_player->p_info.team >= 0) && (Net_player->p_info.team < Num_teams) );
7196 debrief_set_multi_clients( stage_counts[Net_player->p_info.team], stages[Net_player->p_info.team] );
7199 // sends homing information to all clients. We only need signature and num_missiles (because of hornets).
7200 // sends homing_object and homing_subsystem to all clients.
7201 void send_homing_weapon_info( int weapon_num )
7203 ubyte data[MAX_PACKET_SIZE];
7206 object *homing_object;
7207 ushort homing_signature;
7210 wp = &Weapons[weapon_num];
7212 // be sure that this weapon object is a homing object.
7213 if ( !(Weapon_info[wp->weapon_info_index].wi_flags & WIF_HOMING) )
7216 // get the homing signature. If this weapon isn't homing on anything, then sent 0 as the
7217 // homing signature.
7218 homing_signature = 0;
7219 homing_object = wp->homing_object;
7220 if ( homing_object != NULL ) {
7221 homing_signature = homing_object->net_signature;
7223 // get the subsystem index.
7225 if ( (homing_object->type == OBJ_SHIP) && (wp->homing_subsys != NULL) ) {
7228 s_index = ship_get_index_from_subsys( wp->homing_subsys, OBJ_INDEX(homing_object), 1 );
7229 SDL_assert( s_index < CHAR_MAX ); // better be less than this!!!!
7230 t_subsys = (char)s_index;
7234 BUILD_HEADER(HOMING_WEAPON_UPDATE);
7235 ADD_USHORT( Objects[wp->objnum].net_signature );
7236 ADD_USHORT( homing_signature );
7237 ADD_DATA( t_subsys );
7239 multi_io_send_to_all(data, packet_size);
7242 // process a homing weapon info change packet. multiple_missiles parameter specifies is this
7243 // packet contains information for multiple weapons (like hornets).
7244 void process_homing_weapon_info( ubyte *data, header *hinfo )
7247 ushort weapon_signature, homing_signature;
7249 object *homing_object, *weapon_objp;
7252 offset = HEADER_LENGTH;
7254 // get the data for the packet
7255 GET_USHORT( weapon_signature );
7256 GET_USHORT( homing_signature );
7257 GET_DATA( h_subsys );
7260 // deal with changing this weapons homing information
7261 weapon_objp = multi_get_network_object( weapon_signature );
7262 if ( weapon_objp == NULL ) {
7263 nprintf(("Network", "Couldn't find weapon object for homing update -- skipping update\n"));
7266 SDL_assert( weapon_objp->type == OBJ_WEAPON );
7267 wp = &Weapons[weapon_objp->instance];
7269 // be sure that we can find these weapons and
7270 homing_object = multi_get_network_object( homing_signature );
7271 if ( homing_object == NULL ) {
7272 nprintf(("Network", "Couldn't find homing object for homing update\n"));
7276 if ( homing_object->type == OBJ_WEAPON ) {
7277 SDL_assert(Weapon_info[Weapons[homing_object->instance].weapon_info_index].wi_flags & WIF_BOMB);
7280 wp->homing_object = homing_object;
7281 wp->homing_subsys = NULL;
7282 wp->target_num = OBJ_INDEX(homing_object);
7283 wp->target_sig = homing_object->signature;
7284 if ( h_subsys != -1 ) {
7285 SDL_assert( homing_object->type == OBJ_SHIP );
7286 wp->homing_subsys = ship_get_indexed_subsys( &Ships[homing_object->instance], h_subsys);
7289 if ( homing_object->type == OBJ_SHIP ) {
7290 nprintf(("Network", "Updating homing information for weapon -- homing on %s\n", Ships[homing_object->instance].ship_name));
7294 void send_emp_effect(ushort net_sig, float intensity, float time)
7299 SDL_assert(MULTIPLAYER_MASTER);
7301 // build the packet and add the opcode
7302 BUILD_HEADER(EMP_EFFECT);
7303 ADD_USHORT(net_sig);
7304 ADD_FLOAT(intensity);
7307 // send it to the player
7308 multi_io_send_to_all(data, packet_size);
7311 void process_emp_effect(ubyte *data, header *hinfo)
7313 float intensity, time;
7316 int offset = HEADER_LENGTH;
7318 // read in the EMP effect data
7319 GET_USHORT(net_sig);
7320 GET_FLOAT(intensity);
7324 // try and find the object
7325 objp = multi_get_network_object(net_sig);
7326 if((objp != NULL) && (objp->type == OBJ_SHIP)){
7327 // if i'm not an observer and I have a valid ship, play the EMP effect
7328 if(!(Net_player->flags & NETINFO_FLAG_OBSERVER) && (Player_obj != NULL) && (Player_obj->type == OBJ_SHIP) && (Player_obj == objp)){
7329 emp_start_local(intensity, time);
7332 // start the effect for the ship itself
7333 emp_start_ship(objp, intensity, time);
7337 // tells whether or not reinforcements are available
7338 void send_reinforcement_avail( int rnum )
7343 BUILD_HEADER(REINFORCEMENT_AVAIL);
7345 multi_io_send_to_all_reliable(data, packet_size);
7348 void process_reinforcement_avail( ubyte *data, header *hinfo )
7353 offset = HEADER_LENGTH;
7357 // sanity check for a valid reinforcement number
7358 if ( (rnum >= 0) && (rnum < Num_reinforcements) ) {
7359 Reinforcements[rnum].flags |= RF_IS_AVAILABLE;
7363 void send_change_iff_packet(ushort net_signature, int new_team)
7365 ubyte data[MAX_PACKET_SIZE];
7366 int packet_size = 0;
7368 if(Net_player == NULL){
7371 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
7375 // build the packet and add the data
7376 BUILD_HEADER(CHANGE_IFF);
7377 ADD_USHORT(net_signature);
7380 // send to all players
7381 multi_io_send_to_all_reliable(data, packet_size);
7384 void process_change_iff_packet( ubyte *data, header *hinfo)
7386 int offset = HEADER_LENGTH;
7387 ushort net_signature;
7392 GET_USHORT(net_signature);
7396 // lookup the object
7397 objp = multi_get_network_object(net_signature);
7398 if((objp != NULL) && (objp->type == OBJ_SHIP) && (objp->instance >=0)){
7399 Ships[objp->instance].team = new_team;
7403 void send_NEW_primary_fired_packet(ship *shipp, int banks_fired)
7405 int packet_size, objnum;
7406 ubyte data[MAX_PACKET_SIZE]; // ubanks_fired, current_bank;
7409 net_player *ignore = NULL;
7411 // sanity checking for now
7412 SDL_assert ( banks_fired <= 3 );
7414 // get an object pointer for this ship.
7415 objnum = shipp->objnum;
7416 objp = &Objects[objnum];
7418 // if i'm a multiplayer client, I should never send primary fired packets for anyone except me
7419 if(MULTIPLAYER_CLIENT && (Player_obj != objp)){
7423 // just in case nothing got fired
7424 if(banks_fired <= 0){
7428 // ubanks_fired = (ubyte)banks_fired;
7429 // current_bank = (ubyte)shipp->weapons.current_primary_bank;
7430 // SDL_assert( current_bank <= 3 );
7432 // insert the current primary bank into this byte
7433 // ubanks_fired |= (current_bank << CURRENT_BANK_BIT);
7435 // append the SF_PRIMARY_LINKED flag on the top nibble of the banks_fired
7436 // if ( shipp->flags & SF_PRIMARY_LINKED ){
7437 // ubanks_fired |= (1<<7);
7440 // determine if its a player ship and don't send to him if we're in "client firing" mode
7441 // if((Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING) && MULTIPLAYER_MASTER){
7442 if(MULTIPLAYER_MASTER){
7443 np_index = multi_find_player_by_net_signature(objp->net_signature);
7444 if((np_index >= 0) && (np_index < MAX_PLAYERS)){
7445 ignore = &Net_players[np_index];
7449 // build up the standard weapon fired packet. This packet will get sent to all players if an AI
7450 // ship fired the primary weapons. If a player fired the weaspon, then this packet will get sent
7451 // to every player but the guy who actullly fired the weapon. This method is used to help keep client
7452 // and server in sync w.r.t. weapon energy for player ship
7453 BUILD_HEADER( PRIMARY_FIRED_NEW );
7454 ADD_USHORT(objp->net_signature);
7455 // ADD_DATA(ubanks_fired);
7457 // if I'm a server, broadcast to all players
7458 if(MULTIPLAYER_MASTER){
7459 multi_io_send_to_all(data, packet_size, ignore);
7462 multi_rate_add(1, "wfi", packet_size);
7464 // otherwise just send to the server
7466 multi_io_send(Net_player, data, packet_size);
7470 void process_NEW_primary_fired_packet(ubyte *data, header *hinfo)
7472 int offset; // linked;
7473 // ubyte banks_fired, current_bank;
7478 // read all packet info
7479 offset = HEADER_LENGTH;
7480 GET_USHORT(shooter_sig);
7481 // GET_DATA(banks_fired);
7484 // find the object this fired packet is operating on
7485 objp = multi_get_network_object( shooter_sig );
7486 if ( objp == NULL ) {
7487 nprintf(("Network", "Could not find ship for fire primary packet NEW!"));
7490 // if this object is not actually a valid ship, don't do anything
7491 if(objp->type != OBJ_SHIP){
7494 if(objp->instance < 0){
7497 // shipp = &Ships[objp->instance];
7499 // get the link status of the primary banks
7501 // if ( banks_fired & (1<<7) ) {
7503 // banks_fired ^= (1<<7);
7506 // get the current primary bank
7507 // current_bank = (ubyte)(banks_fired >> CURRENT_BANK_BIT);
7508 // current_bank &= 0x3;
7509 // SDL_assert( (current_bank >= 0) && (current_bank < MAX_PRIMARY_BANKS) );
7510 // shipp->weapons.current_primary_bank = current_bank;
7512 // strip off all remaining bits and just keep which banks were actually fired.
7513 // banks_fired &= 0x3;
7515 // set the link status of the ship if not the player. If it is the player, we will do sanity checking
7518 // shipp->flags &= ~SF_PRIMARY_LINKED;
7520 // shipp->flags |= SF_PRIMARY_LINKED;
7523 // if we're in client firing mode, ignore ones for myself
7524 if((Player_obj != NULL) && (Player_obj == objp)){
7528 ship_fire_primary( objp, 0, 1 );
7531 void send_NEW_countermeasure_fired_packet(object *objp, int cmeasure_count, int rand_val)
7533 ubyte data[MAX_PACKET_SIZE];
7536 net_player *ignore = NULL;
7538 // if i'm a multiplayer client, I should never send primary fired packets for anyone except me
7539 if(MULTIPLAYER_CLIENT && (Player_obj != objp)){
7543 SDL_assert ( cmeasure_count < UCHAR_MAX );
7544 BUILD_HEADER(COUNTERMEASURE_NEW);
7545 ADD_USHORT( objp->net_signature );
7546 ADD_INT( rand_val );
7548 nprintf(("Network","Sending NEW countermeasure packet!\n"));
7550 // determine if its a player ship and don't send to him if we're in "client firing" mode
7551 // if((Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING) && MULTIPLAYER_MASTER){
7552 if(MULTIPLAYER_MASTER){
7553 np_index = multi_find_player_by_net_signature(objp->net_signature);
7554 if((np_index >= 0) && (np_index < MAX_PLAYERS)){
7555 ignore = &Net_players[np_index];
7559 // if I'm the server, send to all players
7560 if(MULTIPLAYER_MASTER){
7561 multi_io_send_to_all(data, packet_size, ignore);
7563 // otherwise send to the server
7565 multi_io_send(Net_player, data, packet_size);
7569 void process_NEW_countermeasure_fired_packet(ubyte *data, header *hinfo)
7576 offset = HEADER_LENGTH;
7577 GET_USHORT( signature );
7578 GET_INT( rand_val );
7581 objp = multi_get_network_object( signature );
7582 if ( objp == NULL ) {
7583 nprintf(("network", "Could find object whose countermeasures are being launched!!!\n"));
7586 if(objp->type != OBJ_SHIP){
7590 // if we're in client firing mode, ignore ones for myself
7591 // if((Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING) && (Player_obj != NULL) && (Player_obj == objp)){
7592 if((Player_obj != NULL) && (Player_obj == objp)){
7596 // make it so ship can fire right away!
7597 Ships[objp->instance].cmeasure_fire_stamp = timestamp(0);
7598 if ( objp == Player_obj ){
7599 nprintf(("network", "firing countermeasure from my ship\n"));
7601 ship_launch_countermeasure( objp, rand_val );
7604 void send_beam_fired_packet(object *shooter, ship_subsys *turret, object *target, int beam_info_index, beam_info *override)
7606 ubyte data[MAX_PACKET_SIZE];
7607 int packet_size = 0;
7612 // only the server should ever be doing this
7613 SDL_assert(MULTIPLAYER_MASTER);
7615 // setup outgoing data
7616 SDL_assert(shooter != NULL);
7617 SDL_assert(turret != NULL);
7618 SDL_assert(target != NULL);
7619 SDL_assert(override != NULL);
7620 if((shooter == NULL) || (turret == NULL) || (target == NULL) || (override == NULL)){
7623 u_beam_info = (ubyte)beam_info_index;
7624 subsys_index = (char)ship_get_index_from_subsys(turret, OBJ_INDEX(shooter));
7625 SDL_assert(subsys_index >= 0);
7626 if(subsys_index < 0){
7630 // swap the beam_info override info into little endian byte order
7631 b_info.dir_a.xyz.x = INTEL_FLOAT(override->dir_a.xyz.x);
7632 b_info.dir_a.xyz.y = INTEL_FLOAT(override->dir_a.xyz.y);
7633 b_info.dir_a.xyz.z = INTEL_FLOAT(override->dir_a.xyz.z);
7635 b_info.dir_b.xyz.x = INTEL_FLOAT(override->dir_b.xyz.x);
7636 b_info.dir_b.xyz.y = INTEL_FLOAT(override->dir_b.xyz.y);
7637 b_info.dir_b.xyz.z = INTEL_FLOAT(override->dir_b.xyz.z);
7639 b_info.delta_ang = INTEL_FLOAT(override->delta_ang);
7640 b_info.shot_count = override->shot_count;
7642 for (int i = 0; i < b_info.shot_count; i++) {
7643 b_info.shot_aim[i] = INTEL_FLOAT(override->shot_aim[i]);
7647 BUILD_HEADER(BEAM_FIRED);
7648 ADD_USHORT(shooter->net_signature);
7649 ADD_DATA(subsys_index);
7650 ADD_USHORT(target->net_signature);
7651 ADD_DATA(u_beam_info);
7652 ADD_DATA(b_info); // FIXME: This is still wrong, we shouldn't be sending an entire struct over the wire - taylor
7654 // send to all clients
7655 multi_io_send_to_all_reliable(data, packet_size);
7658 void process_beam_fired_packet(ubyte *data, header *hinfo)
7661 ushort shooter_sig, target_sig;
7665 beam_fire_info fire_info;
7667 // only clients should ever get this
7668 SDL_assert(MULTIPLAYER_CLIENT);
7670 // read in packet data
7671 offset = HEADER_LENGTH;
7672 GET_USHORT(shooter_sig);
7673 GET_DATA(subsys_index);
7674 GET_USHORT(target_sig);
7675 GET_DATA(u_beam_info);
7680 // swap the beam_info override info into native byte order
7681 b_info.dir_a.xyz.x = INTEL_FLOAT( b_info.dir_a.xyz.x );
7682 b_info.dir_a.xyz.y = INTEL_FLOAT( b_info.dir_a.xyz.y );
7683 b_info.dir_a.xyz.z = INTEL_FLOAT( b_info.dir_a.xyz.z );
7684 b_info.dir_b.xyz.x = INTEL_FLOAT( b_info.dir_b.xyz.x );
7685 b_info.dir_b.xyz.y = INTEL_FLOAT( b_info.dir_b.xyz.y );
7686 b_info.dir_b.xyz.z = INTEL_FLOAT( b_info.dir_b.xyz.z );
7687 b_info.delta_ang = INTEL_FLOAT( b_info.delta_ang );
7689 for (i = 0; i < MAX_BEAM_SHOTS; i++) {
7690 b_info.shot_aim[i] = INTEL_FLOAT(b_info.shot_aim[i]);
7693 // lookup all relevant data
7694 fire_info.beam_info_index = (int)u_beam_info;
7695 fire_info.shooter = NULL;
7696 fire_info.target = NULL;
7697 fire_info.turret = NULL;
7698 fire_info.target_subsys = NULL;
7699 fire_info.beam_info_override = NULL;
7700 fire_info.shooter = multi_get_network_object(shooter_sig);
7701 fire_info.target = multi_get_network_object(target_sig);
7702 fire_info.beam_info_override = &b_info;
7703 fire_info.accuracy = 1.0f;
7704 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)){
7705 nprintf(("Network", "Couldn't get shooter/target info for BEAM weapon!\n"));
7708 fire_info.turret = ship_get_indexed_subsys( &Ships[fire_info.shooter->instance], (int)subsys_index);
7709 if(fire_info.turret == NULL){
7710 nprintf(("Network", "Couldn't get turret for BEAM weapon!\n"));
7715 beam_fire(&fire_info);
7718 void send_sw_query_packet(ubyte code, char *txt)
7720 ubyte data[MAX_PACKET_SIZE];
7721 int packet_size = 0;
7723 // build the packet and add the code
7724 BUILD_HEADER(SW_STD_QUERY);
7726 if((code == SW_STD_START) || (code == SW_STD_BAD)){
7727 SDL_assert(txt != NULL);
7731 // if I'm the host, send to standalone
7732 if(MULTIPLAYER_HOST){
7733 SDL_assert(!MULTIPLAYER_MASTER);
7734 SDL_assert(code == SW_STD_START);
7735 multi_io_send_reliable(Net_player, data, packet_size);
7737 // otherwise standalone sends back to host
7739 SDL_assert(Game_mode & GM_STANDALONE_SERVER);
7740 SDL_assert(code != SW_STD_START);
7741 SDL_assert(Netgame.host != NULL);
7742 if(Netgame.host != NULL){
7743 multi_io_send_reliable(Netgame.host, data, packet_size);
7748 void process_sw_query_packet(ubyte *data, header *hinfo)
7750 int offset = HEADER_LENGTH;
7752 char txt[MAX_SQUAD_RESPONSE_LEN+1];
7756 if ( (code == SW_STD_START) || (code == SW_STD_BAD) ) {
7762 // to host from standalone
7763 if (MULTIPLAYER_HOST) {
7764 SDL_assert( !MULTIPLAYER_MASTER );
7766 if (code == SW_STD_OK) {
7767 Multi_sw_std_query = 1;
7769 SDL_assert(code == SW_STD_BAD);
7771 SDL_strlcpy(Multi_sw_bad_reply, txt, SDL_arraysize(Multi_sw_bad_reply));
7772 Multi_sw_std_query = 0;
7775 // to standalone from host
7777 SDL_assert(Game_mode & GM_STANDALONE_SERVER);
7778 SDL_assert(code == SW_STD_START);
7780 multi_sw_std_query(txt);
7784 void send_event_update_packet(int event)
7786 ubyte data[MAX_PACKET_SIZE];
7787 ushort u_event = (ushort)event;
7788 int packet_size = 0;
7790 // build the header and add the event
7791 BUILD_HEADER(EVENT_UPDATE);
7792 ADD_USHORT(u_event);
7793 ADD_INT(Mission_events[event].flags);
7794 ADD_INT(Mission_events[event].formula);
7795 ADD_INT(Mission_events[event].result);
7796 ADD_INT(Mission_events[event].count);
7798 // send to all players
7799 multi_io_send_to_all_reliable(data, packet_size);
7802 void process_event_update_packet(ubyte *data, header *hinfo)
7804 int offset = HEADER_LENGTH;
7809 GET_USHORT(u_event);
7810 store_flags = Mission_events[u_event].flags;
7811 GET_INT(Mission_events[u_event].flags);
7812 GET_INT(Mission_events[u_event].formula);
7813 GET_INT(Mission_events[u_event].result);
7814 GET_INT(Mission_events[u_event].count);
7817 // went from non directive special to directive special
7818 if(!(store_flags & MEF_DIRECTIVE_SPECIAL) && (Mission_events[u_event].flags & MEF_DIRECTIVE_SPECIAL)){
7819 mission_event_set_directive_special(u_event);
7821 // if we went directive special to non directive special
7822 else if((store_flags & MEF_DIRECTIVE_SPECIAL) & !(Mission_events[u_event].flags & MEF_DIRECTIVE_SPECIAL)){
7823 mission_event_unset_directive_special(u_event);
7827 // weapon detonate packet
7828 void send_weapon_detonate_packet(object *objp)
7830 ubyte data[MAX_PACKET_SIZE];
7831 int packet_size = 0;
7834 SDL_assert(MULTIPLAYER_MASTER);
7835 if(!MULTIPLAYER_MASTER){
7838 SDL_assert(objp != NULL);
7843 // build the header and add the data
7844 BUILD_HEADER(WEAPON_DET);
7845 ADD_USHORT(objp->net_signature);
7847 // send to all players
7848 multi_io_send_to_all(data, packet_size);
7851 void process_weapon_detonate_packet(ubyte *data, header *hinfo)
7854 int offset = HEADER_LENGTH;
7855 object *objp = NULL;
7857 // get the weapon signature
7858 GET_USHORT(net_sig);
7861 // lookup the weapon
7862 objp = multi_get_network_object(net_sig);
7863 if((objp != NULL) && (objp->type == OBJ_WEAPON) && (objp->instance >= 0)){
7864 weapon_detonate(objp);
7868 // flak fired packet
7869 void send_flak_fired_packet(int ship_objnum, int subsys_index, int weapon_objnum, float flak_range)
7872 ushort pnet_signature;
7873 ubyte data[MAX_PACKET_SIZE], cindex;
7879 if((weapon_objnum < 0) || (Objects[weapon_objnum].type != OBJ_WEAPON) || (Objects[weapon_objnum].instance < 0) || (Weapons[Objects[weapon_objnum].instance].weapon_info_index < 0)){
7883 // local setup -- be sure we are actually passing a weapon!!!!
7884 objp = &Objects[weapon_objnum];
7885 SDL_assert ( objp->type == OBJ_WEAPON );
7886 pnet_signature = Objects[ship_objnum].net_signature;
7888 SDL_assert( subsys_index < UCHAR_MAX );
7889 cindex = (ubyte)subsys_index;
7891 ssp = ship_get_indexed_subsys( &Ships[Objects[ship_objnum].instance], subsys_index, NULL );
7896 // build the fire turret packet.
7897 BUILD_HEADER(FLAK_FIRED);
7898 packet_size += multi_pack_unpack_position(1, data + packet_size, &objp->orient.v.fvec);
7899 ADD_USHORT( pnet_signature );
7901 val = (short)ssp->submodel_info_1.angs.h;
7903 val = (short)ssp->submodel_info_2.angs.p;
7905 ADD_FLOAT( flak_range );
7907 multi_io_send_to_all(data, packet_size);
7909 multi_rate_add(1, "flk", packet_size);
7912 void process_flak_fired_packet(ubyte *data, header *hinfo)
7914 int offset, weapon_objnum, wid;
7915 ushort pnet_signature;
7923 short pitch, heading;
7926 // get the data for the turret fired packet
7927 offset = HEADER_LENGTH;
7928 offset += multi_pack_unpack_position(0, data + offset, &o_fvec);
7929 GET_USHORT( pnet_signature );
7930 GET_DATA( turret_index );
7931 GET_SHORT( heading );
7933 GET_FLOAT( flak_range );
7934 PACKET_SET_SIZE(); // move our counter forward the number of bytes we have read
7937 objp = multi_get_network_object( pnet_signature );
7938 if ( objp == NULL ) {
7939 nprintf(("network", "could find parent object with net signature %d for flak firing\n", pnet_signature));
7943 // if this isn't a ship, do nothing
7944 if ( objp->type != OBJ_SHIP ){
7948 // make an orientation matrix from the o_fvec
7949 vm_vector_2_matrix(&orient, &o_fvec, NULL, NULL);
7951 // find this turret, and set the position of the turret that just fired to be where it fired. Quite a
7952 // hack, but should be suitable.
7953 shipp = &Ships[objp->instance];
7954 ssp = ship_get_indexed_subsys( shipp, turret_index, NULL );
7958 wid = ssp->system_info->turret_weapon_type;
7959 if((wid < 0) || !(Weapon_info[wid].wi_flags & WIF_FLAK)){
7963 // bash the position and orientation of the turret
7964 ssp->submodel_info_1.angs.h = (float)heading;
7965 ssp->submodel_info_2.angs.p = (float)pitch;
7967 // get the world position of the weapon
7968 ship_get_global_turret_info(objp, ssp->system_info, &pos, &dir);
7970 // create the weapon object
7971 weapon_objnum = weapon_create( &pos, &orient, wid, OBJ_INDEX(objp), 0, -1, 1);
7972 if (weapon_objnum != -1) {
7973 if ( Weapon_info[wid].launch_snd != -1 ) {
7974 snd_play_3d( &Snds[Weapon_info[wid].launch_snd], &pos, &View_position );
7977 // create a muzzle flash from a flak gun based upon firing position and weapon type
7978 flak_muzzle_flash(&pos, &dir, wid);
7980 // set its range explicitly - make it long enough so that it's guaranteed to still exist when the server tells us it blew up
7981 flak_set_range(&Objects[weapon_objnum], &pos, (float)flak_range);
7985 #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);
7986 #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);
7988 // player pain packet
7989 void send_player_pain_packet(net_player *pl, int weapon_info_index, float damage, vector *force, vector *hitpos)
7991 ubyte data[MAX_PACKET_SIZE];
7994 int packet_size = 0;
7996 SDL_assert(MULTIPLAYER_MASTER);
7997 if(!MULTIPLAYER_MASTER){
8000 SDL_assert(pl != NULL);
8005 // build the packet and add the code
8006 BUILD_HEADER(NETPLAYER_PAIN);
8007 windex = (ubyte)weapon_info_index;
8009 udamage = (ushort)damage;
8010 ADD_USHORT(udamage);
8011 //ADD_DATA((*force));
8012 add_vector_data( data, &packet_size, *force );
8013 //ADD_DATA((*hitpos));
8014 add_vector_data( data, &packet_size, *hitpos );
8016 // send to the player
8017 multi_io_send(pl, data, packet_size);
8019 multi_rate_add(1, "pai", packet_size);
8022 void process_player_pain_packet(ubyte *data, header *hinfo)
8027 vector force = ZERO_VECTOR;
8028 vector local_hit_pos = ZERO_VECTOR;
8031 // get the data for the pain packet
8032 offset = HEADER_LENGTH;
8034 GET_USHORT(udamage);
8036 get_vector_data( data, &offset, force );
8037 //GET_DATA(local_hit_pos);
8038 get_vector_data( data, &offset, local_hit_pos );
8041 mprintf(("PAIN!\n"));
8043 // get weapon info pointer
8044 //SDL_assert((windex >= 0) && (windex < Num_weapon_types) && (Weapon_info[windex].subtype == WP_LASER)); // always true
8045 if(! ((windex != 255) && (windex < Num_weapon_types) && (Weapon_info[windex].subtype == WP_LASER)) ){
8048 wip = &Weapon_info[windex];
8050 // play the weapon hit sound
8051 SDL_assert(Player_obj != NULL);
8052 if(Player_obj == NULL){
8055 weapon_hit_do_sound(Player_obj, wip, &Player_obj->pos);
8057 // we need to do 3 things here. player pain (game flash), weapon hit sound, ship_apply_whack()
8058 ship_hit_pain((float)udamage);
8061 ship_apply_whack(&force, &local_hit_pos, Player_obj);
8065 void send_lightning_packet(int bolt_type, vector *start, vector *strike)
8067 ubyte data[MAX_PACKET_SIZE];
8069 int packet_size = 0;
8071 // build the header and add the data
8072 BUILD_HEADER(LIGHTNING_PACKET);
8073 val = (char)bolt_type;
8075 //ADD_DATA((*start));
8076 add_vector_data( data, &packet_size, *start );
8077 //ADD_DATA((*strike));
8078 add_vector_data( data, &packet_size, *strike );
8080 // send to everyone unreliable for now
8081 multi_io_send_to_all(data, packet_size);
8084 void process_lightning_packet(ubyte *data, header *hinfo)
8088 vector start = ZERO_VECTOR, strike = ZERO_VECTOR;
8091 offset = HEADER_LENGTH;
8092 GET_DATA(bolt_type);
8094 get_vector_data(data, &offset, start);
8095 // GET_DATA(strike);
8096 get_vector_data(data, &offset, strike);
8105 nebl_bolt(bolt_type, &start, &strike);
8108 void send_bytes_recvd_packet(net_player *pl)
8110 // only clients should ever be doing this
8115 ubyte data[MAX_PACKET_SIZE];
8116 int packet_size = 0;
8117 BUILD_HEADER(BYTES_SENT);
8118 ADD_INT(pl->cl_bytes_recvd);
8120 // send to the server
8121 multi_io_send_reliable(pl, data, packet_size);
8124 void process_bytes_recvd_packet(ubyte *data, header *hinfo)
8128 net_player *pl = NULL;
8129 int offset = HEADER_LENGTH;
8135 if(Net_player == NULL){
8138 if(!MULTIPLAYER_MASTER){
8142 // make sure we know what player sent this
8143 pid = find_player_id(hinfo->id);
8144 if((pid < 0) || (pid >= MAX_PLAYERS)){
8147 pl = &Net_players[pid];
8150 pl->cl_bytes_recvd = bytes;
8154 pl->sv_last_pl = (int)(100.0f * (1.0f - ((float)pl->cl_bytes_recvd / (float)pl->sv_bytes_sent)));
8157 pl->sv_bytes_sent = 0;
8161 void send_host_captain_change_packet(short player_id, int captain_change)
8163 ubyte data[MAX_PACKET_SIZE];
8164 int packet_size = 0;
8167 BUILD_HEADER(TRANSFER_HOST);
8168 ADD_SHORT(player_id);
8169 ADD_INT(captain_change);
8172 multi_io_send_to_all_reliable(data, packet_size);
8175 void process_host_captain_change_packet(ubyte *data, header *hinfo)
8177 int offset = HEADER_LENGTH;
8178 int idx, found_player, captain_change;
8181 // get the player id
8182 GET_SHORT(player_id);
8183 GET_INT(captain_change);
8189 for(idx=0; idx<MAX_PLAYERS; idx++){
8190 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].player_id == player_id)){
8191 HUD_printf("%s is the new captain of team %d", Net_players[idx].player->callsign, Net_players[idx].p_info.team + 1);
8196 // unflag all old players
8197 for(idx=0; idx<MAX_PLAYERS; idx++){
8198 Net_players[idx].flags &= ~NETINFO_FLAG_GAME_HOST;
8203 for(idx=0; idx<MAX_PLAYERS; idx++){
8204 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].player_id == player_id)){
8205 Net_players[idx].flags |= NETINFO_FLAG_GAME_HOST;
8207 // spew to the HUD config
8208 if(Net_players[idx].player != NULL){
8209 HUD_printf("%s is the new game host", Net_players[idx].player->callsign);
8219 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_HOST_LEFT);
8224 void send_self_destruct_packet()
8226 ubyte data[MAX_PACKET_SIZE];
8227 int packet_size = 0;
8230 if(Net_player == NULL){
8234 // if i'm the server, I shouldn't be here
8235 SDL_assert(!(Net_player->flags & NETINFO_FLAG_AM_MASTER));
8236 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8240 // only if this is valid
8241 if(MULTI_OBSERVER(Net_players[MY_NET_PLAYER_NUM])){
8246 if((Player_ship == NULL) || (Player_obj == NULL)){
8251 BUILD_HEADER(SELF_DESTRUCT);
8252 ADD_USHORT(Player_obj->net_signature);
8254 // send to the server
8255 multi_io_send_reliable(Net_player, data, packet_size);
8258 void process_self_destruct_packet(ubyte *data, header *hinfo)
8260 int offset = HEADER_LENGTH;
8264 // get the net signature
8265 GET_USHORT(net_sig);
8269 np_index = find_player_id(hinfo->id);
8273 if(MULTI_OBSERVER(Net_players[np_index])){
8276 if(Net_players[np_index].player == NULL){
8279 if((Net_players[np_index].player->objnum < 0) || (Net_players[np_index].player->objnum >= MAX_OBJECTS)){
8282 if(Objects[Net_players[np_index].player->objnum].net_signature != net_sig){
8285 if(Objects[Net_players[np_index].player->objnum].type != OBJ_SHIP){
8288 if((Objects[Net_players[np_index].player->objnum].instance < 0) || (Objects[Net_players[np_index].player->objnum].instance >= MAX_SHIPS)){
8293 ship_self_destruct(&Objects[Net_players[np_index].player->objnum]);