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 if (net_player_num < 0) {
3093 // getting here means a player fired. Send the current packet to all players except the player
3094 // who fired. If nothing got fired, then don't send to the other players -- we will just send
3095 // a packet to the player who will find out that he didn't fire anything
3096 if ( num_fired > 0 ) {
3097 multi_io_send_to_all_reliable(data, packet_size, &Net_players[net_player_num]);
3100 // if I (the master) fired, then return
3101 if ( Net_players[net_player_num].flags & NETINFO_FLAG_AM_MASTER ){
3105 // now build up the packet to send to the player who actually fired.
3106 BUILD_HEADER( SECONDARY_FIRED_PLR );
3107 ADD_USHORT(starting_sig);
3110 // add the targeting information so that the player's weapons will always home on the correct
3112 ADD_USHORT( target_signature );
3113 ADD_DATA( t_subsys );
3115 multi_io_send_reliable(&Net_players[net_player_num], data, packet_size);
3118 /// process a packet indicating a secondary weapon was fired
3119 void process_secondary_fired_packet(ubyte* data, header* hinfo, int from_player)
3121 int offset, allow_swarm, target_objnum_save;
3122 ushort net_signature, starting_sig, target_signature;
3123 ubyte sinfo, current_bank;
3124 object* objp, *target_objp;
3128 ship_subsys *targeted_subsys_save;
3130 offset = HEADER_LENGTH; // size of the header
3132 // if from_player is false, it means that the secondary weapon info in this packet was
3133 // fired by an ai object (or another player). from_player == 1 means tha me (the person
3134 // receiving this packet) fired the secondary weapon
3135 if ( !from_player ) {
3136 GET_USHORT( net_signature );
3137 GET_USHORT( starting_sig );
3138 GET_DATA( sinfo ); // are we firing swarm missiles
3140 GET_USHORT( target_signature );
3141 GET_DATA( t_subsys );
3145 // find the object (based on network signatures) for the object that fired
3146 objp = multi_get_network_object( net_signature );
3147 if ( objp == NULL ) {
3148 nprintf(("Network", "Could not find ship for fire secondary packet!"));
3152 // set up the ships current secondary bank and that bank's mode. Below, we will set the timeout
3153 // of the next fire time of this bank to 0 so we can fire right away
3154 shipp = &Ships[objp->instance];
3157 GET_USHORT( starting_sig );
3160 GET_USHORT( target_signature );
3161 GET_DATA( t_subsys );
3165 // get the object and ship
3167 shipp = Player_ship;
3170 // check the allow swarm bit
3172 if ( sinfo & SFPF_ALLOW_SWARM ){
3176 // set the dual fire properties of the ship
3177 if ( sinfo & SFPF_DUAL_FIRE ){
3178 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
3180 shipp->flags &= ~SF_SECONDARY_DUAL_FIRE;
3183 // determine whether current target is locked
3184 SDL_assert( shipp->ai_index != -1 );
3185 aip = &Ai_info[shipp->ai_index];
3186 if ( sinfo & SFPF_TARGET_LOCKED ) {
3187 aip->current_target_is_locked = 1;
3189 aip->current_target_is_locked = 0;
3192 // find out the current bank
3193 current_bank = (ubyte)(sinfo & 0x3);
3194 //SDL_assert( (current_bank >= 0) && (current_bank < MAX_SECONDARY_BANKS) ); // always true
3195 shipp->weapons.current_secondary_bank = current_bank;
3197 // make it so we can fire this ship's secondary bank immediately!!!
3198 shipp->weapons.next_secondary_fire_stamp[shipp->weapons.current_secondary_bank] = timestamp(0);
3199 shipp->weapons.detonate_weapon_time = timestamp(5000); // be sure that we don't detonate a remote weapon before it is time.
3201 // set this ship's target and subsystem information. We will save and restore target and
3202 // targeted subsystem so that we do not accidentally change targets for this player or
3203 // any AI ships on his system.
3204 target_objnum_save = aip->target_objnum;
3205 targeted_subsys_save = aip->targeted_subsys;
3207 // reset these variables for accuracy. They will get reassigned at the end of this fuction
3208 aip->target_objnum = -1;
3209 aip->targeted_subsys = NULL;
3211 target_objp = multi_get_network_object( target_signature );
3212 if ( target_objp != NULL ) {
3213 aip->target_objnum = OBJ_INDEX(target_objp);
3215 if ( (t_subsys != -1) && (target_objp->type == OBJ_SHIP) ) {
3216 aip->targeted_subsys = ship_get_indexed_subsys( &Ships[target_objp->instance], t_subsys);
3220 if ( starting_sig != 0 ){
3221 multi_set_network_signature( starting_sig, MULTI_SIG_NON_PERMANENT );
3223 shipp->weapons.detonate_weapon_time = timestamp(0); // signature of -1 say detonate remote weapon
3226 ship_fire_secondary( objp, allow_swarm );
3228 // restore targeted object and targeted subsystem
3229 aip->target_objnum = target_objnum_save;
3230 aip->targeted_subsys = targeted_subsys_save;
3233 // send a packet indicating a countermeasure was fired
3234 void send_countermeasure_fired_packet( object *objp, int cmeasure_count, int rand_val )
3236 ubyte data[MAX_PACKET_SIZE];
3241 SDL_assert ( cmeasure_count < UCHAR_MAX );
3242 BUILD_HEADER(COUNTERMEASURE_FIRED);
3243 ADD_USHORT( objp->net_signature );
3244 ADD_INT( rand_val );
3246 multi_io_send_to_all(data, packet_size);
3249 // process a packet indicating a countermeasure was fired
3250 void process_countermeasure_fired_packet( ubyte *data, header *hinfo )
3252 int offset, rand_val;
3258 offset = HEADER_LENGTH;
3260 GET_USHORT( signature );
3261 GET_INT( rand_val );
3264 objp = multi_get_network_object( signature );
3265 if ( objp == NULL ) {
3266 nprintf(("network", "Could find object whose countermeasures are being launched!!!\n"));
3269 if(objp->type != OBJ_SHIP){
3272 // SDL_assert ( objp->type == OBJ_SHIP );
3274 // make it so ship can fire right away!
3275 Ships[objp->instance].cmeasure_fire_stamp = timestamp(0);
3276 if ( objp == Player_obj ){
3277 nprintf(("network", "firing countermeasure from my ship\n"));
3280 ship_launch_countermeasure( objp, rand_val );
3283 // send a packet indicating that a turret has been fired
3284 void send_turret_fired_packet( int ship_objnum, int subsys_index, int weapon_objnum )
3287 ushort pnet_signature;
3288 ubyte data[MAX_PACKET_SIZE], cindex;
3295 if((weapon_objnum < 0) || (Objects[weapon_objnum].type != OBJ_WEAPON) || (Objects[weapon_objnum].instance < 0) || (Weapons[Objects[weapon_objnum].instance].weapon_info_index < 0)){
3299 // local setup -- be sure we are actually passing a weapon!!!!
3300 objp = &Objects[weapon_objnum];
3301 SDL_assert ( objp->type == OBJ_WEAPON );
3302 if(Weapon_info[Weapons[objp->instance].weapon_info_index].subtype == WP_MISSILE){
3306 pnet_signature = Objects[ship_objnum].net_signature;
3308 SDL_assert( subsys_index < UCHAR_MAX );
3309 cindex = (ubyte)subsys_index;
3311 ssp = ship_get_indexed_subsys( &Ships[Objects[ship_objnum].instance], subsys_index, NULL );
3316 // build the fire turret packet.
3317 BUILD_HEADER(FIRE_TURRET_WEAPON);
3318 packet_size += multi_pack_unpack_position(1, data + packet_size, &objp->orient.v.fvec);
3319 ADD_DATA( has_sig );
3320 ADD_USHORT( pnet_signature );
3322 ADD_USHORT( objp->net_signature );
3325 val = (short)ssp->submodel_info_1.angs.h;
3327 val = (short)ssp->submodel_info_2.angs.p;
3330 multi_io_send_to_all(data, packet_size);
3332 multi_rate_add(1, "tur", packet_size);
3335 // process a packet indicating a turret has been fired
3336 void process_turret_fired_packet( ubyte *data, header *hinfo )
3338 int offset, weapon_objnum, wid;
3339 ushort pnet_signature, wnet_signature;
3348 short pitch, heading;
3350 // get the data for the turret fired packet
3351 offset = HEADER_LENGTH;
3352 offset += multi_pack_unpack_position(0, data + offset, &o_fvec);
3353 GET_DATA( has_sig );
3354 GET_USHORT( pnet_signature );
3356 GET_USHORT( wnet_signature );
3360 GET_DATA( turret_index );
3361 GET_SHORT( heading );
3363 PACKET_SET_SIZE(); // move our counter forward the number of bytes we have read
3366 objp = multi_get_network_object( pnet_signature );
3367 if ( objp == NULL ) {
3368 nprintf(("network", "could find parent object with net signature %d for turret firing\n", pnet_signature));
3372 // if this isn't a ship, do nothing
3373 if ( objp->type != OBJ_SHIP ){
3377 // make an orientation matrix from the o_fvec
3378 vm_vector_2_matrix(&orient, &o_fvec, NULL, NULL);
3380 // find this turret, and set the position of the turret that just fired to be where it fired. Quite a
3381 // hack, but should be suitable.
3382 shipp = &Ships[objp->instance];
3383 ssp = ship_get_indexed_subsys( shipp, turret_index, NULL );
3387 wid = ssp->system_info->turret_weapon_type;
3389 // bash the position and orientation of the turret
3390 ssp->submodel_info_1.angs.h = (float)heading;
3391 ssp->submodel_info_2.angs.p = (float)pitch;
3393 // get the world position of the weapon
3394 ship_get_global_turret_info(objp, ssp->system_info, &pos, &temp);
3396 // create the weapon object
3397 if(wnet_signature != 0){
3398 multi_set_network_signature( wnet_signature, MULTI_SIG_NON_PERMANENT );
3400 weapon_objnum = weapon_create( &pos, &orient, wid, OBJ_INDEX(objp), 0, -1, 1);
3401 if (weapon_objnum != -1) {
3402 if ( Weapon_info[wid].launch_snd != -1 ) {
3403 snd_play_3d( &Snds[Weapon_info[wid].launch_snd], &pos, &View_position );
3408 // send a mission log item packet
3409 void send_mission_log_packet( int num )
3412 ubyte data[MAX_PACKET_SIZE];
3417 SDL_assert ( (Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER) );
3419 // get the data from the log
3420 entry = &log_entries[num];
3421 type = (ubyte)entry->type; // do the type casting thing to save on packet space
3422 sindex = (ushort)entry->index;
3424 BUILD_HEADER(MISSION_LOG_ENTRY);
3426 ADD_INT(entry->flags);
3428 ADD_DATA(entry->timestamp);
3429 ADD_STRING(entry->pname);
3430 ADD_STRING(entry->sname);
3432 // broadcast the packet to all players
3433 multi_io_send_to_all_reliable(data, packet_size);
3436 // process a mission log item packet
3437 void process_mission_log_packet( ubyte *data, header *hinfo )
3442 char pname[NAME_LENGTH], sname[NAME_LENGTH];
3445 SDL_assert ( (Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
3447 offset = HEADER_LENGTH;
3451 GET_DATA(timestamp);
3457 mission_log_add_entry_multi( type, pname, sname, sindex, timestamp, flags );
3460 // send a mission message packet
3461 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)
3464 ubyte data[MAX_PACKET_SIZE], up, us, utime;
3466 SDL_assert ( Net_player->flags & NETINFO_FLAG_AM_MASTER );
3467 SDL_assert ( (priority >= 0) && (priority < UCHAR_MAX) );
3468 SDL_assert ( (timing >= 0) && (timing < UCHAR_MAX) );
3470 up = (ubyte) priority;
3471 us = (ubyte) source;
3472 utime = (ubyte)timing;
3474 BUILD_HEADER(MISSION_MESSAGE);
3476 ADD_STRING(who_from);
3480 ADD_INT(builtin_type);
3481 ADD_INT(multi_team_filter);
3483 if (multi_target == -1){
3484 multi_io_send_to_all_reliable(data, packet_size);
3486 multi_io_send_reliable(&Net_players[multi_target], data, packet_size);
3490 // process a mission message packet
3491 void process_mission_message_packet( ubyte *data, header *hinfo )
3493 int offset, id, builtin_type;
3494 ubyte priority, source, utiming;
3495 char who_from[NAME_LENGTH];
3496 int multi_team_filter;
3498 SDL_assert( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
3500 offset = HEADER_LENGTH;
3502 GET_STRING(who_from);
3506 GET_INT(builtin_type);
3507 GET_INT(multi_team_filter);
3511 // filter out builtin ones in TvT
3512 if((builtin_type >= 0) && (Netgame.type_flags & NG_TYPE_TEAM) && (Net_player != NULL) && (Net_player->p_info.team != multi_team_filter)){
3516 // maybe filter this out
3517 if(!message_filter_multi(id)){
3518 // send the message as if it came from an sexpression
3519 message_queue_message( id, priority, utiming, who_from, source, 0, 0, builtin_type );
3523 // just send them a pong back as fast as possible
3524 void process_ping_packet(ubyte *data, header *hinfo)
3529 offset = HEADER_LENGTH;
3532 // get the address to return the pong to
3533 fill_net_addr(&addr, hinfo->addr, hinfo->port);
3539 // right now it just routes the pong through to the standalone gui, which is the only
3540 // system which uses ping and pong right now.
3541 void process_pong_packet(ubyte *data, header *hinfo)
3547 offset = HEADER_LENGTH;
3549 fill_net_addr(&addr, hinfo->addr, hinfo->port);
3553 // if we're connected , see who sent us this pong
3554 if(Net_player->flags & NETINFO_FLAG_CONNECTED){
3555 lookup = find_player_id(hinfo->id);
3560 p = &Net_players[lookup];
3562 // evaluate the ping
3563 multi_ping_eval_pong(&Net_players[lookup].s_info.ping);
3565 // put in calls to any functions which may want to know about the ping times from
3567 if(Game_mode & GM_STANDALONE_SERVER){
3568 std_update_player_ping(p);
3572 // mark his socket as still alive (extra precaution)
3573 psnet_mark_received(Net_players[lookup].reliable_socket);
3576 // otherwise, do any special processing
3578 // if we're in the join game state, see if this pong came from a server on our
3580 if(gameseq_get_state() == GS_STATE_MULTI_JOIN_GAME){
3581 multi_join_eval_pong(&addr, timer_get_fixed_seconds());
3586 // send a ping packet
3587 void send_ping(net_addr *addr)
3589 unsigned char data[8];
3592 // build the header and send the packet
3593 BUILD_HEADER( PING );
3594 psnet_send(addr, &data[0], packet_size);
3597 // send a pong packet
3598 void send_pong(net_addr *addr)
3600 unsigned char data[8];
3603 // build the header and send the packet
3605 psnet_send(addr, &data[0], packet_size);
3608 // sent from host to master. give me the list of missions you have.
3609 // this will be used only in a standalone mode
3610 void send_mission_list_request( int what )
3612 ubyte data[MAX_PACKET_SIZE];
3615 // build the header and ask for a list of missions or campaigns (depending
3616 // on the 'what' flag).
3617 BUILD_HEADER(MISSION_REQUEST);
3619 multi_io_send_reliable(Net_player, data, packet_size);
3622 // maximum number of bytes that we can send in a mission items packet.
3623 #define MAX_MISSION_ITEMS_BYTES (MAX_PACKET_SIZE - (sizeof(multi_create_info) + 1) )
3625 // defines used to tell what type of packets are being sent
3626 #define MISSION_LIST_ITEMS 1
3627 #define CAMPAIGN_LIST_ITEMS 2
3629 // send an individual mission file item
3630 void send_mission_items( net_player *pl )
3632 ubyte data[MAX_PACKET_SIZE];
3637 BUILD_HEADER(MISSION_ITEM);
3639 // send the list of missions and campaigns avilable on the server. Stop when
3640 // reaching a certain maximum
3641 type = MISSION_LIST_ITEMS;
3643 for (i = 0; i < Multi_create_mission_count; i++ ) {
3647 ADD_STRING( Multi_create_mission_list[i].filename );
3648 ADD_STRING( Multi_create_mission_list[i].name );
3649 ADD_INT( Multi_create_mission_list[i].flags );
3650 ADD_DATA( Multi_create_mission_list[i].max_players );
3651 ADD_UINT( Multi_create_mission_list[i].respawn );
3654 ADD_DATA( Multi_create_mission_list[i].valid_status );
3656 if ( packet_size > (int)MAX_MISSION_ITEMS_BYTES ) {
3659 multi_io_send_reliable(pl, data, packet_size);
3660 BUILD_HEADER( MISSION_ITEM );
3666 multi_io_send_reliable(pl, data, packet_size);
3668 // send the campaign information
3669 type = CAMPAIGN_LIST_ITEMS;
3670 BUILD_HEADER(MISSION_ITEM);
3672 for (i = 0; i < Multi_create_campaign_count; i++ ) {
3676 ADD_STRING( Multi_create_campaign_list[i].filename );
3677 ADD_STRING( Multi_create_campaign_list[i].name );
3678 ADD_INT( Multi_create_campaign_list[i].flags );
3679 ADD_DATA( Multi_create_campaign_list[i].max_players );
3681 if ( packet_size > (int)MAX_MISSION_ITEMS_BYTES ) {
3684 multi_io_send_reliable(pl, data, packet_size);
3685 BUILD_HEADER( MISSION_ITEM );
3691 multi_io_send_reliable(pl, data, packet_size);
3694 // process a request for a list of missions
3695 void process_mission_request_packet(ubyte *data, header *hinfo)
3697 int player_num,offset;
3699 offset = HEADER_LENGTH;
3702 // fill in the address information of where this came from
3703 player_num = find_player_id(hinfo->id);
3704 if(player_num == -1){
3705 nprintf(("Network","Could not find player to send mission list items to!\n"));
3709 send_mission_items( &Net_players[player_num] );
3712 // process an individual mission file item
3713 void process_mission_item_packet(ubyte *data,header *hinfo)
3716 char filename[MAX_FILENAME_LEN], name[NAME_LENGTH], valid_status;
3717 ubyte stop, type,max_players;
3720 SDL_assert(gameseq_get_state() == GS_STATE_MULTI_HOST_SETUP);
3721 offset = HEADER_LENGTH;
3726 GET_STRING( filename );
3729 GET_DATA( max_players );
3731 // missions also have respawns and a crc32 associated with them
3732 if(type == MISSION_LIST_ITEMS){
3736 GET_DATA(valid_status);
3738 if ( Multi_create_mission_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
3739 SDL_strlcpy(Multi_create_mission_list[Multi_create_mission_count].filename, filename, MAX_FILENAME_LEN );
3740 SDL_strlcpy(Multi_create_mission_list[Multi_create_mission_count].name, name, NAME_LENGTH );
3741 Multi_create_mission_list[Multi_create_mission_count].flags = flags;
3742 Multi_create_mission_list[Multi_create_mission_count].respawn = respawn;
3743 Multi_create_mission_list[Multi_create_mission_count].max_players = max_players;
3746 Multi_create_mission_list[Multi_create_mission_count].valid_status = valid_status;
3748 Multi_create_mission_count++;
3750 } else if ( type == CAMPAIGN_LIST_ITEMS ) {
3751 if ( Multi_create_campaign_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
3752 SDL_strlcpy(Multi_create_campaign_list[Multi_create_campaign_count].filename, filename, MAX_FILENAME_LEN );
3753 SDL_strlcpy(Multi_create_campaign_list[Multi_create_campaign_count].name, name, NAME_LENGTH );
3754 Multi_create_campaign_list[Multi_create_campaign_count].flags = flags;
3755 Multi_create_campaign_list[Multi_create_campaign_count].respawn = 0;
3756 Multi_create_campaign_list[Multi_create_campaign_count].max_players = max_players;
3757 Multi_create_campaign_count++;
3766 // this will cause whatever list to get resorted (although they should be appearing in order)
3767 multi_create_setup_list_data(-1);
3770 // send a request to the server to pause or unpause the game
3771 void send_multi_pause_packet(int pause)
3773 ubyte data[MAX_PACKET_SIZE];
3775 int packet_size = 0;
3777 SDL_assert(!(Net_player->flags & NETINFO_FLAG_AM_MASTER));
3780 BUILD_HEADER(MULTI_PAUSE_REQUEST);
3781 val = (ubyte) pause;
3783 // add the pause info
3786 // send the request to the server
3787 multi_io_send_reliable(Net_player, data, packet_size);
3790 // process a pause update packet (pause, unpause, etc)
3791 void process_multi_pause_packet(ubyte *data, header *hinfo)
3797 offset = HEADER_LENGTH;
3803 // get who sent the packet
3804 player_index = find_player_id(hinfo->id);
3805 // if we don't know who sent the packet, don't do anything
3806 if(player_index == -1){
3810 // if we're the server, we should evaluate whether this guy is allowed to send the packet
3811 multi_pause_server_eval_request(&Net_players[player_index],(int)val);
3814 // send a game information update
3815 void send_game_info_packet()
3818 ubyte data[MAX_PACKET_SIZE], paused;
3820 // set the paused variable
3821 paused = (ubyte)((Netgame.game_state == NETGAME_STATE_PAUSED)?1:0);
3823 BUILD_HEADER(GAME_INFO);
3824 ADD_INT( Missiontime );
3827 multi_io_send_to_all(data, packet_size);
3830 // process a game information update
3831 void process_game_info_packet( ubyte *data, header *hinfo )
3837 offset = HEADER_LENGTH;
3839 // get the mission time -- we should examine our time and the time from the server. If off by some delta
3840 // time, set our time to server time (should take ping time into account!!!)
3841 GET_DATA( mission_time );
3846 // send an ingame nak packet
3847 void send_ingame_nak(int state, net_player *p)
3849 ubyte data[MAX_PACKET_SIZE];
3850 int packet_size = 0;
3852 BUILD_HEADER(INGAME_NAK);
3856 multi_io_send_reliable(p, data, packet_size);
3859 // process an ingame nak packet
3860 void process_ingame_nak(ubyte *data, header *hinfo)
3862 int offset,state,pid;
3864 offset = HEADER_LENGTH;
3868 pid = find_player_id(hinfo->id);
3874 case ACK_FILE_ACCEPTED :
3875 SDL_assert(Net_players[pid].flags & NETINFO_FLAG_INGAME_JOIN);
3876 nprintf(("Network","Mission file rejected by server, aborting...\n"));
3877 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_FILE_REJECTED);
3882 // send a packet telling players to end the mission
3883 void send_endgame_packet(net_player *pl)
3885 ubyte data[MAX_PACKET_SIZE];
3886 int packet_size = 0;
3888 BUILD_HEADER(MISSION_END);
3890 // sending to a specific player?
3892 SDL_assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
3893 multi_io_send_reliable(pl, data, packet_size);
3897 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3898 // send all player stats here
3899 multi_broadcast_stats(STATS_MISSION);
3901 // if in dogfight mode, send all dogfight stats as well
3902 ml_string("Before dogfight stats!");
3903 if(Netgame.type_flags & NG_TYPE_DOGFIGHT){
3904 ml_string("Sending dogfight stats!");
3906 multi_broadcast_stats(STATS_DOGFIGHT_KILLS);
3908 ml_string("After dogfight stats!");
3910 // tell everyone to leave the game
3911 multi_io_send_to_all_reliable(data, packet_size);
3913 multi_io_send_reliable(Net_player, data, packet_size);
3917 // process a packet indicating we should end the current mission
3918 void process_endgame_packet(ubyte *data, header *hinfo)
3923 offset = HEADER_LENGTH;
3927 ml_string("Receiving endgame packet");
3929 // if I'm the server, I should evaluate whether the sender is authorized to end the game
3930 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3931 // determine who this came from and make sure he is allowed to end the game
3932 player_num = find_player_id(hinfo->id);
3933 SDL_assert(player_num != -1);
3938 // if the player is allowed to end the mission
3939 if(!multi_can_end_mission(&Net_players[player_num])){
3943 // act as if we hit alt+j locally
3944 multi_handle_end_mission_request();
3946 // all clients process immediately
3948 // ingame joiners should quit when they receive an endgame packet since the game is over
3949 if(Net_player->flags & NETINFO_FLAG_INGAME_JOIN){
3950 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_EARLY_END);
3954 // do any special processing for being in a state other than the gameplay state
3955 multi_handle_state_special();
3957 // make sure we're not already in the debrief state
3958 if((gameseq_get_state() != GS_STATE_DEBRIEF) && (gameseq_get_state() != GS_STATE_MULTI_DOGFIGHT_DEBRIEF)){
3959 multi_warpout_all_players();
3964 // send a position/orientation update for myself (if I'm an observer)
3965 void send_observer_update_packet()
3967 ubyte data[MAX_PACKET_SIZE];
3968 int packet_size = 0;
3972 // its possible for the master to be an observer if has run out of respawns. In this case, he doesn't need
3973 // to send any update packets to anyone.
3974 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3978 if((Player_obj == NULL) || (Player_obj->type != OBJ_OBSERVER) || (Net_player == NULL) || !(Net_player->flags & NETINFO_FLAG_OBSERVER)){
3982 BUILD_HEADER(OBSERVER_UPDATE);
3984 ret = multi_pack_unpack_position( 1, data + packet_size, &Player_obj->pos );
3986 ret = multi_pack_unpack_orient( 1, data + packet_size, &Player_obj->orient );
3989 // add targeting infomation
3990 if((Player_ai != NULL) && (Player_ai->target_objnum >= 0)){
3991 target_sig = Objects[Player_ai->target_objnum].net_signature;
3995 ADD_USHORT(target_sig);
3997 multi_io_send(Net_player, data, packet_size);
4000 // process a position/orientation update from an observer
4001 void process_observer_update_packet(ubyte *data, header *hinfo)
4007 physics_info bogus_pi;
4010 offset = HEADER_LENGTH;
4012 obs_num = find_player_id(hinfo->id);
4014 memset(&bogus_pi,0,sizeof(physics_info));
4015 ret = multi_pack_unpack_position( 0, data + offset, &g_vec );
4017 ret = multi_pack_unpack_orient( 0, data + offset, &g_mat );
4020 // targeting information
4021 GET_USHORT(target_sig);
4024 if((obs_num < 0) || (Net_players[obs_num].player->objnum < 0)){
4028 // set targeting info
4029 if(target_sig == 0){
4030 Net_players[obs_num].s_info.target_objnum = -1;
4032 target_obj = multi_get_network_object(target_sig);
4033 Net_players[obs_num].s_info.target_objnum = (target_obj == NULL) ? -1 : OBJ_INDEX(target_obj);
4036 Objects[Net_players[obs_num].player->objnum].pos = g_vec;
4037 Objects[Net_players[obs_num].player->objnum].orient = g_mat;
4038 Net_players[obs_num].s_info.eye_pos = g_vec;
4039 Net_players[obs_num].s_info.eye_orient = g_mat;
4042 void send_netplayer_slot_packet()
4044 ubyte data[MAX_PACKET_SIZE];
4045 int packet_size = 0, idx;
4050 BUILD_HEADER(NETPLAYER_SLOTS_P);
4051 for(idx=0;idx<MAX_PLAYERS;idx++){
4052 if( MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_OBSERVER(Net_players[idx])){
4054 ADD_SHORT(Net_players[idx].player_id);
4055 ADD_USHORT(Objects[Net_players[idx].player->objnum].net_signature);
4056 ADD_INT(Net_players[idx].p_info.ship_class);
4057 ADD_INT(Net_players[idx].p_info.ship_index);
4063 // standalone case or not
4064 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4065 multi_io_send_to_all_reliable(data, packet_size);
4067 multi_io_send_reliable(Net_player, data, packet_size);
4071 void process_netplayer_slot_packet(ubyte *data, header *hinfo)
4074 int player_num,ship_class,ship_index;
4080 offset = HEADER_LENGTH;
4082 // first untag all of the player ships and make them OF_COULD_BE_PLAYER
4083 multi_untag_player_ships();
4087 GET_SHORT(player_id);
4088 GET_USHORT(net_sig);
4089 GET_INT(ship_class);
4090 GET_INT(ship_index);
4091 player_num = find_player_id(player_id);
4093 nprintf(("Network","Error looking up player for object/slot assignment!!\n"));
4095 // call the function in multiutil.cpp to set up the player object stuff
4096 // being careful not to muck with the standalone object
4097 if(!((player_num == 0) && (Game_mode & GM_STANDALONE_SERVER))){
4098 objp = multi_get_network_object(net_sig);
4099 SDL_assert(objp != NULL);
4100 multi_assign_player_ship( player_num, objp, ship_class );
4101 Net_players[player_num].p_info.ship_index = ship_index;
4102 objp->flags &= ~(OF_COULD_BE_PLAYER);
4103 objp->flags |= OF_PLAYER_SHIP;
4110 // standalone should forward the packet and wait for a response
4111 if(Game_mode & GM_STANDALONE_SERVER){
4112 send_netplayer_slot_packet();
4115 Net_player->state = NETPLAYER_STATE_SLOT_ACK;
4116 send_netplayer_update_packet();
4119 // two functions to deal with ships changing their primary/secondary weapon status. 'what' indicates
4120 // if this change is a primary or secondary change. new_bank is the new current primary/secondary
4121 // bank, link_status is whether primaries are linked or not, or secondaries are dual fire or not
4122 void send_ship_weapon_change( ship *shipp, int what, int new_bank, int link_status )
4124 ubyte data[MAX_PACKET_SIZE], utmp;
4127 BUILD_HEADER(SHIP_WSTATE_CHANGE);
4128 ADD_USHORT( Objects[shipp->objnum].net_signature );
4129 utmp = (ubyte)(what);
4131 utmp = (ubyte)(new_bank);
4133 utmp = (ubyte)(link_status);
4136 // Removed the above psnet_send() call - it didn't appear to do anything since it was called only from the server anyway - DB
4137 multi_io_send_to_all_reliable(data, packet_size);
4140 void process_ship_weapon_change( ubyte *data, header *hinfo )
4144 ubyte what, new_bank, link_status;
4148 offset = HEADER_LENGTH;
4149 GET_USHORT( signature );
4151 GET_DATA( new_bank );
4152 GET_DATA( link_status );
4155 objp = multi_get_network_object( signature );
4156 if ( objp == NULL ) {
4157 nprintf(("network", "Unable to locate ship with signature %d for weapon state change\n", signature));
4160 // SDL_assert( objp->type == OBJ_SHIP );
4161 if(objp->type != OBJ_SHIP){
4165 // if this is my data, do nothing since I already have my own data
4166 if ( objp == Player_obj ){
4170 // now, get the ship and set the new bank and link modes based on the 'what' value
4171 shipp = &Ships[objp->instance];
4172 if ( what == MULTI_PRIMARY_CHANGED ) {
4173 shipp->weapons.current_primary_bank = new_bank;
4175 shipp->flags |= SF_PRIMARY_LINKED;
4177 shipp->flags &= ~SF_PRIMARY_LINKED;
4180 shipp->weapons.current_secondary_bank = new_bank;
4182 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
4184 shipp->flags &= ~SF_SECONDARY_DUAL_FIRE;
4189 // ship status change procedure
4190 // 1.) <client> - Client runs through the normal button_function procedure. Any remaining control bits are implied as being
4192 // 2.) <client> - Client puts this button_info item into his last_buttons array and sends a bunch of SHIP_STATUS packets
4193 // for added redundancy.
4194 // 3.) <server> - Receives the packet. Checks to see if the net_player on his side already has this one defined. If so, it
4195 // ignores as a repeat packet. Otherwise it puts it in the last_buttons array for the net_player
4196 // 4.) <server> - Server applies the command on his side (with multi_apply_ship_status(...) and sends the ack (also a SHIP_STATUS)
4197 // back to the client. Also sends multiple times for redundancy
4198 // 5.) <client> - Receives the packet back. Does a lookup into his last_buttons array. If he finds the match, apply the functions
4199 // and remove the item from the list. If no match is found it means that either he has received an ack, has acted
4200 // on it and removed it, or that it has been "timed out" and replaced by a newer button_info.
4202 #define SHIP_STATUS_REPEAT 2
4203 void send_ship_status_packet(net_player *pl, button_info *bi, int id)
4206 ubyte data[MAX_PACKET_SIZE];
4207 int packet_size = 0;
4213 BUILD_HEADER(SHIP_STATUS_CHANGE);
4215 for(idx=0;idx<NUM_BUTTON_FIELDS;idx++){
4216 temp = bi->status[idx];
4220 // server should send reliably (response packet)
4221 if(MULTIPLAYER_MASTER){
4222 multi_io_send_reliable(pl, data, packet_size);
4224 multi_io_send(pl, data, packet_size);
4228 void process_ship_status_packet(ubyte *data, header *hinfo)
4232 int player_num,unique_id;
4236 offset = HEADER_LENGTH;
4238 // zero out the button info structure for good measure
4239 memset(&bi,0,sizeof(button_info));
4241 // read the button-info
4244 for(idx=0;idx<NUM_BUTTON_FIELDS;idx++){
4246 bi.status[idx] = i_tmp;
4251 // this will be handled differently client and server side. Duh.
4252 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){ // SERVER SIDE
4253 // find which net-player has sent us butotn information
4254 player_num = find_player_id(hinfo->id);
4255 SDL_assert(player_num >= 0);
4260 // don't process critical button information for observers
4261 // its a new button_info for this guy. apply and ack
4262 if(!MULTI_OBSERVER(Net_players[player_num]) && !lookup_ship_status(&Net_players[player_num],unique_id)){
4263 // mark that he's pressed this button
4264 // add_net_button_info(&Net_players[player_num], &bi, unique_id);
4266 // send a return packet
4267 send_ship_status_packet(&Net_players[player_num], &bi,unique_id);
4269 // apply the button presses to his ship as normal
4270 multi_apply_ship_status(&Net_players[player_num], &bi, 0);
4272 // else ignore it as a repeat from the same guy
4273 } else { // CLIENT SIDE
4274 // this is the return from the server, so we should now apply them locally
4275 // if(lookup_ship_status(Net_player,unique_id,1)){
4276 multi_apply_ship_status(Net_player, &bi, 1);
4281 // MWA 4/28/9 -- redid this function since message all fighers was really broken
4282 // for clients. Left all details to this function instead of higher level messaging
4284 void send_player_order_packet(int type, int index, int cmd)
4286 ubyte data[MAX_PACKET_SIZE];
4288 ushort target_signature;
4290 int packet_size = 0;
4292 BUILD_HEADER(PLAYER_ORDER_PACKET);
4295 ADD_DATA(val); // ship order or wing order, or message all fighters
4297 // if we are not messaging all ships or wings, add the index, which is the shipnum or wingnum
4298 if ( val != SQUAD_MSG_ALL ){
4299 ADD_INT(index); // net signature of target ship
4302 ADD_INT(cmd); // the command itself
4305 target_signature = 0;
4306 if ( Player_ai->target_objnum != -1 ){
4307 target_signature = Objects[Player_ai->target_objnum].net_signature;
4310 ADD_USHORT( target_signature );
4313 if ( (Player_ai->target_objnum != -1) && (Player_ai->targeted_subsys != NULL) ) {
4316 s_index = ship_get_index_from_subsys( Player_ai->targeted_subsys, Player_ai->target_objnum );
4317 SDL_assert( s_index < CHAR_MAX ); // better be less than this!!!!
4318 t_subsys = (char)s_index;
4322 multi_io_send_reliable(Net_player, data, packet_size);
4325 // brief explanation :
4326 // in either case (wing or ship command), we need to send in a pseudo-ai object. Basically, both command handler
4327 // functions "normally" (non multiplayer) use a couple of the Player_ai fields. So, we just fill in the ones necessary
4328 // (which we can reconstruct from the packet data), and pass this as the default variable ai_info *local
4329 // Its kind of a hack, but it eliminates the need to go in and screw around with quite a bit of code
4330 void process_player_order_packet(ubyte *data, header *hinfo)
4332 int offset, player_num, command, index = 0, tobjnum_save;
4333 ushort target_signature;
4334 char t_subsys, type;
4335 object *objp, *target_objp;
4338 ship_subsys *tsubsys_save, *targeted_subsys;
4340 SDL_assert(MULTIPLAYER_MASTER);
4342 // packet values - its easier to read all of these in first
4344 offset = HEADER_LENGTH;
4347 if ( type != SQUAD_MSG_ALL ){
4352 GET_USHORT( target_signature );
4353 GET_DATA( t_subsys );
4357 player_num = find_player_id(hinfo->id);
4358 if(player_num == -1){
4359 nprintf(("Network","Received player order packet from unknown player\n"));
4363 objp = &Objects[Net_players[player_num].player->objnum];
4364 if ( objp->type != OBJ_SHIP ) {
4365 nprintf(("Network", "not doing player order because object requestting is not a ship\n"));
4369 // HACK HACK HACK HACK HACK HACK
4370 // if the player has sent a rearm-repair me message, we should bail here after evaluating it, since most likely the rest of
4371 // the data is BOGUS. All people should be able to to these things as well.
4372 if(command == REARM_REPAIR_ME_ITEM){
4373 hud_squadmsg_repair_rearm(0,&Objects[Net_players[player_num].player->objnum]);
4375 } else if(command == ABORT_REARM_REPAIR_ITEM){
4376 hud_squadmsg_repair_rearm_abort(0,&Objects[Net_players[player_num].player->objnum]);
4380 // if this player is not allowed to do messaging, quit here
4381 if( !multi_can_message(&Net_players[player_num]) ){
4382 nprintf(("Network","Recieved player order packet from player not allowed to give orders!!\n"));
4386 // check to see if the type of order is a reinforcement call. If so, intercept it, and
4387 // then call them in.
4388 if ( type == SQUAD_MSG_REINFORCEMENT ) {
4389 SDL_assert( (index >= 0) && (index < Num_reinforcements) );
4390 hud_squadmsg_call_reinforcement(index, player_num);
4394 // set the player's ai information here
4395 shipp = &Ships[objp->instance];
4396 aip = &Ai_info[shipp->ai_index];
4398 // get the target objnum and targeted subsystem. Quick out if we don't have an object to act on.
4399 target_objp = multi_get_network_object( target_signature );
4400 if ( target_objp == NULL ) {
4404 targeted_subsys = NULL;
4405 if ( t_subsys != -1 ) {
4406 SDL_assert( target_objp != NULL );
4407 targeted_subsys = ship_get_indexed_subsys( &Ships[target_objp->instance], t_subsys);
4410 // save and restore the target objnum and targeted subsystem so that we don't mess up other things
4412 tobjnum_save = aip->target_objnum;
4413 tsubsys_save = aip->targeted_subsys;
4415 if ( target_objp ) {
4416 aip->target_objnum = OBJ_INDEX(target_objp);
4418 aip->target_objnum = -1;
4421 aip->targeted_subsys = targeted_subsys;
4423 if ( type == SQUAD_MSG_SHIP ) {
4424 hud_squadmsg_send_ship_command(index, command, 1, player_num);
4425 } else if ( type == SQUAD_MSG_WING ) {
4426 hud_squadmsg_send_wing_command(index, command, 1, player_num);
4427 } else if ( type == SQUAD_MSG_ALL ) {
4428 hud_squadmsg_send_to_all_fighters( command, player_num );
4431 SDL_assert(tobjnum_save != Ships[aip->shipnum].objnum); // make sure not targeting self
4432 aip->target_objnum = tobjnum_save;
4433 aip->targeted_subsys = tsubsys_save;
4436 // FILE SIGNATURE stuff :
4437 // there are 2 cases for file signature sending which are handled very differently
4438 // 1.) Pregame. In this case, the host requires that all clients send a filesig packet (when process_file_sig() is called, it
4439 // posts an ACK_FILE_ACCEPTED packet to ack_evaluate, so he thinks they have acked).
4440 // 2.) Ingame join. In this case, the client sends his filesig packet automatically to the server and the _client_ waits for
4441 // the ack, before continuing to join. It would be way too messy to have the server wait on the clients ack, since he
4442 // would have to keep track of up to potentially 14 other ack handles (ouch).
4443 void send_file_sig_packet(ushort sum_sig,int length_sig)
4445 ubyte data[MAX_PACKET_SIZE];
4446 int packet_size = 0;
4448 BUILD_HEADER(FILE_SIG_INFO);
4449 ADD_USHORT(sum_sig);
4450 ADD_INT(length_sig);
4452 multi_io_send_reliable(Net_player, data, packet_size);
4455 void process_file_sig_packet(ubyte *data, header *hinfo)
4460 offset = HEADER_LENGTH;
4462 // should only be received on the server-side
4463 SDL_assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
4465 GET_USHORT(sum_sig);
4466 GET_INT(length_sig);
4468 server_verify_filesig(hinfo->id, sum_sig, length_sig);
4471 void send_file_sig_request(char *file_name)
4473 ubyte data[MAX_PACKET_SIZE];
4474 int packet_size = 0;
4476 BUILD_HEADER(FILE_SIG_REQUEST);
4477 ADD_STRING(file_name);
4479 SDL_assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
4481 multi_io_send_to_all_reliable(data, packet_size);
4484 void process_file_sig_request(ubyte *data, header *hinfo)
4486 int offset = HEADER_LENGTH;
4488 // get the mission name
4489 GET_STRING(Netgame.mission_name);
4492 // set the current mission filename
4493 SDL_strlcpy(Game_current_mission_filename, Netgame.mission_name, SDL_arraysize(Game_current_mission_filename));
4496 multi_get_mission_checksum(Game_current_mission_filename);
4498 if(!multi_endgame_ending()){
4499 // reply to the server
4500 send_file_sig_packet(Multi_current_file_checksum,Multi_current_file_length);
4504 // functions to deal with subsystems getting whacked
4505 void send_subsystem_destroyed_packet( ship *shipp, int index, vector world_hitpos )
4507 ubyte data[MAX_PACKET_SIZE];
4510 vector tmp, local_hitpos;
4513 SDL_assert ( index < UCHAR_MAX );
4514 uindex = (ubyte)(index);
4516 objp = &Objects[shipp->objnum];
4518 vm_vec_sub(&tmp, &world_hitpos, &objp->pos );
4519 vm_vec_rotate( &local_hitpos, &tmp, &objp->orient );
4521 BUILD_HEADER(SUBSYSTEM_DESTROYED);
4522 ADD_USHORT( Objects[shipp->objnum].net_signature );
4524 // ADD_DATA( local_hitpos );
4525 add_vector_data(data, &packet_size, local_hitpos);
4527 multi_io_send_to_all_reliable(data, packet_size);
4530 void process_subsystem_destroyed_packet( ubyte *data, header *hinfo )
4536 vector local_hit_pos = ZERO_VECTOR, world_hit_pos;
4538 offset = HEADER_LENGTH;
4540 GET_USHORT( signature );
4542 // GET_DATA( local_hit_pos );
4543 get_vector_data(data, &offset, local_hit_pos);
4545 // get the network object. process it if we find it.
4546 objp = multi_get_network_object( signature );
4547 if ( objp != NULL ) {
4549 ship_subsys *subsysp;
4551 // be sure we have a ship!!!
4552 // SDL_assert ( objp->type == OBJ_SHIP );
4553 if(objp->type != OBJ_SHIP){
4558 shipp = &Ships[objp->instance];
4560 // call to get the pointer to the subsystem we should be working on
4561 subsysp = ship_get_indexed_subsys( shipp, (int)uindex );
4562 vm_vec_unrotate( &world_hit_pos, &local_hit_pos, &objp->orient );
4563 vm_vec_add2( &world_hit_pos, &objp->pos );
4565 do_subobj_destroyed_stuff( shipp, subsysp, &world_hit_pos );
4566 if ( objp == Player_obj ) {
4567 hud_gauge_popup_start(HUD_DAMAGE_GAUGE, 5000);
4575 // packet to tell clients cargo of a ship was revealed to all
4576 void send_subsystem_cargo_revealed_packet( ship *shipp, int index )
4578 ubyte data[MAX_PACKET_SIZE], uindex;
4581 SDL_assert ( index < UCHAR_MAX );
4582 uindex = (ubyte)(index);
4584 // build the header and add the data
4585 BUILD_HEADER(SUBSYS_CARGO_REVEALED);
4586 ADD_USHORT( Objects[shipp->objnum].net_signature );
4589 // server sends to all players
4590 if(MULTIPLAYER_MASTER){
4591 multi_io_send_to_all_reliable(data, packet_size);
4593 // clients just send to the server
4595 multi_io_send_reliable(Net_player, data, packet_size);
4599 // process a subsystem cargo revealed packet
4600 void process_subsystem_cargo_revealed_packet( ubyte *data, header *hinfo )
4607 ship_subsys *subsysp;
4609 offset = HEADER_LENGTH;
4610 GET_USHORT( signature );
4614 // get a ship pointer and call the ship function to reveal the cargo
4615 objp = multi_get_network_object( signature );
4616 if ( objp == NULL ) {
4617 nprintf(("Network", "Could not find object with net signature %d for cargo revealed\n", signature ));
4621 // SDL_assert( objp->type == OBJ_SHIP );
4622 if((objp->type != OBJ_SHIP) || (objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
4626 shipp = &Ships[objp->instance];
4628 // call to get the pointer to the subsystem we should be working on
4629 subsysp = ship_get_indexed_subsys( shipp, (int)uindex );
4630 if (subsysp == NULL) {
4631 nprintf(("Network", "Could not find subsys for ship %s for cargo revealed\n", Ships[objp->instance].ship_name ));
4635 // this will take care of re-routing to all other clients
4636 void ship_do_cap_subsys_cargo_revealed( ship *shipp, ship_subsys *subsys, int from_network );
4637 ship_do_cap_subsys_cargo_revealed( shipp, subsysp, 1 );
4639 // server should rebroadcast
4640 if(MULTIPLAYER_MASTER){
4641 send_subsystem_cargo_revealed_packet(&Ships[objp->instance], (int)uindex);
4645 void send_netplayer_load_packet(net_player *pl)
4647 ubyte data[MAX_PACKET_SIZE];
4648 int packet_size = 0;
4650 BUILD_HEADER(LOAD_MISSION_NOW);
4651 ADD_STRING(Netgame.mission_name);
4654 multi_io_send_to_all_reliable(data, packet_size);
4656 multi_io_send_reliable(pl, data, packet_size);
4660 void process_netplayer_load_packet(ubyte *data, header *hinfo)
4663 int offset = HEADER_LENGTH;
4668 SDL_strlcpy(Netgame.mission_name, str, SDL_arraysize(Netgame.mission_name));
4669 SDL_strlcpy(Game_current_mission_filename, str, SDL_arraysize(Game_current_mission_filename));
4670 if(!Multi_mission_loaded){
4672 // MWA 2/3/98 -- ingame join changes!!!
4673 // everyone can go through the same mission loading path here!!!!
4674 nprintf(("Network","Loading mission..."));
4676 // notify everyone that I'm loading the mission
4677 Net_player->state = NETPLAYER_STATE_MISSION_LOADING;
4678 send_netplayer_update_packet();
4680 // do the load itself
4681 game_start_mission();
4683 // ingame joiners need to "untag" all player ships as could_be_players. The ingame joining
4684 // code will remark the correct player ships
4685 if ( Net_player->flags & NETINFO_FLAG_INGAME_JOIN ) {
4686 multi_untag_player_ships();
4689 Net_player->flags |= NETINFO_FLAG_MISSION_OK;
4690 Net_player->state = NETPLAYER_STATE_MISSION_LOADED;
4691 send_netplayer_update_packet();
4693 Multi_mission_loaded = 1;
4694 nprintf(("Network","Finished loading mission\n"));
4698 void send_jump_into_mission_packet(net_player *pl)
4700 ubyte data[MAX_PACKET_SIZE];
4701 int packet_size = 0;
4703 SDL_assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
4705 BUILD_HEADER(JUMP_INTO_GAME);
4707 // ingame joiners will get special data. We need to tell them about the state of the mission, like paused,
4708 // and possible other things.
4710 if ( pl->flags & NETINFO_FLAG_INGAME_JOIN ) {
4711 ADD_INT(Netgame.game_state);
4717 multi_io_send_to_all_reliable(data, packet_size);
4719 // send to a specific player
4721 multi_io_send_reliable(pl, data, packet_size);
4725 void process_jump_into_mission_packet(ubyte *data, header *hinfo)
4727 int offset = HEADER_LENGTH;
4730 // if I am ingame joining, there should be extra data. For now, this data is the netgame state.
4731 // the game could be paused, so ingame joiner needs to deal with it.
4732 if ( Net_player->flags & NETINFO_FLAG_INGAME_JOIN ) {
4734 Netgame.game_state = state;
4739 // handle any special processing for being in a weird substate
4740 multi_handle_state_special();
4742 // if I'm an ingame joiner, go to the ship select screen, or if I'm an observer, jump right in!
4743 if(Net_player->flags & NETINFO_FLAG_INGAME_JOIN){
4744 if(Net_player->flags & NETINFO_FLAG_OBSERVER){
4745 multi_ingame_observer_finish();
4747 gameseq_post_event(GS_EVENT_INGAME_PRE_JOIN);
4748 Net_player->state = NETPLAYER_STATE_INGAME_SHIP_SELECT;
4749 send_netplayer_update_packet();
4752 // start the mission!!
4753 if(!(Game_mode & GM_IN_MISSION) && !(Game_mode & GM_STANDALONE_SERVER)){
4754 Netgame.game_state = NETGAME_STATE_IN_MISSION;
4755 gameseq_post_event(GS_EVENT_ENTER_GAME);
4756 Net_player->state = NETPLAYER_STATE_IN_MISSION;
4757 send_netplayer_update_packet();
4761 extern time_t Player_multi_died_check;
4762 Player_multi_died_check = -1;
4764 // recalc all object pairs now
4765 extern void obj_reset_all_collisions();
4766 obj_reset_all_collisions();
4768 // display some cool text
4769 multi_common_add_text(XSTR("Received mission start\n",720),1);
4772 ml_string(NOX("Client received mission start from server - entering mission"));
4777 const char *repair_text[] = {
4779 "REPAIR_INFO_BEGIN",
4781 "REPAIR_INFO_UPDATE",
4782 "REPAIR_INFO_QUEUE",
4783 "REPAIR_INFO_ABORT",
4784 "REPAIR_INFO_BROKEN",
4785 "REPAIR_INFO_WARP_ADD",
4786 "REPAIR_INFO_WARP_REMOVE",
4787 "REPAIR_INFO_ONWAY",
4788 "REPAIR_INFO_KILLED",
4789 "REPAIR_INFO_COMPLETE",
4794 // the following two routines deal with updating and sending information regarding players
4795 // rearming and repairing during the game. The process function calls the routines to deal with
4796 // setting flags and other interesting things.
4797 void send_repair_info_packet(object *repaired_objp, object *repair_objp, int code )
4799 int packet_size = 0;
4800 ushort repaired_signature, repair_signature;
4801 ubyte data[MAX_PACKET_SIZE];
4804 // use the network signature of the destination object if there is one, -1 otherwise.
4805 // client will piece it all together
4806 repaired_signature = repaired_objp->net_signature;
4808 // the repair ship may be NULL here since it might have been destroyed
4809 repair_signature = 0;
4811 repair_signature = repair_objp->net_signature;
4814 BUILD_HEADER(CLIENT_REPAIR_INFO);
4817 ADD_USHORT( repaired_signature );
4818 ADD_USHORT( repair_signature );
4820 multi_io_send_to_all_reliable(data, packet_size);
4822 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));
4825 void process_repair_info_packet(ubyte *data, header *hinfo)
4827 int offset = HEADER_LENGTH;
4828 ushort repaired_signature, repair_signature;
4829 object *repaired_objp, *repair_objp;
4833 GET_USHORT( repaired_signature );
4834 GET_USHORT( repair_signature );
4837 repaired_objp = multi_get_network_object( repaired_signature );
4838 repair_objp = multi_get_network_object( repair_signature );
4840 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));
4842 if ( Net_player->flags & NETINFO_FLAG_WARPING_OUT ){
4846 if ( repaired_objp == NULL ) {
4847 Int3(); // Sandeep says this is bad bad bad. No ship to repair.
4851 // the hope is to simply call the routine in the ai code to set/unset flags
4852 // based on the code value and everything else should happen..I hope....
4853 if ( (code != REPAIR_INFO_WARP_ADD) && (code != REPAIR_INFO_WARP_REMOVE ) ) {
4855 ai_do_objects_repairing_stuff( repaired_objp, repair_objp, (int)code );
4857 // set the dock flags when repair begins. Prevents problem in lagging docking
4858 // packet. Also set any other flags/modes which need to be set to prevent Asserts.
4860 if ( (code == REPAIR_INFO_BEGIN) && (repair_objp != NULL) ) {
4861 ai_do_objects_docked_stuff( repaired_objp, repair_objp );
4862 Ai_info[Ships[repair_objp->instance].ai_index].mode = AIM_DOCK;
4865 // if the repair is done (either by abort, or ending), mark the repair ship's goal
4867 if ( ((code == REPAIR_INFO_ABORT) || (code == REPAIR_INFO_END)) && repair_objp ){
4868 ai_mission_goal_complete( &Ai_info[Ships[repair_objp->instance].ai_index] );
4871 if ( code == REPAIR_INFO_WARP_ADD ){
4872 mission_warp_in_support_ship( repaired_objp );
4874 mission_remove_scheduled_repair( repaired_objp );
4879 // sends information updating clients on certain AI information that clients will
4880 // need to know about to keep HUD information up to date. objp is the object that we
4881 // are updating, and what is the type of stuff that we are updating.
4882 void send_ai_info_update_packet( object *objp, char what )
4885 ushort other_signature;
4886 ubyte data[MAX_PACKET_SIZE];
4888 ubyte dock_index, dockee_index;
4890 // SDL_assert( objp->type == OBJ_SHIP );
4891 if(objp->type != OBJ_SHIP){
4894 aip = &Ai_info[Ships[objp->instance].ai_index];
4897 if ( Ships[objp->instance].flags & (SF_DEPARTING | SF_DYING) )
4900 BUILD_HEADER( AI_INFO_UPDATE );
4901 ADD_USHORT( objp->net_signature );
4904 // depending on the "what" value, we will send different information
4908 case AI_UPDATE_DOCK:
4909 // for docking ships, add the signature of the ship that we are docking with.
4910 SDL_assert( aip->dock_objnum != -1 );
4911 other_signature = Objects[aip->dock_objnum].net_signature;
4912 dock_index = (ubyte)(aip->dock_index);
4913 dockee_index = (ubyte)(aip->dockee_index);
4914 ADD_USHORT( other_signature );
4915 ADD_DATA(dock_index);
4916 ADD_DATA(dockee_index);
4919 case AI_UPDATE_UNDOCK:
4920 // for undocking ships, check the dock_objnum since we might or might not have it
4921 // depending on whether or not a ship was destroyed while we were docked.
4922 other_signature = 0;
4923 if ( aip->dock_objnum != -1 )
4924 other_signature = Objects[aip->dock_objnum].net_signature;
4925 ADD_USHORT( other_signature );
4929 case AI_UPDATE_ORDERS: {
4932 // for orders, we only need to send a little bit of information here. Be sure that the
4933 // first order for this ship is active
4934 SDL_assert( (aip->active_goal != AI_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) );
4935 ADD_INT( aip->goals[0].ai_mode );
4936 ADD_INT( aip->goals[0].ai_submode );
4938 if ( aip->goals[0].ship_name != NULL )
4939 shipnum = ship_name_lookup( aip->goals[0].ship_name );
4941 // the ship_name member of the goals structure may or may not contain a real shipname. If we don't
4942 // have a valid shipnum, then don't sweat it since it may not really be a ship.
4943 if ( shipnum != -1 ) {
4944 SDL_assert( Ships[shipnum].objnum != -1 );
4945 other_signature = Objects[Ships[shipnum].objnum].net_signature;
4947 other_signature = 0;
4949 ADD_USHORT( other_signature );
4951 // for docking, add the dock and dockee index
4952 if ( aip->goals[0].ai_mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
4953 SDL_assert( (aip->goals[0].docker.index >= 0) && (aip->goals[0].docker.index < UCHAR_MAX) );
4954 SDL_assert( (aip->goals[0].dockee.index >= 0) && (aip->goals[0].dockee.index < UCHAR_MAX) );
4955 dock_index = (ubyte)aip->goals[0].docker.index;
4956 dockee_index = (ubyte)aip->goals[0].dockee.index;
4957 ADD_DATA( dock_index );
4958 ADD_DATA( dockee_index );
4967 multi_rate_add(1, "aiu", packet_size);
4968 multi_io_send_to_all_reliable(data, packet_size);
4971 // process an ai_info update packet. Docking/undocking, ai orders, etc. are taken care of here. This
4972 // information is mainly used to keep the clients HUD up to date with the appropriate information.
4973 void process_ai_info_update_packet( ubyte *data, header *hinfo)
4975 int offset = HEADER_LENGTH;
4977 ushort net_signature, other_net_signature;
4978 object *objp, *other_objp;
4981 ubyte dock_index = 0, dockee_index = 0;
4983 GET_USHORT( net_signature ); // signature of the object that we are dealing with.
4984 GET_DATA( code ); // code of what we are doing.
4985 objp = multi_get_network_object( net_signature );
4987 nprintf(("Network", "Couldn't find object for ai update\n"));
4991 case AI_UPDATE_DOCK:
4992 GET_USHORT( other_net_signature );
4993 GET_DATA( dock_index );
4994 GET_DATA( dockee_index );
4995 other_objp = multi_get_network_object( other_net_signature );
4996 if ( !other_objp ) {
4997 nprintf(("Network", "Couldn't find other object for ai update on dock\n"));
5000 // if we don't have an object to work with, break out of loop
5001 if ( !objp || !other_objp || (objp->type != OBJ_SHIP) || (other_objp->type != OBJ_SHIP)){
5005 SDL_assert( other_objp->type == OBJ_SHIP );
5006 Ai_info[Ships[objp->instance].ai_index].dock_index = dock_index;
5007 Ai_info[Ships[objp->instance].ai_index].dockee_index = dockee_index;
5009 Ai_info[Ships[other_objp->instance].ai_index].dock_index = dockee_index;
5010 Ai_info[Ships[other_objp->instance].ai_index].dockee_index = dock_index;
5012 ai_do_objects_docked_stuff( objp, other_objp );
5015 case AI_UPDATE_UNDOCK:
5016 GET_USHORT( other_net_signature );
5017 other_objp = multi_get_network_object( other_net_signature );
5019 // if we don't have an object to work with, break out of loop
5023 ai_do_objects_undocked_stuff( objp, other_objp );
5026 case AI_UPDATE_ORDERS:
5029 GET_USHORT( other_net_signature );
5030 if ( mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
5031 GET_DATA(dock_index);
5032 GET_DATA(dockee_index);
5035 // be sure that we have a ship object!!!
5036 if ( !objp || (objp->type != OBJ_SHIP) )
5039 // set up the information in the first goal element of the object in question
5040 aip = &Ai_info[Ships[objp->instance].ai_index];
5041 aip->active_goal = 0;
5042 aip->goals[0].ai_mode = mode;
5043 aip->goals[0].ai_submode = submode;
5045 // for docking, add the dock and dockee index
5046 if ( mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
5047 aip->dock_index = dock_index;
5048 aip->dockee_index = dockee_index;
5051 // get a shipname if we can.
5052 other_objp = multi_get_network_object( other_net_signature );
5053 if ( other_objp && (other_objp->type == OBJ_SHIP) ) {
5054 // get a pointer to the shipname in question. Use the ship_name value in the
5055 // ship. We are only using this for HUD display, so I think that using this
5056 // method will be fine.
5057 aip->goals[0].ship_name = Ships[other_objp->instance].ship_name;
5059 // special case for destroy subsystem -- get the ai_info pointer to our target ship
5060 // so that we can properly set up what subsystem this ship is attacking.
5061 if ( (mode == AI_GOAL_DESTROY_SUBSYSTEM ) && (submode >= 0) )
5062 aip->targeted_subsys = ship_get_indexed_subsys( &Ships[other_objp->instance], submode);
5064 // if docking -- set the dock index and dockee index of this other ship
5065 if ( mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
5066 Ai_info[Ships[other_objp->instance].ai_index].dock_index = dockee_index;
5067 Ai_info[Ships[other_objp->instance].ai_index].dockee_index = dock_index;
5074 Int3(); // this Int3() should be temporary
5075 nprintf(("Network", "Invalid code for ai update: %d\n", code));
5081 // tell the standalone to move into the MISSION_SYNC_STATE
5082 void send_mission_sync_packet(int mode,int start_campaign)
5084 ubyte data[MAX_PACKET_SIZE],is_campaign;
5085 int packet_size = 0;
5087 SDL_assert((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER));
5089 // build the header and add the sync mode (pre or post briefing)
5090 BUILD_HEADER(MISSION_SYNC_DATA);
5093 // if this is a campaign game
5094 if(mode == MULTI_SYNC_PRE_BRIEFING){
5095 if(Game_mode & GM_CAMPAIGN_MODE){
5096 // add a byte indicating campaign mode
5098 ADD_DATA(is_campaign);
5100 // add a byte indicating if we should be starting a campaign or continuing it
5101 is_campaign = (ubyte)start_campaign;
5102 ADD_DATA(is_campaign);
5104 // add the campaign filename
5105 ADD_STRING(Netgame.campaign_name);
5107 // otherwise if this is a single mission
5109 // add a byte indicating single mission mode
5111 ADD_DATA(is_campaign);
5113 // add the mission filename
5114 ADD_STRING(Game_current_mission_filename);
5117 multi_io_send_reliable(Net_player, data, packet_size);
5120 // move into the MISSION_SYNC state when this is received
5121 // this packet is sent only from a game host to a standalone
5122 void process_mission_sync_packet(ubyte *data, header *hinfo)
5125 ubyte campaign_flag;
5126 int offset = HEADER_LENGTH;
5128 SDL_assert(Game_mode & GM_STANDALONE_SERVER);
5130 // if this is a team vs team situation, lock the players send a final team update
5131 if(Netgame.type_flags & NG_TYPE_TEAM){
5132 multi_team_host_lock_all();
5133 multi_team_send_update();
5136 // get the sync mode (pre or post briefing)
5139 if(mode == MULTI_SYNC_PRE_BRIEFING){
5140 // get the flag indicating if this is a single mission or a campaign mode
5141 GET_DATA(campaign_flag);
5143 // get the flag indicating whether we should be starting a new campaign
5144 GET_DATA(campaign_flag);
5146 // get the campaign filename
5147 GET_STRING(Netgame.campaign_name);
5149 // either start a new campaign or continue on to the next mission in the current campaign
5151 multi_campaign_start(Netgame.campaign_name);
5153 multi_campaign_next_mission();
5156 // make sure we remove the campaign mode flag
5157 Game_mode &= ~(GM_CAMPAIGN_MODE);
5159 // get the single mission filename
5160 GET_STRING(Game_current_mission_filename);
5161 SDL_strlcpy(Netgame.mission_name, Game_current_mission_filename, SDL_arraysize(Netgame.mission_name));
5166 // set the correct mode and m ove into the state
5167 Multi_sync_mode = mode;
5168 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
5171 // tell a player to merge his mission stats into his alltime stats
5172 void send_store_stats_packet(int accept)
5175 int packet_size = 0;
5177 BUILD_HEADER(STORE_MISSION_STATS);
5179 // add whether we're accepting or tossing
5180 val = (ubyte)accept;
5183 // if I'm the server, send to everyone, else send to the standalone to be rebroadcasted
5184 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5185 multi_io_send_to_all_reliable(data, packet_size);
5187 multi_io_send_reliable(Net_player, data, packet_size);
5191 void process_store_stats_packet(ubyte *data, header *hinfo)
5193 int offset = HEADER_LENGTH;
5199 // if I'm the standalone, rebroadcast. Otherwise, if I'm a client, merge my mission stats with my alltime stats
5200 if(Game_mode & GM_STANDALONE_SERVER){
5201 // rebroadcast the packet to all others in the game
5202 nprintf(("Network","Standalone received store stats packet - rebroadcasting..\n"));
5203 multi_io_send_to_all_reliable(data, offset);
5206 // all players should mark the stats as being accepted in the debriefing
5207 multi_debrief_stats_accept();
5209 // all players should mark the stats as being "tossed" in the debriefing
5210 multi_debrief_stats_toss();
5215 void send_debris_update_packet(object *objp,int code)
5217 ubyte data[MAX_PACKET_SIZE];
5219 int packet_size = 0;
5221 BUILD_HEADER(DEBRIS_UPDATE);
5222 ADD_USHORT(objp->net_signature);
5226 // add any extra relevant data
5228 case DEBRIS_UPDATE_UPDATE:
5229 // ADD_DATA(objp->pos); // add position
5230 add_vector_data(data, &packet_size, objp->pos);
5231 ADD_ORIENT(objp->orient); // add orientation
5232 // ADD_DATA(objp->phys_info.vel); // add velocity
5233 add_vector_data(data, &packet_size, objp->phys_info.vel);
5234 // ADD_DATA(objp->phys_info.rotvel); // add rotational velocity
5235 add_vector_data(data, &packet_size, objp->phys_info.rotvel);
5238 multi_io_send_to_all(data, packet_size);
5241 void process_debris_update_packet(ubyte *data, header *hinfo)
5245 object bogus_object;
5247 int offset = HEADER_LENGTH;
5249 GET_USHORT(net_sig);
5253 objp = multi_get_network_object(net_sig);
5255 objp = &bogus_object;
5259 // update the object
5260 case DEBRIS_UPDATE_UPDATE:
5261 //GET_DATA(objp->pos);
5262 get_vector_data( data, &offset, objp->pos );
5264 GET_ORIENT(objp->orient);
5265 GET_DATA(objp->phys_info.vel);
5266 GET_DATA(objp->phys_info.rotvel);
5268 // simply remove it (no explosion)
5269 case DEBRIS_UPDATE_REMOVE:
5270 if(objp != &bogus_object){
5271 SDL_assert(objp->type == OBJ_DEBRIS);
5272 obj_delete(OBJ_INDEX(objp));
5276 case DEBRIS_UPDATE_NUKE:
5277 if(objp != &bogus_object)
5278 debris_hit(objp,NULL,&objp->pos,1000000.0f);
5286 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)
5288 ubyte data[MAX_PACKET_SIZE];
5290 int packet_size = 0;
5292 BUILD_HEADER(WSS_REQUEST_PACKET);
5294 // add the request information
5295 ADD_SHORT(player_id);
5297 ADD_INT(from_index);
5300 ADD_INT(wl_ship_slot); // only used in weapons loadout
5301 ADD_INT(ship_class);
5304 // a standard request
5306 multi_io_send_reliable(Net_player, data, packet_size);
5308 // being routed through the standalone to the host of the game
5310 SDL_assert(Game_mode & GM_STANDALONE_SERVER);
5311 multi_io_send_reliable(p, data, packet_size);
5315 void process_wss_request_packet(ubyte *data, header *hinfo)
5317 int offset = HEADER_LENGTH;
5318 int from_slot,from_index;
5319 int to_slot,to_index;
5321 int wl_ship_slot,ship_class;
5325 // determine who this request is from
5326 GET_SHORT(player_id);
5327 player_num = find_player_id(player_id);
5329 // read in the request data
5331 GET_INT(from_index);
5334 GET_INT(wl_ship_slot); // only used in weapons loadout
5335 GET_INT(ship_class); // only used in multi team select
5339 SDL_assert(player_num != -1);
5340 if(player_num == -1){
5344 // if we're the standalone, we have to route this packet to the host of the game
5345 if(Game_mode & GM_STANDALONE_SERVER){
5346 send_wss_request_packet(player_id, from_slot, from_index, to_slot, to_index, wl_ship_slot, ship_class, mode, Netgame.host);
5348 // otherwise we're the host and should process the request
5351 case WSS_WEAPON_SELECT :
5352 wl_drop(from_slot,from_index,to_slot,to_index,wl_ship_slot,player_num);
5354 case WSS_SHIP_SELECT :
5355 multi_ts_drop(from_slot,from_index,to_slot,to_index,ship_class,player_num);
5363 void send_wss_update_packet(int team_num,ubyte *wss_data,int size)
5365 ubyte data[MAX_PACKET_SIZE],team;
5366 int packet_size = 0;
5368 SDL_assert(size <= (MAX_PACKET_SIZE - 10));
5370 BUILD_HEADER(WSS_UPDATE_PACKET);
5372 // add the team/pool # this is for
5373 team = (ubyte)team_num;
5376 // add the data block size
5379 // add the data itself
5380 memcpy(data + packet_size,wss_data,size);
5381 packet_size += size;
5383 // if we're also the master of the game (not on a standalone)
5384 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5385 multi_io_send_to_all_reliable(data, packet_size);
5387 // if we're only the host on the standalone, then send the packet to the standalone to be routed
5389 multi_io_send_reliable(Net_player, data, packet_size);
5393 void process_wss_update_packet(ubyte *data, header *hinfo)
5396 int size,player_index,idx;
5397 int offset = HEADER_LENGTH;
5399 // get the team/pool #
5402 // get the data size
5405 // if we're the standalone, then we should be routing this data to all the other clients
5406 if(Game_mode & GM_STANDALONE_SERVER){
5411 // determine where this came from
5412 player_index = find_player_id(hinfo->id);
5413 SDL_assert(player_index != -1);
5414 if(player_index < 0){
5418 // route the packet (don't resend it to the host)
5419 for(idx=0;idx<MAX_PLAYERS;idx++){
5420 if(MULTI_CONNECTED(Net_players[idx]) && (&Net_players[idx] != Net_player) && (&Net_players[idx] != &Net_players[player_index]) ){
5421 multi_io_send_reliable(&Net_players[idx], data, offset);
5425 // set the proper pool pointers
5426 ss_set_team_pointers((int)team);
5428 // read in the block of data, and apply it to the weapons/ship pools
5429 offset += restore_wss_data(data + offset);
5432 // set the pool pointers back to my own team
5433 ss_set_team_pointers(Net_player->p_info.team);
5435 // sync the interface if this was for my own team
5436 if((int)team == Net_player->p_info.team){
5437 multi_ts_sync_interface();
5444 // function to send firing information from the client to the server once they reach
5445 // the final sync screen.
5446 void send_firing_info_packet()
5448 ubyte data[MAX_PACKET_SIZE];
5450 ubyte plinked, sdual;
5452 SDL_assert( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
5454 BUILD_HEADER(FIRING_INFO);
5455 plinked = (ubyte)((Player_ship->flags & SF_PRIMARY_LINKED)?1:0);
5456 sdual = (ubyte)((Player_ship->flags & SF_SECONDARY_DUAL_FIRE)?1:0);
5457 ADD_DATA( plinked );
5460 multi_io_send_reliable(Net_player, data, packet_size);
5463 void process_firing_info_packet( ubyte *data, header *hinfo )
5465 int offset, player_num;
5466 ubyte plinked, sdual;
5469 // only the master of the game should be dealing with these packets
5470 SDL_assert( Net_player->flags & NETINFO_FLAG_AM_MASTER );
5472 offset = HEADER_LENGTH;
5473 GET_DATA( plinked );
5477 player_num = find_player_id(hinfo->id);
5479 nprintf(("Network","Received firing info packet from unknown player, ignoring\n"));
5483 // get the ship pointer for this player and set the flags accordingly.
5484 shipp = &(Ships[Objects[Net_players[player_num].player->objnum].instance]);
5486 shipp->flags |= SF_PRIMARY_LINKED;
5488 shipp->flags &= ~SF_PRIMARY_LINKED;
5491 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
5493 shipp->flags &= ~SF_SECONDARY_DUAL_FIRE;
5496 // packet to deal with changing status of mission goals. used to be sent every so often from server
5497 // to clients, but with addition of reliable sockets, send when complete, invalid, etc.
5498 // goal_num is the index into mission_goals. new_status means failed, success, etc. -1 if not used.
5499 // valid means goal is changing to invalid(0) or valid(1). only applies if new_status == -1
5500 void send_mission_goal_info_packet( int goal_num, int new_status, int valid )
5502 ubyte data[MAX_PACKET_SIZE];
5505 BUILD_HEADER(MISSION_GOAL_INFO);
5508 ADD_INT(new_status);
5511 multi_io_send_to_all_reliable(data, packet_size);
5514 void process_mission_goal_info_packet( ubyte *data, header *hinfo )
5516 int offset, goal_num, new_status, valid;
5518 offset = HEADER_LENGTH;
5520 GET_INT(new_status);
5524 // if new_status != -1, then this is a change in goal status (i.e. goal failed, or is successful)
5525 if ( new_status != -1 ){
5526 mission_goal_status_change( goal_num, new_status );
5528 mission_goal_validation_change( goal_num, valid );
5532 void send_player_settings_packet(net_player *p)
5534 ubyte data[MAX_PACKET_SIZE];
5537 int packet_size = 0;
5540 BUILD_HEADER(PLAYER_SETTINGS);
5542 // add all the data for all the players
5544 for(idx=0;idx<MAX_PLAYERS;idx++){
5545 if(MULTI_CONNECTED(Net_players[idx])){
5547 ADD_SHORT(Net_players[idx].player_id);
5549 // break the p_info structure by member, so we don't overwrite any absolute pointers
5550 // ADD_DATA(Net_players[idx].p_info);
5551 ADD_INT(Net_players[idx].p_info.team);
5552 ADD_INT(Net_players[idx].p_info.ship_index);
5553 ADD_INT(Net_players[idx].p_info.ship_class);
5556 // add the stop byte
5560 // either broadcast the data or send to a specific player
5562 multi_io_send_to_all_reliable(data, packet_size);
5564 multi_io_send_reliable(p, data, packet_size);
5568 void process_player_settings_packet(ubyte *data, header *hinfo)
5570 int offset,player_num;
5571 net_player_info bogus,*ptr;
5575 offset = HEADER_LENGTH;
5577 // read in the data for all the players
5579 while(stop != 0xff){
5580 // lookup the player
5581 GET_SHORT(player_id);
5582 player_num = find_player_id(player_id);
5584 // make sure this is a valid player
5585 if(player_num == -1){
5588 ptr = &Net_players[player_num].p_info;
5592 GET_INT(ptr->ship_index);
5593 GET_INT(ptr->ship_class);
5600 // update the server with my new state
5601 // MWA -- 3/31/98 -- check for in mission instead of state.
5602 //if ( Netgame.game_state == NETGAME_STATE_MISSION_SYNC) {
5603 if( !(Game_mode & GM_IN_MISSION) ) {
5604 Net_player->state = NETPLAYER_STATE_SETTINGS_ACK;
5605 send_netplayer_update_packet();
5609 // display some cool text
5610 multi_common_add_text(XSTR("Received player settings packet\n",721),1);
5613 void send_deny_packet(net_addr *addr, int code)
5616 int packet_size = 0;
5618 // build the header and add the rejection code
5624 psnet_send(addr, data, packet_size);
5627 void process_deny_packet(ubyte *data, header *hinfo)
5631 // get the denial code
5632 offset = HEADER_LENGTH;
5636 // if there is already a dialog active, do nothing - who cares at this point.
5641 // display the appropriate dialog
5643 case JOIN_DENY_JR_STATE :
5644 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));
5646 case JOIN_DENY_JR_TRACKER_INVAL :
5647 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));
5649 case JOIN_DENY_JR_PASSWD :
5650 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because this is a password protected game",724));
5652 case JOIN_DENY_JR_CLOSED :
5653 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));
5655 case JOIN_DENY_JR_TEMP_CLOSED :
5656 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));
5658 case JOIN_DENY_JR_RANK_HIGH :
5659 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));
5661 case JOIN_DENY_JR_RANK_LOW :
5662 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));
5664 case JOIN_DENY_JR_DUP :
5665 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because there is an identical player already in the game",729));
5667 case JOIN_DENY_JR_FULL :
5668 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because the game is full",730));
5670 case JOIN_DENY_JR_BANNED :
5671 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because you are banned from this server",731));
5673 case JOIN_DENY_JR_NOOBS :
5674 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because this game does not allow observers",732));
5676 case JOIN_DENY_JR_INGAME_JOIN :
5677 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));
5679 case JOIN_DENY_JR_BAD_VERSION :
5680 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));
5682 case JOIN_DENY_JR_TYPE :
5683 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You cannot join a game in progress unless it is a dogfight mission.",1433));
5687 // call this so that the join request timestamp automatically expires when we hear back from the server
5688 multi_join_reset_join_stamp();
5691 // this packet will consist of
5692 // 1.) netplayer ship classes (85 bytes max)
5693 // 2.) ship weapon state data (241 bytes max)
5694 // 3.) player settings et. al. (133 bytes max)
5695 // TOTAL 459 NOTE : keep this in mind when/if adding new data to this packet
5696 void send_post_sync_data_packet(net_player *p, int std_request)
5698 ubyte data[MAX_PACKET_SIZE], val;
5703 ushort sval, ship_ets;
5704 int idx, player_index;
5705 int packet_size = 0;
5709 BUILD_HEADER(POST_SYNC_DATA);
5711 // some header information for standalone packet routing purposes
5712 val = (ubyte)std_request;
5715 // the standalone has two situations
5716 // 1.) sending a request to the host to distribute this block of data
5717 // 2.) having recevied this block of data from the host, it redistributes it
5718 if((Game_mode & GM_STANDALONE_SERVER) && std_request && (Netgame.host != NULL)){
5719 // case 1, send the request
5720 multi_io_send_reliable(Netgame.host, data, packet_size);
5723 // case 2 for the standalone is below (as normal)
5725 // otherwise build the data now
5727 // add all deleted ships
5728 val = (ubyte)Multi_ts_num_deleted;
5730 for(idx=0;idx<Multi_ts_num_deleted;idx++){
5731 sval = (ushort)Objects[Multi_ts_deleted_objnums[idx]].net_signature;
5737 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5738 shipp = &Ships[Objects[so->objnum].instance];
5740 // don't process non player wing ships
5741 if ( !(shipp->flags & SF_FROM_PLAYER_WING ) )
5747 // # of ships - used multiple times in the packet
5748 val = (ubyte)ship_count;
5751 // add ship class information (85 bytes max)
5752 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5753 shipp = &Ships[Objects[so->objnum].instance];
5755 // don't process non player wing ships
5756 if ( !(shipp->flags & SF_FROM_PLAYER_WING ) )
5759 // add the net signature of the object for look up
5760 ADD_USHORT( Objects[so->objnum].net_signature );
5762 // add the ship info index
5763 val = (ubyte)(shipp->ship_info_index);
5766 // add the ships's team select index
5767 val = (ubyte)shipp->ts_index;
5771 // add weapon state information for all starting ships (241 bytes max)
5772 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5773 shipp = &Ships[Objects[so->objnum].instance];
5775 // don't process non player wing ships
5776 if ( !(shipp->flags & SF_FROM_PLAYER_WING ) )
5779 // if this is a ship owned by a player, we should mark down his weapons bank/link settings now if we're the server
5781 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5782 player_index = multi_find_player_by_net_signature(Objects[so->objnum].net_signature);
5783 if(player_index == -1){
5786 pl = &Net_players[player_index];
5790 // add the net signature and other weapon information
5791 ADD_USHORT( Objects[so->objnum].net_signature );
5793 // add number of primary and secondary banks
5794 bval = (char)(shipp->weapons.num_primary_banks);
5796 bval = (char)(shipp->weapons.num_secondary_banks);
5799 // add weapon bank status
5800 bval = (char)(shipp->weapons.current_primary_bank);
5802 pl->s_info.cur_primary_bank = bval;
5804 // SDL_assert(bval != -1);
5807 bval = (char)(shipp->weapons.current_secondary_bank);
5809 pl->s_info.cur_secondary_bank = bval;
5811 // SDL_assert(bval != -1);
5814 // primary weapon info
5815 bval = (char)(shipp->weapons.primary_bank_weapons[0]);
5817 bval = (char)(shipp->weapons.primary_bank_weapons[1]);
5820 // secondary weapon info
5821 bval = (char)(shipp->weapons.secondary_bank_weapons[0]);
5823 val_short = (short)(shipp->weapons.secondary_bank_ammo[0]);
5824 ADD_SHORT(val_short);
5825 bval = (char)(shipp->weapons.secondary_bank_weapons[1]);
5827 val_short = (short)(shipp->weapons.secondary_bank_ammo[1]);
5828 ADD_SHORT(val_short);
5829 bval = (char)(shipp->weapons.secondary_bank_weapons[2]);
5831 val_short = (short)(shipp->weapons.secondary_bank_ammo[2]);
5832 ADD_SHORT(val_short);
5834 // send primary and secondary weapon link status
5836 if(shipp->flags & SF_PRIMARY_LINKED){
5838 pl->s_info.cur_link_status |= (1<<0);
5842 if(shipp->flags & SF_SECONDARY_DUAL_FIRE){
5844 pl->s_info.cur_link_status |= (1<<1);
5848 // if this is a player ship add (1<<2)
5849 if(Objects[shipp->objnum].flags & OF_PLAYER_SHIP){
5854 // add a ship ets value
5857 ship_ets |= ((ushort)shipp->shield_recharge_index << 8);
5859 ship_ets |= ((ushort)shipp->weapon_recharge_index << 4);
5861 ship_ets |= ((ushort)shipp->engine_recharge_index);
5862 ADD_USHORT(ship_ets);
5866 // 2 cases, if I'm the host on a standalone, I should be sending this to the standalone only
5867 // or if I'm the server as well as the host, I should be sending this to all players
5868 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
5869 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5872 multi_io_send_to_all_reliable(data, packet_size);
5874 // send to a specific player
5876 multi_io_send_reliable(p, data, packet_size);
5879 multi_io_send_reliable(Net_player, data, packet_size);
5886 multi_io_send_to_all_reliable(data, packet_size);
5888 // send to a specific player
5890 multi_io_send_reliable(p, data, packet_size);
5895 void process_post_sync_data_packet(ubyte *data, header *hinfo)
5897 ubyte val, sinfo_index, ts_index;
5899 ushort net_sig, ship_ets, sval;
5903 int offset = HEADER_LENGTH;
5907 // packet routing information
5910 // 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
5911 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) && val){
5914 // at this point we want to delete all necessary ships, change all necessary ship classes, and set all weapons up
5915 multi_ts_create_wings();
5917 // send to the standalone through my socket
5918 send_post_sync_data_packet(Net_player);
5924 // add all deleted ships
5926 Multi_ts_num_deleted = (int)val;
5927 for(idx=0;idx<Multi_ts_num_deleted;idx++){
5928 // get the ship's objnum
5931 objp = multi_get_network_object(sval);
5933 // delete the ship appropriately
5934 // mark the object as having been deleted
5935 Multi_ts_deleted_objnums[idx] = OBJ_INDEX(objp);
5938 ship_add_exited_ship(&Ships[objp->instance], SEF_PLAYER_DELETED);
5939 obj_delete(Multi_ts_deleted_objnums[idx]);
5940 ship_wing_cleanup(objp->instance,&Wings[Ships[objp->instance].wingnum]);
5942 Multi_ts_num_deleted--;
5943 nprintf(("Network","Couldn't find object by net signature for ship delete in post sync data packet\n"));
5951 // process ship class information
5952 for(idx=0; idx<ship_count; idx++){
5953 // get the object's net signature
5954 GET_USHORT(net_sig);
5955 GET_DATA(sinfo_index);
5958 // attempt to get the object
5959 objp = multi_get_network_object(net_sig);
5961 // make sure we found a ship
5962 SDL_assert((objp != NULL) && (objp->type == OBJ_SHIP));
5964 // set the ship to be the right class
5965 change_ship_type(objp->instance,(int)sinfo_index);
5967 // set the ship's team select index
5968 Ships[objp->instance].ts_index = (int)ts_index;
5971 // process ship weapon state info
5972 for(idx=0; idx<ship_count; idx++){
5973 // get the object's net signature
5974 GET_USHORT(net_sig);
5976 // attempt to get the object
5977 objp = multi_get_network_object(net_sig);
5979 // make sure we found a ship
5980 SDL_assert((objp != NULL) && (objp->type == OBJ_SHIP));
5982 // get a pointer to the ship
5983 shipp = &Ships[objp->instance];
5985 // get number of primary and secondary banks;
5987 SDL_assert( b != -1 );
5988 shipp->weapons.num_primary_banks = (int)b;
5991 SDL_assert( b != -1 );
5992 shipp->weapons.num_secondary_banks = (int)b;
5994 // get bank selection info
5999 shipp->weapons.current_primary_bank = (int)b;
6005 shipp->weapons.current_secondary_bank = (int)b;
6007 // primary weapon info
6009 shipp->weapons.primary_bank_weapons[0] = (int)b;
6012 shipp->weapons.primary_bank_weapons[1] = (int)b;
6014 // secondary weapon info
6016 shipp->weapons.secondary_bank_weapons[0] = (int)b;
6017 GET_SHORT(val_short);
6018 shipp->weapons.secondary_bank_ammo[0] = (int)val_short;
6021 shipp->weapons.secondary_bank_weapons[1] = (int)b;
6022 GET_SHORT(val_short);
6023 shipp->weapons.secondary_bank_ammo[1] = (int)val_short;
6026 shipp->weapons.secondary_bank_weapons[2] = (int)b;
6027 GET_SHORT(val_short);
6028 shipp->weapons.secondary_bank_ammo[2] = (int)val_short;
6035 shipp->flags |= SF_PRIMARY_LINKED;
6038 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
6040 Objects[shipp->objnum].flags &= ~(OF_PLAYER_SHIP);
6041 Objects[shipp->objnum].flags &= ~(OF_COULD_BE_PLAYER);
6043 Objects[shipp->objnum].flags |= OF_PLAYER_SHIP;
6045 obj_set_flags( &Objects[shipp->objnum], Objects[shipp->objnum].flags | OF_COULD_BE_PLAYER );
6049 GET_USHORT(ship_ets);
6051 shipp->shield_recharge_index = ((ship_ets & 0x0f00) >> 8);
6053 shipp->weapon_recharge_index = ((ship_ets & 0x00f0) >> 4);
6055 shipp->engine_recharge_index = (ship_ets & 0x000f);
6060 Net_player->state = NETPLAYER_STATE_POST_DATA_ACK;
6061 send_netplayer_update_packet();
6063 // the standalone server will receive this packet from the host of the game, to be applied locally and
6064 // also to be rebroadcast.
6065 if(Game_mode & GM_STANDALONE_SERVER){
6066 // update player ets settings
6067 for(idx=0;idx<MAX_PLAYERS;idx++){
6068 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player->objnum != -1)){
6069 multi_server_update_player_weapons(&Net_players[idx],&Ships[Objects[Net_players[idx].player->objnum].instance]);
6073 send_post_sync_data_packet(NULL,0);
6077 void send_wss_slots_data_packet(int team_num,int final,net_player *p,int std_request)
6079 ubyte data[MAX_PACKET_SIZE],val;
6082 int packet_size = 0;
6085 BUILD_HEADER(WSS_SLOTS_DATA);
6087 // some header information for standalone packet routing purposes
6088 val = (ubyte)std_request;
6092 val = (ubyte)team_num;
6095 // add whether this is the final packet or not
6099 // the standalone has two situations
6100 // 1.) sending a request to the host to distribute this block of data
6101 // 2.) having recevied this block of data from the host, it redistributes it
6102 if((Game_mode & GM_STANDALONE_SERVER) && std_request){
6103 // case 1, send the request
6104 multi_io_send_reliable(Netgame.host, data, packet_size);
6107 // case 2 for the standalone is below (as normal)
6109 // add all the slots
6110 for(idx=0;idx<MULTI_TS_NUM_SHIP_SLOTS;idx++){
6111 // add the ship class
6112 val = (ubyte)Wss_slots_teams[team_num][idx].ship_class;
6116 for(i = 0;i<MAX_WL_WEAPONS;i++){
6117 val = (ubyte)Wss_slots_teams[team_num][idx].wep[i];
6121 // add the weapon counts
6122 for(i = 0;i<MAX_WL_WEAPONS;i++){
6123 val_short = (short)Wss_slots_teams[team_num][idx].wep_count[i];
6124 ADD_SHORT(val_short);
6128 // 2 cases, if I'm the host on a standalone, I should be sending this to the standalone only
6129 // or if I'm the server as well as the host, I should be sending this to all players
6130 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
6131 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
6134 multi_io_send_to_all_reliable(data, packet_size);
6136 // send to a specific player
6138 multi_io_send_reliable(p, data, packet_size);
6141 multi_io_send_reliable(Net_player, data, packet_size);
6148 multi_io_send_to_all_reliable(data, packet_size);
6150 // send to a specific player
6152 multi_io_send_reliable(p, data, packet_size);
6157 void process_wss_slots_data_packet(ubyte *data, header *hinfo)
6159 ubyte val,team_num,final;
6161 int offset = HEADER_LENGTH;
6164 // packet routing information
6170 // get whether this is the final packet or not
6173 // 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
6174 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) && val){
6177 // send to the standalone through my socket
6178 send_wss_slots_data_packet((int)team_num,(int)final,Net_player);
6182 // read in all the slot data
6183 for(idx=0;idx<MULTI_TS_NUM_SHIP_SLOTS;idx++){
6184 memset(&Wss_slots_teams[team_num][idx],0,sizeof(wss_unit));
6186 // get the ship class
6188 Wss_slots_teams[team_num][idx].ship_class = (int)val;
6191 for(i = 0;i<MAX_WL_WEAPONS;i++){
6193 Wss_slots_teams[team_num][idx].wep[i] = (int)val;
6195 // check for signed/unsigned problems
6196 if(Wss_slots_teams[team_num][idx].wep[i] == 255){
6197 Wss_slots_teams[team_num][idx].wep[i] = -1;
6201 // get the weapon counts
6202 for(i = 0;i<MAX_WL_WEAPONS;i++){
6203 GET_SHORT(val_short);
6204 Wss_slots_teams[team_num][idx].wep_count[i] = (int)val_short;
6209 // update my netplayer state if this is the final packet
6211 Net_player->state = NETPLAYER_STATE_WSS_ACK;
6212 send_netplayer_update_packet();
6215 // the standalone server will receive this packet from the host of the game, to be applied locally and
6216 // also to be rebroadcast.
6217 if(Game_mode & GM_STANDALONE_SERVER){
6218 send_wss_slots_data_packet((int)team_num,(int)final,NULL,0);
6220 // add some mission sync text
6221 multi_common_add_text(XSTR("Weapon slots packet\n",735),1);
6225 #define OBJ_VISIBILITY_DOT 0.6f
6227 // send and receive packets for shield explosion information
6228 void send_shield_explosion_packet( int objnum, int tri_num, vector hit_pos )
6231 ubyte data[MAX_PACKET_SIZE], utri_num;
6234 // SDL_assert(!(Netgame.debug_flags & NETD_FLAG_CLIENT_NODAMAGE));
6236 SDL_assert( tri_num < UCHAR_MAX );
6237 utri_num = (ubyte)tri_num;
6239 // for each player, determine if this object is behind the player -- if so, don't
6241 for ( i = 0; i < MAX_PLAYERS; i++ ) {
6242 if ( MULTI_CONNECTED(Net_players[i]) && (&Net_players[i] != Net_player) ) {
6244 vector eye_to_obj_vec, diff, eye_pos;
6247 eye_pos = Net_players[i].s_info.eye_pos;
6248 eye_orient = Net_players[i].s_info.eye_orient;
6250 // check for same vectors
6251 vm_vec_sub(&diff, &Objects[objnum].pos, &eye_pos);
6252 if ( vm_vec_mag_quick(&diff) < 0.01 ){
6256 vm_vec_normalized_dir(&eye_to_obj_vec, &Objects[objnum].pos, &eye_pos);
6257 dot = vm_vec_dot(&eye_orient.v.fvec, &eye_to_obj_vec);
6259 if ( dot < OBJ_VISIBILITY_DOT ){
6263 BUILD_HEADER(SHIELD_EXPLOSION);
6265 ADD_USHORT( Objects[objnum].net_signature );
6268 multi_io_send(&Net_players[i], data, packet_size);
6273 void add_shield_point_multi(int objnum, int tri_num, vector *hit_pos);
6275 void process_shield_explosion_packet( ubyte *data, header *hinfo)
6282 // get the shield hit data
6283 offset = HEADER_LENGTH;
6284 GET_USHORT(signature);
6286 //GET_DATA(hit_pos);
6289 // find the object with this signature. If found, then do a shield explosion
6290 objp = multi_get_network_object( signature );
6293 shield_info *shieldp;
6298 // given the tri num, find the local position which is the average of the
6299 // three vertices of the triangle affected. Use this average point as the hit
6301 // SDL_assert( objp->type == OBJ_SHIP );
6302 if(objp->type != OBJ_SHIP){
6306 pm = model_get(Ships[objp->instance].modelnum);
6307 shieldp = &pm->shield;
6308 SDL_assert( utri_num < shieldp->ntris );
6309 stri = shieldp->tris[utri_num];
6310 vm_vec_zero(&hit_pos);
6311 for ( i = 0; i < 3; i++ ) {
6312 vm_vec_add2( &hit_pos, &(shieldp->verts[stri.verts[i]].pos) );
6314 vm_vec_scale( &hit_pos, 1.0f/3.0f );
6315 add_shield_point_multi( OBJ_INDEX(objp), utri_num, &hit_pos );
6319 void send_player_stats_block_packet(net_player *pl, int stats_code, net_player *target)
6322 ubyte data[MAX_PACKET_SIZE], val;
6324 int packet_size = 0;
6329 sc = &pl->player->stats;
6332 BUILD_HEADER(PLAYER_STATS);
6334 // add the player id
6335 ADD_SHORT(pl->player_id);
6337 // add the byte indicating whether these stats are all-time or not
6338 val = (ubyte)stats_code;
6341 // kill information - alltime
6345 for(idx=0;idx<MAX_SHIP_TYPES;idx++){
6346 u_tmp = sc->kills[idx];
6349 // medal information
6350 for(idx=0;idx<NUM_MEDALS;idx++){
6351 i_tmp = sc->medals[idx];
6357 ADD_INT(sc->assists);
6358 ADD_INT(sc->kill_count);
6359 ADD_INT(sc->kill_count_ok);
6360 ADD_UINT(sc->p_shots_fired);
6361 ADD_UINT(sc->s_shots_fired);
6362 ADD_UINT(sc->p_shots_hit);
6363 ADD_UINT(sc->s_shots_hit);
6364 ADD_UINT(sc->p_bonehead_hits);
6365 ADD_UINT(sc->s_bonehead_hits);
6366 ADD_INT(sc->bonehead_kills);
6368 ADD_UINT(sc->missions_flown);
6369 ADD_UINT(sc->flight_time);
6370 ADD_INT(sc->last_flown);
6371 ADD_INT(sc->last_backup);
6376 for(idx=0;idx<MAX_SHIP_TYPES;idx++){
6377 u_tmp = sc->m_okKills[idx];
6381 ADD_INT(sc->m_score);
6382 ADD_INT(sc->m_assists);
6383 ADD_INT(sc->m_kill_count);
6384 ADD_INT(sc->m_kill_count_ok);
6385 ADD_UINT(sc->mp_shots_fired);
6386 ADD_UINT(sc->ms_shots_fired);
6387 ADD_UINT(sc->mp_shots_hit);
6388 ADD_UINT(sc->ms_shots_hit);
6389 ADD_UINT(sc->mp_bonehead_hits);
6390 ADD_UINT(sc->ms_bonehead_hits);
6391 ADD_INT(sc->m_bonehead_kills);
6392 ADD_INT(sc->m_player_deaths);
6393 ADD_INT(sc->m_medal_earned);
6396 case STATS_MISSION_KILLS:
6397 ADD_INT(sc->m_kill_count);
6398 ADD_INT(sc->m_kill_count_ok);
6399 ADD_INT(sc->m_assists);
6402 case STATS_DOGFIGHT_KILLS:
6403 for(idx=0; idx<MAX_PLAYERS; idx++){
6405 u_tmp = sc->m_dogfight_kills[idx];
6411 ADD_INT(sc->m_kill_count);
6412 ADD_INT(sc->m_kill_count_ok);
6413 ADD_INT(sc->m_assists);
6417 SDL_assert(packet_size < MAX_PACKET_SIZE);
6419 // if we're a client, always send the data to the server
6420 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
6421 multi_io_send_reliable(Net_player, data, packet_size);
6423 // otherwise do server specific stuff
6425 // send to a specific target
6427 multi_io_send_reliable(target, data, packet_size);
6429 // otherwise, send to everyone
6431 multi_io_send_to_all_reliable(data, packet_size);
6436 void process_player_stats_block_packet(ubyte *data, header *hinfo)
6440 scoring_struct *sc,bogus;
6442 int offset = HEADER_LENGTH;
6446 // nprintf(("Network","----------++++++++++********RECEIVED STATS***********+++++++++----------\n"));
6448 // get the player who these stats are for
6449 GET_SHORT(player_id);
6450 player_num = find_player_id(player_id);
6451 if (player_num == -1) {
6452 nprintf(("Network", "Couldn't find player for stats update!\n"));
6453 ml_string("Couldn't find player for stats update!");
6458 sc = &Net_players[player_num].player->stats;
6461 // get the stats code
6465 ml_string("Received STATS_ALLTIME");
6468 for (idx=0; idx<MAX_SHIP_TYPES; idx++) {
6470 sc->kills[idx] = u_tmp;
6473 // read in the stats
6474 for (idx=0; idx<NUM_MEDALS; idx++) {
6476 sc->medals[idx] = i_tmp;
6481 GET_INT(sc->assists);
6482 GET_INT(sc->kill_count);
6483 GET_INT(sc->kill_count_ok);
6484 GET_UINT(sc->p_shots_fired);
6485 GET_UINT(sc->s_shots_fired);
6486 GET_UINT(sc->p_shots_hit);
6487 GET_UINT(sc->s_shots_hit);
6488 GET_UINT(sc->p_bonehead_hits);
6489 GET_UINT(sc->s_bonehead_hits);
6490 GET_INT(sc->bonehead_kills);
6492 GET_UINT(sc->missions_flown);
6493 GET_UINT(sc->flight_time);
6494 GET_INT(sc->last_flown);
6495 GET_INT(sc->last_backup);
6499 ml_string("Received STATS_MISSION");
6501 // kills - mission OK
6502 for (idx=0; idx<MAX_SHIP_TYPES; idx++) {
6504 sc->m_okKills[idx] = u_tmp;
6507 GET_INT(sc->m_score);
6508 GET_INT(sc->m_assists);
6509 GET_INT(sc->m_kill_count);
6510 GET_INT(sc->m_kill_count_ok);
6511 GET_UINT(sc->mp_shots_fired);
6512 GET_UINT(sc->ms_shots_fired);
6513 GET_UINT(sc->mp_shots_hit);
6514 GET_UINT(sc->ms_shots_hit);
6515 GET_UINT(sc->mp_bonehead_hits);
6516 GET_UINT(sc->ms_bonehead_hits);
6517 GET_INT(sc->m_bonehead_kills);
6518 GET_INT(sc->m_player_deaths);
6519 GET_INT(sc->m_medal_earned);
6522 case STATS_MISSION_KILLS:
6523 ml_string("Received STATS_MISSION_KILLS");
6525 GET_INT(sc->m_kill_count);
6526 GET_INT(sc->m_kill_count_ok);
6527 GET_INT(sc->m_assists);
6530 case STATS_DOGFIGHT_KILLS:
6531 ml_string("Received STATS_DOGFIGHT_KILLS");
6532 if(player_num >= 0){
6533 ml_printf("Dogfight stats for %s", Net_players[player_num].player->callsign);
6535 for(idx=0; idx<MAX_PLAYERS; idx++){
6538 sc->m_dogfight_kills[idx] = u_tmp;
6539 if(player_num >= 0){
6540 ml_printf("%d", Net_players[player_num].player->stats.m_dogfight_kills[idx]);
6544 GET_INT(sc->m_kill_count);
6545 GET_INT(sc->m_kill_count_ok);
6546 GET_INT(sc->m_assists);
6551 // if I'm the server of the game, I should always rebroadcast these stats
6552 if ((Net_player->flags & NETINFO_FLAG_AM_MASTER) && (sc != &bogus)) {
6553 // make sure these are alltime stats
6554 SDL_assert(val == STATS_ALLTIME);
6556 multi_broadcast_stats(STATS_ALLTIME);
6560 // called to create asteroid stuff
6561 void send_asteroid_create( object *new_objp, object *parent_objp, int asteroid_type, vector *relvec )
6564 ubyte data[MAX_PACKET_SIZE];
6565 ubyte packet_type, atype;
6569 if (relvec != NULL )
6572 BUILD_HEADER( ASTEROID_INFO );
6573 packet_type = ASTEROID_CREATE;
6575 SDL_assert( asteroid_type < UCHAR_MAX );
6576 atype = (ubyte)asteroid_type;
6578 ADD_DATA( packet_type );
6579 ADD_USHORT( parent_objp->net_signature );
6580 ADD_USHORT( new_objp->net_signature );
6583 add_vector_data( data, &packet_size, vec );
6585 multi_io_send_to_all(data, packet_size);
6588 void send_asteroid_throw( object *objp )
6591 ubyte data[MAX_PACKET_SIZE], packet_type;
6593 BUILD_HEADER( ASTEROID_INFO );
6595 // this packet type is an asteroid throw
6596 packet_type = ASTEROID_THROW;
6597 ADD_DATA( packet_type );
6598 ADD_USHORT( objp->net_signature );
6599 //ADD_DATA( objp->pos );
6600 add_vector_data( data, &packet_size, objp->pos );
6601 //ADD_DATA( objp->phys_info.vel );
6602 add_vector_data( data, &packet_size, objp->phys_info.vel );
6604 multi_io_send_to_all(data, packet_size);
6607 void send_asteroid_hit( object *objp, object *other_objp, vector *hitpos, float damage )
6610 ubyte data[MAX_PACKET_SIZE], packet_type;
6614 if ( hitpos != NULL )
6617 // build up an asteroid hit packet
6618 BUILD_HEADER( ASTEROID_INFO );
6619 packet_type = ASTEROID_HIT;
6620 ADD_DATA( packet_type );
6621 ADD_USHORT( objp->net_signature );
6623 if(other_objp == NULL){
6624 ushort invalid_sig = 0xffff;
6625 ADD_USHORT(invalid_sig);
6627 ADD_USHORT( other_objp->net_signature );
6630 add_vector_data( data, &packet_size, vec );
6631 ADD_FLOAT( damage );
6633 multi_io_send_to_all(data, packet_size);
6636 void process_asteroid_info( ubyte *data, header *hinfo )
6641 offset = HEADER_LENGTH;
6642 GET_DATA( packet_type );
6644 // based on the packet type, do something interesting with an asteroid!
6645 switch( packet_type ) {
6647 case ASTEROID_CREATE: {
6648 ushort psignature, signature;
6650 vector relvec = ZERO_VECTOR;
6651 object *parent_objp;
6653 GET_USHORT( psignature );
6654 GET_USHORT( signature );
6656 //GET_DATA( relvec );
6657 get_vector_data( data, &offset, relvec );
6659 // after getting the values, set the next network signature, and call the create sub function
6660 multi_set_network_signature( signature, MULTI_SIG_ASTEROID );
6661 parent_objp = multi_get_network_object( psignature );
6662 if ( parent_objp ) {
6663 asteroid_sub_create( parent_objp, atype, &relvec );
6665 nprintf(("Network", "Couldn't create asteroid because parent wasn't found!!!\n"));
6672 // asteroid throw packet -- asteroid has wrapped bounds
6673 case ASTEROID_THROW: {
6675 vector pos = ZERO_VECTOR, vel = ZERO_VECTOR;
6678 GET_USHORT( signature );
6680 get_vector_data( data, &offset, pos );
6682 get_vector_data( data, &offset, vel );
6683 objp = multi_get_network_object( signature );
6685 nprintf(("Network", "Couldn't throw asteroid because couldn't find it\n"));
6689 objp->phys_info.vel = vel;
6690 objp->phys_info.desired_vel = vel;
6694 case ASTEROID_HIT: {
6695 ushort signature, osignature;
6696 object *objp, *other_objp;
6697 vector hitpos = ZERO_VECTOR;
6700 GET_USHORT( signature );
6701 GET_USHORT( osignature );
6702 //GET_DATA( hitpos );
6703 get_vector_data( data, &offset, hitpos );
6704 GET_FLOAT( damage );
6706 objp = multi_get_network_object( signature );
6707 if(osignature == 0xffff){
6710 other_objp = multi_get_network_object( osignature );
6713 nprintf(("Network", "Cannot hit asteroid because signature isn't found\n"));
6717 if ( IS_VEC_NULL(&hitpos) ){
6718 asteroid_hit( objp, other_objp, NULL, damage );
6720 asteroid_hit( objp, other_objp, &hitpos, damage );
6723 // if we know the other object is a weapon, then do a weapon hit to kill the weapon
6724 if ( other_objp && (other_objp->type == OBJ_WEAPON) ){
6725 weapon_hit( other_objp, objp, &hitpos );
6738 void send_host_restr_packet(const char *callsign, int code, int mode)
6740 ubyte data[MAX_PACKET_SIZE],val;
6741 int packet_size = 0;
6743 // build the header and add the opcode
6744 BUILD_HEADER(HOST_RESTR_QUERY);
6751 ADD_STRING(callsign);
6753 // if I'm the standalone server, I should be sending this to the game host
6754 if((Game_mode & GM_STANDALONE_SERVER) && (Netgame.host != NULL)){
6755 multi_io_send_reliable(Netgame.host, data, packet_size);
6757 // otherwise if I'm the host, I should be sending a reply back to the standalone server
6759 SDL_assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
6760 multi_io_send_reliable(Net_player, data, packet_size);
6764 void process_host_restr_packet(ubyte *data, header *hinfo)
6768 int offset = HEADER_LENGTH;
6770 // get the opcode and the callsign
6773 GET_STRING(callsign);
6776 // do code specific operations
6778 // query to the host from standalone
6780 SDL_assert((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER));
6782 // set the join mode
6783 Multi_join_restr_mode = mode;
6785 // set the timestamp
6786 Multi_restr_query_timestamp = timestamp(MULTI_QUERY_RESTR_STAMP);
6788 // notify the host of the event
6789 gamesnd_play_iface(SND_BRIEF_STAGE_CHG_FAIL);
6790 HUD_printf(XSTR("Player %s has tried to join - allow (y/n) ?",736),callsign);
6793 // affirmative reply from the host to the standalone
6795 SDL_assert(Game_mode & GM_STANDALONE_SERVER);
6797 // let the player join if the timestamp has not already elapsed on the server
6798 if(Multi_restr_query_timestamp != -1){
6799 multi_process_valid_join_request(&Multi_restr_join_request,&Multi_restr_addr,(int)mode);
6805 SDL_assert(Game_mode & GM_STANDALONE_SERVER);
6806 Netgame.flags &= ~(NG_FLAG_INGAME_JOINING);
6807 Multi_restr_query_timestamp = -1;
6812 void send_netgame_end_error_packet(int notify_code,int err_code)
6816 int packet_size = 0;
6818 // only the server should ever be here - although this might change if for some reason the host wants to end the game
6819 SDL_assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
6821 // build the header and add the notification and error codes
6822 BUILD_HEADER(NETGAME_END_ERROR);
6823 code = (char)notify_code;
6825 code = (char)err_code;
6829 multi_io_send_to_all_reliable(data, packet_size);
6832 void process_netgame_end_error_packet(ubyte *data, header *hinfo)
6834 int offset = HEADER_LENGTH;
6835 char notify_code,error_code;
6837 // get the error and notification codes
6838 GET_DATA(notify_code);
6839 GET_DATA(error_code);
6843 multi_quit_game(PROMPT_NONE,notify_code,error_code);
6846 // sends info that a countermeasure succeeded.
6847 void send_countermeasure_success_packet( int objnum )
6849 int pnum, packet_size;
6850 ubyte data[MAX_PACKET_SIZE];
6852 pnum = multi_find_player_by_object( &Objects[objnum] );
6854 nprintf(("Network", "Coulnd't find player for countermeasure success packet\n"));
6858 BUILD_HEADER(COUNTERMEASURE_SUCCESS);
6859 multi_io_send(&Net_players[pnum], data, packet_size);
6862 // start the flashing of my hud gauge
6863 void process_countermeasure_success_packet( ubyte *data, header *hinfo )
6867 offset = HEADER_LENGTH;
6870 hud_start_text_flash(XSTR("Evaded", 1430), 800);
6871 snd_play(&Snds[SND_MISSILE_EVADED_POPUP]);
6874 #define UPDATE_IS_PAUSED (1<<0)
6875 #define UPDATE_HULL_INFO (1<<1)
6877 void send_client_update_packet(net_player *pl)
6879 ubyte data[MAX_PACKET_SIZE],val;
6880 int packet_size = 0;
6883 BUILD_HEADER(CLIENT_UPDATE);
6887 // add the pause status
6888 if ( Multi_pause_status ) {
6889 val |= UPDATE_IS_PAUSED;
6890 } 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) ) {
6891 val |= UPDATE_HULL_INFO;
6892 SDL_assert( Player_ship ); // I"d better have one of these!!!!
6897 // if paused, add the net address of the guy who paused
6898 if(val & UPDATE_IS_PAUSED){
6899 SDL_assert(Multi_pause_pauser != NULL);
6900 ADD_SHORT(Multi_pause_pauser->player_id);
6903 // when not paused, send hull/shield/subsystem updates to all clients (except for ingame joiners)
6904 if ( val & UPDATE_HULL_INFO ) {
6906 ubyte percent, ns, threats;
6909 ship_subsys *subsysp;
6912 // get the object for the player
6913 SDL_assert( pl->player->objnum != -1 );
6915 objp = &Objects[pl->player->objnum];
6917 SDL_assert ( objp->type == OBJ_SHIP );
6918 shipp = &Ships[objp->instance];
6919 sip = &Ship_info[shipp->ship_info_index];
6921 // hull strength and sheild mesh information are floats (as a percentage). Pass the integer
6922 // percentage value since that should be close enough
6923 float temp = (objp->hull_strength / sip->initial_hull_strength * 100.0f);
6927 percent = (ubyte)temp;
6929 ADD_DATA( percent );
6931 for (i = 0; i < MAX_SHIELD_SECTIONS; i++ ) {
6932 percent = (ubyte)(objp->shields[i] / (sip->shields / MAX_SHIELD_SECTIONS) * 100.0f);
6933 ADD_DATA( percent );
6936 // add the data for the subsystem hits. We can assume that the lists will be the same side of
6937 // both machines. Added as percent since that number <= 100
6939 // also write out the number of subsystems. We do this because the client might not know
6940 // about the object he is getting data for. (i.e. he killed the object already).
6941 ns = (ubyte)sip->n_subsystems;
6944 // now the subsystems.
6945 for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) {
6946 percent = (ubyte)(subsysp->current_hits / subsysp->system_info->max_hits * 100.0f);
6947 ADD_DATA( percent );
6950 // compute the threats for this player. Only compute the threats if this player is actually
6951 // playing (i.e. he has a ship)
6952 hud_update_reticle( pl->player );
6953 threats = (ubyte)pl->player->threat_flags;
6954 ADD_DATA( threats );
6956 // add his energy level for guns
6957 ADD_FLOAT(shipp->weapon_energy);
6959 // add his secondary bank ammo
6960 ADD_INT(shipp->weapons.num_secondary_banks);
6961 for(i=0; i<shipp->weapons.num_secondary_banks; i++){
6962 ADD_INT(shipp->weapons.secondary_bank_ammo[i]);
6967 ADD_INT(pl->sv_last_pl);
6969 // send the packet reliably to the player
6970 multi_io_send(pl, data, packet_size);
6973 void process_client_update_packet(ubyte *data, header *hinfo)
6978 int is_paused, have_hull_info;
6981 float weapon_energy;
6982 int offset = HEADER_LENGTH;
6984 // get the header byte containing useful information
6987 is_paused = (val & UPDATE_IS_PAUSED)?1:0;
6988 have_hull_info = (val & UPDATE_HULL_INFO)?1:0;
6990 // if we are paused, get who paused
6993 player_index = find_player_id(pauser);
6994 if(player_index != -1){
6995 Multi_pause_pauser = &Net_players[player_index];
6997 Multi_pause_pauser = NULL;
7001 // if we have hull information, then read it in.
7002 if ( have_hull_info ) {
7006 ubyte hull_percent, shield_percent[MAX_SHIELD_SECTIONS], n_subsystems, subsystem_percent[MAX_MODEL_SUBSYSTEMS], threats;
7008 ship_subsys *subsysp;
7012 // hull strength and sheild mesh information are floats (as a percentage). Pass the integer
7013 // percentage value since that should be close enough
7014 GET_DATA( hull_percent );
7016 for (i = 0; i < MAX_SHIELD_SECTIONS; i++ ){
7018 shield_percent[i] = ub_tmp;
7021 // get the data for the subsystems
7022 GET_DATA( n_subsystems );
7023 for ( i = 0; i < n_subsystems; i++ ){
7025 subsystem_percent[i] = ub_tmp;
7028 GET_DATA( threats );
7030 // add his energy level for guns
7031 GET_FLOAT(weapon_energy);
7033 // add his secondary bank ammo
7034 GET_INT(ammo_count);
7035 for(i=0; i<ammo_count; i++){
7039 // assign the above information to my ship, assuming that I can find it! Ingame joiners might get this
7040 // packet because of delay between reliable packet acknowledging my ingame ship and the start of these
7041 // UDP client update packets. Only read this info if I have a ship.
7042 if ( !(Net_player->flags & NETINFO_FLAG_INGAME_JOIN) && (Player_ship != NULL) && (Player_obj != NULL) && (Net_player != NULL)) {
7043 shipp = Player_ship;
7045 sip = &Ship_info[shipp->ship_info_index];
7047 fval = hull_percent * sip->initial_hull_strength / 100.0f;
7048 objp->hull_strength = fval;
7050 for ( i = 0; i < MAX_SHIELD_SECTIONS; i++ ) {
7051 fval = (shield_percent[i] * sip->shields / 100.0f) / MAX_SHIELD_SECTIONS;
7052 objp->shields[i] = fval;
7055 // for sanity, be sure that the number of susbystems that I read in matches the player. If not,
7056 // then don't read these in.
7057 if ( n_subsystems == sip->n_subsystems ) {
7059 n_subsystems = 0; // reuse this variable
7060 for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) {
7063 fval = subsystem_percent[n_subsystems] * subsysp->system_info->max_hits / 100.0f;
7064 subsysp->current_hits = fval;
7066 // add the value just generated (it was zero'ed above) into the array of generic system types
7067 subsys_type = subsysp->system_info->type; // this is the generic type of subsystem
7068 SDL_assert ( subsys_type < SUBSYSTEM_MAX );
7069 shipp->subsys_info[subsys_type].current_hits += fval;
7073 ship_recalc_subsys_strength( shipp );
7075 shipp->weapon_energy = weapon_energy;
7076 for(i=0; i<ammo_count; i++){
7077 shipp->weapons.secondary_bank_ammo[i] = ammo[i];
7080 // update my threat flags.
7081 // temporarily commented out until tested.
7082 Net_player->player->threat_flags = threats;
7089 if(Net_player != NULL){
7090 Net_player->cl_last_pl = pl;
7094 // note, if we're already paused or unpaused, calling these will have no effect, so it is safe to do so
7095 if(!popup_active() && !(Net_player->flags & NETINFO_FLAG_RESPAWNING) && !(Net_player->flags & NETINFO_FLAG_LIMBO)){
7097 multi_pause_pause();
7099 multi_pause_unpause();
7104 void send_countdown_packet(int time)
7108 int packet_size = 0;
7110 // build the header and add the time
7111 BUILD_HEADER(COUNTDOWN);
7115 // if we're the server, we should broadcast to everyone
7116 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
7117 multi_io_send_to_all_reliable(data, packet_size);
7119 // otherwise we'de better be a host sending to the standalone
7121 SDL_assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
7122 multi_io_send_reliable(Net_player, data, packet_size);
7126 void process_countdown_packet(ubyte *data, header *hinfo)
7128 int offset = HEADER_LENGTH;
7135 // if we're not in the post sync data screen, ignore it
7136 if(gameseq_get_state() != GS_STATE_MULTI_MISSION_SYNC){
7140 // if we're the standalone, this should be a -1 telling us to start the countdown
7141 if(Game_mode & GM_STANDALONE_SERVER){
7142 SDL_assert((int)time == -1);
7144 // start the countdown
7145 multi_sync_start_countdown();
7147 // otherwise if we're clients, just bash the countdown
7149 Multi_sync_countdown = (int)time;
7153 // packets for debriefing information
7154 void send_debrief_info( int stage_count[], int *stages[] )
7156 ubyte data[MAX_PACKET_SIZE];
7157 int packet_size, i, j;
7160 BUILD_HEADER(DEBRIEF_INFO);
7162 // add the data for the teams
7163 for ( i = 0; i < Num_teams; i++ ) {
7166 count = stage_count[i];
7168 for ( j = 0; j < count; j++ ) {
7169 i_tmp = stages[i][j];
7174 multi_io_send_to_all_reliable(data, packet_size);
7177 // process a debrief info packet from the server
7178 void process_debrief_info( ubyte *data, header *hinfo )
7181 int stage_counts[MAX_TEAMS], active_stages[MAX_TEAMS][MAX_DEBRIEF_STAGES], *stages[MAX_TEAMS];
7184 offset = HEADER_LENGTH;
7185 for ( i = 0; i < Num_teams; i++ ) {
7189 stage_counts[i] = count;
7190 stages[i] = active_stages[i];
7191 for ( j = 0; j < count; j++ ) {
7193 active_stages[i][j] = i_tmp;
7198 // now that we have the stage data for the debriefing stages, call debrief function with the
7199 // data so that clients can now see the debriefing stuff. Do it only for my team.
7200 SDL_assert( (Net_player->p_info.team >= 0) && (Net_player->p_info.team < Num_teams) );
7201 debrief_set_multi_clients( stage_counts[Net_player->p_info.team], stages[Net_player->p_info.team] );
7204 // sends homing information to all clients. We only need signature and num_missiles (because of hornets).
7205 // sends homing_object and homing_subsystem to all clients.
7206 void send_homing_weapon_info( int weapon_num )
7208 ubyte data[MAX_PACKET_SIZE];
7211 object *homing_object;
7212 ushort homing_signature;
7215 wp = &Weapons[weapon_num];
7217 // be sure that this weapon object is a homing object.
7218 if ( !(Weapon_info[wp->weapon_info_index].wi_flags & WIF_HOMING) )
7221 // get the homing signature. If this weapon isn't homing on anything, then sent 0 as the
7222 // homing signature.
7223 homing_signature = 0;
7224 homing_object = wp->homing_object;
7225 if ( homing_object != NULL ) {
7226 homing_signature = homing_object->net_signature;
7228 // get the subsystem index.
7230 if ( (homing_object->type == OBJ_SHIP) && (wp->homing_subsys != NULL) ) {
7233 s_index = ship_get_index_from_subsys( wp->homing_subsys, OBJ_INDEX(homing_object), 1 );
7234 SDL_assert( s_index < CHAR_MAX ); // better be less than this!!!!
7235 t_subsys = (char)s_index;
7239 BUILD_HEADER(HOMING_WEAPON_UPDATE);
7240 ADD_USHORT( Objects[wp->objnum].net_signature );
7241 ADD_USHORT( homing_signature );
7242 ADD_DATA( t_subsys );
7244 multi_io_send_to_all(data, packet_size);
7247 // process a homing weapon info change packet. multiple_missiles parameter specifies is this
7248 // packet contains information for multiple weapons (like hornets).
7249 void process_homing_weapon_info( ubyte *data, header *hinfo )
7252 ushort weapon_signature, homing_signature;
7254 object *homing_object, *weapon_objp;
7257 offset = HEADER_LENGTH;
7259 // get the data for the packet
7260 GET_USHORT( weapon_signature );
7261 GET_USHORT( homing_signature );
7262 GET_DATA( h_subsys );
7265 // deal with changing this weapons homing information
7266 weapon_objp = multi_get_network_object( weapon_signature );
7267 if ( weapon_objp == NULL ) {
7268 nprintf(("Network", "Couldn't find weapon object for homing update -- skipping update\n"));
7271 SDL_assert( weapon_objp->type == OBJ_WEAPON );
7272 wp = &Weapons[weapon_objp->instance];
7274 // be sure that we can find these weapons and
7275 homing_object = multi_get_network_object( homing_signature );
7276 if ( homing_object == NULL ) {
7277 nprintf(("Network", "Couldn't find homing object for homing update\n"));
7281 if ( homing_object->type == OBJ_WEAPON ) {
7282 SDL_assert(Weapon_info[Weapons[homing_object->instance].weapon_info_index].wi_flags & WIF_BOMB);
7285 wp->homing_object = homing_object;
7286 wp->homing_subsys = NULL;
7287 wp->target_num = OBJ_INDEX(homing_object);
7288 wp->target_sig = homing_object->signature;
7289 if ( h_subsys != -1 ) {
7290 SDL_assert( homing_object->type == OBJ_SHIP );
7291 wp->homing_subsys = ship_get_indexed_subsys( &Ships[homing_object->instance], h_subsys);
7294 if ( homing_object->type == OBJ_SHIP ) {
7295 nprintf(("Network", "Updating homing information for weapon -- homing on %s\n", Ships[homing_object->instance].ship_name));
7299 void send_emp_effect(ushort net_sig, float intensity, float time)
7304 SDL_assert(MULTIPLAYER_MASTER);
7306 // build the packet and add the opcode
7307 BUILD_HEADER(EMP_EFFECT);
7308 ADD_USHORT(net_sig);
7309 ADD_FLOAT(intensity);
7312 // send it to the player
7313 multi_io_send_to_all(data, packet_size);
7316 void process_emp_effect(ubyte *data, header *hinfo)
7318 float intensity, time;
7321 int offset = HEADER_LENGTH;
7323 // read in the EMP effect data
7324 GET_USHORT(net_sig);
7325 GET_FLOAT(intensity);
7329 // try and find the object
7330 objp = multi_get_network_object(net_sig);
7331 if((objp != NULL) && (objp->type == OBJ_SHIP)){
7332 // if i'm not an observer and I have a valid ship, play the EMP effect
7333 if(!(Net_player->flags & NETINFO_FLAG_OBSERVER) && (Player_obj != NULL) && (Player_obj->type == OBJ_SHIP) && (Player_obj == objp)){
7334 emp_start_local(intensity, time);
7337 // start the effect for the ship itself
7338 emp_start_ship(objp, intensity, time);
7342 // tells whether or not reinforcements are available
7343 void send_reinforcement_avail( int rnum )
7348 BUILD_HEADER(REINFORCEMENT_AVAIL);
7350 multi_io_send_to_all_reliable(data, packet_size);
7353 void process_reinforcement_avail( ubyte *data, header *hinfo )
7358 offset = HEADER_LENGTH;
7362 // sanity check for a valid reinforcement number
7363 if ( (rnum >= 0) && (rnum < Num_reinforcements) ) {
7364 Reinforcements[rnum].flags |= RF_IS_AVAILABLE;
7368 void send_change_iff_packet(ushort net_signature, int new_team)
7370 ubyte data[MAX_PACKET_SIZE];
7371 int packet_size = 0;
7373 if(Net_player == NULL){
7376 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
7380 // build the packet and add the data
7381 BUILD_HEADER(CHANGE_IFF);
7382 ADD_USHORT(net_signature);
7385 // send to all players
7386 multi_io_send_to_all_reliable(data, packet_size);
7389 void process_change_iff_packet( ubyte *data, header *hinfo)
7391 int offset = HEADER_LENGTH;
7392 ushort net_signature;
7397 GET_USHORT(net_signature);
7401 // lookup the object
7402 objp = multi_get_network_object(net_signature);
7403 if((objp != NULL) && (objp->type == OBJ_SHIP) && (objp->instance >=0)){
7404 Ships[objp->instance].team = new_team;
7408 void send_NEW_primary_fired_packet(ship *shipp, int banks_fired)
7410 int packet_size, objnum;
7411 ubyte data[MAX_PACKET_SIZE]; // ubanks_fired, current_bank;
7414 net_player *ignore = NULL;
7416 // sanity checking for now
7417 SDL_assert ( banks_fired <= 3 );
7419 // get an object pointer for this ship.
7420 objnum = shipp->objnum;
7421 objp = &Objects[objnum];
7423 // if i'm a multiplayer client, I should never send primary fired packets for anyone except me
7424 if(MULTIPLAYER_CLIENT && (Player_obj != objp)){
7428 // just in case nothing got fired
7429 if(banks_fired <= 0){
7433 // ubanks_fired = (ubyte)banks_fired;
7434 // current_bank = (ubyte)shipp->weapons.current_primary_bank;
7435 // SDL_assert( current_bank <= 3 );
7437 // insert the current primary bank into this byte
7438 // ubanks_fired |= (current_bank << CURRENT_BANK_BIT);
7440 // append the SF_PRIMARY_LINKED flag on the top nibble of the banks_fired
7441 // if ( shipp->flags & SF_PRIMARY_LINKED ){
7442 // ubanks_fired |= (1<<7);
7445 // determine if its a player ship and don't send to him if we're in "client firing" mode
7446 // if((Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING) && MULTIPLAYER_MASTER){
7447 if(MULTIPLAYER_MASTER){
7448 np_index = multi_find_player_by_net_signature(objp->net_signature);
7449 if((np_index >= 0) && (np_index < MAX_PLAYERS)){
7450 ignore = &Net_players[np_index];
7454 // build up the standard weapon fired packet. This packet will get sent to all players if an AI
7455 // ship fired the primary weapons. If a player fired the weaspon, then this packet will get sent
7456 // to every player but the guy who actullly fired the weapon. This method is used to help keep client
7457 // and server in sync w.r.t. weapon energy for player ship
7458 BUILD_HEADER( PRIMARY_FIRED_NEW );
7459 ADD_USHORT(objp->net_signature);
7460 // ADD_DATA(ubanks_fired);
7462 // if I'm a server, broadcast to all players
7463 if(MULTIPLAYER_MASTER){
7464 multi_io_send_to_all(data, packet_size, ignore);
7467 multi_rate_add(1, "wfi", packet_size);
7469 // otherwise just send to the server
7471 multi_io_send(Net_player, data, packet_size);
7475 void process_NEW_primary_fired_packet(ubyte *data, header *hinfo)
7477 int offset; // linked;
7478 // ubyte banks_fired, current_bank;
7483 // read all packet info
7484 offset = HEADER_LENGTH;
7485 GET_USHORT(shooter_sig);
7486 // GET_DATA(banks_fired);
7489 // find the object this fired packet is operating on
7490 objp = multi_get_network_object( shooter_sig );
7491 if ( objp == NULL ) {
7492 nprintf(("Network", "Could not find ship for fire primary packet NEW!"));
7495 // if this object is not actually a valid ship, don't do anything
7496 if(objp->type != OBJ_SHIP){
7499 if(objp->instance < 0){
7502 // shipp = &Ships[objp->instance];
7504 // get the link status of the primary banks
7506 // if ( banks_fired & (1<<7) ) {
7508 // banks_fired ^= (1<<7);
7511 // get the current primary bank
7512 // current_bank = (ubyte)(banks_fired >> CURRENT_BANK_BIT);
7513 // current_bank &= 0x3;
7514 // SDL_assert( (current_bank >= 0) && (current_bank < MAX_PRIMARY_BANKS) );
7515 // shipp->weapons.current_primary_bank = current_bank;
7517 // strip off all remaining bits and just keep which banks were actually fired.
7518 // banks_fired &= 0x3;
7520 // set the link status of the ship if not the player. If it is the player, we will do sanity checking
7523 // shipp->flags &= ~SF_PRIMARY_LINKED;
7525 // shipp->flags |= SF_PRIMARY_LINKED;
7528 // if we're in client firing mode, ignore ones for myself
7529 if((Player_obj != NULL) && (Player_obj == objp)){
7533 ship_fire_primary( objp, 0, 1 );
7536 void send_NEW_countermeasure_fired_packet(object *objp, int cmeasure_count, int rand_val)
7538 ubyte data[MAX_PACKET_SIZE];
7541 net_player *ignore = NULL;
7543 // if i'm a multiplayer client, I should never send primary fired packets for anyone except me
7544 if(MULTIPLAYER_CLIENT && (Player_obj != objp)){
7548 SDL_assert ( cmeasure_count < UCHAR_MAX );
7549 BUILD_HEADER(COUNTERMEASURE_NEW);
7550 ADD_USHORT( objp->net_signature );
7551 ADD_INT( rand_val );
7553 nprintf(("Network","Sending NEW countermeasure packet!\n"));
7555 // determine if its a player ship and don't send to him if we're in "client firing" mode
7556 // if((Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING) && MULTIPLAYER_MASTER){
7557 if(MULTIPLAYER_MASTER){
7558 np_index = multi_find_player_by_net_signature(objp->net_signature);
7559 if((np_index >= 0) && (np_index < MAX_PLAYERS)){
7560 ignore = &Net_players[np_index];
7564 // if I'm the server, send to all players
7565 if(MULTIPLAYER_MASTER){
7566 multi_io_send_to_all(data, packet_size, ignore);
7568 // otherwise send to the server
7570 multi_io_send(Net_player, data, packet_size);
7574 void process_NEW_countermeasure_fired_packet(ubyte *data, header *hinfo)
7581 offset = HEADER_LENGTH;
7582 GET_USHORT( signature );
7583 GET_INT( rand_val );
7586 objp = multi_get_network_object( signature );
7587 if ( objp == NULL ) {
7588 nprintf(("network", "Could find object whose countermeasures are being launched!!!\n"));
7591 if(objp->type != OBJ_SHIP){
7595 // if we're in client firing mode, ignore ones for myself
7596 // if((Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING) && (Player_obj != NULL) && (Player_obj == objp)){
7597 if((Player_obj != NULL) && (Player_obj == objp)){
7601 // make it so ship can fire right away!
7602 Ships[objp->instance].cmeasure_fire_stamp = timestamp(0);
7603 if ( objp == Player_obj ){
7604 nprintf(("network", "firing countermeasure from my ship\n"));
7606 ship_launch_countermeasure( objp, rand_val );
7609 void send_beam_fired_packet(object *shooter, ship_subsys *turret, object *target, int beam_info_index, beam_info *override)
7611 ubyte data[MAX_PACKET_SIZE];
7612 int packet_size = 0;
7617 // only the server should ever be doing this
7618 SDL_assert(MULTIPLAYER_MASTER);
7620 // setup outgoing data
7621 SDL_assert(shooter != NULL);
7622 SDL_assert(turret != NULL);
7623 SDL_assert(target != NULL);
7624 SDL_assert(override != NULL);
7625 if((shooter == NULL) || (turret == NULL) || (target == NULL) || (override == NULL)){
7628 u_beam_info = (ubyte)beam_info_index;
7629 subsys_index = (char)ship_get_index_from_subsys(turret, OBJ_INDEX(shooter));
7630 SDL_assert(subsys_index >= 0);
7631 if(subsys_index < 0){
7635 // swap the beam_info override info into little endian byte order
7636 b_info.dir_a.xyz.x = INTEL_FLOAT(override->dir_a.xyz.x);
7637 b_info.dir_a.xyz.y = INTEL_FLOAT(override->dir_a.xyz.y);
7638 b_info.dir_a.xyz.z = INTEL_FLOAT(override->dir_a.xyz.z);
7640 b_info.dir_b.xyz.x = INTEL_FLOAT(override->dir_b.xyz.x);
7641 b_info.dir_b.xyz.y = INTEL_FLOAT(override->dir_b.xyz.y);
7642 b_info.dir_b.xyz.z = INTEL_FLOAT(override->dir_b.xyz.z);
7644 b_info.delta_ang = INTEL_FLOAT(override->delta_ang);
7645 b_info.shot_count = override->shot_count;
7647 for (int i = 0; i < b_info.shot_count; i++) {
7648 b_info.shot_aim[i] = INTEL_FLOAT(override->shot_aim[i]);
7652 BUILD_HEADER(BEAM_FIRED);
7653 ADD_USHORT(shooter->net_signature);
7654 ADD_DATA(subsys_index);
7655 ADD_USHORT(target->net_signature);
7656 ADD_DATA(u_beam_info);
7657 ADD_DATA(b_info); // FIXME: This is still wrong, we shouldn't be sending an entire struct over the wire - taylor
7659 // send to all clients
7660 multi_io_send_to_all_reliable(data, packet_size);
7663 void process_beam_fired_packet(ubyte *data, header *hinfo)
7666 ushort shooter_sig, target_sig;
7670 beam_fire_info fire_info;
7672 // only clients should ever get this
7673 SDL_assert(MULTIPLAYER_CLIENT);
7675 // read in packet data
7676 offset = HEADER_LENGTH;
7677 GET_USHORT(shooter_sig);
7678 GET_DATA(subsys_index);
7679 GET_USHORT(target_sig);
7680 GET_DATA(u_beam_info);
7685 // swap the beam_info override info into native byte order
7686 b_info.dir_a.xyz.x = INTEL_FLOAT( b_info.dir_a.xyz.x );
7687 b_info.dir_a.xyz.y = INTEL_FLOAT( b_info.dir_a.xyz.y );
7688 b_info.dir_a.xyz.z = INTEL_FLOAT( b_info.dir_a.xyz.z );
7689 b_info.dir_b.xyz.x = INTEL_FLOAT( b_info.dir_b.xyz.x );
7690 b_info.dir_b.xyz.y = INTEL_FLOAT( b_info.dir_b.xyz.y );
7691 b_info.dir_b.xyz.z = INTEL_FLOAT( b_info.dir_b.xyz.z );
7692 b_info.delta_ang = INTEL_FLOAT( b_info.delta_ang );
7694 for (i = 0; i < MAX_BEAM_SHOTS; i++) {
7695 b_info.shot_aim[i] = INTEL_FLOAT(b_info.shot_aim[i]);
7698 // lookup all relevant data
7699 fire_info.beam_info_index = (int)u_beam_info;
7700 fire_info.shooter = NULL;
7701 fire_info.target = NULL;
7702 fire_info.turret = NULL;
7703 fire_info.target_subsys = NULL;
7704 fire_info.beam_info_override = NULL;
7705 fire_info.shooter = multi_get_network_object(shooter_sig);
7706 fire_info.target = multi_get_network_object(target_sig);
7707 fire_info.beam_info_override = &b_info;
7708 fire_info.accuracy = 1.0f;
7709 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)){
7710 nprintf(("Network", "Couldn't get shooter/target info for BEAM weapon!\n"));
7713 fire_info.turret = ship_get_indexed_subsys( &Ships[fire_info.shooter->instance], (int)subsys_index);
7714 if(fire_info.turret == NULL){
7715 nprintf(("Network", "Couldn't get turret for BEAM weapon!\n"));
7720 beam_fire(&fire_info);
7723 void send_sw_query_packet(ubyte code, char *txt)
7725 ubyte data[MAX_PACKET_SIZE];
7726 int packet_size = 0;
7728 // build the packet and add the code
7729 BUILD_HEADER(SW_STD_QUERY);
7731 if((code == SW_STD_START) || (code == SW_STD_BAD)){
7732 SDL_assert(txt != NULL);
7736 // if I'm the host, send to standalone
7737 if(MULTIPLAYER_HOST){
7738 SDL_assert(!MULTIPLAYER_MASTER);
7739 SDL_assert(code == SW_STD_START);
7740 multi_io_send_reliable(Net_player, data, packet_size);
7742 // otherwise standalone sends back to host
7744 SDL_assert(Game_mode & GM_STANDALONE_SERVER);
7745 SDL_assert(code != SW_STD_START);
7746 SDL_assert(Netgame.host != NULL);
7747 if(Netgame.host != NULL){
7748 multi_io_send_reliable(Netgame.host, data, packet_size);
7753 void process_sw_query_packet(ubyte *data, header *hinfo)
7755 int offset = HEADER_LENGTH;
7757 char txt[MAX_SQUAD_RESPONSE_LEN+1];
7761 if ( (code == SW_STD_START) || (code == SW_STD_BAD) ) {
7767 // to host from standalone
7768 if (MULTIPLAYER_HOST) {
7769 SDL_assert( !MULTIPLAYER_MASTER );
7771 if (code == SW_STD_OK) {
7772 Multi_sw_std_query = 1;
7774 SDL_assert(code == SW_STD_BAD);
7776 SDL_strlcpy(Multi_sw_bad_reply, txt, SDL_arraysize(Multi_sw_bad_reply));
7777 Multi_sw_std_query = 0;
7780 // to standalone from host
7782 SDL_assert(Game_mode & GM_STANDALONE_SERVER);
7783 SDL_assert(code == SW_STD_START);
7785 multi_sw_std_query(txt);
7789 void send_event_update_packet(int event)
7791 ubyte data[MAX_PACKET_SIZE];
7792 ushort u_event = (ushort)event;
7793 int packet_size = 0;
7795 // build the header and add the event
7796 BUILD_HEADER(EVENT_UPDATE);
7797 ADD_USHORT(u_event);
7798 ADD_INT(Mission_events[event].flags);
7799 ADD_INT(Mission_events[event].formula);
7800 ADD_INT(Mission_events[event].result);
7801 ADD_INT(Mission_events[event].count);
7803 // send to all players
7804 multi_io_send_to_all_reliable(data, packet_size);
7807 void process_event_update_packet(ubyte *data, header *hinfo)
7809 int offset = HEADER_LENGTH;
7814 GET_USHORT(u_event);
7815 store_flags = Mission_events[u_event].flags;
7816 GET_INT(Mission_events[u_event].flags);
7817 GET_INT(Mission_events[u_event].formula);
7818 GET_INT(Mission_events[u_event].result);
7819 GET_INT(Mission_events[u_event].count);
7822 // went from non directive special to directive special
7823 if(!(store_flags & MEF_DIRECTIVE_SPECIAL) && (Mission_events[u_event].flags & MEF_DIRECTIVE_SPECIAL)){
7824 mission_event_set_directive_special(u_event);
7826 // if we went directive special to non directive special
7827 else if((store_flags & MEF_DIRECTIVE_SPECIAL) & !(Mission_events[u_event].flags & MEF_DIRECTIVE_SPECIAL)){
7828 mission_event_unset_directive_special(u_event);
7832 // weapon detonate packet
7833 void send_weapon_detonate_packet(object *objp)
7835 ubyte data[MAX_PACKET_SIZE];
7836 int packet_size = 0;
7839 SDL_assert(MULTIPLAYER_MASTER);
7840 if(!MULTIPLAYER_MASTER){
7843 SDL_assert(objp != NULL);
7848 // build the header and add the data
7849 BUILD_HEADER(WEAPON_DET);
7850 ADD_USHORT(objp->net_signature);
7852 // send to all players
7853 multi_io_send_to_all(data, packet_size);
7856 void process_weapon_detonate_packet(ubyte *data, header *hinfo)
7859 int offset = HEADER_LENGTH;
7860 object *objp = NULL;
7862 // get the weapon signature
7863 GET_USHORT(net_sig);
7866 // lookup the weapon
7867 objp = multi_get_network_object(net_sig);
7868 if((objp != NULL) && (objp->type == OBJ_WEAPON) && (objp->instance >= 0)){
7869 weapon_detonate(objp);
7873 // flak fired packet
7874 void send_flak_fired_packet(int ship_objnum, int subsys_index, int weapon_objnum, float flak_range)
7877 ushort pnet_signature;
7878 ubyte data[MAX_PACKET_SIZE], cindex;
7884 if((weapon_objnum < 0) || (Objects[weapon_objnum].type != OBJ_WEAPON) || (Objects[weapon_objnum].instance < 0) || (Weapons[Objects[weapon_objnum].instance].weapon_info_index < 0)){
7888 // local setup -- be sure we are actually passing a weapon!!!!
7889 objp = &Objects[weapon_objnum];
7890 SDL_assert ( objp->type == OBJ_WEAPON );
7891 pnet_signature = Objects[ship_objnum].net_signature;
7893 SDL_assert( subsys_index < UCHAR_MAX );
7894 cindex = (ubyte)subsys_index;
7896 ssp = ship_get_indexed_subsys( &Ships[Objects[ship_objnum].instance], subsys_index, NULL );
7901 // build the fire turret packet.
7902 BUILD_HEADER(FLAK_FIRED);
7903 packet_size += multi_pack_unpack_position(1, data + packet_size, &objp->orient.v.fvec);
7904 ADD_USHORT( pnet_signature );
7906 val = (short)ssp->submodel_info_1.angs.h;
7908 val = (short)ssp->submodel_info_2.angs.p;
7910 ADD_FLOAT( flak_range );
7912 multi_io_send_to_all(data, packet_size);
7914 multi_rate_add(1, "flk", packet_size);
7917 void process_flak_fired_packet(ubyte *data, header *hinfo)
7919 int offset, weapon_objnum, wid;
7920 ushort pnet_signature;
7928 short pitch, heading;
7931 // get the data for the turret fired packet
7932 offset = HEADER_LENGTH;
7933 offset += multi_pack_unpack_position(0, data + offset, &o_fvec);
7934 GET_USHORT( pnet_signature );
7935 GET_DATA( turret_index );
7936 GET_SHORT( heading );
7938 GET_FLOAT( flak_range );
7939 PACKET_SET_SIZE(); // move our counter forward the number of bytes we have read
7942 objp = multi_get_network_object( pnet_signature );
7943 if ( objp == NULL ) {
7944 nprintf(("network", "could find parent object with net signature %d for flak firing\n", pnet_signature));
7948 // if this isn't a ship, do nothing
7949 if ( objp->type != OBJ_SHIP ){
7953 // make an orientation matrix from the o_fvec
7954 vm_vector_2_matrix(&orient, &o_fvec, NULL, NULL);
7956 // find this turret, and set the position of the turret that just fired to be where it fired. Quite a
7957 // hack, but should be suitable.
7958 shipp = &Ships[objp->instance];
7959 ssp = ship_get_indexed_subsys( shipp, turret_index, NULL );
7963 wid = ssp->system_info->turret_weapon_type;
7964 if((wid < 0) || !(Weapon_info[wid].wi_flags & WIF_FLAK)){
7968 // bash the position and orientation of the turret
7969 ssp->submodel_info_1.angs.h = (float)heading;
7970 ssp->submodel_info_2.angs.p = (float)pitch;
7972 // get the world position of the weapon
7973 ship_get_global_turret_info(objp, ssp->system_info, &pos, &dir);
7975 // create the weapon object
7976 weapon_objnum = weapon_create( &pos, &orient, wid, OBJ_INDEX(objp), 0, -1, 1);
7977 if (weapon_objnum != -1) {
7978 if ( Weapon_info[wid].launch_snd != -1 ) {
7979 snd_play_3d( &Snds[Weapon_info[wid].launch_snd], &pos, &View_position );
7982 // create a muzzle flash from a flak gun based upon firing position and weapon type
7983 flak_muzzle_flash(&pos, &dir, wid);
7985 // set its range explicitly - make it long enough so that it's guaranteed to still exist when the server tells us it blew up
7986 flak_set_range(&Objects[weapon_objnum], &pos, (float)flak_range);
7990 #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);
7991 #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);
7993 // player pain packet
7994 void send_player_pain_packet(net_player *pl, int weapon_info_index, float damage, vector *force, vector *hitpos)
7996 ubyte data[MAX_PACKET_SIZE];
7999 int packet_size = 0;
8001 SDL_assert(MULTIPLAYER_MASTER);
8002 if(!MULTIPLAYER_MASTER){
8005 SDL_assert(pl != NULL);
8010 // build the packet and add the code
8011 BUILD_HEADER(NETPLAYER_PAIN);
8012 windex = (ubyte)weapon_info_index;
8014 udamage = (ushort)damage;
8015 ADD_USHORT(udamage);
8016 //ADD_DATA((*force));
8017 add_vector_data( data, &packet_size, *force );
8018 //ADD_DATA((*hitpos));
8019 add_vector_data( data, &packet_size, *hitpos );
8021 // send to the player
8022 multi_io_send(pl, data, packet_size);
8024 multi_rate_add(1, "pai", packet_size);
8027 void process_player_pain_packet(ubyte *data, header *hinfo)
8032 vector force = ZERO_VECTOR;
8033 vector local_hit_pos = ZERO_VECTOR;
8036 // get the data for the pain packet
8037 offset = HEADER_LENGTH;
8039 GET_USHORT(udamage);
8041 get_vector_data( data, &offset, force );
8042 //GET_DATA(local_hit_pos);
8043 get_vector_data( data, &offset, local_hit_pos );
8046 mprintf(("PAIN!\n"));
8048 // get weapon info pointer
8049 //SDL_assert((windex >= 0) && (windex < Num_weapon_types) && (Weapon_info[windex].subtype == WP_LASER)); // always true
8050 if(! ((windex != 255) && (windex < Num_weapon_types) && (Weapon_info[windex].subtype == WP_LASER)) ){
8053 wip = &Weapon_info[windex];
8055 // play the weapon hit sound
8056 SDL_assert(Player_obj != NULL);
8057 if(Player_obj == NULL){
8060 weapon_hit_do_sound(Player_obj, wip, &Player_obj->pos);
8062 // we need to do 3 things here. player pain (game flash), weapon hit sound, ship_apply_whack()
8063 ship_hit_pain((float)udamage);
8066 ship_apply_whack(&force, &local_hit_pos, Player_obj);
8070 void send_lightning_packet(int bolt_type, vector *start, vector *strike)
8072 ubyte data[MAX_PACKET_SIZE];
8074 int packet_size = 0;
8076 // build the header and add the data
8077 BUILD_HEADER(LIGHTNING_PACKET);
8078 val = (char)bolt_type;
8080 //ADD_DATA((*start));
8081 add_vector_data( data, &packet_size, *start );
8082 //ADD_DATA((*strike));
8083 add_vector_data( data, &packet_size, *strike );
8085 // send to everyone unreliable for now
8086 multi_io_send_to_all(data, packet_size);
8089 void process_lightning_packet(ubyte *data, header *hinfo)
8093 vector start = ZERO_VECTOR, strike = ZERO_VECTOR;
8096 offset = HEADER_LENGTH;
8097 GET_DATA(bolt_type);
8099 get_vector_data(data, &offset, start);
8100 // GET_DATA(strike);
8101 get_vector_data(data, &offset, strike);
8110 nebl_bolt(bolt_type, &start, &strike);
8113 void send_bytes_recvd_packet(net_player *pl)
8115 // only clients should ever be doing this
8120 ubyte data[MAX_PACKET_SIZE];
8121 int packet_size = 0;
8122 BUILD_HEADER(BYTES_SENT);
8123 ADD_INT(pl->cl_bytes_recvd);
8125 // send to the server
8126 multi_io_send_reliable(pl, data, packet_size);
8129 void process_bytes_recvd_packet(ubyte *data, header *hinfo)
8133 net_player *pl = NULL;
8134 int offset = HEADER_LENGTH;
8140 if(Net_player == NULL){
8143 if(!MULTIPLAYER_MASTER){
8147 // make sure we know what player sent this
8148 pid = find_player_id(hinfo->id);
8149 if((pid < 0) || (pid >= MAX_PLAYERS)){
8152 pl = &Net_players[pid];
8155 pl->cl_bytes_recvd = bytes;
8159 pl->sv_last_pl = (int)(100.0f * (1.0f - ((float)pl->cl_bytes_recvd / (float)pl->sv_bytes_sent)));
8162 pl->sv_bytes_sent = 0;
8166 void send_host_captain_change_packet(short player_id, int captain_change)
8168 ubyte data[MAX_PACKET_SIZE];
8169 int packet_size = 0;
8172 BUILD_HEADER(TRANSFER_HOST);
8173 ADD_SHORT(player_id);
8174 ADD_INT(captain_change);
8177 multi_io_send_to_all_reliable(data, packet_size);
8180 void process_host_captain_change_packet(ubyte *data, header *hinfo)
8182 int offset = HEADER_LENGTH;
8183 int idx, found_player, captain_change;
8186 // get the player id
8187 GET_SHORT(player_id);
8188 GET_INT(captain_change);
8194 for(idx=0; idx<MAX_PLAYERS; idx++){
8195 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].player_id == player_id)){
8196 HUD_printf("%s is the new captain of team %d", Net_players[idx].player->callsign, Net_players[idx].p_info.team + 1);
8201 // unflag all old players
8202 for(idx=0; idx<MAX_PLAYERS; idx++){
8203 Net_players[idx].flags &= ~NETINFO_FLAG_GAME_HOST;
8208 for(idx=0; idx<MAX_PLAYERS; idx++){
8209 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].player_id == player_id)){
8210 Net_players[idx].flags |= NETINFO_FLAG_GAME_HOST;
8212 // spew to the HUD config
8213 if(Net_players[idx].player != NULL){
8214 HUD_printf("%s is the new game host", Net_players[idx].player->callsign);
8224 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_HOST_LEFT);
8229 void send_self_destruct_packet()
8231 ubyte data[MAX_PACKET_SIZE];
8232 int packet_size = 0;
8235 if(Net_player == NULL){
8239 // if i'm the server, I shouldn't be here
8240 SDL_assert(!(Net_player->flags & NETINFO_FLAG_AM_MASTER));
8241 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8245 // only if this is valid
8246 if(MULTI_OBSERVER(Net_players[MY_NET_PLAYER_NUM])){
8251 if((Player_ship == NULL) || (Player_obj == NULL)){
8256 BUILD_HEADER(SELF_DESTRUCT);
8257 ADD_USHORT(Player_obj->net_signature);
8259 // send to the server
8260 multi_io_send_reliable(Net_player, data, packet_size);
8263 void process_self_destruct_packet(ubyte *data, header *hinfo)
8265 int offset = HEADER_LENGTH;
8269 // get the net signature
8270 GET_USHORT(net_sig);
8274 np_index = find_player_id(hinfo->id);
8278 if(MULTI_OBSERVER(Net_players[np_index])){
8281 if(Net_players[np_index].player == NULL){
8284 if((Net_players[np_index].player->objnum < 0) || (Net_players[np_index].player->objnum >= MAX_OBJECTS)){
8287 if(Objects[Net_players[np_index].player->objnum].net_signature != net_sig){
8290 if(Objects[Net_players[np_index].player->objnum].type != OBJ_SHIP){
8293 if((Objects[Net_players[np_index].player->objnum].instance < 0) || (Objects[Net_players[np_index].player->objnum].instance >= MAX_SHIPS)){
8298 ship_self_destruct(&Objects[Net_players[np_index].player->objnum]);