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.10 2005/10/02 09:30:10 taylor
19 * sync up rest of big-endian network changes. it should at least be as good as what's in FS2_Open now, only better :)
21 * Revision 1.9 2005/08/12 08:59:17 taylor
24 * Revision 1.8 2004/09/20 01:31:44 theoddone33
27 * Revision 1.7 2004/06/11 01:49:45 tigital
28 * byte-swapping changes for bigendian systems
30 * Revision 1.6 2003/08/03 16:10:29 taylor
31 * cleanup; compile warning fixes
33 * Revision 1.5 2002/06/17 06:33:10 relnev
34 * ryan's struct patch for gcc 2.95
36 * Revision 1.4 2002/06/09 04:41:24 relnev
37 * added copyright header
39 * Revision 1.3 2002/05/26 20:49:54 theoddone33
42 * Revision 1.2 2002/05/07 03:16:47 theoddone33
43 * The Great Newline Fix
45 * Revision 1.1.1.1 2002/05/03 03:28:10 root
49 * 83 9/14/99 2:21p Dave
50 * Fixed observer mode joining and ingame stuff.
52 * 82 9/14/99 3:26a Dave
53 * Fixed laser fogging problem in nebula (D3D)> Fixed multiplayer
54 * respawn-too-early problem. Made a few crash points safe.
56 * 81 9/13/99 4:52p Dave
59 * 80 9/08/99 10:01p Dave
60 * Make sure game won't run in a drive's root directory. Make sure
61 * standalone routes suqad war messages properly to the host.
63 * 79 8/28/99 4:54p Dave
64 * Fixed directives display for multiplayer clients for wings with
65 * multiple waves. Fixed hud threat indicator rendering color.
67 * 78 8/27/99 12:32a Dave
68 * Allow the user to specify a local port through the launcher.
70 * 77 8/26/99 8:51p Dave
71 * Gave multiplayer TvT messaging a heavy dose of sanity. Cheat codes.
73 * 76 8/25/99 4:38p Dave
74 * Updated PXO stuff. Make squad war report stuff much more nicely.
76 * 75 8/24/99 1:50a Dave
77 * Fixed client-side afterburner stuttering. Added checkbox for no version
78 * checking on PXO join. Made button info passing more friendly between
81 * 74 8/22/99 5:53p Dave
82 * Scoring fixes. Added self destruct key. Put callsigns in the logfile
83 * instead of ship designations for multiplayer players.
85 * 73 8/22/99 1:55p Dave
86 * Cleaned up host/team-captain leaving code.
88 * 72 8/22/99 1:19p Dave
89 * Fixed up http proxy code. Cleaned up scoring code. Reverse the order in
90 * which d3d cards are detected.
92 * 71 8/19/99 10:59a Dave
93 * Packet loss detection.
95 * 70 8/17/99 1:12p Dave
96 * Send TvT update when a client has finished joining so he stays nice and
99 * 69 8/16/99 4:05p Dave
100 * Big honking checkin.
102 * 68 8/11/99 5:54p Dave
103 * Fixed collision problem. Fixed standalone ghost problem.
105 * 67 8/06/99 9:46p Dave
106 * Hopefully final changes for the demo.
108 * 66 8/05/99 2:06a Dave
111 * 65 7/30/99 7:01p Dave
112 * Dogfight escort gauge. Fixed up laser rendering in Glide.
114 * 64 7/29/99 5:41p Jefff
115 * Sound hooks for cmeasure success
117 * 63 7/28/99 5:34p Dave
118 * Nailed the missing stats bug to the wall. Problem was optimized build
119 * and using GET_DATA() with array elements. BLECH.
121 * 62 7/26/99 5:50p Dave
122 * Revised ingame join. Better? We'll see....
124 * 61 7/24/99 1:54p Dave
125 * Hud text flash gauge. Reworked dead popup to use 4 buttons in red-alert
128 * 60 7/22/99 7:17p Dave
129 * Fixed excessive whacks in multiplayer.
131 * 59 7/08/99 10:53a Dave
132 * New multiplayer interpolation scheme. Not 100% done yet, but still
133 * better than the old way.
135 * 58 7/03/99 5:50p Dave
136 * Make rotated bitmaps draw properly in padlock views.
138 * 57 7/03/99 4:08p Dave
139 * Fixed wss_slots size issues. Fixed potentially nasty bug in low level
142 * 56 6/21/99 7:24p Dave
143 * netplayer pain packet. Added type E unmoving beams.
145 * 55 6/18/99 5:16p Dave
146 * Added real beam weapon lighting. Fixed beam weapon sounds. Added MOTD
147 * dialog to PXO screen.
149 * 54 6/16/99 4:06p Dave
150 * New pilot info popup. Added new draw-bitmap-as-poly function.
152 * 53 6/16/99 10:20a Dave
153 * Added send-message-list sexpression.
155 * 52 6/04/99 3:52p Anoop
156 * Removed bogus assert.
158 * 51 6/01/99 8:35p Dave
159 * Finished lockarm weapons. Added proper supercap weapons/damage. Added
160 * awacs-set-radius sexpression.
162 * 50 5/21/99 5:03p Andsager
163 * Add code to display engine wash death. Modify ship_kill_packet
165 * 49 5/18/99 1:30p Dave
166 * Added muzzle flash table stuff.
168 * 48 5/14/99 1:59p Andsager
169 * Multiplayer message for subsystem cargo revealed.
171 * 47 5/14/99 12:15p Andsager
172 * Add vaporized to kill packet
174 * 46 5/03/99 8:32p Dave
175 * New version of multi host options screen.
177 * 45 4/30/99 12:18p Dave
178 * Several minor bug fixes.
180 * 44 4/29/99 2:29p Dave
181 * Made flak work much better in multiplayer.
183 * 43 4/28/99 11:13p Dave
184 * Temporary checkin of artillery code.
186 * 42 4/16/99 5:54p Dave
187 * Support for on/off style "stream" weapons. Real early support for
188 * target-painting lasers.
190 * 41 4/12/99 2:22p Dave
191 * More checks for dogfight stats.
193 * 40 4/09/99 2:21p Dave
194 * Multiplayer beta stuff. CD checking.
196 * 39 4/02/99 9:55a Dave
197 * Added a few more options in the weapons.tbl for beam weapons. Attempt
198 * at putting "pain" packets into multiplayer.
200 * 38 4/01/99 3:41p Anoop
201 * Removed bogus Int3().
203 * 37 3/19/99 9:51a Dave
204 * Checkin to repair massive source safe crash. Also added support for
205 * pof-style nebulae, and some new weapons code.
207 * 38 3/12/99 2:32p Anoop
208 * Removed bogus asserts.
210 * 37 3/11/99 11:41a Neilk
211 * Don't do multi_io_* operations in single-player
213 * 36 3/10/99 6:50p Dave
214 * Changed the way we buffer packets for all clients. Optimized turret
215 * fired packets. Did some weapon firing optimizations.
217 * 35 3/09/99 6:24p Dave
218 * More work on object update revamping. Identified several sources of
219 * unnecessary bandwidth.
221 * 34 3/08/99 7:03p Dave
222 * First run of new object update system. Looks very promising.
224 * 33 3/04/99 6:09p Dave
225 * Added in sexpressions for firing beams and checking for if a ship is
228 * 32 3/01/99 10:00a Dave
229 * Fxied several dogfight related stats bugs.
231 * 31 2/24/99 2:25p Dave
232 * Fixed up chatbox bugs. Made squad war reporting better. Fixed a respawn
233 * bug for dogfight more.
235 * 30 2/23/99 2:29p Dave
236 * First run of oldschool dogfight mode.
238 * 29 2/21/99 6:01p Dave
239 * Fixed standalone WSS packets.
241 * 28 2/21/99 1:48p Dave
242 * Some code for monitoring datarate for multiplayer in detail.
244 * 27 2/17/99 2:11p Dave
245 * First full run of squad war. All freespace and tracker side stuff
248 * 26 2/12/99 6:16p Dave
249 * Pre-mission Squad War code is 95% done.
251 * 25 2/11/99 3:08p Dave
252 * PXO refresh button. Very preliminary squad war support.
254 * 24 1/29/99 5:07p Dave
255 * Fixed multiplayer stuff. Put in multiplayer support for rapid fire
258 * 23 1/27/99 9:56a Dave
259 * Temporary checkin of beam weapons for Dan to make cool sounds.
261 * 22 1/26/99 6:33p Anoop
262 * Fixed multiplayer slot switching problem (be sure to remember that
263 * hinfo->id is player id# _not_ player index #)
265 * 21 1/24/99 11:37p Dave
266 * First full rev of beam weapons. Very customizable. Removed some bogus
267 * Int3()'s in low level net code.
269 * 20 1/15/99 4:37p Dave
270 * Potential fix for weapon pair problem.
272 * 19 1/14/99 6:06p Dave
273 * 100% full squad logo support for single player and multiplayer.
275 * 18 1/14/99 12:48a Dave
276 * Todo list bug fixes. Made a pass at putting briefing icons back into
277 * FRED. Sort of works :(
279 * 17 1/12/99 5:45p Dave
280 * Moved weapon pipeline in multiplayer to almost exclusively client side.
281 * Very good results. Bandwidth goes down, playability goes up for crappy
282 * connections. Fixed object update problem for ship subsystems.
284 * 16 1/08/99 4:56p Anoop
285 * Fixed a problem with wss request packets.
287 * 15 12/18/98 12:24p Markm
288 * Fixed a dumb bug where player image_filenames were not being passed
289 * properly in new players packet.
291 * 14 12/14/98 12:13p Dave
292 * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
295 * 13 11/30/98 1:07p Dave
296 * 16 bit conversion, first run.
298 * 12 11/20/98 4:08p Dave
299 * Fixed flak effect in multiplayer.
301 * 11 11/19/98 4:19p Dave
302 * Put IPX sockets back in psnet. Consolidated all multiplayer config
305 * 10 11/19/98 8:04a Dave
306 * Full support for D3-style reliable sockets. Revamped packet lag/loss
307 * system, made it receiver side and at the lowest possible level.
309 * 9 11/17/98 11:12a Dave
310 * Removed player identification by address. Now assign explicit id #'s.
312 * 8 11/12/98 11:50a Dave
313 * Multiplayer clients set flak range to be very long.
315 * 7 11/12/98 12:13a Dave
316 * Tidied code up for multiplayer test. Put in network support for flak
319 * 6 11/05/98 5:55p Dave
320 * Big pass at reducing #includes
322 * 5 10/20/98 1:39p Andsager
323 * Make so sparks follow animated ship submodels. Modify
324 * ship_weapon_do_hit_stuff() and ship_apply_local_damage() to add
325 * submodel_num. Add submodel_num to multiplayer hit packet.
327 * 4 10/13/98 9:29a Dave
328 * Started neatening up freespace.h. Many variables renamed and
329 * reorganized. Added AlphaColors.[h,cpp]
331 * 3 10/07/98 6:27p Dave
332 * Globalized mission and campaign file extensions. Removed Silent Threat
333 * special code. Moved \cache \players and \multidata into the \data
336 * 2 10/07/98 10:53a Dave
339 * 1 10/07/98 10:50a Dave
341 * 506 10/02/98 3:22p Allender
342 * fix up the -connect option and fix the -port option
344 * 505 10/02/98 11:45a Dave
345 * Fixed stupid chat message bug.
347 * 504 9/29/98 1:33p Dave
348 * Remove standalone only conditional compiles for pre 1.04 stuff.
350 * 503 9/28/98 1:54p Dave
351 * Make sure French and German don't xfer builtin files they don't have
354 * 502 9/20/98 7:19p Dave
355 * Added CHANGE_IFF packet.
357 * 501 9/17/98 3:08p Dave
358 * PXO to non-pxo game warning popup. Player icon stuff in create and join
359 * game screens. Upped server count refresh time in PXO to 35 secs (from
368 #include <io.h> // for findfirst/findnext, etc
371 #include "multimsgs.h"
372 #include "multiutil.h"
375 #include "multiteamselect.h"
376 #include "linklist.h"
377 #include "gamesequence.h"
378 #include "hudmessage.h"
379 #include "hudsquadmsg.h"
380 #include "freespace.h"
384 #include "missiongoals.h"
385 #include "missionparse.h"
386 #include "missionlog.h"
387 #include "missionmessage.h"
388 #include "missionbrief.h"
390 #include "cmeasure.h"
391 #include "model.h" // for some limits
392 #include "afterburner.h"
393 #include "stand_gui.h"
394 #include "multi_xfer.h"
400 #include "managepilot.h"
401 #include "hudsquadmsg.h"
403 #include "missionweaponchoice.h"
404 #include "missionshipchoice.h"
405 #include "fireballs.h"
408 #include "multi_ingame.h"
409 #include "multiteamselect.h"
411 #include "multi_campaign.h"
412 #include "multi_team.h"
413 #include "multi_respawn.h"
414 #include "multi_observer.h"
415 #include "multi_voice.h"
416 #include "asteroid.h"
417 #include "multi_pmsg.h"
418 #include "multi_data.h"
419 #include "multi_options.h"
420 #include "objcollide.h"
421 #include "hudreticle.h"
422 #include "multi_pause.h"
423 #include "multi_endgame.h"
424 #include "missiondebrief.h"
425 #include "multi_obj.h"
426 #include "multi_log.h"
428 #include "multi_kick.h"
432 #include "multi_rate.h"
433 #include "neblightning.h"
434 #include "hudescort.h"
436 // #define _MULTI_SUPER_WACKY_COMPRESSION
438 #ifdef _MULTI_SUPER_WACKY_COMPRESSION
440 #define MAX_CODE ( ( 1 << BITS ) - 1 )
441 #define TABLE_SIZE 35023L
442 #define END_OF_STREAM 256
443 #define BUMP_CODE 257
444 #define FLUSH_CODE 258
445 #define FIRST_CODE 259
454 static DICTIONARY dict[TABLE_SIZE];
455 static char decode_stack[TABLE_SIZE];
456 static uint next_code;
457 static int current_code_bits;
458 static uint next_bump_code;
460 typedef struct BitBuf {
466 void output_bits( BitBuf *bitbuf, uint code, int count )
470 mask = 1L << ( count - 1 );
473 bitbuf->rack |= bitbuf->mask;
475 if ( bitbuf->mask == 0 ) {
476 *bitbuf->data++=(ubyte)bitbuf->rack;
484 uint input_bits( BitBuf *bitbuf, int bit_count )
489 mask = 1L << ( bit_count - 1 );
492 if ( bitbuf->mask == 0x80 ) {
493 bitbuf->rack = *bitbuf->data++;
494 if ( bitbuf->rack == EOF )
495 return END_OF_STREAM;
497 if ( bitbuf->rack & bitbuf->mask )
498 return_value |= mask;
501 if ( bitbuf->mask == 0 )
504 return( return_value );
508 static void InitializeDictionary()
512 for ( i = 0 ; i < TABLE_SIZE ; i++ )
513 dict[i].code_value = UNUSED;
515 next_code = FIRST_CODE;
516 current_code_bits = 9;
517 next_bump_code = 511;
521 static uint find_child_node( int parent_code, int child_character )
526 index = ( child_character << ( BITS - 8 ) ) ^ parent_code;
530 offset = TABLE_SIZE - index;
532 if ( dict[ index ].code_value == UNUSED )
533 return( (uint) index );
534 if ( dict[ index ].parent_code == parent_code &&
535 dict[ index ].character == (char) child_character )
537 if ( (int) index >= offset )
540 index += TABLE_SIZE - offset;
545 static uint decode_string( uint count, uint code )
547 while ( code > 255 ) {
548 decode_stack[ count++ ] = dict[ code ].character;
549 code = dict[ code ].parent_code;
551 decode_stack[ count++ ] = (char) code;
555 int lzw_compress( ubyte *outputbuf, ubyte *inputbuf, int input_size )
563 // Init output bit buffer
566 output.data = outputbuf;
568 InitializeDictionary();
570 string_code = *inputbuf++;
572 for ( i=1 ; i<input_size ; i++ ) {
573 character = *inputbuf++;
574 index = find_child_node( string_code, character );
575 if ( dict[ index ].code_value != - 1 )
576 string_code = dict[ index ].code_value;
578 dict[ index ].code_value = next_code++;
579 dict[ index ].parent_code = string_code;
580 dict[ index ].character = (char) character;
581 output_bits( &output, (unsigned long) string_code, current_code_bits );
582 string_code = character;
583 if ( next_code > MAX_CODE ) {
584 output_bits( &output, (unsigned long) FLUSH_CODE, current_code_bits );
585 InitializeDictionary();
586 } else if ( next_code > next_bump_code ) {
587 output_bits( &output, (unsigned long) BUMP_CODE, current_code_bits );
589 next_bump_code <<= 1;
594 output_bits( &output, (unsigned long) string_code, current_code_bits );
595 output_bits( &output, (unsigned long) END_OF_STREAM, current_code_bits);
597 if ( output.mask != 0x80 )
598 *output.data++ = (ubyte)output.rack;
600 return output.data-outputbuf;
604 int lzw_expand( ubyte *outputbuf, ubyte *inputbuf )
615 input.data = inputbuf;
619 InitializeDictionary();
620 old_code = (uint) input_bits( &input, current_code_bits );
621 if ( old_code == END_OF_STREAM )
623 character = old_code;
624 outputbuf[counter++] = ( ubyte )old_code;
626 new_code = (uint) input_bits( &input, current_code_bits );
627 if ( new_code == END_OF_STREAM )
629 if ( new_code == FLUSH_CODE )
631 if ( new_code == BUMP_CODE ) {
635 if ( new_code >= next_code ) {
636 decode_stack[ 0 ] = (char) character;
637 count = decode_string( 1, old_code );
639 count = decode_string( 0, new_code );
641 character = decode_stack[ count - 1 ];
643 outputbuf[counter++] = ( ubyte )decode_stack[ --count ];
644 dict[ next_code ].parent_code = old_code;
645 dict[ next_code ].character = (char) character;
653 // process a join request packet add
654 void add_join_request(ubyte *data, int *size, join_request *jr)
656 int packet_size = *size;
657 join_request *jr_tmp = jr;
659 jr_tmp->tracker_id = INTEL_INT(jr->tracker_id);
660 jr_tmp->player_options.flags = INTEL_INT(jr->player_options.flags);
661 jr_tmp->player_options.obj_update_level = INTEL_INT(jr->player_options.obj_update_level);
668 // process a join request packet get
669 void get_join_request(ubyte *data, int *size, join_request jr)
675 jr.tracker_id = INTEL_INT(jr.tracker_id);
676 jr.player_options.flags = INTEL_INT(jr.player_options.flags);
677 jr.player_options.obj_update_level = INTEL_INT(jr.player_options.obj_update_level);
682 void add_net_addr(ubyte *data, int *size, net_addr addr)
684 int packet_size = *size;
685 net_addr addr_tmp = addr;
687 addr_tmp.type = INTEL_INT(addr.type);
688 addr_tmp.port = INTEL_SHORT(addr.port);
695 void get_net_addr(ubyte *data, int *size, net_addr addr)
701 addr.type = INTEL_INT(addr.type);
702 addr.port = INTEL_SHORT(addr.port);
707 void add_vector_data(ubyte *data, int *size, vector vec)
709 int packet_size = *size;
711 ADD_FLOAT(vec.xyz.x);
712 ADD_FLOAT(vec.xyz.y);
713 ADD_FLOAT(vec.xyz.z);
718 void get_vector_data(ubyte *data, int *size, vector vec)
722 GET_FLOAT(vec.xyz.x);
723 GET_FLOAT(vec.xyz.y);
724 GET_FLOAT(vec.xyz.z);
729 // send the specified data packet to all players
730 void multi_io_send(net_player *pl, ubyte *data, int len)
733 if((pl == NULL) || (NET_PLAYER_NUM(pl) >= MAX_PLAYERS)){
737 // don't do it for single player
738 if(!(Game_mode & GM_MULTIPLAYER)){
743 if(MULTIPLAYER_CLIENT){
744 // Assert(pl == Net_player);
745 if(pl != Net_player){
749 // Assert(pl != Net_player);
750 if(pl == Net_player){
755 // If this packet will push the buffer over MAX_PACKET_SIZE, send the current send_buffer
756 if ((pl->s_info.unreliable_buffer_size + len) > MAX_PACKET_SIZE) {
757 multi_io_send_force(pl);
758 pl->s_info.unreliable_buffer_size = 0;
761 Assert((pl->s_info.unreliable_buffer_size + len) <= MAX_PACKET_SIZE);
763 memcpy(pl->s_info.unreliable_buffer + pl->s_info.unreliable_buffer_size, data, len);
764 pl->s_info.unreliable_buffer_size += len;
767 void multi_io_send_to_all(ubyte *data, int length, net_player *ignore)
770 Assert(MULTIPLAYER_MASTER);
772 // need to check for i > 1, hmmm... and connected. I don't know.
773 for (i = 0; i < MAX_PLAYERS; i++ ) {
774 if ( !MULTI_CONNECTED(Net_players[i]) || (Net_player == &Net_players[i])){
778 // maybe ignore a player
779 if((ignore != NULL) && (&Net_players[i] == ignore)){
783 // ingame joiners not waiting to select a ship doesn't get any packets
784 if ( (Net_players[i].flags & NETINFO_FLAG_INGAME_JOIN) && !(Net_players[i].flags & INGAME_JOIN_FLAG_PICK_SHIP) ){
789 multi_io_send(&Net_players[i], data, length);
793 void multi_io_send_force(net_player *pl)
796 if((pl == NULL) || (NET_PLAYER_NUM(pl) >= MAX_PLAYERS)){
800 // don't do it for single player
801 if(!(Game_mode & GM_MULTIPLAYER)){
805 // send everything in
806 if (MULTIPLAYER_MASTER) {
807 psnet_send(&pl->p_info.addr, pl->s_info.unreliable_buffer, pl->s_info.unreliable_buffer_size, NET_PLAYER_NUM(pl));
809 // add the bytes sent to this player
810 pl->sv_bytes_sent += pl->s_info.unreliable_buffer_size;
812 psnet_send(&Netgame.server_addr, pl->s_info.unreliable_buffer, pl->s_info.unreliable_buffer_size, NET_PLAYER_NUM(pl));
814 pl->s_info.unreliable_buffer_size = 0;
817 // send the data packet to all players via their reliable sockets
818 void multi_io_send_reliable(net_player *pl, ubyte *data, int len)
821 if((pl == NULL) || (NET_PLAYER_NUM(pl) >= MAX_PLAYERS)){
825 // don't do it for single player
826 if(!(Game_mode & GM_MULTIPLAYER)){
831 if(MULTIPLAYER_CLIENT){
832 // Assert(pl == Net_player);
833 if(pl != Net_player){
837 // Assert(pl != Net_player);
838 if(pl == Net_player){
843 // If this packet will push the buffer over MAX_PACKET_SIZE, send the current send_buffer
844 if ((pl->s_info.reliable_buffer_size + len) > MAX_PACKET_SIZE) {
845 multi_io_send_reliable_force(pl);
846 pl->s_info.reliable_buffer_size = 0;
849 Assert((pl->s_info.reliable_buffer_size + len) <= MAX_PACKET_SIZE);
851 memcpy(pl->s_info.reliable_buffer + pl->s_info.reliable_buffer_size, data, len);
852 pl->s_info.reliable_buffer_size += len;
855 void multi_io_send_to_all_reliable(ubyte* data, int length, net_player *ignore)
858 Assert(MULTIPLAYER_MASTER);
860 // need to check for i > 1, hmmm... and connected. I don't know.
861 for (i = 0; i < MAX_PLAYERS; i++ ) {
862 if ( !MULTI_CONNECTED(Net_players[i]) || (Net_player == &Net_players[i])){
866 // maybe ignore a player
867 if((ignore != NULL) && (&Net_players[i] == ignore)){
871 // ingame joiners not waiting to select a ship doesn't get any packets
872 if ( (Net_players[i].flags & NETINFO_FLAG_INGAME_JOIN) && !(Net_players[i].flags & INGAME_JOIN_FLAG_PICK_SHIP) ){
877 multi_io_send_reliable(&Net_players[i], data, length);
881 void multi_io_send_reliable_force(net_player *pl)
884 if((pl == NULL) || (NET_PLAYER_NUM(pl) >= MAX_PLAYERS)){
888 // don't do it for single player
889 if(!(Game_mode & GM_MULTIPLAYER)){
893 // send everything in
894 if(MULTIPLAYER_MASTER) {
895 psnet_rel_send(pl->reliable_socket, pl->s_info.reliable_buffer, pl->s_info.reliable_buffer_size, NET_PLAYER_NUM(pl));
896 } else if(Net_player != NULL){
897 psnet_rel_send(Net_player->reliable_socket, pl->s_info.reliable_buffer, pl->s_info.reliable_buffer_size, NET_PLAYER_NUM(pl));
899 pl->s_info.reliable_buffer_size = 0;
902 // send all buffered packets
903 void multi_io_send_buffered_packets()
907 // don't do it for single player
908 if(!(Game_mode & GM_MULTIPLAYER)){
913 if(MULTIPLAYER_MASTER){
914 for(idx=0; idx<MAX_PLAYERS; idx++){
915 if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx])){
916 // force unreliable data
917 if(Net_players[idx].s_info.unreliable_buffer_size > 0){
918 multi_io_send_force(&Net_players[idx]);
919 Net_players[idx].s_info.unreliable_buffer_size = 0;
922 // force reliable data
923 if(Net_players[idx].s_info.reliable_buffer_size > 0){
924 multi_io_send_reliable_force(&Net_players[idx]);
925 Net_players[idx].s_info.reliable_buffer_size = 0;
931 else if(Net_player != NULL){
932 // force unreliable data
933 if(Net_player->s_info.unreliable_buffer_size > 0){
934 multi_io_send_force(Net_player);
935 Net_player->s_info.unreliable_buffer_size = 0;
938 // force reliable data
939 if(Net_player->s_info.reliable_buffer_size > 0){
940 multi_io_send_reliable_force(Net_player);
941 Net_player->s_info.reliable_buffer_size = 0;
946 // 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)
947 void send_game_chat_packet(net_player *from, char *msg, int msg_mode, net_player *to, char *expr, int server_msg)
949 ubyte data[MAX_PACKET_SIZE],mode;
952 BUILD_HEADER(GAME_CHAT);
955 ADD_SHORT(from->player_id);
957 // add the message mode and if in MSG_TARGET mode, add who the target is
959 mode = (ubyte)msg_mode;
962 case MULTI_MSG_TARGET:
964 ADD_SHORT(to->player_id);
967 Assert(expr != NULL);
971 // add the message itself
974 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
976 // message all players
978 for(idx=0;idx<MAX_PLAYERS;idx++){
979 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (&Net_players[idx] != from)){
980 multi_io_send_reliable(&Net_players[idx], data, packet_size);
985 // message only friendly players
986 case MULTI_MSG_FRIENDLY:
987 for(idx=0;idx<MAX_PLAYERS;idx++){
988 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)){
989 multi_io_send_reliable(&Net_players[idx], data, packet_size);
994 // message only hostile players
995 case MULTI_MSG_HOSTILE:
996 for(idx=0;idx<MAX_PLAYERS;idx++){
997 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)){
998 multi_io_send_reliable(&Net_players[idx], data, packet_size);
1003 // message the player's target
1004 case MULTI_MSG_TARGET:
1006 if(MULTI_CONNECTED((*to)) && !MULTI_STANDALONE((*to))){
1007 multi_io_send_reliable(to, data, packet_size);
1011 // message all players who match the expression string
1012 case MULTI_MSG_EXPR:
1013 Assert(expr != NULL);
1014 for(idx=0;idx<MAX_PLAYERS;idx++){
1015 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (&Net_players[idx] != from) && multi_msg_matches_expr(&Net_players[idx],expr) ){
1016 multi_io_send_reliable(&Net_players[idx], data, packet_size);
1022 // send to the server, who will take care of routing it
1024 multi_io_send_reliable(Net_player, data, packet_size);
1028 // process a general game chat packet, if we're the standalone we should rebroadcast
1029 void process_game_chat_packet( ubyte *data, header *hinfo )
1033 int color_index,player_index,to_player_index,should_display,server_msg;
1034 char msg[MULTI_MSG_MAX_TEXT_LEN+CALLSIGN_LEN+2];
1038 offset = HEADER_LENGTH;
1040 // get the id of the sender
1043 // determine if this is a server message
1044 GET_INT(server_msg);
1049 // if targeting a specific player, get the address
1052 case MULTI_MSG_TARGET:
1055 case MULTI_MSG_EXPR:
1059 // get the message itself
1063 // get the index of the sending player
1064 color_index = find_player_id(from);
1065 player_index = color_index;
1067 // if we couldn't find the player - bail
1068 if(player_index == -1){
1069 nprintf(("Network","Could not find player for processing game chat packet!\n"));
1075 // if we're the server, determine what to do with the packet here
1076 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
1077 // if he's targeting a specific player, find out who it is
1078 if(mode == MULTI_MSG_TARGET){
1079 to_player_index = find_player_id(to);
1081 to_player_index = -1;
1084 // if we couldn't find who sent the message or who should be getting the message, the bail
1085 if(((to_player_index == -1) && (mode == MULTI_MSG_TARGET)) || (player_index == -1)){
1089 // determine if _I_ should be seeing the text
1090 if(Game_mode & GM_STANDALONE_SERVER){
1093 // check against myself for several specific cases
1095 if((mode == MULTI_MSG_ALL) ||
1096 ((mode == MULTI_MSG_FRIENDLY) && (Net_player->p_info.team == Net_players[player_index].p_info.team)) ||
1097 ((mode == MULTI_MSG_HOSTILE) && (Net_player->p_info.team != Net_players[player_index].p_info.team)) ||
1098 ((mode == MULTI_MSG_TARGET) && (MY_NET_PLAYER_NUM == to_player_index)) ||
1099 ((mode == MULTI_MSG_EXPR) && multi_msg_matches_expr(Net_player,expr)) ){
1104 // if we're the server of a game, we need to rebroadcast to all other players
1106 // individual target mission
1107 case MULTI_MSG_TARGET:
1108 // if I was the inteneded target, or we couldn't find the intended target, don't rebroadcast
1109 if(to_player_index != MY_NET_PLAYER_NUM){
1110 send_game_chat_packet(&Net_players[player_index], msg, (int)mode, &Net_players[to_player_index], NULL, server_msg);
1114 case MULTI_MSG_EXPR:
1115 send_game_chat_packet(&Net_players[player_index], msg, (int)mode, NULL, expr, server_msg);
1119 send_game_chat_packet(&Net_players[player_index], msg, (int)mode, NULL, NULL, server_msg);
1123 // if a client receives this packet, its always ok for him to display it
1128 // if we're not on a standalone
1130 if(server_msg == 2){
1133 multi_display_chat_msg(msg, player_index, !server_msg);
1138 // broadcast a hud message to all players
1139 void send_hud_msg_to_all( char* msg )
1141 ubyte data[MAX_PACKET_SIZE];
1144 // only the server should be sending this packet
1145 BUILD_HEADER(HUD_MSG);
1149 multi_io_send_to_all( data, packet_size );
1152 // process an incoming hud message packet
1153 void process_hud_message(ubyte* data, header* hinfo)
1156 char msg_buffer[255];
1158 offset = HEADER_LENGTH;
1160 GET_STRING(msg_buffer);
1163 // this is the only safe place to do this since only in the mission is the HUD guaranteed to be inited
1164 if(Game_mode & GM_IN_MISSION){
1165 HUD_printf(msg_buffer);
1169 // send a join packet request to the specified address (should be a server)
1170 void send_join_packet(net_addr* addr,join_request *jr)
1172 ubyte data[MAX_PACKET_SIZE];
1175 // build the header and add the request
1178 add_join_request(data, &packet_size, jr);
1180 psnet_send(addr, data, packet_size);
1183 // process an incoming join request packet
1184 void process_join_packet(ubyte* data, header* hinfo)
1189 int host_restr_mode;
1190 // int team0_avail,team1_avail;
1191 char join_string[255];
1194 // only the server of the game should ever receive this packet
1195 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) )
1198 offset = HEADER_LENGTH;
1200 // read in the request info
1201 memset(&jr,0,sizeof(join_request));
1204 jr.tracker_id = INTEL_INT(jr.tracker_id);
1208 // fill in the address information of where this came from
1209 fill_net_addr(&addr, hinfo->addr, hinfo->net_id, hinfo->port);
1211 // determine if we should accept this guy, or return a reason we should reject him
1212 // see the DENY_* codes in multi.h
1213 ret_code = multi_eval_join_request(&jr,&addr);
1215 // evaluate the return code
1217 // he should be accepted
1221 // we have to query the host because this is a restricted game
1222 case JOIN_QUERY_RESTRICTED :
1223 if(!(Game_mode & GM_STANDALONE_SERVER)){
1224 // notify the host of the event
1225 snd_play(&Snds[SND_CUE_VOICE]);
1228 // set the query timestamp
1229 Multi_restr_query_timestamp = timestamp(MULTI_QUERY_RESTR_STAMP);
1230 Netgame.flags |= NG_FLAG_INGAME_JOINING;
1232 // determine what mode we're in
1233 host_restr_mode = -1;
1234 memset(join_string,0,255);
1235 // if(Netgame.type == NG_TYPE_TEAM){
1236 // multi_player_ships_available(&team0_avail,&team1_avail);
1238 // if(team0_avail && team1_avail){
1239 // host_restr_mode = MULTI_JOIN_RESTR_MODE_4;
1240 // sprintf(join_string,"Player %s has tried to join. Accept on team 1 or 2 ?",jr.callsign);
1241 // } else if(team0_avail && !team1_avail){
1242 // host_restr_mode = MULTI_JOIN_RESTR_MODE_2;
1243 // sprintf(join_string,"Player %s has tried to join team 0, accept y/n ? ?",jr.callsign);
1244 // } else if(!team0_avail && team1_avail){
1245 // host_restr_mode = MULTI_JOIN_RESTR_MODE_3;
1246 // sprintf(join_string,"Player %s has tried to join team 1, accept y/n ?",jr.callsign);
1248 // } else if(Netgame.mode == NG_MODE_RESTRICTED){
1249 host_restr_mode = MULTI_JOIN_RESTR_MODE_1;
1250 sprintf(join_string,XSTR("Player %s has tried to join, accept y/n ?",715),jr.callsign);
1252 Assert(host_restr_mode != -1);
1254 // store the request info
1255 memcpy(&Multi_restr_join_request,&jr,sizeof(join_request));
1256 memcpy(&Multi_restr_addr,&addr,sizeof(net_addr));
1257 Multi_join_restr_mode = host_restr_mode;
1259 // if i'm the standalone server, I need to send a query to the host
1260 if(Game_mode & GM_STANDALONE_SERVER){
1261 send_host_restr_packet(jr.callsign,0,Multi_join_restr_mode);
1263 HUD_printf(join_string);
1267 ml_printf(NOX("Receive restricted join request from %s"), jr.callsign);
1271 // he'e being denied for some reason
1273 // send him the reason he is being denied
1274 send_deny_packet(&addr,ret_code);
1278 // process the rest of the request
1279 multi_process_valid_join_request(&jr,&addr);
1282 // send a notification that a new player has joined the game (if target != NULL, broadcast the packet)
1283 void send_new_player_packet(int new_player_num,net_player *target)
1285 ubyte data[MAX_PACKET_SIZE], val;
1286 int packet_size = 0;
1288 BUILD_HEADER( NOTIFY_NEW_PLAYER );
1290 // add the new player's info
1291 ADD_INT(new_player_num);
1292 // ADD_DATA(Net_players[new_player_num].p_info.addr);
1294 add_net_addr(data, &packet_size, Net_players[new_player_num].p_info.addr);
1296 ADD_SHORT(Net_players[new_player_num].player_id);
1297 ADD_INT(Net_players[new_player_num].flags);
1298 ADD_STRING(Net_players[new_player_num].player->callsign);
1299 ADD_STRING(Net_players[new_player_num].player->image_filename);
1300 ADD_STRING(Net_players[new_player_num].player->squad_filename);
1301 ADD_STRING(Net_players[new_player_num].p_info.pxo_squad_name);
1303 val = (ubyte)Net_players[new_player_num].p_info.team;
1306 // broadcast the data
1308 multi_io_send_reliable(target, data, packet_size);
1310 multi_io_send_to_all_reliable(data, packet_size);
1314 // process a notification for a new player who has joined the game
1315 void process_new_player_packet(ubyte* data, header* hinfo)
1317 int already_in_game = 0;
1318 int offset, new_player_num,player_num,new_flags;
1320 char new_player_name[CALLSIGN_LEN+2] = "";
1321 char new_player_image[MAX_FILENAME_LEN+1] = "";
1322 char new_player_squad[MAX_FILENAME_LEN+1] = "";
1323 char new_player_pxo_squad[LOGIN_LEN+1] = "";
1324 char notify_string[256];
1328 offset = HEADER_LENGTH;
1330 // get the new players information
1331 GET_INT(new_player_num);
1332 get_net_addr(data, &offset, new_addr);
1336 GET_STRING(new_player_name);
1337 GET_STRING(new_player_image);
1338 GET_STRING(new_player_squad);
1339 GET_STRING(new_player_pxo_squad);
1343 player_num = multi_find_open_player_slot();
1344 Assert(player_num != -1);
1346 // note that this new code does not check for duplicate IPs. It merely checks to see if
1347 // the slot referenced by new_player_num is already occupied by a connected player
1348 if(MULTI_CONNECTED(Net_players[new_player_num])){
1352 // if he's not alreayd in the game for one reason or another
1353 if ( !already_in_game ) {
1354 if ( Game_mode & GM_IN_MISSION ){
1355 HUD_sourced_printf(HUD_SOURCE_COMPUTER, XSTR("%s has entered the game\n",716), new_player_name);
1358 // create the player
1359 memcpy(new_addr.net_id, Psnet_my_addr.net_id, 4);
1361 if(new_flags & NETINFO_FLAG_OBSERVER){
1362 multi_obs_create_player(new_player_num,new_player_name,&new_addr,&Players[player_num]);
1363 Net_players[new_player_num].flags |= new_flags;
1365 multi_create_player( new_player_num, &Players[player_num],new_player_name, &new_addr, -1, new_id );
1366 Net_players[new_player_num].flags |= new_flags;
1369 // copy in the filename
1370 if(strlen(new_player_image) > 0){
1371 strcpy(Net_players[new_player_num].player->image_filename, new_player_image);
1373 strcpy(Net_players[new_player_num].player->image_filename, "");
1375 // copy his pilot squad filename
1376 Net_players[new_player_num].player->insignia_texture = -1;
1377 player_set_squad_bitmap(Net_players[new_player_num].player, new_player_squad);
1379 // copy in his pxo squad name
1380 strcpy(Net_players[new_player_num].p_info.pxo_squad_name, new_player_pxo_squad);
1382 // since we just created the player, set the last_heard_time here.
1383 Net_players[new_player_num].last_heard_time = timer_get_fixed_seconds();
1385 Net_players[new_player_num].p_info.team = team;
1387 Net_players[new_player_num].player_id = new_id;
1389 // zero out this players ping
1390 multi_ping_reset(&Net_players[new_player_num].s_info.ping);
1392 // add a chat message
1393 if(Net_players[new_player_num].player->callsign != NULL){
1394 sprintf(notify_string,XSTR("<%s has joined>",717),Net_players[new_player_num].player->callsign);
1395 multi_display_chat_msg(notify_string,0,0);
1400 ml_printf(NOX("Received notification of new player %s"), Net_players[new_player_num].player->callsign);
1402 // let the current ui screen know someone joined
1403 switch(gameseq_get_state()){
1404 case GS_STATE_MULTI_HOST_SETUP :
1405 multi_create_handle_join(&Net_players[new_player_num]);
1407 case GS_STATE_MULTI_CLIENT_SETUP :
1408 multi_jw_handle_join(&Net_players[new_player_num]);
1413 #define PLAYER_DATA_SLOP 100
1415 void send_accept_player_data( net_player *npp, int is_ingame )
1419 ubyte data[MAX_PACKET_SIZE], stop;
1421 BUILD_HEADER(ACCEPT_PLAYER_DATA);
1423 // add in the netplayer data for all players
1425 for (i=0; i<MAX_PLAYERS; i++) {
1426 // skip non connected players
1427 if ( !MULTI_CONNECTED(Net_players[i]) ){
1431 // skip this new player's entry
1432 if ( npp->player_id == Net_players[i].player_id ){
1436 // add the stop byte
1439 // add the player's number
1442 // add the player's address
1443 // ADD_DATA(Net_players[i].p_info.addr);
1444 add_net_addr(data, &packet_size, Net_players[i].p_info.addr);
1447 ADD_SHORT(Net_players[i].player_id);
1450 ADD_STRING(Net_players[i].player->callsign);
1452 // add his image filename
1453 ADD_STRING(Net_players[i].player->image_filename);
1455 // add his squad filename
1456 ADD_STRING(Net_players[i].player->squad_filename);
1458 // add his PXO squad name
1459 ADD_STRING(Net_players[i].p_info.pxo_squad_name);
1462 ADD_INT(Net_players[i].flags);
1464 // add his object's net sig
1466 ADD_USHORT( Objects[Net_players[i].player->objnum].net_signature );
1469 if ( (packet_size + PLAYER_DATA_SLOP) > MAX_PACKET_SIZE ) {
1470 stop = APD_END_PACKET;
1472 multi_io_send_reliable( npp, data, packet_size );
1473 BUILD_HEADER(ACCEPT_PLAYER_DATA);
1479 // add the stop byte
1480 stop = APD_END_DATA;
1482 multi_io_send_reliable(npp, data, packet_size);
1485 // send an accept packet to a client in response to a request to join the game
1486 void send_accept_packet(int new_player_num, int code, int ingame_join_team)
1489 ubyte data[MAX_PACKET_SIZE],val;
1490 char notify_string[256];
1493 Assert(new_player_num >= 0);
1495 // setup his "reliable" socket
1496 Net_players[new_player_num].last_heard_time = timer_get_fixed_seconds();
1498 // build the packet header
1500 BUILD_HEADER(ACCEPT);
1502 // add the accept code
1505 // add code specific accept data
1506 if (code & ACCEPT_INGAME) {
1507 // the game filename
1508 ADD_STRING(Game_current_mission_filename);
1510 // if he is joining on a specific team, mark it here
1511 if(ingame_join_team != -1){
1514 val = (ubyte)ingame_join_team;
1522 if (code & ACCEPT_OBSERVER) {
1523 Assert(!(code & (ACCEPT_CLIENT | ACCEPT_HOST)));
1526 if (code & ACCEPT_HOST) {
1527 Assert(!(code & (ACCEPT_CLIENT | ACCEPT_OBSERVER | ACCEPT_INGAME)));
1530 if (code & ACCEPT_CLIENT) {
1531 Assert(!(code & (ACCEPT_HOST | ACCEPT_OBSERVER | ACCEPT_INGAME)));
1534 // add the current skill level setting on the host
1535 ADD_INT(Game_skill_level);
1537 // add this guys player num
1538 ADD_INT(new_player_num);
1540 // add his player id
1541 ADD_SHORT(Net_players[new_player_num].player_id);
1543 // add netgame type flags
1544 ADD_INT(Netgame.type_flags);
1547 // char buffer[100];
1548 // nprintf(("Network", "About to send accept packet to %s on port %d\n", get_text_address(buffer, addr->addr), addr->port ));
1551 // actually send the packet
1552 psnet_send(&Net_players[new_player_num].p_info.addr, data, packet_size);
1554 // if he's not an observer, inform all the other players in the game about him
1555 // inform the other players in the game about this new player
1556 for (i=0; i<MAX_PLAYERS; i++) {
1557 // skip unconnected players as well as this new guy himself
1558 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])) {
1562 // send the new packet
1563 send_new_player_packet(new_player_num,&Net_players[i]);
1566 // add a chat message
1567 if(Net_players[new_player_num].player->callsign != NULL){
1568 sprintf(notify_string,XSTR("<%s has joined>",717), Net_players[new_player_num].player->callsign);
1569 multi_display_chat_msg(notify_string, 0, 0);
1572 // handle any team vs. team details
1573 if (!(code & ACCEPT_OBSERVER)) {
1574 multi_team_handle_join(&Net_players[new_player_num]);
1578 if(Net_players[new_player_num].tracker_player_id >= 0){
1579 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);
1581 ml_printf(NOX("Server accepted %s as new client"), Net_players[new_player_num].player->callsign);
1586 // process the player data from the server
1587 void process_accept_player_data( ubyte *data, header *hinfo )
1589 int offset, player_num, player_slot_num, new_flags;
1590 char name[CALLSIGN_LEN + 1] = "";
1591 char image_name[MAX_FILENAME_LEN + 1] = "";
1592 char squad_name[MAX_FILENAME_LEN + 1] = "";
1593 char pxo_squad_name[LOGIN_LEN+1] = "";
1597 ushort ig_signature;
1599 offset = HEADER_LENGTH;
1602 while ( stop == APD_NEXT ) {
1603 player_slot_num = multi_find_open_player_slot();
1604 Assert(player_slot_num != -1);
1606 // get the player's number
1607 GET_INT(player_num);
1609 // add the player's address
1610 get_net_addr(data, &offset, addr);
1612 // get the player's id#
1613 GET_SHORT(player_id);
1618 // add his image filename
1619 GET_STRING(image_name);
1621 // get his squad logo filename
1622 GET_STRING(squad_name);
1624 // get his PXO squad name
1625 GET_STRING(pxo_squad_name);
1630 if (Net_players[player_num].flags & NETINFO_FLAG_OBSERVER) {
1631 if (!multi_obs_create_player(player_num, name, &addr, &Players[player_slot_num])) {
1636 // the error handling here is less than stellar. We should probably put up a popup and go
1637 // back to the main menu. But then again, this should never ever happen!
1638 if ( !multi_create_player(player_num, &Players[player_slot_num],name, &addr, -1, player_id) ) {
1643 // copy his image filename
1644 strcpy(Net_players[player_num].player->image_filename, image_name);
1646 // copy his pilot squad filename
1647 Net_players[player_num].player->insignia_texture = -1;
1648 player_set_squad_bitmap(Net_players[player_num].player, squad_name);
1650 // copy his pxo squad name
1651 strcpy(Net_players[player_num].p_info.pxo_squad_name, pxo_squad_name);
1653 // set his player id#
1654 Net_players[player_num].player_id = player_id;
1656 // mark him as being connected
1657 Net_players[player_num].flags |= NETINFO_FLAG_CONNECTED;
1658 Net_players[player_num].flags |= new_flags;
1660 // set the server pointer
1661 if ( Net_players[player_num].flags & NETINFO_FLAG_AM_MASTER ) {
1662 Netgame.server = &Net_players[player_num];
1663 Netgame.server->last_heard_time = timer_get_fixed_seconds();
1665 // also - always set the server address to be where this data came from, NOT from
1666 // the data in the packet
1667 fill_net_addr(&Net_players[player_num].p_info.addr, hinfo->addr, hinfo->net_id, hinfo->port);
1670 // set the host pointer
1671 if ( Net_players[player_num].flags & NETINFO_FLAG_GAME_HOST ) {
1672 Netgame.host = &Net_players[player_num];
1675 // read in the player's object net signature and store as his objnum for now
1676 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_INGAME ) {
1677 GET_USHORT( ig_signature );
1678 Net_players[player_num].player->objnum = ig_signature;
1681 // get the stop byte
1686 if ( stop == APD_END_DATA ) {
1687 // if joining a game automatically, set the connect address to NULl so we don't try and
1688 // do this next time we enter a game
1689 if (Cmdline_connect_addr != NULL) {
1690 Cmdline_connect_addr = NULL;
1693 // send my stats to the server if I'm not in observer mode
1694 if (!(Net_player->flags & NETINFO_FLAG_ACCEPT_OBSERVER)) {
1695 send_player_stats_block_packet(Net_player, STATS_ALLTIME);
1698 // if i'm being accepted as a host, then move into the host setup state
1699 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_HOST) {
1700 // set my permission bits
1701 Net_player->flags |= NETINFO_FLAG_GAME_HOST;
1702 Net_player->state = NETPLAYER_STATE_STD_HOST_SETUP;
1704 gameseq_post_event(GS_EVENT_MULTI_START_GAME);
1707 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_OBSERVER) {
1708 Net_player->flags |= NETINFO_FLAG_OBSERVER;
1710 // since observers can join 1 of 2 ways, only do this if we're not doing an ingame observer join
1711 if ( !(Net_player->flags & NETINFO_FLAG_ACCEPT_INGAME) ) {
1712 gameseq_post_event(GS_EVENT_MULTI_CLIENT_SETUP);
1716 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_CLIENT) {
1717 gameseq_post_event(GS_EVENT_MULTI_CLIENT_SETUP);
1720 if ( Net_player->flags & NETINFO_FLAG_ACCEPT_INGAME) {
1721 // flag myself as being an ingame joiner
1722 Net_player->flags |= NETINFO_FLAG_INGAME_JOIN;
1724 // move myself into the ingame join mission sync state
1725 Multi_sync_mode = MULTI_SYNC_INGAME;
1726 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
1729 // update my options on the server
1730 multi_options_update_local();
1732 // if we're in PXO mode, mark it down in our player struct
1733 if(MULTI_IS_TRACKER_GAME){
1734 Player->flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
1735 Player->save_flags |= PLAYER_FLAGS_HAS_PLAYED_PXO;
1740 // process an accept packet from the server
1741 extern int Select_default_ship;
1743 void process_accept_packet(ubyte* data, header* hinfo)
1745 int code, my_player_num, offset;
1749 // get the accept code
1750 offset = HEADER_LENGTH;
1754 // read in the accept code specific data
1756 if (code & ACCEPT_INGAME) {
1757 // the game filename
1758 GET_STRING(Game_current_mission_filename);
1759 Select_default_ship = 0;
1761 // determine if I'm being placed on a team
1768 if (code & ACCEPT_OBSERVER) {
1769 Assert(!(code & (ACCEPT_CLIENT | ACCEPT_HOST)));
1772 if (code & ACCEPT_HOST) {
1773 Assert(!(code & (ACCEPT_CLIENT | ACCEPT_OBSERVER | ACCEPT_INGAME)));
1776 if (code & ACCEPT_CLIENT) {
1777 Assert(!(code & (ACCEPT_HOST | ACCEPT_OBSERVER | ACCEPT_INGAME)));
1780 // fill in the netgame server address
1781 fill_net_addr( &Netgame.server_addr, hinfo->addr, hinfo->net_id, hinfo->port );
1783 // get the skill level setting
1784 GET_INT(Game_skill_level);
1786 // get my netplayer number
1787 GET_INT(my_player_num);
1790 GET_SHORT(player_id);
1792 // get netgame type flags
1793 GET_INT(Netgame.type_flags);
1795 // setup the Net_players structure for myself first
1796 Net_player = &Net_players[my_player_num];
1797 Net_player->flags = 0;
1798 Net_player->tracker_player_id = Multi_tracker_id;
1799 Net_player->player_id = player_id;
1800 Net_player->s_info.xfer_handle = -1;
1801 // stuff_netplayer_info( Net_player, &Psnet_my_addr, Ships[Objects[Player->objnum].instance].ship_info_index, Player );
1802 stuff_netplayer_info( Net_player, &Psnet_my_addr, 0, Player );
1803 multi_options_local_load(&Net_player->p_info.options, Net_player);
1805 Net_player->p_info.team = team;
1808 // determine if I have a CD
1810 Net_player->flags |= NETINFO_FLAG_HAS_CD;
1813 // set accept code in netplayer for this guy
1814 if ( code & ACCEPT_INGAME ){
1815 Net_player->flags |= NETINFO_FLAG_ACCEPT_INGAME;
1817 if ( code & ACCEPT_OBSERVER ){
1818 Net_player->flags |= NETINFO_FLAG_ACCEPT_OBSERVER;
1820 if ( code & ACCEPT_HOST ){
1821 Net_player->flags |= NETINFO_FLAG_ACCEPT_HOST;
1823 if ( code & ACCEPT_CLIENT ){
1824 Net_player->flags |= NETINFO_FLAG_ACCEPT_CLIENT;
1827 // if I have hacked data
1828 if(game_hacked_data()){
1829 Net_player->flags |= NETINFO_FLAG_HAXOR;
1832 // if we're supposed to flush our local data cache, do so now
1833 if(Net_player->p_info.options.flags & MLO_FLAG_FLUSH_CACHE){
1834 multi_flush_multidata_cache();
1837 Net_player->sv_bytes_sent = 0;
1838 Net_player->sv_last_pl = -1;
1839 Net_player->cl_bytes_recvd = 0;
1840 Net_player->cl_last_pl = -1;
1842 // intiialize endgame stuff
1843 multi_endgame_init();
1847 // make a call to psnet to initialize and try to connect with the server.
1848 psnet_rel_connect_to_server( &Net_player->reliable_socket, &Netgame.server_addr );
1849 if ( Net_player->reliable_socket == INVALID_SOCKET ) {
1850 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_CONNECT_FAIL);
1854 // send a notice that the player at net_addr is leaving (if target is NULL, the broadcast the packet)
1855 void send_leave_game_packet(short player_id, int kicked_reason, net_player *target)
1857 ubyte data[MAX_PACKET_SIZE];
1859 int packet_size = 0;
1861 BUILD_HEADER(LEAVE_GAME);
1863 // add a flag indicating whether he was kicked or not
1864 val = (char)kicked_reason;
1867 if (player_id < 0) {
1868 ADD_SHORT(Net_player->player_id);
1870 // inform the host that we are leaving the game
1871 if (Net_player->flags & NETINFO_FLAG_AM_MASTER) {
1872 multi_io_send_to_all_reliable(data, packet_size);
1874 multi_io_send_reliable(Net_player, data, packet_size);
1877 // this is the case where to server is tossing a player (or indicating a respawned player has quit or become an observer)
1878 // so he has to tell everyone that this guy left
1880 nprintf(("Network","Sending a leave game packet to all players (server)\n"));
1882 // a couple of important checks
1883 Assert(player_id != Net_player->player_id);
1884 Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
1886 // add the id of the guy to be kicked
1887 ADD_SHORT(player_id);
1889 // broadcast to everyone
1890 if (target == NULL) {
1891 multi_io_send_to_all_reliable(data, packet_size);
1893 multi_io_send_reliable(target, data, packet_size);
1898 // process a notification the a player has left the game
1899 void process_leave_game_packet(ubyte* data, header* hinfo)
1907 offset = HEADER_LENGTH;
1909 // get whether he was kicked
1910 GET_DATA(kicked_reason);
1912 // get the address of the guy who is to leave
1913 GET_SHORT(deader_id);
1916 // determine who is dropping and printf out a notification
1917 player_num = find_player_id(deader_id);
1918 if (player_num == -1) {
1919 nprintf(("Network", "Received leave game packet for unknown player, ignoring\n"));
1923 nprintf(("Network", "Received a leave game notice for %s\n", Net_players[player_num].player->callsign));
1926 // a hook to display that a player was kicked
1927 if (kicked_reason >= 0){
1928 // if it was me that was kicked, leave the game
1929 if((Net_player != NULL) && (Net_player->player_id == deader_id)){
1932 switch(kicked_reason){
1933 case KICK_REASON_BAD_XFER:
1934 notify_code = MULTI_END_NOTIFY_KICKED_BAD_XFER;
1936 case KICK_REASON_CANT_XFER:
1937 notify_code = MULTI_END_NOTIFY_KICKED_CANT_XFER;
1939 case KICK_REASON_INGAME_ENDED:
1940 notify_code = MULTI_END_NOTIFY_KICKED_INGAME_ENDED;
1943 notify_code = MULTI_END_NOTIFY_KICKED;
1947 multi_quit_game(PROMPT_NONE, notify_code);
1950 // otherwise indicate someone was kicked
1952 nprintf(("Network","%s was kicked\n",Net_players[player_num].player->callsign));
1954 // display the result
1955 memset(str, 0, 512);
1956 multi_kick_get_text(&Net_players[player_num], kicked_reason, str);
1957 multi_display_chat_msg(str, player_num, 0);
1961 // first of all, if we're the master, we should be rebroadcasting this packet
1962 if (Net_player->flags & NETINFO_FLAG_AM_MASTER) {
1965 sprintf(msg, XSTR("%s has left the game",719), Net_players[player_num].player->callsign );
1967 if (!(Game_mode & GM_STANDALONE_SERVER)){
1968 HUD_sourced_printf(HUD_SOURCE_HIDDEN, msg);
1971 send_hud_msg_to_all(msg);
1972 multi_io_send_to_all_reliable(data, offset);
1975 // leave the game if the host and/or master has dropped
1977 if (((Net_players[player_num].flags & NETINFO_FLAG_AM_MASTER) || (Net_players[player_num].flags & NETINFO_FLAG_GAME_HOST)) ) {
1978 nprintf(("Network","Host and/or server has left the game - aborting...\n"));
1981 ml_string(NOX("Host and/or server has left the game"));
1983 // if the host leaves in the debriefing state, we should still wait until the player selects accept before we quit
1984 if (gameseq_get_state() != GS_STATE_DEBRIEF) {
1985 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_SERVER_LEFT);
1988 delete_player(player_num);
1991 delete_player(player_num);
1993 // OSAPI GUI stuff (if standalone)
1994 if (Game_mode & GM_STANDALONE_SERVER) {
1995 // returns true if we should reset the standalone
1996 if (std_remove_player(&Net_players[player_num])) {
1997 nprintf(("Network", "Should reset!!\n"));
2001 // update these gui vals
2002 std_connect_set_host_connect_status();
2003 std_connect_set_connect_count();
2007 // send information about this currently active game to the specified address
2008 void send_game_active_packet(net_addr* addr)
2012 ubyte data[MAX_PACKET_SIZE],val;
2014 // build the header and add the data
2015 BUILD_HEADER(GAME_ACTIVE);
2017 // add the server version and compatible version #
2018 val = MULTI_FS_SERVER_VERSION;
2020 val = MULTI_FS_SERVER_COMPATIBLE_VERSION;
2023 ADD_STRING(Netgame.name);
2024 ADD_STRING(Netgame.mission_name);
2025 ADD_STRING(Netgame.title);
2026 val = (ubyte)multi_num_players();
2029 // add the proper flags
2031 if((Netgame.mode == NG_MODE_PASSWORD) || ((Game_mode & GM_STANDALONE_SERVER) && (multi_num_players() == 0) && (std_is_host_passwd()))){
2032 flags |= AG_FLAG_PASSWD;
2035 // proper netgame type flags
2036 if(Netgame.type_flags & NG_TYPE_TEAM){
2037 flags |= AG_FLAG_TEAMS;
2038 } else if(Netgame.type_flags & NG_TYPE_DOGFIGHT){
2039 flags |= AG_FLAG_DOGFIGHT;
2041 flags |= AG_FLAG_COOP;
2044 // proper netgame state flags
2045 switch(Netgame.game_state){
2046 case NETGAME_STATE_FORMING:
2047 flags |= AG_FLAG_FORMING;
2050 case NETGAME_STATE_BRIEFING:
2051 case NETGAME_STATE_MISSION_SYNC:
2052 case NETGAME_STATE_HOST_SETUP:
2053 flags |= AG_FLAG_BRIEFING;
2056 case NETGAME_STATE_IN_MISSION:
2057 flags |= AG_FLAG_IN_MISSION;
2060 case NETGAME_STATE_PAUSED:
2061 flags |= AG_FLAG_PAUSE;
2064 case NETGAME_STATE_ENDGAME:
2065 case NETGAME_STATE_DEBRIEF:
2066 flags |= AG_FLAG_DEBRIEF;
2070 // if this is a standalone
2071 if(Game_mode & GM_STANDALONE_SERVER){
2072 flags |= AG_FLAG_STANDALONE;
2075 // if we're in campaign mode
2076 if(Netgame.campaign_mode == MP_CAMPAIGN){
2077 flags |= AG_FLAG_CAMPAIGN;
2080 // add the data about the connection speed of the host machine
2081 Assert( (Multi_connection_speed >= 0) && (Multi_connection_speed <= 4) );
2082 flags |= (Multi_connection_speed << AG_FLAG_CONNECTION_BIT);
2087 psnet_send(addr, data, packet_size);
2090 // process information about an active game
2091 void process_game_active_packet(ubyte* data, header* hinfo)
2096 int modes_compatible;
2098 fill_net_addr(&ag.server_addr, hinfo->addr, hinfo->net_id, hinfo->port);
2100 // read this game into a temporary structure
2101 offset = HEADER_LENGTH;
2103 // get the server version and compatible version
2104 GET_DATA(ag.version);
2105 GET_DATA(ag.comp_version);
2107 GET_STRING(ag.name);
2108 GET_STRING(ag.mission_name);
2109 GET_STRING(ag.title);
2111 ag.num_players = val;
2112 GET_USHORT(ag.flags);
2116 modes_compatible = 1;
2118 if((ag.flags & AG_FLAG_TRACKER) && !Multi_options_g.pxo){
2119 modes_compatible = 0;
2121 if(!(ag.flags & AG_FLAG_TRACKER) && Multi_options_g.pxo){
2122 modes_compatible = 0;
2126 // if this is a compatible version, and our modes are compatible, register it
2127 if( (ag.version == MULTI_FS_SERVER_VERSION) && modes_compatible ){
2128 multi_update_active_games(&ag);
2132 // send_game_update_packet sends an updated Netgame structure to all players currently connected. The update
2133 // is used to change the current mission, current state, etc.
2134 void send_netgame_update_packet(net_player *pl)
2138 ubyte data[MAX_PACKET_SIZE];
2141 BUILD_HEADER(GAME_UPDATE);
2143 // with new mission description field, this becomes way to large
2144 // so we must add every element piece by piece except the
2145 ADD_STRING(Netgame.name);
2146 ADD_STRING(Netgame.mission_name);
2147 ADD_STRING(Netgame.title);
2148 ADD_STRING(Netgame.campaign_name);
2149 ADD_INT(Netgame.campaign_mode);
2150 ADD_INT(Netgame.max_players);
2151 ADD_INT(Netgame.security);
2152 ADD_UINT(Netgame.respawn);
2153 ADD_INT(Netgame.flags);
2154 ADD_INT(Netgame.type_flags);
2155 ADD_INT(Netgame.version_info);
2156 ADD_DATA(Netgame.debug_flags);
2158 // only the server should ever send the netgame state (standalone situation)
2159 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2160 ADD_INT(Netgame.game_state);
2163 // if we're the host on a standalone, send to the standalone and let him rebroadcast
2164 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2166 multi_io_send_to_all_reliable(data, packet_size);
2168 for(idx=0; idx<MAX_PLAYERS; idx++){
2169 if(MULTI_CONNECTED(Net_players[idx]) && (Net_player != &Net_players[idx])){
2170 send_netgame_descript_packet(&Net_players[idx].p_info.addr, 1);
2174 multi_io_send_reliable(pl, data, packet_size);
2175 send_netgame_descript_packet( &pl->p_info.addr , 1 );
2178 Assert( pl == NULL ); // I don't think that a host in a standalone game would get here.
2179 multi_io_send_reliable(Net_player, data, packet_size);
2182 // host should always send a netgame options update as well
2183 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
2184 multi_options_update_netgame();
2188 // process information about the netgame sent from the server/host
2189 void process_netgame_update_packet( ubyte *data, header *hinfo )
2191 int offset,old_flags;
2194 Assert(!(Game_mode & GM_STANDALONE_SERVER));
2195 Assert(!(Net_player->flags & NETINFO_FLAG_AM_MASTER));
2197 // read in the netgame information
2198 offset = HEADER_LENGTH;
2199 GET_STRING(Netgame.name);
2200 GET_STRING(Netgame.mission_name);
2201 GET_STRING(Netgame.title);
2202 GET_STRING(Netgame.campaign_name);
2203 GET_INT(Netgame.campaign_mode);
2204 GET_INT(Netgame.max_players); // ignore on the standalone, who keeps track of this himself
2205 GET_INT(Netgame.security);
2206 GET_UINT(Netgame.respawn);
2208 // be sure not to blast the quitting flag because of the "one frame extra" problem
2209 old_flags = Netgame.flags;
2210 GET_INT(Netgame.flags);
2211 GET_INT(Netgame.type_flags);
2212 GET_INT(Netgame.version_info);
2213 GET_DATA(Netgame.debug_flags);
2220 // now compare the passed in game state to our current known state. If it has changed, then maybe
2221 // do something interesting.
2222 // move from the forming or debriefing state to the mission sync state
2223 if ( ng_state == NETGAME_STATE_MISSION_SYNC ){
2224 // if coming from the forming state
2225 if( (Netgame.game_state == NETGAME_STATE_FORMING) ||
2226 ((Netgame.game_state != NETGAME_STATE_FORMING) && ((gameseq_get_state() == GS_STATE_MULTI_HOST_SETUP) || (gameseq_get_state() == GS_STATE_MULTI_CLIENT_SETUP))) ){
2227 // do any special processing for forced state transitions
2228 multi_handle_state_special();
2230 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
2231 strncpy( Game_current_mission_filename, Netgame.mission_name, MAX_FILENAME_LEN );
2232 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
2234 // if coming from the debriefing state
2235 else if( (Netgame.game_state == NETGAME_STATE_DEBRIEF) ||
2236 ((Netgame.game_state != NETGAME_STATE_DEBRIEF) && ((gameseq_get_state() == GS_STATE_DEBRIEF) || (gameseq_get_state() == GS_STATE_MULTI_DOGFIGHT_DEBRIEF)) ) ){
2238 // do any special processing for forced state transitions
2239 multi_handle_state_special();
2241 multi_flush_mission_stuff();
2243 Multi_sync_mode = MULTI_SYNC_PRE_BRIEFING;
2244 strncpy( Game_current_mission_filename, Netgame.mission_name, MAX_FILENAME_LEN );
2245 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
2248 // move from mission sync to team select
2249 else if ( ng_state == NETGAME_STATE_BRIEFING ){
2250 if( (Netgame.game_state == NETGAME_STATE_MISSION_SYNC) ||
2251 ((Netgame.game_state != NETGAME_STATE_MISSION_SYNC) && (gameseq_get_state() == GS_STATE_MULTI_MISSION_SYNC) && (Multi_sync_mode != MULTI_SYNC_POST_BRIEFING)) ){
2253 // do any special processing for forced state transitions
2254 multi_handle_state_special();
2256 strncpy( Game_current_mission_filename, Netgame.mission_name, MAX_FILENAME_LEN );
2257 gameseq_post_event(GS_EVENT_START_BRIEFING);
2260 // move from the debriefing to the create game screen
2261 else if ( ng_state == NETGAME_STATE_FORMING ){
2262 if( (Netgame.game_state == NETGAME_STATE_DEBRIEF) ||
2263 ((Netgame.game_state != NETGAME_STATE_DEBRIEF) && ((gameseq_get_state() == GS_STATE_DEBRIEF) || (gameseq_get_state() == GS_STATE_MULTI_DOGFIGHT_DEBRIEF)) ) ){
2264 // do any special processing for forced state transitions
2265 multi_handle_state_special();
2267 multi_flush_mission_stuff();
2269 // move to the proper screen
2270 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
2271 gameseq_post_event(GS_EVENT_MULTI_HOST_SETUP);
2273 gameseq_post_event(GS_EVENT_MULTI_CLIENT_SETUP);
2278 Netgame.game_state = ng_state;
2281 // send a request or a reply for mission description, if code == 0, request, if code == 1, reply
2282 void send_netgame_descript_packet(net_addr *addr, int code)
2284 ubyte data[MAX_PACKET_SIZE],val;
2286 int packet_size = 0;
2289 BUILD_HEADER(UPDATE_DESCRIPT);
2295 // add as much of the description as we dare
2296 len = strlen(The_mission.mission_desc);
2297 if(len > MAX_PACKET_SIZE - 10){
2298 len = MAX_PACKET_SIZE - 10;
2300 memcpy(data+packet_size,The_mission.mission_desc,len);
2303 ADD_STRING(The_mission.mission_desc);
2307 Assert(addr != NULL);
2309 psnet_send(addr, data, packet_size);
2313 // process an incoming netgame description packet
2314 void process_netgame_descript_packet( ubyte *data, header *hinfo )
2318 char mission_desc[MISSION_DESC_LENGTH+2];
2321 fill_net_addr(&addr, hinfo->addr, hinfo->net_id, hinfo->port);
2323 // read this game into a temporary structure
2324 offset = HEADER_LENGTH;
2327 // if this is a request for mission description
2329 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
2334 // send an update to this guy
2335 send_netgame_descript_packet(&addr, 1);
2337 memset(mission_desc,0,MISSION_DESC_LENGTH+2);
2338 GET_STRING(mission_desc);
2340 // only display if we're in the proper state
2341 state = gameseq_get_state();
2343 case GS_STATE_MULTI_JOIN_GAME:
2344 case GS_STATE_MULTI_CLIENT_SETUP:
2345 case GS_STATE_MULTI_HOST_SETUP:
2346 multi_common_set_text(mission_desc);
2354 // broadcast a query for active games. IPX will use net broadcast and TCP will either request from the MT or from the specified list
2355 void broadcast_game_query()
2359 server_item *s_moveup;
2360 ubyte data[MAX_PACKET_SIZE];
2362 BUILD_HEADER(GAME_QUERY);
2364 // go through the server list and query each of those as well
2365 s_moveup = Game_server_head;
2366 if(s_moveup != NULL){
2368 send_server_query(&s_moveup->server_addr);
2369 s_moveup = s_moveup->next;
2370 } while(s_moveup != Game_server_head);
2373 fill_net_addr(&addr, Psnet_my_addr.addr, Psnet_my_addr.net_id, DEFAULT_GAME_PORT);
2375 // send out a broadcast if our options allow us
2376 if(Net_player->p_info.options.flags & MLO_FLAG_LOCAL_BROADCAST){
2377 psnet_broadcast( &addr, data, packet_size);
2381 // send an individual query to an address to see if there is an active game
2382 void send_server_query(net_addr *addr)
2385 ubyte data[MAX_PACKET_SIZE];
2387 // build the header and send the data
2388 BUILD_HEADER(GAME_QUERY);
2389 psnet_send(addr, data, packet_size);
2392 // process a query from a client looking for active freespace games
2393 void process_game_query(ubyte* data, header* hinfo)
2398 offset = HEADER_LENGTH;
2402 // check to be sure that we don't capture our own broadcast message
2403 fill_net_addr(&addr, hinfo->addr, hinfo->net_id, hinfo->port);
2404 if ( psnet_same( &addr, &Psnet_my_addr) ){
2408 // if I am not a server of a game, don't send a reply!!!
2409 if ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) ){
2413 // if the game options are being selected, then ignore the request
2414 // also, if Netgame.max_players == -1, the host has not chosen a mission yet and we should wait
2415 if((Netgame.game_state == NETGAME_STATE_STD_HOST_SETUP) || (Netgame.game_state == NETGAME_STATE_HOST_SETUP) || (Netgame.game_state == 0) || (Netgame.max_players == -1)){
2419 // send information about this active game
2420 send_game_active_packet(&addr);
2423 // sends information about netplayers in the game. if called on the server, broadcasts information about _all_ players
2424 void send_netplayer_update_packet( net_player *pl )
2426 int packet_size,idx;
2427 ubyte data[MAX_PACKET_SIZE],val;
2429 BUILD_HEADER(NETPLAYER_UPDATE);
2431 // if I'm the server of the game, I should send an update for _all_players in the game
2432 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2433 for(idx=0;idx<MAX_PLAYERS;idx++){
2434 // only send info for connected players
2435 if(MULTI_CONNECTED(Net_players[idx])){
2440 // add the net player's information
2441 ADD_SHORT(Net_players[idx].player_id);
2442 ADD_INT(Net_players[idx].state);
2443 ADD_INT(Net_players[idx].p_info.ship_class);
2444 ADD_INT(Net_players[idx].tracker_player_id);
2446 if(Net_players[idx].flags & NETINFO_FLAG_HAS_CD){
2454 // add the final stop byte
2458 // broadcast the packet
2459 if(!(Game_mode & GM_IN_MISSION)){
2461 multi_io_send_to_all_reliable(data, packet_size);
2463 multi_io_send_reliable(pl, data, packet_size);
2467 multi_io_send_to_all(data, packet_size);
2469 multi_io_send(pl, data, packet_size);
2477 // add my current state in the netgame to this packet
2478 ADD_SHORT(Net_player->player_id);
2479 ADD_INT(Net_player->state);
2480 ADD_INT(Net_player->p_info.ship_class);
2481 ADD_INT(Multi_tracker_id);
2483 // add if I have a CD or not
2491 // add a final stop byte
2495 // send the packet to the server
2496 Assert( pl == NULL ); // shouldn't ever be the case that pl is non-null here.
2497 if(!(Game_mode & GM_IN_MISSION)){
2498 multi_io_send_reliable(Net_player, data, packet_size);
2500 multi_io_send(Net_player, data, packet_size);
2505 // process an incoming netplayer state update. if we're the server, we should rebroadcast
2506 void process_netplayer_update_packet( ubyte *data, header *hinfo )
2508 int offset, player_num;
2514 offset = HEADER_LENGTH;
2516 // get the first stop byte
2519 while(stop != 0xff){
2520 // look the player up
2521 GET_SHORT(player_id);
2522 player_num = find_player_id(player_id);
2523 // if we couldn't find him, read in the bogus data
2524 if((player_num == -1) || (Net_player == &Net_players[player_num])){
2525 GET_INT(bogus.state);
2526 GET_INT(bogus.p_info.ship_class);
2527 GET_INT(bogus.tracker_player_id);
2531 // otherwise read in the data correctly
2534 GET_INT(Net_players[player_num].p_info.ship_class);
2535 GET_INT(Net_players[player_num].tracker_player_id);
2538 Net_players[player_num].flags |= NETINFO_FLAG_HAS_CD;
2540 Net_players[player_num].flags &= ~(NETINFO_FLAG_HAS_CD);
2543 // if he's changing state to joined, send a team update
2544 if((Net_players[player_num].state == NETPLAYER_STATE_JOINING) && (new_state == NETPLAYER_STATE_JOINED) && (Netgame.type_flags & NG_TYPE_TEAM)){
2545 multi_team_send_update();
2549 Net_players[player_num].state = new_state;
2552 // get the next stop byte
2558 // if I'm the host or the server of the game, update everyone else so things are synched up as tightly as possible
2559 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
2560 send_netplayer_update_packet(NULL);
2563 // if i'm the standalone and this is an update from the host, maybe change some netgame settings
2564 if((Game_mode & GM_STANDALONE_SERVER) && (player_num != -1) && (Net_players[player_num].flags & NETINFO_FLAG_GAME_HOST)){
2565 switch(Net_players[player_num].state){
2566 case NETPLAYER_STATE_STD_HOST_SETUP:
2567 Netgame.game_state = NETGAME_STATE_STD_HOST_SETUP;
2570 case NETPLAYER_STATE_HOST_SETUP:
2571 // check for race conditions
2572 if(Netgame.game_state != NETGAME_STATE_MISSION_SYNC){
2573 Netgame.game_state = NETGAME_STATE_FORMING;
2580 #define EXTRA_DEATH_VAPORIZED (1<<0)
2581 #define EXTRA_DEATH_WASHED (1<<1)
2582 // send a packet indicating a ship has been killed
2583 void send_ship_kill_packet( object *objp, object *other_objp, float percent_killed, int self_destruct )
2585 int packet_size, model;
2586 ubyte data[MAX_PACKET_SIZE], was_player, extra_death_info, vaporized;
2587 ushort debris_signature;
2591 // only sendable from the master
2592 Assert ( Net_player->flags & NETINFO_FLAG_AM_MASTER );
2595 vaporized = ( (Ships[objp->instance].flags & SF_VAPORIZE) > 0 );
2597 extra_death_info = 0;
2599 extra_death_info |= EXTRA_DEATH_VAPORIZED;
2602 if ( Ships[objp->instance].wash_killed ) {
2603 extra_death_info |= EXTRA_DEATH_WASHED;
2606 // find out the next network signature that will be used for the debris pieces.
2607 model = Ships[objp->instance].modelnum;
2608 pm = model_get(model);
2609 debris_signature = 0;
2610 if ( pm && !vaporized ) {
2611 debris_signature = multi_get_next_network_signature( MULTI_SIG_DEBRIS );
2612 multi_set_network_signature( (ushort)(debris_signature + pm->num_debris_objects), MULTI_SIG_DEBRIS );
2613 Ships[objp->instance].arrival_distance = debris_signature;
2616 BUILD_HEADER(SHIP_KILL);
2617 ADD_USHORT(objp->net_signature);
2619 // ships which are initially killed get the rest of the data sent. self destructed ships and
2620 if ( other_objp == NULL ) {
2625 nprintf(("Network","Don't know other_obj for ship kill packet, sending NULL\n"));
2627 ADD_USHORT( other_objp->net_signature );
2630 ADD_USHORT( debris_signature );
2631 ADD_FLOAT( percent_killed );
2632 sd = (ubyte)self_destruct;
2634 ADD_DATA( extra_death_info );
2636 // if the ship who died is a player, then send some extra info, like who killed him, etc.
2638 if ( objp->flags & OF_PLAYER_SHIP ) {
2642 pnum = multi_find_player_by_object( objp );
2645 ADD_DATA( was_player );
2647 Assert(Net_players[pnum].player->killer_objtype < CHAR_MAX);
2648 temp = (char)Net_players[pnum].player->killer_objtype;
2651 Assert(Net_players[pnum].player->killer_species < CHAR_MAX);
2652 temp = (char)Net_players[pnum].player->killer_species;
2655 Assert(Net_players[pnum].player->killer_weapon_index < CHAR_MAX);
2656 temp = (char)Net_players[pnum].player->killer_weapon_index;
2659 ADD_STRING( Net_players[pnum].player->killer_parent_name );
2661 ADD_DATA( was_player );
2664 ADD_DATA( was_player );
2667 // send the packet reliably!!!
2668 multi_io_send_to_all_reliable(data, packet_size);
2671 // process a packet indicating that a ship has been killed
2672 void process_ship_kill_packet( ubyte *data, header *hinfo )
2675 ushort ship_sig, other_sig, debris_sig;
2676 object *sobjp, *oobjp;
2677 float percent_killed;
2678 ubyte was_player, extra_death_info, sd;
2679 char killer_name[NAME_LENGTH], killer_objtype = OBJ_NONE, killer_species = SPECIES_TERRAN, killer_weapon_index = -1;
2681 offset = HEADER_LENGTH;
2682 GET_USHORT(ship_sig);
2684 GET_USHORT( other_sig );
2685 GET_USHORT( debris_sig );
2686 GET_FLOAT( percent_killed );
2688 GET_DATA( extra_death_info );
2689 GET_DATA( was_player );
2692 // pnum is >=0 when the dying ship is a pleyer ship. Get the info about how he died
2693 if ( was_player != 0 ) {
2694 GET_DATA( killer_objtype );
2695 GET_DATA( killer_species );
2696 GET_DATA( killer_weapon_index );
2697 GET_STRING( killer_name );
2702 sobjp = multi_get_network_object( ship_sig );
2704 // if I am unable to find the ship object which was killed, I have to bail and rely on getting
2705 // another message from the server that this happened!
2706 if ( sobjp == NULL ) {
2707 nprintf(("Network", "Couldn't find net signature %d for kill packet\n", ship_sig));
2711 // set this ship's hull value to 0
2712 sobjp->hull_strength = 0.0f;
2714 // maybe set vaporized
2715 if (extra_death_info & EXTRA_DEATH_VAPORIZED) {
2716 Ships[sobjp->instance].flags |= SF_VAPORIZE;
2719 // maybe set wash_killed
2720 if (extra_death_info & EXTRA_DEATH_VAPORIZED) {
2721 Ships[sobjp->instance].wash_killed = 1;
2724 oobjp = multi_get_network_object( other_sig );
2726 if ( was_player != 0 ) {
2729 pnum = multi_find_player_by_object( sobjp );
2731 Net_players[pnum].player->killer_objtype = killer_objtype;
2732 Net_players[pnum].player->killer_species = killer_species;
2733 Net_players[pnum].player->killer_weapon_index = killer_weapon_index;
2734 strcpy( Net_players[pnum].player->killer_parent_name, killer_name );
2738 // check to see if I need to respawn myself
2739 multi_respawn_check(sobjp);
2741 // store the debris signature in the arrival distance which will never get used for player ships
2742 Ships[sobjp->instance].arrival_distance = debris_sig;
2744 // set this bit so that we don't accidentally start switching targets when we die
2745 if(sobjp == Player_obj){
2746 Game_mode |= GM_DEAD_DIED;
2749 nprintf(("Network", "Killing off %s\n", Ships[sobjp->instance].ship_name));
2751 // do the normal thing when not ingame joining. When ingame joining, simply kill off the ship.
2752 if ( !(Net_player->flags & NETINFO_FLAG_INGAME_JOIN) ) {
2753 ship_hit_kill( sobjp, oobjp, percent_killed, sd );
2755 extern void ship_destroyed( int shipnum );
2756 ship_destroyed( sobjp->instance );
2757 sobjp->flags |= OF_SHOULD_BE_DEAD;
2758 obj_delete( OBJ_INDEX(sobjp) );
2762 // send a packet indicating a ship should be created
2763 void send_ship_create_packet( object *objp, int is_support )
2766 ubyte data[MAX_PACKET_SIZE];
2768 // We will pass the ship to create by name.
2769 BUILD_HEADER(SHIP_CREATE);
2770 ADD_USHORT(objp->net_signature);
2771 ADD_INT( is_support );
2773 add_vector_data(data, &packet_size, objp->pos);
2776 // broadcast the packet
2777 multi_io_send_to_all_reliable(data, packet_size);
2780 // process a packet indicating a ship should be created
2781 void process_ship_create_packet( ubyte *data, header *hinfo )
2783 int offset, objnum, is_support;
2786 vector pos = ZERO_VECTOR;
2788 Assert ( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
2789 offset = HEADER_LENGTH;
2790 GET_USHORT(signature);
2791 GET_INT( is_support );
2793 get_vector_data(data, &offset, pos);
2798 // find the name of this ship on ship ship arrival list. if found, pass it to parse_object_create
2799 if ( !is_support ) {
2800 objp = mission_parse_get_arrival_ship( signature );
2801 if ( objp != NULL ) {
2802 objnum = parse_create_object(objp);
2804 nprintf(("Network", "Ship with sig %d not found on ship arrival list -- not creating!!\n", signature));
2807 Assert( Arriving_support_ship );
2808 if(Arriving_support_ship == NULL){
2811 Arriving_support_ship->pos = pos;
2812 Arriving_support_ship->net_signature = signature;
2813 objnum = parse_create_object( Arriving_support_ship );
2814 Assert( objnum != -1 );
2816 mission_parse_support_arrived( objnum );
2821 // send a packet indicating a wing of ships should be created
2822 void send_wing_create_packet( wing *wingp, int num_to_create, int pre_create_count )
2824 int packet_size, index, ship_instance;
2825 ubyte data[MAX_PACKET_SIZE];
2829 // for creating wing -- we just send the index into the wing array of this wing.
2830 // all players load the same mission, and so their array's should all match. We also
2831 // need to send the signature of the first ship that was created. We can find this by
2832 // looking num_to_create places back in the ship_index field in the wing structure.
2834 index = WING_INDEX(wingp);
2835 ship_instance = wingp->ship_index[wingp->current_count - num_to_create];
2836 signature = Objects[Ships[ship_instance].objnum].net_signature;
2838 BUILD_HEADER( WING_CREATE );
2840 ADD_INT(num_to_create);
2841 ADD_USHORT(signature);
2842 ADD_INT(pre_create_count);
2843 val = wingp->current_wave - 1;
2846 multi_io_send_to_all_reliable(data, packet_size);
2849 // process a packet saying that a wing should be created
2850 void process_wing_create_packet( ubyte *data, header *hinfo )
2852 int offset, index, num_to_create;
2854 int total_arrived_count, current_wave;
2856 offset = HEADER_LENGTH;
2858 GET_INT(num_to_create);
2859 GET_USHORT(signature);
2860 GET_INT(total_arrived_count);
2861 GET_INT(current_wave);
2865 // do a sanity check on the wing to be sure that we are actually working on a valid wing
2866 if ( (index < 0) || (index >= num_wings) || (Wings[index].num_waves == -1) ) {
2867 nprintf(("Network", "invalid index %d for wing create packet\n"));
2870 if ( (num_to_create <= 0) || (num_to_create > Wings[index].wave_count) ) {
2871 nprintf(("Network", "Invalid number of ships to create (%d) for wing %s\n", num_to_create, Wings[index].name));
2876 Wings[index].current_count = 0;
2877 Wings[index].total_arrived_count = total_arrived_count;
2878 Wings[index].current_wave = current_wave;
2880 // set the network signature that was passed. The client should create ships in the same order
2881 // as the server -- so all ships should get the same sigs as assigned by the server. We also
2882 // need to set some timestamps and cues correctly to be sure that these things get created on
2883 // the clients correctly
2884 multi_set_network_signature( signature, MULTI_SIG_SHIP );
2885 parse_wing_create_ships( &Wings[index], num_to_create, 1 );
2888 // packet indicating a ship is departing
2889 void send_ship_depart_packet( object *objp )
2891 ubyte data[MAX_PACKET_SIZE];
2895 signature = objp->net_signature;
2897 BUILD_HEADER(SHIP_DEPART);
2898 ADD_USHORT( signature );
2900 multi_io_send_to_all_reliable(data, packet_size);
2903 // process a packet indicating a ship is departing
2904 void process_ship_depart_packet( ubyte *data, header *hinfo )
2910 offset = HEADER_LENGTH;
2911 GET_USHORT( signature );
2914 // find the object which is departing
2915 objp = multi_get_network_object( signature );
2916 if ( objp == NULL ) {
2917 nprintf(("network", "Couldn't find object with net signature %d to depart\n", signature ));
2921 // start warping him out
2922 shipfx_warpout_start( objp );
2925 // packet to tell clients cargo of a ship was revealed to all
2926 void send_cargo_revealed_packet( ship *shipp )
2928 ubyte data[MAX_PACKET_SIZE];
2931 // build the header and add the data
2932 BUILD_HEADER(CARGO_REVEALED);
2933 ADD_USHORT( Objects[shipp->objnum].net_signature );
2935 // server sends to all players
2936 if(MULTIPLAYER_MASTER){
2937 multi_io_send_to_all_reliable(data, packet_size);
2939 // clients just send to the server
2941 multi_io_send_reliable(Net_player, data, packet_size);
2945 // process a cargo revealed packet
2946 void process_cargo_revealed_packet( ubyte *data, header *hinfo )
2952 offset = HEADER_LENGTH;
2953 GET_USHORT(signature);
2956 // get a ship pointer and call the ship function to reveal the cargo
2957 objp = multi_get_network_object( signature );
2958 if ( objp == NULL ) {
2959 nprintf(("Network", "Could not find object with net signature %d for cargo revealed\n", signature ));
2963 // Assert( objp->type == OBJ_SHIP );
2964 if((objp->type != OBJ_SHIP) || (objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
2968 // this will take care of re-routing to all other clients
2969 ship_do_cargo_revealed( &Ships[objp->instance], 1);
2971 // server should rebroadcast
2972 if(MULTIPLAYER_MASTER){
2973 send_cargo_revealed_packet(&Ships[objp->instance]);
2977 // defines used for secondary fire packet
2978 #define SFPF_ALLOW_SWARM (1<<7)
2979 #define SFPF_DUAL_FIRE (1<<6)
2980 #define SFPF_TARGET_LOCKED (1<<5)
2982 // send a packet indicating a secondary weapon was fired
2983 void send_secondary_fired_packet( ship *shipp, ushort starting_sig, int starting_count, int num_fired, int allow_swarm )
2985 int packet_size, net_player_num;
2986 ubyte data[MAX_PACKET_SIZE], sinfo, current_bank;
2988 ushort target_signature;
2992 // Assert ( starting_count < UCHAR_MAX );
2994 // get the object for this ship. If it is an AI object, send all the info to all player. Otherwise,
2995 // we might send the info to the other player different than the one who fired
2996 objp = &Objects[shipp->objnum];
2997 if ( !(objp->flags & OF_PLAYER_SHIP) ) {
2998 if ( num_fired == 0 ) {
3003 aip = &Ai_info[shipp->ai_index];
3005 current_bank = (ubyte)shipp->weapons.current_secondary_bank;
3006 //Assert( (current_bank >= 0) && (current_bank < MAX_SECONDARY_BANKS) ); // always true
3008 // build up the header portion
3009 BUILD_HEADER( SECONDARY_FIRED_AI );
3011 ADD_USHORT( Objects[shipp->objnum].net_signature );
3012 ADD_USHORT( starting_sig );
3014 // add a couple of bits for swarm missiles and dual fire secondary weaspons
3017 sinfo = current_bank;
3020 sinfo |= SFPF_ALLOW_SWARM;
3023 if ( shipp->flags & SF_SECONDARY_DUAL_FIRE ){
3024 sinfo |= SFPF_DUAL_FIRE;
3027 if ( aip->current_target_is_locked ){
3028 sinfo |= SFPF_TARGET_LOCKED;
3033 // add the ship's target and any targeted subsystem
3034 target_signature = 0;
3036 if ( aip->target_objnum != -1) {
3037 target_signature = Objects[aip->target_objnum].net_signature;
3038 if ( (Objects[aip->target_objnum].type == OBJ_SHIP) && (aip->targeted_subsys != NULL) ) {
3041 s_index = ship_get_index_from_subsys( aip->targeted_subsys, aip->target_objnum );
3042 Assert( s_index < CHAR_MAX ); // better be less than this!!!!
3043 t_subsys = (char)s_index;
3046 if ( Objects[aip->target_objnum].type == OBJ_WEAPON ) {
3047 Assert(Weapon_info[Weapons[Objects[aip->target_objnum].instance].weapon_info_index].wi_flags & WIF_BOMB);
3052 ADD_USHORT( target_signature );
3053 ADD_DATA( t_subsys );
3055 // just send this packet to everyone, then bail if an AI ship fired.
3056 if ( !(objp->flags & OF_PLAYER_SHIP) ) {
3057 multi_io_send_to_all(data, packet_size);
3061 net_player_num = multi_find_player_by_object( objp );
3063 // getting here means a player fired. Send the current packet to all players except the player
3064 // who fired. If nothing got fired, then don't send to the other players -- we will just send
3065 // a packet to the player who will find out that he didn't fire anything
3066 if ( num_fired > 0 ) {
3067 multi_io_send_to_all_reliable(data, packet_size, &Net_players[net_player_num]);
3070 // if I (the master) fired, then return
3071 if ( Net_players[net_player_num].flags & NETINFO_FLAG_AM_MASTER ){
3075 // now build up the packet to send to the player who actually fired.
3076 BUILD_HEADER( SECONDARY_FIRED_PLR );
3077 ADD_USHORT(starting_sig);
3080 // add the targeting information so that the player's weapons will always home on the correct
3082 ADD_USHORT( target_signature );
3083 ADD_DATA( t_subsys );
3085 multi_io_send_reliable(&Net_players[net_player_num], data, packet_size);
3088 /// process a packet indicating a secondary weapon was fired
3089 void process_secondary_fired_packet(ubyte* data, header* hinfo, int from_player)
3091 int offset, allow_swarm, target_objnum_save;
3092 ushort net_signature, starting_sig, target_signature;
3093 ubyte sinfo, current_bank;
3094 object* objp, *target_objp;
3098 ship_subsys *targeted_subsys_save;
3100 offset = HEADER_LENGTH; // size of the header
3102 // if from_player is false, it means that the secondary weapon info in this packet was
3103 // fired by an ai object (or another player). from_player == 1 means tha me (the person
3104 // receiving this packet) fired the secondary weapon
3105 if ( !from_player ) {
3106 GET_USHORT( net_signature );
3107 GET_USHORT( starting_sig );
3108 GET_DATA( sinfo ); // are we firing swarm missiles
3110 GET_USHORT( target_signature );
3111 GET_DATA( t_subsys );
3115 // find the object (based on network signatures) for the object that fired
3116 objp = multi_get_network_object( net_signature );
3117 if ( objp == NULL ) {
3118 nprintf(("Network", "Could not find ship for fire secondary packet!"));
3122 // set up the ships current secondary bank and that bank's mode. Below, we will set the timeout
3123 // of the next fire time of this bank to 0 so we can fire right away
3124 shipp = &Ships[objp->instance];
3127 GET_USHORT( starting_sig );
3130 GET_USHORT( target_signature );
3131 GET_DATA( t_subsys );
3135 // get the object and ship
3137 shipp = Player_ship;
3140 // check the allow swarm bit
3142 if ( sinfo & SFPF_ALLOW_SWARM ){
3146 // set the dual fire properties of the ship
3147 if ( sinfo & SFPF_DUAL_FIRE ){
3148 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
3150 shipp->flags &= ~SF_SECONDARY_DUAL_FIRE;
3153 // determine whether current target is locked
3154 Assert( shipp->ai_index != -1 );
3155 aip = &Ai_info[shipp->ai_index];
3156 if ( sinfo & SFPF_TARGET_LOCKED ) {
3157 aip->current_target_is_locked = 1;
3159 aip->current_target_is_locked = 0;
3162 // find out the current bank
3163 current_bank = (ubyte)(sinfo & 0x3);
3164 //Assert( (current_bank >= 0) && (current_bank < MAX_SECONDARY_BANKS) ); // always true
3165 shipp->weapons.current_secondary_bank = current_bank;
3167 // make it so we can fire this ship's secondary bank immediately!!!
3168 shipp->weapons.next_secondary_fire_stamp[shipp->weapons.current_secondary_bank] = timestamp(0);
3169 shipp->weapons.detonate_weapon_time = timestamp(5000); // be sure that we don't detonate a remote weapon before it is time.
3171 // set this ship's target and subsystem information. We will save and restore target and
3172 // targeted subsystem so that we do not accidentally change targets for this player or
3173 // any AI ships on his system.
3174 target_objnum_save = aip->target_objnum;
3175 targeted_subsys_save = aip->targeted_subsys;
3177 // reset these variables for accuracy. They will get reassigned at the end of this fuction
3178 aip->target_objnum = -1;
3179 aip->targeted_subsys = NULL;
3181 target_objp = multi_get_network_object( target_signature );
3182 if ( target_objp != NULL ) {
3183 aip->target_objnum = OBJ_INDEX(target_objp);
3185 if ( (t_subsys != -1) && (target_objp->type == OBJ_SHIP) ) {
3186 aip->targeted_subsys = ship_get_indexed_subsys( &Ships[target_objp->instance], t_subsys);
3190 if ( starting_sig != 0 ){
3191 multi_set_network_signature( starting_sig, MULTI_SIG_NON_PERMANENT );
3193 shipp->weapons.detonate_weapon_time = timestamp(0); // signature of -1 say detonate remote weapon
3196 ship_fire_secondary( objp, allow_swarm );
3198 // restore targeted object and targeted subsystem
3199 aip->target_objnum = target_objnum_save;
3200 aip->targeted_subsys = targeted_subsys_save;
3203 // send a packet indicating a countermeasure was fired
3204 void send_countermeasure_fired_packet( object *objp, int cmeasure_count, int rand_val )
3206 ubyte data[MAX_PACKET_SIZE];
3211 Assert ( cmeasure_count < UCHAR_MAX );
3212 BUILD_HEADER(COUNTERMEASURE_FIRED);
3213 ADD_USHORT( objp->net_signature );
3214 ADD_INT( rand_val );
3216 multi_io_send_to_all(data, packet_size);
3219 // process a packet indicating a countermeasure was fired
3220 void process_countermeasure_fired_packet( ubyte *data, header *hinfo )
3222 int offset, rand_val;
3228 offset = HEADER_LENGTH;
3230 GET_USHORT( signature );
3231 GET_INT( rand_val );
3234 objp = multi_get_network_object( signature );
3235 if ( objp == NULL ) {
3236 nprintf(("network", "Could find object whose countermeasures are being launched!!!\n"));
3239 if(objp->type != OBJ_SHIP){
3242 // Assert ( objp->type == OBJ_SHIP );
3244 // make it so ship can fire right away!
3245 Ships[objp->instance].cmeasure_fire_stamp = timestamp(0);
3246 if ( objp == Player_obj ){
3247 nprintf(("network", "firing countermeasure from my ship\n"));
3250 ship_launch_countermeasure( objp, rand_val );
3253 // send a packet indicating that a turret has been fired
3254 void send_turret_fired_packet( int ship_objnum, int subsys_index, int weapon_objnum )
3257 ushort pnet_signature;
3258 ubyte data[MAX_PACKET_SIZE], cindex;
3265 if((weapon_objnum < 0) || (Objects[weapon_objnum].type != OBJ_WEAPON) || (Objects[weapon_objnum].instance < 0) || (Weapons[Objects[weapon_objnum].instance].weapon_info_index < 0)){
3269 // local setup -- be sure we are actually passing a weapon!!!!
3270 objp = &Objects[weapon_objnum];
3271 Assert ( objp->type == OBJ_WEAPON );
3272 if(Weapon_info[Weapons[objp->instance].weapon_info_index].subtype == WP_MISSILE){
3276 pnet_signature = Objects[ship_objnum].net_signature;
3278 Assert( subsys_index < UCHAR_MAX );
3279 cindex = (ubyte)subsys_index;
3281 ssp = ship_get_indexed_subsys( &Ships[Objects[ship_objnum].instance], subsys_index, NULL );
3286 // build the fire turret packet.
3287 BUILD_HEADER(FIRE_TURRET_WEAPON);
3288 packet_size += multi_pack_unpack_position(1, data + packet_size, &objp->orient.v.fvec);
3289 ADD_DATA( has_sig );
3290 ADD_USHORT( pnet_signature );
3292 ADD_USHORT( objp->net_signature );
3295 val = (short)ssp->submodel_info_1.angs.h;
3297 val = (short)ssp->submodel_info_2.angs.p;
3300 multi_io_send_to_all(data, packet_size);
3302 multi_rate_add(1, "tur", packet_size);
3305 // process a packet indicating a turret has been fired
3306 void process_turret_fired_packet( ubyte *data, header *hinfo )
3308 int offset, weapon_objnum, wid;
3309 ushort pnet_signature, wnet_signature;
3318 short pitch, heading;
3320 // get the data for the turret fired packet
3321 offset = HEADER_LENGTH;
3322 offset += multi_pack_unpack_position(0, data + offset, &o_fvec);
3323 GET_DATA( has_sig );
3324 GET_USHORT( pnet_signature );
3326 GET_USHORT( wnet_signature );
3330 GET_DATA( turret_index );
3331 GET_SHORT( heading );
3333 PACKET_SET_SIZE(); // move our counter forward the number of bytes we have read
3336 objp = multi_get_network_object( pnet_signature );
3337 if ( objp == NULL ) {
3338 nprintf(("network", "could find parent object with net signature %d for turret firing\n", pnet_signature));
3342 // if this isn't a ship, do nothing
3343 if ( objp->type != OBJ_SHIP ){
3347 // make an orientation matrix from the o_fvec
3348 vm_vector_2_matrix(&orient, &o_fvec, NULL, NULL);
3350 // find this turret, and set the position of the turret that just fired to be where it fired. Quite a
3351 // hack, but should be suitable.
3352 shipp = &Ships[objp->instance];
3353 ssp = ship_get_indexed_subsys( shipp, turret_index, NULL );
3357 wid = ssp->system_info->turret_weapon_type;
3359 // bash the position and orientation of the turret
3360 ssp->submodel_info_1.angs.h = (float)heading;
3361 ssp->submodel_info_2.angs.p = (float)pitch;
3363 // get the world position of the weapon
3364 ship_get_global_turret_info(objp, ssp->system_info, &pos, &temp);
3366 // create the weapon object
3367 if(wnet_signature != 0){
3368 multi_set_network_signature( wnet_signature, MULTI_SIG_NON_PERMANENT );
3370 weapon_objnum = weapon_create( &pos, &orient, wid, OBJ_INDEX(objp), 0, -1, 1);
3371 if (weapon_objnum != -1) {
3372 if ( Weapon_info[wid].launch_snd != -1 ) {
3373 snd_play_3d( &Snds[Weapon_info[wid].launch_snd], &pos, &View_position );
3378 // send a mission log item packet
3379 void send_mission_log_packet( int num )
3382 ubyte data[MAX_PACKET_SIZE];
3387 Assert ( (Game_mode & GM_MULTIPLAYER) && (Net_player->flags & NETINFO_FLAG_AM_MASTER) );
3389 // get the data from the log
3390 entry = &log_entries[num];
3391 type = (ubyte)entry->type; // do the type casting thing to save on packet space
3392 sindex = (ushort)entry->index;
3394 BUILD_HEADER(MISSION_LOG_ENTRY);
3396 ADD_INT(entry->flags);
3398 ADD_DATA(entry->timestamp);
3399 ADD_STRING(entry->pname);
3400 ADD_STRING(entry->sname);
3402 // broadcast the packet to all players
3403 multi_io_send_to_all_reliable(data, packet_size);
3406 // process a mission log item packet
3407 void process_mission_log_packet( ubyte *data, header *hinfo )
3412 char pname[NAME_LENGTH], sname[NAME_LENGTH];
3415 Assert ( (Game_mode & GM_MULTIPLAYER) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
3417 offset = HEADER_LENGTH;
3421 GET_DATA(timestamp);
3427 mission_log_add_entry_multi( type, pname, sname, sindex, timestamp, flags );
3430 // send a mission message packet
3431 void send_mission_message_packet( int id, char *who_from, int priority, int timing, int source, int builtin_type, int multi_target, int multi_team_filter)
3434 ubyte data[MAX_PACKET_SIZE], up, us, utime;
3436 Assert ( Net_player->flags & NETINFO_FLAG_AM_MASTER );
3437 Assert ( (priority >= 0) && (priority < UCHAR_MAX) );
3438 Assert ( (timing >= 0) && (timing < UCHAR_MAX) );
3440 up = (ubyte) priority;
3441 us = (ubyte) source;
3442 utime = (ubyte)timing;
3444 BUILD_HEADER(MISSION_MESSAGE);
3446 ADD_STRING(who_from);
3450 ADD_INT(builtin_type);
3451 ADD_INT(multi_team_filter);
3453 if (multi_target == -1){
3454 multi_io_send_to_all_reliable(data, packet_size);
3456 multi_io_send_reliable(&Net_players[multi_target], data, packet_size);
3460 // process a mission message packet
3461 void process_mission_message_packet( ubyte *data, header *hinfo )
3463 int offset, id, builtin_type;
3464 ubyte priority, source, utiming;
3465 char who_from[NAME_LENGTH];
3466 int multi_team_filter;
3468 Assert( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
3470 offset = HEADER_LENGTH;
3472 GET_STRING(who_from);
3476 GET_INT(builtin_type);
3477 GET_INT(multi_team_filter);
3481 // filter out builtin ones in TvT
3482 if((builtin_type >= 0) && (Netgame.type_flags & NG_TYPE_TEAM) && (Net_player != NULL) && (Net_player->p_info.team != multi_team_filter)){
3486 // maybe filter this out
3487 if(!message_filter_multi(id)){
3488 // send the message as if it came from an sexpression
3489 message_queue_message( id, priority, utiming, who_from, source, 0, 0, builtin_type );
3493 // just send them a pong back as fast as possible
3494 void process_ping_packet(ubyte *data, header *hinfo)
3499 offset = HEADER_LENGTH;
3502 // get the address to return the pong to
3503 fill_net_addr(&addr, hinfo->addr, hinfo->net_id, hinfo->port);
3509 // right now it just routes the pong through to the standalone gui, which is the only
3510 // system which uses ping and pong right now.
3511 void process_pong_packet(ubyte *data, header *hinfo)
3517 offset = HEADER_LENGTH;
3519 fill_net_addr(&addr, hinfo->addr, hinfo->net_id, hinfo->port);
3523 // if we're connected , see who sent us this pong
3524 if(Net_player->flags & NETINFO_FLAG_CONNECTED){
3525 lookup = find_player_id(hinfo->id);
3530 p = &Net_players[lookup];
3532 // evaluate the ping
3533 multi_ping_eval_pong(&Net_players[lookup].s_info.ping);
3535 // put in calls to any functions which may want to know about the ping times from
3537 if(Game_mode & GM_STANDALONE_SERVER){
3538 std_update_player_ping(p);
3541 // mark his socket as still alive (extra precaution)
3542 psnet_mark_received(Net_players[lookup].reliable_socket);
3544 // otherwise, do any special processing
3546 // if we're in the join game state, see if this pong came from a server on our
3548 if(gameseq_get_state() == GS_STATE_MULTI_JOIN_GAME){
3549 multi_join_eval_pong(&addr, timer_get_fixed_seconds());
3554 // send a ping packet
3555 void send_ping(net_addr *addr)
3557 unsigned char data[8];
3560 // build the header and send the packet
3561 BUILD_HEADER( PING );
3562 psnet_send(addr, &data[0], packet_size);
3565 // send a pong packet
3566 void send_pong(net_addr *addr)
3568 unsigned char data[8];
3571 // build the header and send the packet
3573 psnet_send(addr, &data[0], packet_size);
3576 // sent from host to master. give me the list of missions you have.
3577 // this will be used only in a standalone mode
3578 void send_mission_list_request( int what )
3580 ubyte data[MAX_PACKET_SIZE];
3583 // build the header and ask for a list of missions or campaigns (depending
3584 // on the 'what' flag).
3585 BUILD_HEADER(MISSION_REQUEST);
3587 multi_io_send_reliable(Net_player, data, packet_size);
3590 // maximum number of bytes that we can send in a mission items packet.
3591 #define MAX_MISSION_ITEMS_BYTES (MAX_PACKET_SIZE - (sizeof(multi_create_info) + 1) )
3593 // defines used to tell what type of packets are being sent
3594 #define MISSION_LIST_ITEMS 1
3595 #define CAMPAIGN_LIST_ITEMS 2
3597 // send an individual mission file item
3598 void send_mission_items( net_player *pl )
3600 ubyte data[MAX_PACKET_SIZE];
3605 BUILD_HEADER(MISSION_ITEM);
3607 // send the list of missions and campaigns avilable on the server. Stop when
3608 // reaching a certain maximum
3609 type = MISSION_LIST_ITEMS;
3611 for (i = 0; i < Multi_create_mission_count; i++ ) {
3615 ADD_STRING( Multi_create_mission_list[i].filename );
3616 ADD_STRING( Multi_create_mission_list[i].name );
3617 ADD_INT( Multi_create_mission_list[i].flags );
3618 ADD_DATA( Multi_create_mission_list[i].max_players );
3619 ADD_UINT( Multi_create_mission_list[i].respawn );
3622 ADD_DATA( Multi_create_mission_list[i].valid_status );
3624 if ( packet_size > (int)MAX_MISSION_ITEMS_BYTES ) {
3627 multi_io_send_reliable(pl, data, packet_size);
3628 BUILD_HEADER( MISSION_ITEM );
3634 multi_io_send_reliable(pl, data, packet_size);
3636 // send the campaign information
3637 type = CAMPAIGN_LIST_ITEMS;
3638 BUILD_HEADER(MISSION_ITEM);
3640 for (i = 0; i < Multi_create_campaign_count; i++ ) {
3644 ADD_STRING( Multi_create_campaign_list[i].filename );
3645 ADD_STRING( Multi_create_campaign_list[i].name );
3646 ADD_INT( Multi_create_campaign_list[i].flags );
3647 ADD_DATA( Multi_create_campaign_list[i].max_players );
3649 if ( packet_size > (int)MAX_MISSION_ITEMS_BYTES ) {
3652 multi_io_send_reliable(pl, data, packet_size);
3653 BUILD_HEADER( MISSION_ITEM );
3659 multi_io_send_reliable(pl, data, packet_size);
3662 // process a request for a list of missions
3663 void process_mission_request_packet(ubyte *data, header *hinfo)
3665 int player_num,offset;
3667 offset = HEADER_LENGTH;
3670 // fill in the address information of where this came from
3671 player_num = find_player_id(hinfo->id);
3672 if(player_num == -1){
3673 nprintf(("Network","Could not find player to send mission list items to!\n"));
3677 send_mission_items( &Net_players[player_num] );
3680 // process an individual mission file item
3681 void process_mission_item_packet(ubyte *data,header *hinfo)
3684 char filename[MAX_FILENAME_LEN], name[NAME_LENGTH], valid_status;
3685 ubyte stop, type,max_players;
3688 Assert(gameseq_get_state() == GS_STATE_MULTI_HOST_SETUP);
3689 offset = HEADER_LENGTH;
3694 GET_STRING( filename );
3697 GET_DATA( max_players );
3699 // missions also have respawns and a crc32 associated with them
3700 if(type == MISSION_LIST_ITEMS){
3704 GET_DATA(valid_status);
3706 if ( Multi_create_mission_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
3707 strcpy(Multi_create_mission_list[Multi_create_mission_count].filename, filename );
3708 strcpy(Multi_create_mission_list[Multi_create_mission_count].name, name );
3709 Multi_create_mission_list[Multi_create_mission_count].flags = flags;
3710 Multi_create_mission_list[Multi_create_mission_count].respawn = respawn;
3711 Multi_create_mission_list[Multi_create_mission_count].max_players = max_players;
3714 Multi_create_mission_list[Multi_create_mission_count].valid_status = valid_status;
3716 Multi_create_mission_count++;
3718 } else if ( type == CAMPAIGN_LIST_ITEMS ) {
3719 if ( Multi_create_campaign_count < MULTI_CREATE_MAX_LIST_ITEMS ) {
3720 strcpy(Multi_create_campaign_list[Multi_create_campaign_count].filename, filename );
3721 strcpy(Multi_create_campaign_list[Multi_create_campaign_count].name, name );
3722 Multi_create_campaign_list[Multi_create_campaign_count].flags = flags;
3723 Multi_create_campaign_list[Multi_create_campaign_count].respawn = 0;
3724 Multi_create_campaign_list[Multi_create_campaign_count].max_players = max_players;
3725 Multi_create_campaign_count++;
3734 // this will cause whatever list to get resorted (although they should be appearing in order)
3735 multi_create_setup_list_data(-1);
3738 // send a request to the server to pause or unpause the game
3739 void send_multi_pause_packet(int pause)
3741 ubyte data[MAX_PACKET_SIZE];
3743 int packet_size = 0;
3745 Assert(!(Net_player->flags & NETINFO_FLAG_AM_MASTER));
3748 BUILD_HEADER(MULTI_PAUSE_REQUEST);
3749 val = (ubyte) pause;
3751 // add the pause info
3754 // send the request to the server
3755 multi_io_send_reliable(Net_player, data, packet_size);
3758 // process a pause update packet (pause, unpause, etc)
3759 void process_multi_pause_packet(ubyte *data, header *hinfo)
3765 offset = HEADER_LENGTH;
3771 // get who sent the packet
3772 player_index = find_player_id(hinfo->id);
3773 // if we don't know who sent the packet, don't do anything
3774 if(player_index == -1){
3778 // if we're the server, we should evaluate whether this guy is allowed to send the packet
3779 multi_pause_server_eval_request(&Net_players[player_index],(int)val);
3782 // send a game information update
3783 void send_game_info_packet()
3786 ubyte data[MAX_PACKET_SIZE], paused;
3788 // set the paused variable
3789 paused = (ubyte)((Netgame.game_state == NETGAME_STATE_PAUSED)?1:0);
3791 BUILD_HEADER(GAME_INFO);
3792 ADD_INT( Missiontime );
3795 multi_io_send_to_all(data, packet_size);
3798 // process a game information update
3799 void process_game_info_packet( ubyte *data, header *hinfo )
3805 offset = HEADER_LENGTH;
3807 // get the mission time -- we should examine our time and the time from the server. If off by some delta
3808 // time, set our time to server time (should take ping time into account!!!)
3809 GET_DATA( mission_time );
3814 // send an ingame nak packet
3815 void send_ingame_nak(int state, net_player *p)
3817 ubyte data[MAX_PACKET_SIZE];
3820 BUILD_HEADER(INGAME_NAK);
3824 multi_io_send_reliable(p, data, packet_size);
3827 // process an ingame nak packet
3828 void process_ingame_nak(ubyte *data, header *hinfo)
3830 int offset,state,pid;
3833 offset = HEADER_LENGTH;
3837 pid = find_player_id(hinfo->id);
3841 pl = &Net_players[pid];
3844 case ACK_FILE_ACCEPTED :
3845 Assert(Net_player->flags & NETINFO_FLAG_INGAME_JOIN);
3846 nprintf(("Network","Mission file rejected by server, aborting...\n"));
3847 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_FILE_REJECTED);
3852 // send a packet telling players to end the mission
3853 void send_endgame_packet(net_player *pl)
3855 ubyte data[MAX_PACKET_SIZE];
3859 BUILD_HEADER(MISSION_END);
3861 // sending to a specific player?
3863 Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
3864 multi_io_send_reliable(pl, data, packet_size);
3868 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3869 // send all player stats here
3870 multi_broadcast_stats(STATS_MISSION);
3872 // if in dogfight mode, send all dogfight stats as well
3873 ml_string("Before dogfight stats!");
3874 if(Netgame.type_flags & NG_TYPE_DOGFIGHT){
3875 ml_string("Sending dogfight stats!");
3877 multi_broadcast_stats(STATS_DOGFIGHT_KILLS);
3879 ml_string("After dogfight stats!");
3881 // tell everyone to leave the game
3882 multi_io_send_to_all_reliable(data, packet_size);
3884 multi_io_send_reliable(Net_player, data, packet_size);
3888 // process a packet indicating we should end the current mission
3889 void process_endgame_packet(ubyte *data, header *hinfo)
3894 offset = HEADER_LENGTH;
3898 ml_string("Receiving endgame packet");
3900 // if I'm the server, I should evaluate whether the sender is authorized to end the game
3901 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3902 // determine who this came from and make sure he is allowed to end the game
3903 player_num = find_player_id(hinfo->id);
3904 Assert(player_num != -1);
3909 // if the player is allowed to end the mission
3910 if(!multi_can_end_mission(&Net_players[player_num])){
3914 // act as if we hit alt+j locally
3915 multi_handle_end_mission_request();
3917 // all clients process immediately
3919 // ingame joiners should quit when they receive an endgame packet since the game is over
3920 if(Net_player->flags & NETINFO_FLAG_INGAME_JOIN){
3921 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_EARLY_END);
3925 // do any special processing for being in a state other than the gameplay state
3926 multi_handle_state_special();
3928 // make sure we're not already in the debrief state
3929 if((gameseq_get_state() != GS_STATE_DEBRIEF) && (gameseq_get_state() != GS_STATE_MULTI_DOGFIGHT_DEBRIEF)){
3930 multi_warpout_all_players();
3935 // send a position/orientation update for myself (if I'm an observer)
3936 void send_observer_update_packet()
3938 ubyte data[MAX_PACKET_SIZE];
3943 // its possible for the master to be an observer if has run out of respawns. In this case, he doesn't need
3944 // to send any update packets to anyone.
3945 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
3949 if((Player_obj == NULL) || (Player_obj->type != OBJ_OBSERVER) || (Net_player == NULL) || !(Net_player->flags & NETINFO_FLAG_OBSERVER)){
3955 BUILD_HEADER(OBSERVER_UPDATE);
3957 ret = multi_pack_unpack_position( 1, data + packet_size, &Player_obj->pos );
3959 ret = multi_pack_unpack_orient( 1, data + packet_size, &Player_obj->orient );
3962 // add targeting infomation
3963 if((Player_ai != NULL) && (Player_ai->target_objnum >= 0)){
3964 target_sig = Objects[Player_ai->target_objnum].net_signature;
3968 ADD_USHORT(target_sig);
3970 multi_io_send(Net_player, data, packet_size);
3973 // process a position/orientation update from an observer
3974 void process_observer_update_packet(ubyte *data, header *hinfo)
3980 physics_info bogus_pi;
3983 offset = HEADER_LENGTH;
3985 obs_num = find_player_id(hinfo->id);
3987 memset(&bogus_pi,0,sizeof(physics_info));
3988 ret = multi_pack_unpack_position( 0, data + offset, &g_vec );
3990 ret = multi_pack_unpack_orient( 0, data + offset, &g_mat );
3993 // targeting information
3994 GET_USHORT(target_sig);
3997 if((obs_num < 0) || (Net_players[obs_num].player->objnum < 0)){
4001 // set targeting info
4002 if(target_sig == 0){
4003 Net_players[obs_num].s_info.target_objnum = -1;
4005 target_obj = multi_get_network_object(target_sig);
4006 Net_players[obs_num].s_info.target_objnum = (target_obj == NULL) ? -1 : OBJ_INDEX(target_obj);
4009 Objects[Net_players[obs_num].player->objnum].pos = g_vec;
4010 Objects[Net_players[obs_num].player->objnum].orient = g_mat;
4011 Net_players[obs_num].s_info.eye_pos = g_vec;
4012 Net_players[obs_num].s_info.eye_orient = g_mat;
4015 void send_netplayer_slot_packet()
4017 ubyte data[MAX_PACKET_SIZE];
4018 int packet_size,idx;
4023 BUILD_HEADER(NETPLAYER_SLOTS_P);
4024 for(idx=0;idx<MAX_PLAYERS;idx++){
4025 if( MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_OBSERVER(Net_players[idx])){
4027 ADD_SHORT(Net_players[idx].player_id);
4028 ADD_USHORT(Objects[Net_players[idx].player->objnum].net_signature);
4029 ADD_INT(Net_players[idx].p_info.ship_class);
4030 ADD_INT(Net_players[idx].p_info.ship_index);
4036 // standalone case or not
4037 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
4038 multi_io_send_to_all_reliable(data, packet_size);
4040 multi_io_send_reliable(Net_player, data, packet_size);
4044 void process_netplayer_slot_packet(ubyte *data, header *hinfo)
4047 int player_num,ship_class,ship_index;
4053 offset = HEADER_LENGTH;
4055 // first untag all of the player ships and make them OF_COULD_BE_PLAYER
4056 multi_untag_player_ships();
4060 GET_SHORT(player_id);
4061 GET_USHORT(net_sig);
4062 GET_INT(ship_class);
4063 GET_INT(ship_index);
4064 player_num = find_player_id(player_id);
4066 nprintf(("Network","Error looking up player for object/slot assignment!!\n"));
4068 // call the function in multiutil.cpp to set up the player object stuff
4069 // being careful not to muck with the standalone object
4070 if(!((player_num == 0) && (Game_mode & GM_STANDALONE_SERVER))){
4071 objp = multi_get_network_object(net_sig);
4072 Assert(objp != NULL);
4073 multi_assign_player_ship( player_num, objp, ship_class );
4074 Net_players[player_num].p_info.ship_index = ship_index;
4075 objp->flags &= ~(OF_COULD_BE_PLAYER);
4076 objp->flags |= OF_PLAYER_SHIP;
4083 // standalone should forward the packet and wait for a response
4084 if(Game_mode & GM_STANDALONE_SERVER){
4085 send_netplayer_slot_packet();
4088 Net_player->state = NETPLAYER_STATE_SLOT_ACK;
4089 send_netplayer_update_packet();
4092 // two functions to deal with ships changing their primary/secondary weapon status. 'what' indicates
4093 // if this change is a primary or secondary change. new_bank is the new current primary/secondary
4094 // bank, link_status is whether primaries are linked or not, or secondaries are dual fire or not
4095 void send_ship_weapon_change( ship *shipp, int what, int new_bank, int link_status )
4097 ubyte data[MAX_PACKET_SIZE], utmp;
4100 BUILD_HEADER(SHIP_WSTATE_CHANGE);
4101 ADD_USHORT( Objects[shipp->objnum].net_signature );
4102 utmp = (ubyte)(what);
4104 utmp = (ubyte)(new_bank);
4106 utmp = (ubyte)(link_status);
4109 // Removed the above psnet_send() call - it didn't appear to do anything since it was called only from the server anyway - DB
4110 multi_io_send_to_all_reliable(data, packet_size);
4113 void process_ship_weapon_change( ubyte *data, header *hinfo )
4117 ubyte what, new_bank, link_status;
4121 offset = HEADER_LENGTH;
4122 GET_USHORT( signature );
4124 GET_DATA( new_bank );
4125 GET_DATA( link_status );
4128 objp = multi_get_network_object( signature );
4129 if ( objp == NULL ) {
4130 nprintf(("network", "Unable to locate ship with signature %d for weapon state change\n", signature));
4133 // Assert( objp->type == OBJ_SHIP );
4134 if(objp->type != OBJ_SHIP){
4138 // if this is my data, do nothing since I already have my own data
4139 if ( objp == Player_obj ){
4143 // now, get the ship and set the new bank and link modes based on the 'what' value
4144 shipp = &Ships[objp->instance];
4145 if ( what == MULTI_PRIMARY_CHANGED ) {
4146 shipp->weapons.current_primary_bank = new_bank;
4148 shipp->flags |= SF_PRIMARY_LINKED;
4150 shipp->flags &= ~SF_PRIMARY_LINKED;
4153 shipp->weapons.current_secondary_bank = new_bank;
4155 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
4157 shipp->flags &= ~SF_SECONDARY_DUAL_FIRE;
4162 // ship status change procedure
4163 // 1.) <client> - Client runs through the normal button_function procedure. Any remaining control bits are implied as being
4165 // 2.) <client> - Client puts this button_info item into his last_buttons array and sends a bunch of SHIP_STATUS packets
4166 // for added redundancy.
4167 // 3.) <server> - Receives the packet. Checks to see if the net_player on his side already has this one defined. If so, it
4168 // ignores as a repeat packet. Otherwise it puts it in the last_buttons array for the net_player
4169 // 4.) <server> - Server applies the command on his side (with multi_apply_ship_status(...) and sends the ack (also a SHIP_STATUS)
4170 // back to the client. Also sends multiple times for redundancy
4171 // 5.) <client> - Receives the packet back. Does a lookup into his last_buttons array. If he finds the match, apply the functions
4172 // and remove the item from the list. If no match is found it means that either he has received an ack, has acted
4173 // on it and removed it, or that it has been "timed out" and replaced by a newer button_info.
4175 #define SHIP_STATUS_REPEAT 2
4176 void send_ship_status_packet(net_player *pl, button_info *bi, int id)
4179 ubyte data[MAX_PACKET_SIZE];
4180 int packet_size = 0;
4186 BUILD_HEADER(SHIP_STATUS_CHANGE);
4188 for(idx=0;idx<NUM_BUTTON_FIELDS;idx++){
4189 temp = bi->status[idx];
4193 // server should send reliably (response packet)
4194 if(MULTIPLAYER_MASTER){
4195 multi_io_send_reliable(pl, data, packet_size);
4197 multi_io_send(pl, data, packet_size);
4201 void process_ship_status_packet(ubyte *data, header *hinfo)
4205 int player_num,unique_id;
4209 offset = HEADER_LENGTH;
4211 // zero out the button info structure for good measure
4212 memset(&bi,0,sizeof(button_info));
4214 // read the button-info
4217 for(idx=0;idx<NUM_BUTTON_FIELDS;idx++){
4219 bi.status[idx] = i_tmp;
4224 // this will be handled differently client and server side. Duh.
4225 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){ // SERVER SIDE
4226 // find which net-player has sent us butotn information
4227 player_num = find_player_id(hinfo->id);
4228 Assert(player_num >= 0);
4233 // don't process critical button information for observers
4234 // its a new button_info for this guy. apply and ack
4235 if(!MULTI_OBSERVER(Net_players[player_num]) && !lookup_ship_status(&Net_players[player_num],unique_id)){
4236 // mark that he's pressed this button
4237 // add_net_button_info(&Net_players[player_num], &bi, unique_id);
4239 // send a return packet
4240 send_ship_status_packet(&Net_players[player_num], &bi,unique_id);
4242 // apply the button presses to his ship as normal
4243 multi_apply_ship_status(&Net_players[player_num], &bi, 0);
4245 // else ignore it as a repeat from the same guy
4246 } else { // CLIENT SIDE
4247 // this is the return from the server, so we should now apply them locally
4248 // if(lookup_ship_status(Net_player,unique_id,1)){
4249 multi_apply_ship_status(Net_player, &bi, 1);
4254 // MWA 4/28/9 -- redid this function since message all fighers was really broken
4255 // for clients. Left all details to this function instead of higher level messaging
4257 void send_player_order_packet(int type, int index, int cmd)
4259 ubyte data[MAX_PACKET_SIZE];
4261 ushort target_signature;
4263 int packet_size = 0;
4265 BUILD_HEADER(PLAYER_ORDER_PACKET);
4268 ADD_DATA(val); // ship order or wing order, or message all fighters
4270 // if we are not messaging all ships or wings, add the index, which is the shipnum or wingnum
4271 if ( val != SQUAD_MSG_ALL ){
4272 ADD_INT(index); // net signature of target ship
4275 ADD_INT(cmd); // the command itself
4278 target_signature = 0;
4279 if ( Player_ai->target_objnum != -1 ){
4280 target_signature = Objects[Player_ai->target_objnum].net_signature;
4283 ADD_USHORT( target_signature );
4286 if ( (Player_ai->target_objnum != -1) && (Player_ai->targeted_subsys != NULL) ) {
4289 s_index = ship_get_index_from_subsys( Player_ai->targeted_subsys, Player_ai->target_objnum );
4290 Assert( s_index < CHAR_MAX ); // better be less than this!!!!
4291 t_subsys = (char)s_index;
4295 multi_io_send_reliable(Net_player, data, packet_size);
4298 // brief explanation :
4299 // in either case (wing or ship command), we need to send in a pseudo-ai object. Basically, both command handler
4300 // functions "normally" (non multiplayer) use a couple of the Player_ai fields. So, we just fill in the ones necessary
4301 // (which we can reconstruct from the packet data), and pass this as the default variable ai_info *local
4302 // Its kind of a hack, but it eliminates the need to go in and screw around with quite a bit of code
4303 void process_player_order_packet(ubyte *data, header *hinfo)
4305 int offset, player_num, command, index = 0, tobjnum_save;
4306 ushort target_signature;
4307 char t_subsys, type;
4308 object *objp, *target_objp;
4311 ship_subsys *tsubsys_save, *targeted_subsys;
4313 Assert(MULTIPLAYER_MASTER);
4315 // packet values - its easier to read all of these in first
4317 offset = HEADER_LENGTH;
4320 if ( type != SQUAD_MSG_ALL ){
4325 GET_USHORT( target_signature );
4326 GET_DATA( t_subsys );
4330 player_num = find_player_id(hinfo->id);
4331 if(player_num == -1){
4332 nprintf(("Network","Received player order packet from unknown player\n"));
4336 objp = &Objects[Net_players[player_num].player->objnum];
4337 if ( objp->type != OBJ_SHIP ) {
4338 nprintf(("Network", "not doing player order because object requestting is not a ship\n"));
4342 // HACK HACK HACK HACK HACK HACK
4343 // if the player has sent a rearm-repair me message, we should bail here after evaluating it, since most likely the rest of
4344 // the data is BOGUS. All people should be able to to these things as well.
4345 if(command == REARM_REPAIR_ME_ITEM){
4346 hud_squadmsg_repair_rearm(0,&Objects[Net_players[player_num].player->objnum]);
4348 } else if(command == ABORT_REARM_REPAIR_ITEM){
4349 hud_squadmsg_repair_rearm_abort(0,&Objects[Net_players[player_num].player->objnum]);
4353 // if this player is not allowed to do messaging, quit here
4354 if( !multi_can_message(&Net_players[player_num]) ){
4355 nprintf(("Network","Recieved player order packet from player not allowed to give orders!!\n"));
4359 // check to see if the type of order is a reinforcement call. If so, intercept it, and
4360 // then call them in.
4361 if ( type == SQUAD_MSG_REINFORCEMENT ) {
4362 Assert( (index >= 0) && (index < Num_reinforcements) );
4363 hud_squadmsg_call_reinforcement(index, player_num);
4367 // set the player's ai information here
4368 shipp = &Ships[objp->instance];
4369 aip = &Ai_info[shipp->ai_index];
4371 // get the target objnum and targeted subsystem. Quick out if we don't have an object to act on.
4372 target_objp = multi_get_network_object( target_signature );
4373 if ( target_objp == NULL ) {
4377 targeted_subsys = NULL;
4378 if ( t_subsys != -1 ) {
4379 Assert( target_objp != NULL );
4380 targeted_subsys = ship_get_indexed_subsys( &Ships[target_objp->instance], t_subsys);
4383 // save and restore the target objnum and targeted subsystem so that we don't mess up other things
4385 tobjnum_save = aip->target_objnum;
4386 tsubsys_save = aip->targeted_subsys;
4388 if ( target_objp ) {
4389 aip->target_objnum = OBJ_INDEX(target_objp);
4391 aip->target_objnum = -1;
4394 aip->targeted_subsys = targeted_subsys;
4396 if ( type == SQUAD_MSG_SHIP ) {
4397 hud_squadmsg_send_ship_command(index, command, 1, player_num);
4398 } else if ( type == SQUAD_MSG_WING ) {
4399 hud_squadmsg_send_wing_command(index, command, 1, player_num);
4400 } else if ( type == SQUAD_MSG_ALL ) {
4401 hud_squadmsg_send_to_all_fighters( command, player_num );
4404 Assert(tobjnum_save != Ships[aip->shipnum].objnum); // make sure not targeting self
4405 aip->target_objnum = tobjnum_save;
4406 aip->targeted_subsys = tsubsys_save;
4409 // FILE SIGNATURE stuff :
4410 // there are 2 cases for file signature sending which are handled very differently
4411 // 1.) Pregame. In this case, the host requires that all clients send a filesig packet (when process_file_sig() is called, it
4412 // posts an ACK_FILE_ACCEPTED packet to ack_evaluate, so he thinks they have acked).
4413 // 2.) Ingame join. In this case, the client sends his filesig packet automatically to the server and the _client_ waits for
4414 // the ack, before continuing to join. It would be way too messy to have the server wait on the clients ack, since he
4415 // would have to keep track of up to potentially 14 other ack handles (ouch).
4416 void send_file_sig_packet(ushort sum_sig,int length_sig)
4418 ubyte data[MAX_PACKET_SIZE];
4419 int packet_size = 0;
4421 BUILD_HEADER(FILE_SIG_INFO);
4422 ADD_USHORT(sum_sig);
4423 ADD_SHORT(length_sig);
4425 multi_io_send_reliable(Net_player, data, packet_size);
4428 void process_file_sig_packet(ubyte *data, header *hinfo)
4433 offset = HEADER_LENGTH;
4435 // should only be received on the server-side
4436 Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
4438 GET_USHORT(sum_sig);
4439 GET_INT(length_sig);
4441 server_verify_filesig(hinfo->id, sum_sig, length_sig);
4444 void send_file_sig_request(char *file_name)
4446 ubyte data[MAX_PACKET_SIZE];
4447 int packet_size = 0;
4449 BUILD_HEADER(FILE_SIG_REQUEST);
4450 ADD_STRING(file_name);
4452 Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
4454 multi_io_send_to_all_reliable(data, packet_size);
4457 void process_file_sig_request(ubyte *data, header *hinfo)
4459 int offset = HEADER_LENGTH;
4461 // get the mission name
4462 GET_STRING(Netgame.mission_name);
4465 // set the current mission filename
4466 strcpy(Game_current_mission_filename,Netgame.mission_name);
4469 multi_get_mission_checksum(Game_current_mission_filename);
4471 if(!multi_endgame_ending()){
4472 // reply to the server
4473 send_file_sig_packet(Multi_current_file_checksum,Multi_current_file_length);
4477 // functions to deal with subsystems getting whacked
4478 void send_subsystem_destroyed_packet( ship *shipp, int index, vector world_hitpos )
4480 ubyte data[MAX_PACKET_SIZE];
4483 vector tmp, local_hitpos;
4486 Assert ( index < UCHAR_MAX );
4487 uindex = (ubyte)(index);
4489 objp = &Objects[shipp->objnum];
4491 vm_vec_sub(&tmp, &world_hitpos, &objp->pos );
4492 vm_vec_rotate( &local_hitpos, &tmp, &objp->orient );
4494 BUILD_HEADER(SUBSYSTEM_DESTROYED);
4495 ADD_USHORT( Objects[shipp->objnum].net_signature );
4497 // ADD_DATA( local_hitpos );
4498 add_vector_data(data, &packet_size, local_hitpos);
4500 multi_io_send_to_all_reliable(data, packet_size);
4503 void process_subsystem_destroyed_packet( ubyte *data, header *hinfo )
4509 vector local_hit_pos, world_hit_pos;
4511 offset = HEADER_LENGTH;
4513 GET_USHORT( signature );
4515 // GET_DATA( local_hit_pos );
4516 get_vector_data(data, &offset, local_hit_pos);
4518 // get the network object. process it if we find it.
4519 objp = multi_get_network_object( signature );
4520 if ( objp != NULL ) {
4522 ship_subsys *subsysp;
4524 // be sure we have a ship!!!
4525 // Assert ( objp->type == OBJ_SHIP );
4526 if(objp->type != OBJ_SHIP){
4531 shipp = &Ships[objp->instance];
4533 // call to get the pointer to the subsystem we should be working on
4534 subsysp = ship_get_indexed_subsys( shipp, (int)uindex );
4535 vm_vec_unrotate( &world_hit_pos, &local_hit_pos, &objp->orient );
4536 vm_vec_add2( &world_hit_pos, &objp->pos );
4538 do_subobj_destroyed_stuff( shipp, subsysp, &world_hit_pos );
4539 if ( objp == Player_obj ) {
4540 hud_gauge_popup_start(HUD_DAMAGE_GAUGE, 5000);
4548 // packet to tell clients cargo of a ship was revealed to all
4549 void send_subsystem_cargo_revealed_packet( ship *shipp, int index )
4551 ubyte data[MAX_PACKET_SIZE], uindex;
4554 Assert ( index < UCHAR_MAX );
4555 uindex = (ubyte)(index);
4557 // build the header and add the data
4558 BUILD_HEADER(SUBSYS_CARGO_REVEALED);
4559 ADD_USHORT( Objects[shipp->objnum].net_signature );
4562 // server sends to all players
4563 if(MULTIPLAYER_MASTER){
4564 multi_io_send_to_all_reliable(data, packet_size);
4566 // clients just send to the server
4568 multi_io_send_reliable(Net_player, data, packet_size);
4572 // process a subsystem cargo revealed packet
4573 void process_subsystem_cargo_revealed_packet( ubyte *data, header *hinfo )
4580 ship_subsys *subsysp;
4582 offset = HEADER_LENGTH;
4583 GET_USHORT( signature );
4587 // get a ship pointer and call the ship function to reveal the cargo
4588 objp = multi_get_network_object( signature );
4589 if ( objp == NULL ) {
4590 nprintf(("Network", "Could not find object with net signature %d for cargo revealed\n", signature ));
4594 // Assert( objp->type == OBJ_SHIP );
4595 if((objp->type != OBJ_SHIP) || (objp->instance < 0) || (objp->instance >= MAX_SHIPS)){
4599 shipp = &Ships[objp->instance];
4601 // call to get the pointer to the subsystem we should be working on
4602 subsysp = ship_get_indexed_subsys( shipp, (int)uindex );
4603 if (subsysp == NULL) {
4604 nprintf(("Network", "Could not find subsys for ship %s for cargo revealed\n", Ships[objp->instance].ship_name ));
4608 // this will take care of re-routing to all other clients
4609 void ship_do_cap_subsys_cargo_revealed( ship *shipp, ship_subsys *subsys, int from_network );
4610 ship_do_cap_subsys_cargo_revealed( shipp, subsysp, 1 );
4612 // server should rebroadcast
4613 if(MULTIPLAYER_MASTER){
4614 send_subsystem_cargo_revealed_packet(&Ships[objp->instance], (int)uindex);
4618 void send_netplayer_load_packet(net_player *pl)
4620 ubyte data[MAX_PACKET_SIZE];
4621 int packet_size = 0;
4623 BUILD_HEADER(LOAD_MISSION_NOW);
4624 ADD_STRING(Netgame.mission_name);
4627 multi_io_send_to_all_reliable(data, packet_size);
4629 multi_io_send_reliable(pl, data, packet_size);
4633 void process_netplayer_load_packet(ubyte *data, header *hinfo)
4636 int offset = HEADER_LENGTH;
4641 strcpy(Netgame.mission_name,str);
4642 strcpy(Game_current_mission_filename,str);
4643 if(!Multi_mission_loaded){
4645 // MWA 2/3/98 -- ingame join changes!!!
4646 // everyone can go through the same mission loading path here!!!!
4647 nprintf(("Network","Loading mission..."));
4649 // notify everyone that I'm loading the mission
4650 Net_player->state = NETPLAYER_STATE_MISSION_LOADING;
4651 send_netplayer_update_packet();
4653 // do the load itself
4654 game_start_mission();
4656 // ingame joiners need to "untag" all player ships as could_be_players. The ingame joining
4657 // code will remark the correct player ships
4658 if ( Net_player->flags & NETINFO_FLAG_INGAME_JOIN ) {
4659 multi_untag_player_ships();
4662 Net_player->flags |= NETINFO_FLAG_MISSION_OK;
4663 Net_player->state = NETPLAYER_STATE_MISSION_LOADED;
4664 send_netplayer_update_packet();
4666 Multi_mission_loaded = 1;
4667 nprintf(("Network","Finished loading mission\n"));
4671 void send_jump_into_mission_packet(net_player *pl)
4673 ubyte data[MAX_PACKET_SIZE];
4674 int packet_size = 0;
4676 Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
4678 BUILD_HEADER(JUMP_INTO_GAME);
4680 // ingame joiners will get special data. We need to tell them about the state of the mission, like paused,
4681 // and possible other things.
4683 if ( pl->flags & NETINFO_FLAG_INGAME_JOIN ) {
4684 ADD_INT(Netgame.game_state);
4690 multi_io_send_to_all_reliable(data, packet_size);
4692 // send to a specific player
4694 multi_io_send_reliable(pl, data, packet_size);
4698 void process_jump_into_mission_packet(ubyte *data, header *hinfo)
4700 int offset = HEADER_LENGTH;
4705 // if I am ingame joining, there should be extra data. For now, this data is the netgame state.
4706 // the game could be paused, so ingame joiner needs to deal with it.
4707 if ( Net_player->flags & NETINFO_FLAG_INGAME_JOIN ) {
4709 Netgame.game_state = state;
4714 // handle any special processing for being in a weird substate
4715 multi_handle_state_special();
4717 // if I'm an ingame joiner, go to the ship select screen, or if I'm an observer, jump right in!
4718 if(Net_player->flags & NETINFO_FLAG_INGAME_JOIN){
4719 if(Net_player->flags & NETINFO_FLAG_OBSERVER){
4720 multi_ingame_observer_finish();
4722 gameseq_post_event(GS_EVENT_INGAME_PRE_JOIN);
4723 Net_player->state = NETPLAYER_STATE_INGAME_SHIP_SELECT;
4724 send_netplayer_update_packet();
4727 // start the mission!!
4728 if(!(Game_mode & GM_IN_MISSION) && !(Game_mode & GM_STANDALONE_SERVER)){
4729 Netgame.game_state = NETGAME_STATE_IN_MISSION;
4730 gameseq_post_event(GS_EVENT_ENTER_GAME);
4731 Net_player->state = NETPLAYER_STATE_IN_MISSION;
4732 send_netplayer_update_packet();
4736 extern int Player_multi_died_check;
4737 Player_multi_died_check = -1;
4739 // recalc all object pairs now
4740 extern void obj_reset_all_collisions();
4741 obj_reset_all_collisions();
4743 // display some cool text
4744 multi_common_add_text(XSTR("Received mission start\n",720),1);
4747 ml_string(NOX("Client received mission start from server - entering mission"));
4752 char *repair_text[] = {
4754 "REPAIR_INFO_BEGIN",
4756 "REPAIR_INFO_UPDATE",
4757 "REPAIR_INFO_QUEUE",
4758 "REPAIR_INFO_ABORT",
4759 "REPAIR_INFO_BROKEN",
4760 "REPAIR_INFO_WARP_ADD",
4761 "REPAIR_INFO_WARP_REMOVE",
4762 "REPAIR_INFO_ONWAY",
4763 "REPAIR_INFO_KILLED",
4764 "REPAIR_INFO_COMPLETE",
4769 // the following two routines deal with updating and sending information regarding players
4770 // rearming and repairing during the game. The process function calls the routines to deal with
4771 // setting flags and other interesting things.
4772 void send_repair_info_packet(object *repaired_objp, object *repair_objp, int code )
4774 int packet_size = 0;
4775 ushort repaired_signature, repair_signature;
4776 ubyte data[MAX_PACKET_SIZE];
4779 // use the network signature of the destination object if there is one, -1 otherwise.
4780 // client will piece it all together
4781 repaired_signature = repaired_objp->net_signature;
4783 // the repair ship may be NULL here since it might have been destroyed
4784 repair_signature = 0;
4786 repair_signature = repair_objp->net_signature;
4789 BUILD_HEADER(CLIENT_REPAIR_INFO);
4792 ADD_USHORT( repaired_signature );
4793 ADD_USHORT( repair_signature );
4795 multi_io_send_to_all_reliable(data, packet_size);
4797 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));
4800 void process_repair_info_packet(ubyte *data, header *hinfo)
4802 int offset = HEADER_LENGTH;
4803 ushort repaired_signature, repair_signature;
4804 object *repaired_objp, *repair_objp;
4808 GET_USHORT( repaired_signature );
4809 GET_USHORT( repair_signature );
4812 repaired_objp = multi_get_network_object( repaired_signature );
4813 repair_objp = multi_get_network_object( repair_signature );
4815 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));
4817 if ( Net_player->flags & NETINFO_FLAG_WARPING_OUT ){
4821 if ( repaired_objp == NULL ) {
4822 Int3(); // Sandeep says this is bad bad bad. No ship to repair.
4826 // the hope is to simply call the routine in the ai code to set/unset flags
4827 // based on the code value and everything else should happen..I hope....
4828 if ( (code != REPAIR_INFO_WARP_ADD) && (code != REPAIR_INFO_WARP_REMOVE ) ) {
4830 ai_do_objects_repairing_stuff( repaired_objp, repair_objp, (int)code );
4832 // set the dock flags when repair begins. Prevents problem in lagging docking
4833 // packet. Also set any other flags/modes which need to be set to prevent Asserts.
4835 if ( (code == REPAIR_INFO_BEGIN) && (repair_objp != NULL) ) {
4836 ai_do_objects_docked_stuff( repaired_objp, repair_objp );
4837 Ai_info[Ships[repair_objp->instance].ai_index].mode = AIM_DOCK;
4840 // if the repair is done (either by abort, or ending), mark the repair ship's goal
4842 if ( ((code == REPAIR_INFO_ABORT) || (code == REPAIR_INFO_END)) && repair_objp ){
4843 ai_mission_goal_complete( &Ai_info[Ships[repair_objp->instance].ai_index] );
4846 if ( code == REPAIR_INFO_WARP_ADD ){
4847 mission_warp_in_support_ship( repaired_objp );
4849 mission_remove_scheduled_repair( repaired_objp );
4854 // sends information updating clients on certain AI information that clients will
4855 // need to know about to keep HUD information up to date. objp is the object that we
4856 // are updating, and what is the type of stuff that we are updating.
4857 void send_ai_info_update_packet( object *objp, char what )
4860 ushort other_signature;
4861 ubyte data[MAX_PACKET_SIZE];
4863 ubyte dock_index, dockee_index;
4865 // Assert( objp->type == OBJ_SHIP );
4866 if(objp->type != OBJ_SHIP){
4869 aip = &Ai_info[Ships[objp->instance].ai_index];
4872 if ( Ships[objp->instance].flags & (SF_DEPARTING | SF_DYING) )
4875 BUILD_HEADER( AI_INFO_UPDATE );
4876 ADD_USHORT( objp->net_signature );
4879 // depending on the "what" value, we will send different information
4883 case AI_UPDATE_DOCK:
4884 // for docking ships, add the signature of the ship that we are docking with.
4885 Assert( aip->dock_objnum != -1 );
4886 other_signature = Objects[aip->dock_objnum].net_signature;
4887 dock_index = (ubyte)(aip->dock_index);
4888 dockee_index = (ubyte)(aip->dockee_index);
4889 ADD_USHORT( other_signature );
4890 ADD_DATA(dock_index);
4891 ADD_DATA(dockee_index);
4894 case AI_UPDATE_UNDOCK:
4895 // for undocking ships, check the dock_objnum since we might or might not have it
4896 // depending on whether or not a ship was destroyed while we were docked.
4897 other_signature = 0;
4898 if ( aip->dock_objnum != -1 )
4899 other_signature = Objects[aip->dock_objnum].net_signature;
4900 ADD_USHORT( other_signature );
4904 case AI_UPDATE_ORDERS: {
4907 // for orders, we only need to send a little bit of information here. Be sure that the
4908 // first order for this ship is active
4909 Assert( (aip->active_goal != AI_GOAL_NONE) && (aip->active_goal != AI_ACTIVE_GOAL_DYNAMIC) );
4910 ADD_INT( aip->goals[0].ai_mode );
4911 ADD_INT( aip->goals[0].ai_submode );
4913 if ( aip->goals[0].ship_name != NULL )
4914 shipnum = ship_name_lookup( aip->goals[0].ship_name );
4916 // the ship_name member of the goals structure may or may not contain a real shipname. If we don't
4917 // have a valid shipnum, then don't sweat it since it may not really be a ship.
4918 if ( shipnum != -1 ) {
4919 Assert( Ships[shipnum].objnum != -1 );
4920 other_signature = Objects[Ships[shipnum].objnum].net_signature;
4922 other_signature = 0;
4924 ADD_USHORT( other_signature );
4926 // for docking, add the dock and dockee index
4927 if ( aip->goals[0].ai_mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
4928 Assert( (aip->goals[0].docker.index >= 0) && (aip->goals[0].docker.index < UCHAR_MAX) );
4929 Assert( (aip->goals[0].dockee.index >= 0) && (aip->goals[0].dockee.index < UCHAR_MAX) );
4930 dock_index = (ubyte)aip->goals[0].docker.index;
4931 dockee_index = (ubyte)aip->goals[0].dockee.index;
4932 ADD_DATA( dock_index );
4933 ADD_DATA( dockee_index );
4942 multi_rate_add(1, "aiu", packet_size);
4943 multi_io_send_to_all_reliable(data, packet_size);
4946 // process an ai_info update packet. Docking/undocking, ai orders, etc. are taken care of here. This
4947 // information is mainly used to keep the clients HUD up to date with the appropriate information.
4948 void process_ai_info_update_packet( ubyte *data, header *hinfo)
4950 int offset = HEADER_LENGTH;
4952 ushort net_signature, other_net_signature;
4953 object *objp, *other_objp;
4956 ubyte dock_index = 0, dockee_index = 0;
4958 GET_USHORT( net_signature ); // signature of the object that we are dealing with.
4959 GET_DATA( code ); // code of what we are doing.
4960 objp = multi_get_network_object( net_signature );
4962 nprintf(("Network", "Couldn't find object for ai update\n"));
4965 case AI_UPDATE_DOCK:
4966 GET_USHORT( other_net_signature );
4967 GET_DATA( dock_index );
4968 GET_DATA( dockee_index );
4969 other_objp = multi_get_network_object( other_net_signature );
4971 nprintf(("Network", "Couldn't find other object for ai update on dock\n"));
4973 // if we don't have an object to work with, break out of loop
4974 if ( !objp || !other_objp || (objp->type != OBJ_SHIP) || (other_objp->type != OBJ_SHIP)){
4978 Assert( other_objp->type == OBJ_SHIP );
4979 Ai_info[Ships[objp->instance].ai_index].dock_index = dock_index;
4980 Ai_info[Ships[objp->instance].ai_index].dockee_index = dockee_index;
4982 Ai_info[Ships[other_objp->instance].ai_index].dock_index = dockee_index;
4983 Ai_info[Ships[other_objp->instance].ai_index].dockee_index = dock_index;
4985 ai_do_objects_docked_stuff( objp, other_objp );
4988 case AI_UPDATE_UNDOCK:
4989 GET_USHORT( other_net_signature );
4990 other_objp = multi_get_network_object( other_net_signature );
4992 // if we don't have an object to work with, break out of loop
4996 ai_do_objects_undocked_stuff( objp, other_objp );
4999 case AI_UPDATE_ORDERS:
5002 GET_USHORT( other_net_signature );
5003 if ( mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
5004 GET_DATA(dock_index);
5005 GET_DATA(dockee_index);
5008 // be sure that we have a ship object!!!
5009 if ( !objp || (objp->type != OBJ_SHIP) )
5012 // set up the information in the first goal element of the object in question
5013 aip = &Ai_info[Ships[objp->instance].ai_index];
5014 aip->active_goal = 0;
5015 aip->goals[0].ai_mode = mode;
5016 aip->goals[0].ai_submode = submode;
5018 // for docking, add the dock and dockee index
5019 if ( mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
5020 aip->dock_index = dock_index;
5021 aip->dockee_index = dockee_index;
5024 // get a shipname if we can.
5025 other_objp = multi_get_network_object( other_net_signature );
5026 if ( other_objp && (other_objp->type == OBJ_SHIP) ) {
5027 // get a pointer to the shipname in question. Use the ship_name value in the
5028 // ship. We are only using this for HUD display, so I think that using this
5029 // method will be fine.
5030 aip->goals[0].ship_name = Ships[other_objp->instance].ship_name;
5032 // special case for destroy subsystem -- get the ai_info pointer to our target ship
5033 // so that we can properly set up what subsystem this ship is attacking.
5034 if ( (mode == AI_GOAL_DESTROY_SUBSYSTEM ) && (submode >= 0) )
5035 aip->targeted_subsys = ship_get_indexed_subsys( &Ships[other_objp->instance], submode);
5037 // if docking -- set the dock index and dockee index of this other ship
5038 if ( mode & (AI_GOAL_DOCK|AI_GOAL_REARM_REPAIR) ) {
5039 Ai_info[Ships[other_objp->instance].ai_index].dock_index = dockee_index;
5040 Ai_info[Ships[other_objp->instance].ai_index].dockee_index = dock_index;
5047 Int3(); // this Int3() should be temporary
5048 nprintf(("Network", "Invalid code for ai update: %d\n", code));
5054 // tell the standalone to move into the MISSION_SYNC_STATE
5055 void send_mission_sync_packet(int mode,int start_campaign)
5057 ubyte data[MAX_PACKET_SIZE],is_campaign;
5058 int packet_size = 0;
5060 Assert((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER));
5062 // build the header and add the sync mode (pre or post briefing)
5063 BUILD_HEADER(MISSION_SYNC_DATA);
5066 // if this is a campaign game
5067 if(mode == MULTI_SYNC_PRE_BRIEFING){
5068 if(Game_mode & GM_CAMPAIGN_MODE){
5069 // add a byte indicating campaign mode
5071 ADD_DATA(is_campaign);
5073 // add a byte indicating if we should be starting a campaign or continuing it
5074 is_campaign = (ubyte)start_campaign;
5075 ADD_DATA(is_campaign);
5077 // add the campaign filename
5078 ADD_STRING(Netgame.campaign_name);
5080 // otherwise if this is a single mission
5082 // add a byte indicating single mission mode
5084 ADD_DATA(is_campaign);
5086 // add the mission filename
5087 ADD_STRING(Game_current_mission_filename);
5090 multi_io_send_reliable(Net_player, data, packet_size);
5093 // move into the MISSION_SYNC state when this is received
5094 // this packet is sent only from a game host to a standalone
5095 void process_mission_sync_packet(ubyte *data, header *hinfo)
5098 ubyte campaign_flag;
5099 int offset = HEADER_LENGTH;
5101 Assert(Game_mode & GM_STANDALONE_SERVER);
5103 // if this is a team vs team situation, lock the players send a final team update
5104 if(Netgame.type_flags & NG_TYPE_TEAM){
5105 multi_team_host_lock_all();
5106 multi_team_send_update();
5109 // get the sync mode (pre or post briefing)
5112 if(mode == MULTI_SYNC_PRE_BRIEFING){
5113 // get the flag indicating if this is a single mission or a campaign mode
5114 GET_DATA(campaign_flag);
5116 // get the flag indicating whether we should be starting a new campaign
5117 GET_DATA(campaign_flag);
5119 // get the campaign filename
5120 GET_STRING(Netgame.campaign_name);
5122 // either start a new campaign or continue on to the next mission in the current campaign
5124 multi_campaign_start(Netgame.campaign_name);
5126 multi_campaign_next_mission();
5129 // make sure we remove the campaign mode flag
5130 Game_mode &= ~(GM_CAMPAIGN_MODE);
5132 // get the single mission filename
5133 GET_STRING(Game_current_mission_filename);
5134 strcpy(Netgame.mission_name,Game_current_mission_filename);
5139 // set the correct mode and m ove into the state
5140 Multi_sync_mode = mode;
5141 gameseq_post_event(GS_EVENT_MULTI_MISSION_SYNC);
5144 // tell a player to merge his mission stats into his alltime stats
5145 void send_store_stats_packet(int accept)
5148 int packet_size = 0;
5150 BUILD_HEADER(STORE_MISSION_STATS);
5152 // add whether we're accepting or tossing
5153 val = (ubyte)accept;
5156 // if I'm the server, send to everyone, else send to the standalone to be rebroadcasted
5157 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5158 multi_io_send_to_all_reliable(data, packet_size);
5160 multi_io_send_reliable(Net_player, data, packet_size);
5164 void process_store_stats_packet(ubyte *data, header *hinfo)
5166 int offset = HEADER_LENGTH;
5172 // if I'm the standalone, rebroadcast. Otherwise, if I'm a client, merge my mission stats with my alltime stats
5173 if(Game_mode & GM_STANDALONE_SERVER){
5174 // rebroadcast the packet to all others in the game
5175 nprintf(("Network","Standalone received store stats packet - rebroadcasting..\n"));
5176 multi_io_send_to_all_reliable(data, offset);
5179 // all players should mark the stats as being accepted in the debriefing
5180 multi_debrief_stats_accept();
5182 // all players should mark the stats as being "tossed" in the debriefing
5183 multi_debrief_stats_toss();
5188 void send_debris_update_packet(object *objp,int code)
5190 ubyte data[MAX_PACKET_SIZE];
5192 int packet_size = 0;
5194 BUILD_HEADER(DEBRIS_UPDATE);
5195 ADD_USHORT(objp->net_signature);
5199 // add any extra relevant data
5201 case DEBRIS_UPDATE_UPDATE:
5202 // ADD_DATA(objp->pos); // add position
5203 add_vector_data(data, &packet_size, objp->pos);
5204 ADD_ORIENT(objp->orient); // add orientation
5205 // ADD_DATA(objp->phys_info.vel); // add velocity
5206 add_vector_data(data, &packet_size, objp->phys_info.vel);
5207 // ADD_DATA(objp->phys_info.rotvel); // add rotational velocity
5208 add_vector_data(data, &packet_size, objp->phys_info.rotvel);
5211 multi_io_send_to_all(data, packet_size);
5214 void process_debris_update_packet(ubyte *data, header *hinfo)
5218 object bogus_object;
5220 int offset = HEADER_LENGTH;
5222 GET_USHORT(net_sig);
5226 objp = multi_get_network_object(net_sig);
5228 objp = &bogus_object;
5232 // update the object
5233 case DEBRIS_UPDATE_UPDATE:
5234 //GET_DATA(objp->pos);
5235 get_vector_data( data, &offset, objp->pos );
5237 GET_ORIENT(objp->orient);
5238 GET_DATA(objp->phys_info.vel);
5239 GET_DATA(objp->phys_info.rotvel);
5241 // simply remove it (no explosion)
5242 case DEBRIS_UPDATE_REMOVE:
5243 if(objp != &bogus_object){
5244 Assert(objp->type == OBJ_DEBRIS);
5245 obj_delete(OBJ_INDEX(objp));
5249 case DEBRIS_UPDATE_NUKE:
5250 if(objp != &bogus_object)
5251 debris_hit(objp,NULL,&objp->pos,1000000.0f);
5259 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)
5261 ubyte data[MAX_PACKET_SIZE];
5263 int packet_size = 0;
5265 BUILD_HEADER(WSS_REQUEST_PACKET);
5267 // add the request information
5268 ADD_SHORT(player_id);
5270 ADD_INT(from_index);
5273 ADD_INT(wl_ship_slot); // only used in weapons loadout
5274 ADD_INT(ship_class);
5277 // a standard request
5279 multi_io_send_reliable(Net_player, data, packet_size);
5281 // being routed through the standalone to the host of the game
5283 Assert(Game_mode & GM_STANDALONE_SERVER);
5284 multi_io_send_reliable(p, data, packet_size);
5288 void process_wss_request_packet(ubyte *data, header *hinfo)
5290 int offset = HEADER_LENGTH;
5291 int from_slot,from_index;
5292 int to_slot,to_index;
5294 int wl_ship_slot,ship_class;
5298 // determine who this request is from
5299 GET_SHORT(player_id);
5300 player_num = find_player_id(player_id);
5302 // read in the request data
5304 GET_INT(from_index);
5307 GET_INT(wl_ship_slot); // only used in weapons loadout
5308 GET_INT(ship_class); // only used in multi team select
5312 Assert(player_num != -1);
5313 if(player_num == -1){
5317 // if we're the standalone, we have to route this packet to the host of the game
5318 if(Game_mode & GM_STANDALONE_SERVER){
5319 send_wss_request_packet(player_id, from_slot, from_index, to_slot, to_index, wl_ship_slot, ship_class, mode, Netgame.host);
5321 // otherwise we're the host and should process the request
5324 case WSS_WEAPON_SELECT :
5325 wl_drop(from_slot,from_index,to_slot,to_index,wl_ship_slot,player_num);
5327 case WSS_SHIP_SELECT :
5328 multi_ts_drop(from_slot,from_index,to_slot,to_index,ship_class,player_num);
5336 void send_wss_update_packet(int team_num,ubyte *wss_data,int size)
5338 ubyte data[MAX_PACKET_SIZE],team;
5339 int packet_size = 0;
5341 Assert(size <= (MAX_PACKET_SIZE - 10));
5343 BUILD_HEADER(WSS_UPDATE_PACKET);
5345 // add the team/pool # this is for
5346 team = (ubyte)team_num;
5349 // add the data block size
5352 // add the data itself
5353 memcpy(data + packet_size,wss_data,size);
5354 packet_size += size;
5356 // if we're also the master of the game (not on a standalone)
5357 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5358 multi_io_send_to_all_reliable(data, packet_size);
5360 // if we're only the host on the standalone, then send the packet to the standalone to be routed
5362 multi_io_send_reliable(Net_player, data, packet_size);
5366 void process_wss_update_packet(ubyte *data, header *hinfo)
5369 int size,player_index,idx;
5370 int offset = HEADER_LENGTH;
5372 // get the team/pool #
5375 // get the data size
5378 // if we're the standalone, then we should be routing this data to all the other clients
5379 if(Game_mode & GM_STANDALONE_SERVER){
5384 // determine where this came from
5385 player_index = find_player_id(hinfo->id);
5386 Assert(player_index != -1);
5387 if(player_index < 0){
5391 // route the packet (don't resend it to the host)
5392 for(idx=0;idx<MAX_PLAYERS;idx++){
5393 if(MULTI_CONNECTED(Net_players[idx]) && (&Net_players[idx] != Net_player) && (&Net_players[idx] != &Net_players[player_index]) ){
5394 multi_io_send_reliable(&Net_players[idx], data, offset);
5398 // set the proper pool pointers
5399 ss_set_team_pointers((int)team);
5401 // read in the block of data, and apply it to the weapons/ship pools
5402 offset += restore_wss_data(data + offset);
5405 // set the pool pointers back to my own team
5406 ss_set_team_pointers(Net_player->p_info.team);
5408 // sync the interface if this was for my own team
5409 if((int)team == Net_player->p_info.team){
5410 multi_ts_sync_interface();
5417 // function to send firing information from the client to the server once they reach
5418 // the final sync screen.
5419 void send_firing_info_packet()
5421 ubyte data[MAX_PACKET_SIZE];
5423 ubyte plinked, sdual;
5425 Assert( !(Net_player->flags & NETINFO_FLAG_AM_MASTER) );
5427 BUILD_HEADER(FIRING_INFO);
5428 plinked = (ubyte)((Player_ship->flags & SF_PRIMARY_LINKED)?1:0);
5429 sdual = (ubyte)((Player_ship->flags & SF_SECONDARY_DUAL_FIRE)?1:0);
5430 ADD_DATA( plinked );
5433 multi_io_send_reliable(Net_player, data, packet_size);
5436 void process_firing_info_packet( ubyte *data, header *hinfo )
5438 int offset, player_num;
5439 ubyte plinked, sdual;
5442 // only the master of the game should be dealing with these packets
5443 Assert( Net_player->flags & NETINFO_FLAG_AM_MASTER );
5445 offset = HEADER_LENGTH;
5446 GET_DATA( plinked );
5450 player_num = find_player_id(hinfo->id);
5452 nprintf(("Network","Received firing info packet from unknown player, ignoring\n"));
5456 // get the ship pointer for this player and set the flags accordingly.
5457 shipp = &(Ships[Objects[Net_players[player_num].player->objnum].instance]);
5459 shipp->flags |= SF_PRIMARY_LINKED;
5461 shipp->flags &= ~SF_PRIMARY_LINKED;
5464 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
5466 shipp->flags &= ~SF_SECONDARY_DUAL_FIRE;
5469 // packet to deal with changing status of mission goals. used to be sent every so often from server
5470 // to clients, but with addition of reliable sockets, send when complete, invalid, etc.
5471 // goal_num is the index into mission_goals. new_status means failed, success, etc. -1 if not used.
5472 // valid means goal is changing to invalid(0) or valid(1). only applies if new_status == -1
5473 void send_mission_goal_info_packet( int goal_num, int new_status, int valid )
5475 ubyte data[MAX_PACKET_SIZE];
5478 BUILD_HEADER(MISSION_GOAL_INFO);
5481 ADD_INT(new_status);
5484 multi_io_send_to_all_reliable(data, packet_size);
5487 void process_mission_goal_info_packet( ubyte *data, header *hinfo )
5489 int offset, goal_num, new_status, valid;
5491 offset = HEADER_LENGTH;
5493 GET_INT(new_status);
5497 // if new_status != -1, then this is a change in goal status (i.e. goal failed, or is successful)
5498 if ( new_status != -1 ){
5499 mission_goal_status_change( goal_num, new_status );
5501 mission_goal_validation_change( goal_num, valid );
5505 void send_player_settings_packet(net_player *p)
5507 ubyte data[MAX_PACKET_SIZE];
5510 int packet_size = 0;
5513 BUILD_HEADER(PLAYER_SETTINGS);
5515 // add all the data for all the players
5517 for(idx=0;idx<MAX_PLAYERS;idx++){
5518 if(MULTI_CONNECTED(Net_players[idx])){
5520 ADD_SHORT(Net_players[idx].player_id);
5522 // break the p_info structure by member, so we don't overwrite any absolute pointers
5523 // ADD_DATA(Net_players[idx].p_info);
5524 ADD_INT(Net_players[idx].p_info.team);
5525 ADD_INT(Net_players[idx].p_info.ship_index);
5526 ADD_INT(Net_players[idx].p_info.ship_class);
5529 // add the stop byte
5533 // either broadcast the data or send to a specific player
5535 multi_io_send_to_all_reliable(data, packet_size);
5537 multi_io_send_reliable(p, data, packet_size);
5541 void process_player_settings_packet(ubyte *data, header *hinfo)
5543 int offset,player_num;
5544 net_player_info bogus,*ptr;
5548 offset = HEADER_LENGTH;
5550 // read in the data for all the players
5552 while(stop != 0xff){
5553 // lookup the player
5554 GET_SHORT(player_id);
5555 player_num = find_player_id(player_id);
5557 // make sure this is a valid player
5558 if(player_num == -1){
5561 ptr = &Net_players[player_num].p_info;
5565 GET_INT(ptr->ship_index);
5566 GET_INT(ptr->ship_class);
5573 // update the server with my new state
5574 // MWA -- 3/31/98 -- check for in mission instead of state.
5575 //if ( Netgame.game_state == NETGAME_STATE_MISSION_SYNC) {
5576 if( !(Game_mode & GM_IN_MISSION) ) {
5577 Net_player->state = NETPLAYER_STATE_SETTINGS_ACK;
5578 send_netplayer_update_packet();
5582 // display some cool text
5583 multi_common_add_text(XSTR("Received player settings packet\n",721),1);
5586 void send_deny_packet(net_addr *addr, int code)
5589 int packet_size = 0;
5591 // build the header and add the rejection code
5597 psnet_send(addr, data, packet_size);
5600 void process_deny_packet(ubyte *data, header *hinfo)
5604 // get the denial code
5605 offset = HEADER_LENGTH;
5609 // if there is already a dialog active, do nothing - who cares at this point.
5614 // display the appropriate dialog
5616 case JOIN_DENY_JR_STATE :
5617 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));
5619 case JOIN_DENY_JR_TRACKER_INVAL :
5620 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));
5622 case JOIN_DENY_JR_PASSWD :
5623 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because this is a password protected game",724));
5625 case JOIN_DENY_JR_CLOSED :
5626 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));
5628 case JOIN_DENY_JR_TEMP_CLOSED :
5629 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));
5631 case JOIN_DENY_JR_RANK_HIGH :
5632 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));
5634 case JOIN_DENY_JR_RANK_LOW :
5635 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because this is a rank limited game and your rank is too low",728));
5637 case JOIN_DENY_JR_DUP :
5638 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because there is an identical player already in the game",729));
5640 case JOIN_DENY_JR_FULL :
5641 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because the game is full",730));
5643 case JOIN_DENY_JR_BANNED :
5644 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because you are banned from this server",731));
5646 case JOIN_DENY_JR_NOOBS :
5647 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been rejected because this game does not allow observers",732));
5649 case JOIN_DENY_JR_INGAME_JOIN :
5650 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));
5652 case JOIN_DENY_JR_BAD_VERSION :
5653 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));
5655 case JOIN_DENY_JR_TYPE :
5656 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You cannot join a game in progress unless it is a dogfight mission.",1433));
5660 // call this so that the join request timestamp automatically expires when we hear back from the server
5661 multi_join_reset_join_stamp();
5664 // this packet will consist of
5665 // 1.) netplayer ship classes (85 bytes max)
5666 // 2.) ship weapon state data (241 bytes max)
5667 // 3.) player settings et. al. (133 bytes max)
5668 // TOTAL 459 NOTE : keep this in mind when/if adding new data to this packet
5669 void send_post_sync_data_packet(net_player *p, int std_request)
5671 ubyte data[MAX_PACKET_SIZE], val;
5676 ushort sval, ship_ets;
5677 int idx, player_index;
5678 int packet_size = 0;
5682 BUILD_HEADER(POST_SYNC_DATA);
5684 // some header information for standalone packet routing purposes
5685 val = (ubyte)std_request;
5688 // the standalone has two situations
5689 // 1.) sending a request to the host to distribute this block of data
5690 // 2.) having recevied this block of data from the host, it redistributes it
5691 if((Game_mode & GM_STANDALONE_SERVER) && std_request && (Netgame.host != NULL)){
5692 // case 1, send the request
5693 multi_io_send_reliable(Netgame.host, data, packet_size);
5696 // case 2 for the standalone is below (as normal)
5698 // otherwise build the data now
5700 // add all deleted ships
5701 val = (ubyte)Multi_ts_num_deleted;
5703 for(idx=0;idx<Multi_ts_num_deleted;idx++){
5704 sval = (ushort)Objects[Multi_ts_deleted_objnums[idx]].net_signature;
5710 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5711 shipp = &Ships[Objects[so->objnum].instance];
5713 // don't process non player wing ships
5714 if ( !(shipp->flags & SF_FROM_PLAYER_WING ) )
5720 // # of ships - used multiple times in the packet
5721 val = (ubyte)ship_count;
5724 // add ship class information (85 bytes max)
5725 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5726 shipp = &Ships[Objects[so->objnum].instance];
5728 // don't process non player wing ships
5729 if ( !(shipp->flags & SF_FROM_PLAYER_WING ) )
5732 // add the net signature of the object for look up
5733 ADD_USHORT( Objects[so->objnum].net_signature );
5735 // add the ship info index
5736 val = (ubyte)(shipp->ship_info_index);
5739 // add the ships's team select index
5740 val = (ubyte)shipp->ts_index;
5744 // add weapon state information for all starting ships (241 bytes max)
5745 for ( so = GET_FIRST(&Ship_obj_list); so != END_OF_LIST(&Ship_obj_list); so = GET_NEXT(so) ) {
5746 shipp = &Ships[Objects[so->objnum].instance];
5748 // don't process non player wing ships
5749 if ( !(shipp->flags & SF_FROM_PLAYER_WING ) )
5752 // if this is a ship owned by a player, we should mark down his weapons bank/link settings now if we're the server
5754 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5755 player_index = multi_find_player_by_net_signature(Objects[so->objnum].net_signature);
5756 if(player_index == -1){
5759 pl = &Net_players[player_index];
5763 // add the net signature and other weapon information
5764 ADD_USHORT( Objects[so->objnum].net_signature );
5766 // add number of primary and secondary banks
5767 bval = (char)(shipp->weapons.num_primary_banks);
5769 bval = (char)(shipp->weapons.num_secondary_banks);
5772 // add weapon bank status
5773 bval = (char)(shipp->weapons.current_primary_bank);
5775 pl->s_info.cur_primary_bank = bval;
5777 // Assert(bval != -1);
5780 bval = (char)(shipp->weapons.current_secondary_bank);
5782 pl->s_info.cur_secondary_bank = bval;
5784 // Assert(bval != -1);
5787 // primary weapon info
5788 bval = (char)(shipp->weapons.primary_bank_weapons[0]);
5790 bval = (char)(shipp->weapons.primary_bank_weapons[1]);
5793 // secondary weapon info
5794 bval = (char)(shipp->weapons.secondary_bank_weapons[0]);
5796 val_short = (short)(shipp->weapons.secondary_bank_ammo[0]);
5797 ADD_SHORT(val_short);
5798 bval = (char)(shipp->weapons.secondary_bank_weapons[1]);
5800 val_short = (short)(shipp->weapons.secondary_bank_ammo[1]);
5801 ADD_SHORT(val_short);
5802 bval = (char)(shipp->weapons.secondary_bank_weapons[2]);
5804 val_short = (short)(shipp->weapons.secondary_bank_ammo[2]);
5805 ADD_SHORT(val_short);
5807 // send primary and secondary weapon link status
5809 if(shipp->flags & SF_PRIMARY_LINKED){
5811 pl->s_info.cur_link_status |= (1<<0);
5815 if(shipp->flags & SF_SECONDARY_DUAL_FIRE){
5817 pl->s_info.cur_link_status |= (1<<1);
5821 // if this is a player ship add (1<<2)
5822 if(Objects[shipp->objnum].flags & OF_PLAYER_SHIP){
5827 // add a ship ets value
5830 ship_ets |= ((ushort)shipp->shield_recharge_index << 8);
5832 ship_ets |= ((ushort)shipp->weapon_recharge_index << 4);
5834 ship_ets |= ((ushort)shipp->engine_recharge_index);
5835 ADD_USHORT(ship_ets);
5839 // 2 cases, if I'm the host on a standalone, I should be sending this to the standalone only
5840 // or if I'm the server as well as the host, I should be sending this to all players
5841 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
5842 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
5845 multi_io_send_to_all_reliable(data, packet_size);
5847 // send to a specific player
5849 multi_io_send_reliable(p, data, packet_size);
5852 multi_io_send_reliable(Net_player, data, packet_size);
5859 multi_io_send_to_all_reliable(data, packet_size);
5861 // send to a specific player
5863 multi_io_send_reliable(p, data, packet_size);
5868 void process_post_sync_data_packet(ubyte *data, header *hinfo)
5870 ubyte val, sinfo_index, ts_index;
5872 ushort net_sig, ship_ets, sval;
5876 int offset = HEADER_LENGTH;
5880 // packet routing information
5883 // 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
5884 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) && val){
5887 // at this point we want to delete all necessary ships, change all necessary ship classes, and set all weapons up
5888 multi_ts_create_wings();
5890 // send to the standalone through my socket
5891 send_post_sync_data_packet(Net_player);
5897 // add all deleted ships
5899 Multi_ts_num_deleted = (int)val;
5900 for(idx=0;idx<Multi_ts_num_deleted;idx++){
5901 // get the ship's objnum
5904 objp = multi_get_network_object(sval);
5906 // delete the ship appropriately
5907 // mark the object as having been deleted
5908 Multi_ts_deleted_objnums[idx] = OBJ_INDEX(objp);
5911 ship_add_exited_ship(&Ships[objp->instance], SEF_PLAYER_DELETED);
5912 obj_delete(Multi_ts_deleted_objnums[idx]);
5913 ship_wing_cleanup(objp->instance,&Wings[Ships[objp->instance].wingnum]);
5915 Multi_ts_num_deleted--;
5916 nprintf(("Network","Couldn't find object by net signature for ship delete in post sync data packet\n"));
5924 // process ship class information
5925 for(idx=0; idx<ship_count; idx++){
5926 // get the object's net signature
5927 GET_USHORT(net_sig);
5928 GET_DATA(sinfo_index);
5931 // attempt to get the object
5932 objp = multi_get_network_object(net_sig);
5934 // make sure we found a ship
5935 Assert((objp != NULL) && (objp->type == OBJ_SHIP));
5937 // set the ship to be the right class
5938 change_ship_type(objp->instance,(int)sinfo_index);
5940 // set the ship's team select index
5941 Ships[objp->instance].ts_index = (int)ts_index;
5944 // process ship weapon state info
5945 for(idx=0; idx<ship_count; idx++){
5946 // get the object's net signature
5947 GET_USHORT(net_sig);
5949 // attempt to get the object
5950 objp = multi_get_network_object(net_sig);
5952 // make sure we found a ship
5953 Assert((objp != NULL) && (objp->type == OBJ_SHIP));
5955 // get a pointer to the ship
5956 shipp = &Ships[objp->instance];
5958 // get number of primary and secondary banks;
5961 shipp->weapons.num_primary_banks = (int)b;
5965 shipp->weapons.num_secondary_banks = (int)b;
5967 // get bank selection info
5972 shipp->weapons.current_primary_bank = (int)b;
5978 shipp->weapons.current_secondary_bank = (int)b;
5980 // primary weapon info
5982 shipp->weapons.primary_bank_weapons[0] = (int)b;
5985 shipp->weapons.primary_bank_weapons[1] = (int)b;
5987 // secondary weapon info
5989 shipp->weapons.secondary_bank_weapons[0] = (int)b;
5990 GET_SHORT(val_short);
5991 shipp->weapons.secondary_bank_ammo[0] = (int)val_short;
5994 shipp->weapons.secondary_bank_weapons[1] = (int)b;
5995 GET_SHORT(val_short);
5996 shipp->weapons.secondary_bank_ammo[1] = (int)val_short;
5999 shipp->weapons.secondary_bank_weapons[2] = (int)b;
6000 GET_SHORT(val_short);
6001 shipp->weapons.secondary_bank_ammo[2] = (int)val_short;
6008 shipp->flags |= SF_PRIMARY_LINKED;
6011 shipp->flags |= SF_SECONDARY_DUAL_FIRE;
6013 Objects[shipp->objnum].flags &= ~(OF_PLAYER_SHIP);
6014 Objects[shipp->objnum].flags &= ~(OF_COULD_BE_PLAYER);
6016 Objects[shipp->objnum].flags |= OF_PLAYER_SHIP;
6018 obj_set_flags( &Objects[shipp->objnum], Objects[shipp->objnum].flags | OF_COULD_BE_PLAYER );
6022 GET_USHORT(ship_ets);
6024 shipp->shield_recharge_index = ((ship_ets & 0x0f00) >> 8);
6026 shipp->weapon_recharge_index = ((ship_ets & 0x00f0) >> 4);
6028 shipp->engine_recharge_index = (ship_ets & 0x000f);
6033 Net_player->state = NETPLAYER_STATE_POST_DATA_ACK;
6034 send_netplayer_update_packet();
6036 // the standalone server will receive this packet from the host of the game, to be applied locally and
6037 // also to be rebroadcast.
6038 if(Game_mode & GM_STANDALONE_SERVER){
6039 // update player ets settings
6040 for(idx=0;idx<MAX_PLAYERS;idx++){
6041 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && !MULTI_PERM_OBSERVER(Net_players[idx]) && (Net_players[idx].player->objnum != -1)){
6042 multi_server_update_player_weapons(&Net_players[idx],&Ships[Objects[Net_players[idx].player->objnum].instance]);
6046 send_post_sync_data_packet(NULL,0);
6050 void send_wss_slots_data_packet(int team_num,int final,net_player *p,int std_request)
6052 ubyte data[MAX_PACKET_SIZE],val;
6055 int packet_size = 0;
6058 BUILD_HEADER(WSS_SLOTS_DATA);
6060 // some header information for standalone packet routing purposes
6061 val = (ubyte)std_request;
6065 val = (ubyte)team_num;
6068 // add whether this is the final packet or not
6072 // the standalone has two situations
6073 // 1.) sending a request to the host to distribute this block of data
6074 // 2.) having recevied this block of data from the host, it redistributes it
6075 if((Game_mode & GM_STANDALONE_SERVER) && std_request){
6076 // case 1, send the request
6077 multi_io_send_reliable(Netgame.host, data, packet_size);
6080 // case 2 for the standalone is below (as normal)
6082 // add all the slots
6083 for(idx=0;idx<MULTI_TS_NUM_SHIP_SLOTS;idx++){
6084 // add the ship class
6085 val = (ubyte)Wss_slots_teams[team_num][idx].ship_class;
6089 for(i = 0;i<MAX_WL_WEAPONS;i++){
6090 val = (ubyte)Wss_slots_teams[team_num][idx].wep[i];
6094 // add the weapon counts
6095 for(i = 0;i<MAX_WL_WEAPONS;i++){
6096 val_short = (short)Wss_slots_teams[team_num][idx].wep_count[i];
6097 ADD_SHORT(val_short);
6101 // 2 cases, if I'm the host on a standalone, I should be sending this to the standalone only
6102 // or if I'm the server as well as the host, I should be sending this to all players
6103 if(Net_player->flags & NETINFO_FLAG_GAME_HOST){
6104 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
6107 multi_io_send_to_all_reliable(data, packet_size);
6109 // send to a specific player
6111 multi_io_send_reliable(p, data, packet_size);
6114 multi_io_send_reliable(Net_player, data, packet_size);
6121 multi_io_send_to_all_reliable(data, packet_size);
6123 // send to a specific player
6125 multi_io_send_reliable(p, data, packet_size);
6130 void process_wss_slots_data_packet(ubyte *data, header *hinfo)
6132 ubyte val,team_num,final;
6134 int offset = HEADER_LENGTH;
6137 // packet routing information
6143 // get whether this is the final packet or not
6146 // 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
6147 if((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) && val){
6150 // send to the standalone through my socket
6151 send_wss_slots_data_packet((int)team_num,(int)final,Net_player);
6155 // read in all the slot data
6156 for(idx=0;idx<MULTI_TS_NUM_SHIP_SLOTS;idx++){
6157 memset(&Wss_slots_teams[team_num][idx],0,sizeof(wss_unit));
6159 // get the ship class
6161 Wss_slots_teams[team_num][idx].ship_class = (int)val;
6164 for(i = 0;i<MAX_WL_WEAPONS;i++){
6166 Wss_slots_teams[team_num][idx].wep[i] = (int)val;
6168 // check for signed/unsigned problems
6169 if(Wss_slots_teams[team_num][idx].wep[i] == 255){
6170 Wss_slots_teams[team_num][idx].wep[i] = -1;
6174 // get the weapon counts
6175 for(i = 0;i<MAX_WL_WEAPONS;i++){
6176 GET_SHORT(val_short);
6177 Wss_slots_teams[team_num][idx].wep_count[i] = (int)val_short;
6182 // update my netplayer state if this is the final packet
6184 Net_player->state = NETPLAYER_STATE_WSS_ACK;
6185 send_netplayer_update_packet();
6188 // the standalone server will receive this packet from the host of the game, to be applied locally and
6189 // also to be rebroadcast.
6190 if(Game_mode & GM_STANDALONE_SERVER){
6191 send_wss_slots_data_packet((int)team_num,(int)final,NULL,0);
6193 // add some mission sync text
6194 multi_common_add_text(XSTR("Weapon slots packet\n",735),1);
6198 #define OBJ_VISIBILITY_DOT 0.6f
6200 // send and receive packets for shield explosion information
6201 void send_shield_explosion_packet( int objnum, int tri_num, vector hit_pos )
6204 ubyte data[MAX_PACKET_SIZE], utri_num;
6207 // Assert(!(Netgame.debug_flags & NETD_FLAG_CLIENT_NODAMAGE));
6209 Assert( tri_num < UCHAR_MAX );
6210 utri_num = (ubyte)tri_num;
6212 // for each player, determine if this object is behind the player -- if so, don't
6214 for ( i = 0; i < MAX_PLAYERS; i++ ) {
6215 if ( MULTI_CONNECTED(Net_players[i]) && (&Net_players[i] != Net_player) ) {
6217 vector eye_to_obj_vec, diff, eye_pos;
6220 eye_pos = Net_players[i].s_info.eye_pos;
6221 eye_orient = Net_players[i].s_info.eye_orient;
6223 // check for same vectors
6224 vm_vec_sub(&diff, &Objects[objnum].pos, &eye_pos);
6225 if ( vm_vec_mag_quick(&diff) < 0.01 ){
6229 vm_vec_normalized_dir(&eye_to_obj_vec, &Objects[objnum].pos, &eye_pos);
6230 dot = vm_vec_dot(&eye_orient.v.fvec, &eye_to_obj_vec);
6232 if ( dot < OBJ_VISIBILITY_DOT ){
6236 BUILD_HEADER(SHIELD_EXPLOSION);
6238 ADD_USHORT( Objects[objnum].net_signature );
6241 multi_io_send(&Net_players[i], data, packet_size);
6246 void add_shield_point_multi(int objnum, int tri_num, vector *hit_pos);
6248 void process_shield_explosion_packet( ubyte *data, header *hinfo)
6255 // get the shield hit data
6256 offset = HEADER_LENGTH;
6257 GET_USHORT(signature);
6259 //GET_DATA(hit_pos);
6262 // find the object with this signature. If found, then do a shield explosion
6263 objp = multi_get_network_object( signature );
6266 shield_info *shieldp;
6271 // given the tri num, find the local position which is the average of the
6272 // three vertices of the triangle affected. Use this average point as the hit
6274 // Assert( objp->type == OBJ_SHIP );
6275 if(objp->type != OBJ_SHIP){
6279 pm = model_get(Ships[objp->instance].modelnum);
6280 shieldp = &pm->shield;
6281 Assert( utri_num < shieldp->ntris );
6282 stri = shieldp->tris[utri_num];
6283 vm_vec_zero(&hit_pos);
6284 for ( i = 0; i < 3; i++ ) {
6285 vm_vec_add2( &hit_pos, &(shieldp->verts[stri.verts[i]].pos) );
6287 vm_vec_scale( &hit_pos, 1.0f/3.0f );
6288 add_shield_point_multi( OBJ_INDEX(objp), utri_num, &hit_pos );
6292 void send_player_stats_block_packet(net_player *pl, int stats_code, net_player *target)
6295 ubyte data[MAX_PACKET_SIZE], val;
6297 int packet_size = 0;
6302 sc = &pl->player->stats;
6305 BUILD_HEADER(PLAYER_STATS);
6307 // add the player id
6308 ADD_SHORT(pl->player_id);
6310 // add the byte indicating whether these stats are all-time or not
6311 val = (ubyte)stats_code;
6314 // kill information - alltime
6318 for(idx=0;idx<MAX_SHIP_TYPES;idx++){
6319 u_tmp = sc->kills[idx];
6322 // medal information
6323 for(idx=0;idx<NUM_MEDALS;idx++){
6324 i_tmp = sc->medals[idx];
6330 ADD_INT(sc->assists);
6331 ADD_INT(sc->kill_count);
6332 ADD_INT(sc->kill_count_ok);
6333 ADD_UINT(sc->p_shots_fired);
6334 ADD_UINT(sc->s_shots_fired);
6335 ADD_UINT(sc->p_shots_hit);
6336 ADD_UINT(sc->s_shots_hit);
6337 ADD_UINT(sc->p_bonehead_hits);
6338 ADD_UINT(sc->s_bonehead_hits);
6339 ADD_INT(sc->bonehead_kills);
6341 ADD_UINT(sc->missions_flown);
6342 ADD_UINT(sc->flight_time);
6343 ADD_INT(sc->last_flown);
6344 ADD_INT(sc->last_backup);
6349 for(idx=0;idx<MAX_SHIP_TYPES;idx++){
6350 u_tmp = sc->m_okKills[idx];
6354 ADD_INT(sc->m_score);
6355 ADD_INT(sc->m_assists);
6356 ADD_INT(sc->m_kill_count);
6357 ADD_INT(sc->m_kill_count_ok);
6358 ADD_UINT(sc->mp_shots_fired);
6359 ADD_UINT(sc->ms_shots_fired);
6360 ADD_UINT(sc->mp_shots_hit);
6361 ADD_UINT(sc->ms_shots_hit);
6362 ADD_UINT(sc->mp_bonehead_hits);
6363 ADD_UINT(sc->ms_bonehead_hits);
6364 ADD_INT(sc->m_bonehead_kills);
6365 ADD_INT(sc->m_player_deaths);
6366 ADD_INT(sc->m_medal_earned);
6369 case STATS_MISSION_KILLS:
6370 ADD_INT(sc->m_kill_count);
6371 ADD_INT(sc->m_kill_count_ok);
6372 ADD_INT(sc->m_assists);
6375 case STATS_DOGFIGHT_KILLS:
6376 for(idx=0; idx<MAX_PLAYERS; idx++){
6377 u_tmp = sc->m_dogfight_kills[idx];
6380 ADD_INT(sc->m_kill_count);
6381 ADD_INT(sc->m_kill_count_ok);
6382 ADD_INT(sc->m_assists);
6386 Assert(packet_size < MAX_PACKET_SIZE);
6388 // if we're a client, always send the data to the server
6389 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
6390 multi_io_send_reliable(Net_player, data, packet_size);
6392 // otherwise do server specific stuff
6394 // send to a specific target
6396 multi_io_send_reliable(target, data, packet_size);
6398 // otherwise, send to everyone
6400 multi_io_send_to_all_reliable(data, packet_size);
6405 void process_player_stats_block_packet(ubyte *data, header *hinfo)
6409 scoring_struct *sc,bogus;
6411 int offset = HEADER_LENGTH;
6415 // nprintf(("Network","----------++++++++++********RECEIVED STATS***********+++++++++----------\n"));
6417 // get the player who these stats are for
6418 GET_SHORT(player_id);
6419 player_num = find_player_id(player_id);
6420 if (player_num == -1) {
6421 nprintf(("Network", "Couldn't find player for stats update!\n"));
6422 ml_string("Couldn't find player for stats update!\n");
6427 sc = &Net_players[player_num].player->stats;
6430 // get the stats code
6434 ml_string("Received STATS_ALLTIME\n");
6437 for (idx=0; idx<MAX_SHIP_TYPES; idx++) {
6439 sc->kills[idx] = u_tmp;
6442 // read in the stats
6443 for (idx=0; idx<NUM_MEDALS; idx++) {
6445 sc->medals[idx] = i_tmp;
6450 GET_INT(sc->assists);
6451 GET_INT(sc->kill_count);
6452 GET_INT(sc->kill_count_ok);
6453 GET_UINT(sc->p_shots_fired);
6454 GET_UINT(sc->s_shots_fired);
6455 GET_UINT(sc->p_shots_hit);
6456 GET_UINT(sc->s_shots_hit);
6457 GET_UINT(sc->p_bonehead_hits);
6458 GET_UINT(sc->s_bonehead_hits);
6459 GET_INT(sc->bonehead_kills);
6461 GET_UINT(sc->missions_flown);
6462 GET_UINT(sc->flight_time);
6463 GET_INT(sc->last_flown);
6464 GET_INT(sc->last_backup);
6468 ml_string("Received STATS_MISSION\n");
6470 // kills - mission OK
6471 for (idx=0; idx<MAX_SHIP_TYPES; idx++) {
6473 sc->m_okKills[idx] = u_tmp;
6476 GET_INT(sc->m_score);
6477 GET_INT(sc->m_assists);
6478 GET_INT(sc->m_kill_count);
6479 GET_INT(sc->m_kill_count_ok);
6480 GET_UINT(sc->mp_shots_fired);
6481 GET_UINT(sc->ms_shots_fired);
6482 GET_UINT(sc->mp_shots_hit);
6483 GET_UINT(sc->ms_shots_hit);
6484 GET_UINT(sc->mp_bonehead_hits);
6485 GET_UINT(sc->ms_bonehead_hits);
6486 GET_INT(sc->m_bonehead_kills);
6487 GET_INT(sc->m_player_deaths);
6488 GET_INT(sc->m_medal_earned);
6491 case STATS_MISSION_KILLS:
6492 ml_string("Received STATS_MISSION_KILLS\n");
6494 GET_INT(sc->m_kill_count);
6495 GET_INT(sc->m_kill_count_ok);
6496 GET_INT(sc->m_assists);
6499 case STATS_DOGFIGHT_KILLS:
6500 ml_string("Received STATS_DOGFIGHT_KILLS\n");
6501 if(player_num >= 0){
6502 ml_printf("Dogfight stats for %s", Net_players[player_num].player->callsign);
6504 for(idx=0; idx<MAX_PLAYERS; idx++){
6506 sc->m_dogfight_kills[idx] = u_tmp;
6507 if(player_num >= 0){
6508 ml_printf("%d", Net_players[player_num].player->stats.m_dogfight_kills[idx]);
6511 GET_INT(sc->m_kill_count);
6512 GET_INT(sc->m_kill_count_ok);
6513 GET_INT(sc->m_assists);
6518 // if I'm the server of the game, I should always rebroadcast these stats
6519 if ((Net_player->flags & NETINFO_FLAG_AM_MASTER) && (sc != &bogus)) {
6520 // make sure these are alltime stats
6521 Assert(val == STATS_ALLTIME);
6523 multi_broadcast_stats(STATS_ALLTIME);
6527 // called to create asteroid stuff
6528 void send_asteroid_create( object *new_objp, object *parent_objp, int asteroid_type, vector *relvec )
6531 ubyte data[MAX_PACKET_SIZE];
6532 ubyte packet_type, atype;
6536 if (relvec != NULL )
6539 BUILD_HEADER( ASTEROID_INFO );
6540 packet_type = ASTEROID_CREATE;
6542 Assert( asteroid_type < UCHAR_MAX );
6543 atype = (ubyte)asteroid_type;
6545 ADD_DATA( packet_type );
6546 ADD_USHORT( parent_objp->net_signature );
6547 ADD_USHORT( new_objp->net_signature );
6550 add_vector_data( data, &packet_size, vec );
6552 multi_io_send_to_all(data, packet_size);
6555 void send_asteroid_throw( object *objp )
6558 ubyte data[MAX_PACKET_SIZE], packet_type;
6560 BUILD_HEADER( ASTEROID_INFO );
6562 // this packet type is an asteroid throw
6563 packet_type = ASTEROID_THROW;
6564 ADD_DATA( packet_type );
6565 ADD_USHORT( objp->net_signature );
6566 //ADD_DATA( objp->pos );
6567 add_vector_data( data, &packet_size, objp->pos );
6568 //ADD_DATA( objp->phys_info.vel );
6569 add_vector_data( data, &packet_size, objp->phys_info.vel );
6571 multi_io_send_to_all(data, packet_size);
6574 void send_asteroid_hit( object *objp, object *other_objp, vector *hitpos, float damage )
6577 ubyte data[MAX_PACKET_SIZE], packet_type;
6581 if ( hitpos != NULL )
6584 // build up an asteroid hit packet
6585 BUILD_HEADER( ASTEROID_INFO );
6586 packet_type = ASTEROID_HIT;
6587 ADD_DATA( packet_type );
6588 ADD_USHORT( objp->net_signature );
6590 if(other_objp == NULL){
6591 ushort invalid_sig = 0xffff;
6592 ADD_USHORT(invalid_sig);
6594 ADD_USHORT( other_objp->net_signature );
6597 add_vector_data( data, &packet_size, vec );
6598 ADD_FLOAT( damage );
6600 multi_io_send_to_all(data, packet_size);
6603 void process_asteroid_info( ubyte *data, header *hinfo )
6608 offset = HEADER_LENGTH;
6609 GET_DATA( packet_type );
6611 // based on the packet type, do something interesting with an asteroid!
6612 switch( packet_type ) {
6614 case ASTEROID_CREATE: {
6615 ushort psignature, signature;
6618 object *parent_objp;
6620 GET_USHORT( psignature );
6621 GET_USHORT( signature );
6623 //GET_DATA( relvec );
6624 get_vector_data( data, &offset, relvec );
6626 // after getting the values, set the next network signature, and call the create sub function
6627 multi_set_network_signature( signature, MULTI_SIG_ASTEROID );
6628 parent_objp = multi_get_network_object( psignature );
6629 if ( parent_objp ) {
6630 asteroid_sub_create( parent_objp, atype, &relvec );
6632 nprintf(("Network", "Couldn't create asteroid because parent wasn't found!!!\n"));
6639 // asteroid throw packet -- asteroid has wrapped bounds
6640 case ASTEROID_THROW: {
6645 GET_USHORT( signature );
6647 get_vector_data( data, &offset, pos );
6649 get_vector_data( data, &offset, vel );
6650 objp = multi_get_network_object( signature );
6652 nprintf(("Network", "Couldn't throw asteroid because couldn't find it\n"));
6656 objp->phys_info.vel = vel;
6657 objp->phys_info.desired_vel = vel;
6661 case ASTEROID_HIT: {
6662 ushort signature, osignature;
6663 object *objp, *other_objp;
6667 GET_USHORT( signature );
6668 GET_USHORT( osignature );
6669 //GET_DATA( hitpos );
6670 get_vector_data( data, &offset, hitpos );
6671 GET_FLOAT( damage );
6673 objp = multi_get_network_object( signature );
6674 if(osignature == 0xffff){
6677 other_objp = multi_get_network_object( osignature );
6680 nprintf(("Network", "Cannot hit asteroid because signature isn't found\n"));
6684 if ( IS_VEC_NULL(&hitpos) ){
6685 asteroid_hit( objp, other_objp, NULL, damage );
6687 asteroid_hit( objp, other_objp, &hitpos, damage );
6690 // if we know the other object is a weapon, then do a weapon hit to kill the weapon
6691 if ( other_objp && (other_objp->type == OBJ_WEAPON) ){
6692 weapon_hit( other_objp, objp, &hitpos );
6705 void send_host_restr_packet(char *callsign,int code,int mode)
6707 ubyte data[MAX_PACKET_SIZE],val;
6708 int packet_size = 0;
6710 // build the header and add the opcode
6711 BUILD_HEADER(HOST_RESTR_QUERY);
6718 ADD_STRING(callsign);
6720 // if I'm the standalone server, I should be sending this to the game host
6721 if((Game_mode & GM_STANDALONE_SERVER) && (Netgame.host != NULL)){
6722 multi_io_send_reliable(Netgame.host, data, packet_size);
6724 // otherwise if I'm the host, I should be sending a reply back to the standalone server
6726 Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
6727 multi_io_send_reliable(Net_player, data, packet_size);
6731 void process_host_restr_packet(ubyte *data, header *hinfo)
6735 int offset = HEADER_LENGTH;
6737 // get the opcode and the callsign
6740 GET_STRING(callsign);
6743 // do code specific operations
6745 // query to the host from standalone
6747 Assert((Net_player->flags & NETINFO_FLAG_GAME_HOST) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER));
6749 // set the join mode
6750 Multi_join_restr_mode = mode;
6752 // set the timestamp
6753 Multi_restr_query_timestamp = timestamp(MULTI_QUERY_RESTR_STAMP);
6755 // notify the host of the event
6756 gamesnd_play_iface(SND_BRIEF_STAGE_CHG_FAIL);
6757 HUD_printf(XSTR("Player %s has tried to join - allow (y/n) ?",736),callsign);
6760 // affirmative reply from the host to the standalone
6762 Assert(Game_mode & GM_STANDALONE_SERVER);
6764 // let the player join if the timestamp has not already elapsed on the server
6765 if(Multi_restr_query_timestamp != -1){
6766 multi_process_valid_join_request(&Multi_restr_join_request,&Multi_restr_addr,(int)mode);
6772 Assert(Game_mode & GM_STANDALONE_SERVER);
6773 Netgame.flags &= ~(NG_FLAG_INGAME_JOINING);
6774 Multi_restr_query_timestamp = -1;
6779 void send_netgame_end_error_packet(int notify_code,int err_code)
6783 int packet_size = 0;
6785 // only the server should ever be here - although this might change if for some reason the host wants to end the game
6786 Assert(Net_player->flags & NETINFO_FLAG_AM_MASTER);
6788 // build the header and add the notification and error codes
6789 BUILD_HEADER(NETGAME_END_ERROR);
6790 code = (char)notify_code;
6792 code = (char)err_code;
6796 multi_io_send_to_all_reliable(data, packet_size);
6799 void process_netgame_end_error_packet(ubyte *data, header *hinfo)
6801 int offset = HEADER_LENGTH;
6802 char notify_code,error_code;
6804 // get the error and notification codes
6805 GET_DATA(notify_code);
6806 GET_DATA(error_code);
6810 multi_quit_game(PROMPT_NONE,notify_code,error_code);
6813 // sends info that a countermeasure succeeded.
6814 void send_countermeasure_success_packet( int objnum )
6816 int pnum, packet_size;
6817 ubyte data[MAX_PACKET_SIZE];
6819 pnum = multi_find_player_by_object( &Objects[objnum] );
6821 nprintf(("Network", "Coulnd't find player for countermeasure success packet\n"));
6825 BUILD_HEADER(COUNTERMEASURE_SUCCESS);
6826 multi_io_send(&Net_players[pnum], data, packet_size);
6829 // start the flashing of my hud gauge
6830 void process_countermeasure_success_packet( ubyte *data, header *hinfo )
6834 offset = HEADER_LENGTH;
6837 hud_start_text_flash(XSTR("Evaded", 1430), 800);
6838 snd_play(&Snds[SND_MISSILE_EVADED_POPUP]);
6841 #define UPDATE_IS_PAUSED (1<<0)
6842 #define UPDATE_HULL_INFO (1<<1)
6844 void send_client_update_packet(net_player *pl)
6846 ubyte data[MAX_PACKET_SIZE],val;
6847 int packet_size = 0;
6850 BUILD_HEADER(CLIENT_UPDATE);
6854 // add the pause status
6855 if ( Multi_pause_status ) {
6856 val |= UPDATE_IS_PAUSED;
6857 } 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) ) {
6858 val |= UPDATE_HULL_INFO;
6859 Assert( Player_ship ); // I"d better have one of these!!!!
6864 // if paused, add the net address of the guy who paused
6865 if(val & UPDATE_IS_PAUSED){
6866 Assert(Multi_pause_pauser != NULL);
6867 ADD_SHORT(Multi_pause_pauser->player_id);
6870 // when not paused, send hull/shield/subsystem updates to all clients (except for ingame joiners)
6871 if ( val & UPDATE_HULL_INFO ) {
6873 ubyte percent, ns, threats;
6876 ship_subsys *subsysp;
6879 // get the object for the player
6880 Assert( pl->player->objnum != -1 );
6882 objp = &Objects[pl->player->objnum];
6884 Assert ( objp->type == OBJ_SHIP );
6885 shipp = &Ships[objp->instance];
6886 sip = &Ship_info[shipp->ship_info_index];
6888 // hull strength and sheild mesh information are floats (as a percentage). Pass the integer
6889 // percentage value since that should be close enough
6890 float temp = (objp->hull_strength / sip->initial_hull_strength * 100.0f);
6894 percent = (ubyte)temp;
6896 ADD_DATA( percent );
6898 for (i = 0; i < MAX_SHIELD_SECTIONS; i++ ) {
6899 percent = (ubyte)(objp->shields[i] / (sip->shields / MAX_SHIELD_SECTIONS) * 100.0f);
6900 ADD_DATA( percent );
6903 // add the data for the subsystem hits. We can assume that the lists will be the same side of
6904 // both machines. Added as percent since that number <= 100
6906 // also write out the number of subsystems. We do this because the client might not know
6907 // about the object he is getting data for. (i.e. he killed the object already).
6908 ns = (ubyte)sip->n_subsystems;
6911 // now the subsystems.
6912 for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) {
6913 percent = (ubyte)(subsysp->current_hits / subsysp->system_info->max_hits * 100.0f);
6914 ADD_DATA( percent );
6917 // compute the threats for this player. Only compute the threats if this player is actually
6918 // playing (i.e. he has a ship)
6919 hud_update_reticle( pl->player );
6920 threats = (ubyte)pl->player->threat_flags;
6921 ADD_DATA( threats );
6923 // add his energy level for guns
6924 ADD_FLOAT(shipp->weapon_energy);
6926 // add his secondary bank ammo
6927 ADD_INT(shipp->weapons.num_secondary_banks);
6928 for(i=0; i<shipp->weapons.num_secondary_banks; i++){
6929 ADD_INT(shipp->weapons.secondary_bank_ammo[i]);
6934 ADD_INT(pl->sv_last_pl);
6936 // send the packet reliably to the player
6937 multi_io_send(pl, data, packet_size);
6940 void process_client_update_packet(ubyte *data, header *hinfo)
6945 int is_paused, have_hull_info;
6948 float weapon_energy;
6949 int offset = HEADER_LENGTH;
6951 // get the header byte containing useful information
6954 is_paused = (val & UPDATE_IS_PAUSED)?1:0;
6955 have_hull_info = (val & UPDATE_HULL_INFO)?1:0;
6957 // if we are paused, get who paused
6960 player_index = find_player_id(pauser);
6961 if(player_index != -1){
6962 Multi_pause_pauser = &Net_players[player_index];
6964 Multi_pause_pauser = NULL;
6968 // if we have hull information, then read it in.
6969 if ( have_hull_info ) {
6973 ubyte hull_percent, shield_percent[MAX_SHIELD_SECTIONS], n_subsystems, subsystem_percent[MAX_MODEL_SUBSYSTEMS], threats;
6975 ship_subsys *subsysp;
6979 // hull strength and sheild mesh information are floats (as a percentage). Pass the integer
6980 // percentage value since that should be close enough
6981 GET_DATA( hull_percent );
6983 for (i = 0; i < MAX_SHIELD_SECTIONS; i++ ){
6985 shield_percent[i] = ub_tmp;
6988 // get the data for the subsystems
6989 GET_DATA( n_subsystems );
6990 for ( i = 0; i < n_subsystems; i++ ){
6992 subsystem_percent[i] = ub_tmp;
6995 GET_DATA( threats );
6997 // add his energy level for guns
6998 GET_FLOAT(weapon_energy);
7000 // add his secondary bank ammo
7001 GET_INT(ammo_count);
7002 for(i=0; i<ammo_count; i++){
7006 // assign the above information to my ship, assuming that I can find it! Ingame joiners might get this
7007 // packet because of delay between reliable packet acknowledging my ingame ship and the start of these
7008 // UDP client update packets. Only read this info if I have a ship.
7009 if ( !(Net_player->flags & NETINFO_FLAG_INGAME_JOIN) && (Player_ship != NULL) && (Player_obj != NULL) && (Net_player != NULL)) {
7010 shipp = Player_ship;
7012 sip = &Ship_info[shipp->ship_info_index];
7014 val = hull_percent * sip->initial_hull_strength / 100.0f;
7015 objp->hull_strength = val;
7017 for ( i = 0; i < MAX_SHIELD_SECTIONS; i++ ) {
7018 val = (shield_percent[i] * sip->shields / 100.0f) / MAX_SHIELD_SECTIONS;
7019 objp->shields[i] = val;
7022 // for sanity, be sure that the number of susbystems that I read in matches the player. If not,
7023 // then don't read these in.
7024 if ( n_subsystems == sip->n_subsystems ) {
7026 n_subsystems = 0; // reuse this variable
7027 for ( subsysp = GET_FIRST(&shipp->subsys_list); subsysp != END_OF_LIST(&shipp->subsys_list); subsysp = GET_NEXT(subsysp) ) {
7030 val = subsystem_percent[n_subsystems] * subsysp->system_info->max_hits / 100.0f;
7031 subsysp->current_hits = val;
7033 // add the value just generated (it was zero'ed above) into the array of generic system types
7034 subsys_type = subsysp->system_info->type; // this is the generic type of subsystem
7035 Assert ( subsys_type < SUBSYSTEM_MAX );
7036 shipp->subsys_info[subsys_type].current_hits += val;
7040 ship_recalc_subsys_strength( shipp );
7042 shipp->weapon_energy = weapon_energy;
7043 for(i=0; i<ammo_count; i++){
7044 shipp->weapons.secondary_bank_ammo[i] = ammo[i];
7047 // update my threat flags.
7048 // temporarily commented out until tested.
7049 Net_player->player->threat_flags = threats;
7056 if(Net_player != NULL){
7057 Net_player->cl_last_pl = pl;
7061 // note, if we're already paused or unpaused, calling these will have no effect, so it is safe to do so
7062 if(!popup_active() && !(Net_player->flags & NETINFO_FLAG_RESPAWNING) && !(Net_player->flags & NETINFO_FLAG_LIMBO)){
7064 multi_pause_pause();
7066 multi_pause_unpause();
7071 void send_countdown_packet(int time)
7075 int packet_size = 0;
7077 // build the header and add the time
7078 BUILD_HEADER(COUNTDOWN);
7082 // if we're the server, we should broadcast to everyone
7083 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
7084 multi_io_send_to_all_reliable(data, packet_size);
7086 // otherwise we'de better be a host sending to the standalone
7088 Assert(Net_player->flags & NETINFO_FLAG_GAME_HOST);
7089 multi_io_send_reliable(Net_player, data, packet_size);
7093 void process_countdown_packet(ubyte *data, header *hinfo)
7095 int offset = HEADER_LENGTH;
7102 // if we're not in the post sync data screen, ignore it
7103 if(gameseq_get_state() != GS_STATE_MULTI_MISSION_SYNC){
7107 // if we're the standalone, this should be a -1 telling us to start the countdown
7108 if(Game_mode & GM_STANDALONE_SERVER){
7109 Assert((int)time == -1);
7111 // start the countdown
7112 multi_sync_start_countdown();
7114 // otherwise if we're clients, just bash the countdown
7116 Multi_sync_countdown = (int)time;
7120 // packets for debriefing information
7121 void send_debrief_info( int stage_count[], int *stages[] )
7123 ubyte data[MAX_PACKET_SIZE];
7124 int packet_size, i, j;
7127 BUILD_HEADER(DEBRIEF_INFO);
7129 // add the data for the teams
7130 for ( i = 0; i < Num_teams; i++ ) {
7133 count = stage_count[i];
7135 for ( j = 0; j < count; j++ ) {
7136 i_tmp = stages[i][j];
7141 multi_io_send_to_all_reliable(data, packet_size);
7144 // process a debrief info packet from the server
7145 void process_debrief_info( ubyte *data, header *hinfo )
7148 int stage_counts[MAX_TEAMS], active_stages[MAX_TEAMS][MAX_DEBRIEF_STAGES], *stages[MAX_TEAMS];
7151 offset = HEADER_LENGTH;
7152 for ( i = 0; i < Num_teams; i++ ) {
7156 stage_counts[i] = count;
7157 stages[i] = active_stages[i];
7158 for ( j = 0; j < count; j++ ) {
7160 active_stages[i][j] = i_tmp;
7165 // now that we have the stage data for the debriefing stages, call debrief function with the
7166 // data so that clients can now see the debriefing stuff. Do it only for my team.
7167 Assert( (Net_player->p_info.team >= 0) && (Net_player->p_info.team < Num_teams) );
7168 debrief_set_multi_clients( stage_counts[Net_player->p_info.team], stages[Net_player->p_info.team] );
7171 // sends homing information to all clients. We only need signature and num_missiles (because of hornets).
7172 // sends homing_object and homing_subsystem to all clients.
7173 void send_homing_weapon_info( int weapon_num )
7175 ubyte data[MAX_PACKET_SIZE];
7178 object *homing_object;
7179 ushort homing_signature;
7182 wp = &Weapons[weapon_num];
7184 // be sure that this weapon object is a homing object.
7185 if ( !(Weapon_info[wp->weapon_info_index].wi_flags & WIF_HOMING) )
7188 // get the homing signature. If this weapon isn't homing on anything, then sent 0 as the
7189 // homing signature.
7190 homing_signature = 0;
7191 homing_object = wp->homing_object;
7192 if ( homing_object != NULL ) {
7193 homing_signature = homing_object->net_signature;
7195 // get the subsystem index.
7197 if ( (homing_object->type == OBJ_SHIP) && (wp->homing_subsys != NULL) ) {
7200 s_index = ship_get_index_from_subsys( wp->homing_subsys, OBJ_INDEX(homing_object), 1 );
7201 Assert( s_index < CHAR_MAX ); // better be less than this!!!!
7202 t_subsys = (char)s_index;
7206 BUILD_HEADER(HOMING_WEAPON_UPDATE);
7207 ADD_USHORT( Objects[wp->objnum].net_signature );
7208 ADD_USHORT( homing_signature );
7209 ADD_DATA( t_subsys );
7211 multi_io_send_to_all(data, packet_size);
7214 // process a homing weapon info change packet. multiple_missiles parameter specifies is this
7215 // packet contains information for multiple weapons (like hornets).
7216 void process_homing_weapon_info( ubyte *data, header *hinfo )
7219 ushort weapon_signature, homing_signature;
7221 object *homing_object, *weapon_objp;
7224 offset = HEADER_LENGTH;
7226 // get the data for the packet
7227 GET_USHORT( weapon_signature );
7228 GET_USHORT( homing_signature );
7229 GET_DATA( h_subsys );
7232 // deal with changing this weapons homing information
7233 weapon_objp = multi_get_network_object( weapon_signature );
7234 if ( weapon_objp == NULL ) {
7235 nprintf(("Network", "Couldn't find weapon object for homing update -- skipping update\n"));
7238 Assert( weapon_objp->type == OBJ_WEAPON );
7239 wp = &Weapons[weapon_objp->instance];
7241 // be sure that we can find these weapons and
7242 homing_object = multi_get_network_object( homing_signature );
7243 if ( homing_object == NULL ) {
7244 nprintf(("Network", "Couldn't find homing object for homing update\n"));
7248 if ( homing_object->type == OBJ_WEAPON ) {
7249 Assert(Weapon_info[Weapons[homing_object->instance].weapon_info_index].wi_flags & WIF_BOMB);
7252 wp->homing_object = homing_object;
7253 wp->homing_subsys = NULL;
7254 wp->target_num = OBJ_INDEX(homing_object);
7255 wp->target_sig = homing_object->signature;
7256 if ( h_subsys != -1 ) {
7257 Assert( homing_object->type == OBJ_SHIP );
7258 wp->homing_subsys = ship_get_indexed_subsys( &Ships[homing_object->instance], h_subsys);
7261 if ( homing_object->type == OBJ_SHIP ) {
7262 nprintf(("Network", "Updating homing information for weapon -- homing on %s\n", Ships[homing_object->instance].ship_name));
7266 void send_emp_effect(ushort net_sig, float intensity, float time)
7271 Assert(MULTIPLAYER_MASTER);
7273 // build the packet and add the opcode
7274 BUILD_HEADER(EMP_EFFECT);
7275 ADD_USHORT(net_sig);
7276 ADD_FLOAT(intensity);
7279 // send it to the player
7280 multi_io_send_to_all(data, packet_size);
7283 void process_emp_effect(ubyte *data, header *hinfo)
7285 float intensity, time;
7288 int offset = HEADER_LENGTH;
7290 // read in the EMP effect data
7291 GET_USHORT(net_sig);
7292 GET_FLOAT(intensity);
7296 // try and find the object
7297 objp = multi_get_network_object(net_sig);
7298 if((objp != NULL) && (objp->type == OBJ_SHIP)){
7299 // if i'm not an observer and I have a valid ship, play the EMP effect
7300 if(!(Net_player->flags & NETINFO_FLAG_OBSERVER) && (Player_obj != NULL) && (Player_obj->type == OBJ_SHIP) && (Player_obj == objp)){
7301 emp_start_local(intensity, time);
7304 // start the effect for the ship itself
7305 emp_start_ship(objp, intensity, time);
7309 // tells whether or not reinforcements are available
7310 void send_reinforcement_avail( int rnum )
7315 BUILD_HEADER(REINFORCEMENT_AVAIL);
7317 multi_io_send_to_all_reliable(data, packet_size);
7320 void process_reinforcement_avail( ubyte *data, header *hinfo )
7325 offset = HEADER_LENGTH;
7329 // sanity check for a valid reinforcement number
7330 if ( (rnum >= 0) && (rnum < Num_reinforcements) ) {
7331 Reinforcements[rnum].flags |= RF_IS_AVAILABLE;
7335 void send_change_iff_packet(ushort net_signature, int new_team)
7337 ubyte data[MAX_PACKET_SIZE];
7338 int packet_size = 0;
7340 if(Net_player == NULL){
7343 if(!(Net_player->flags & NETINFO_FLAG_AM_MASTER)){
7347 // build the packet and add the data
7348 BUILD_HEADER(CHANGE_IFF);
7349 ADD_USHORT(net_signature);
7352 // send to all players
7353 multi_io_send_to_all_reliable(data, packet_size);
7356 void process_change_iff_packet( ubyte *data, header *hinfo)
7358 int offset = HEADER_LENGTH;
7359 ushort net_signature;
7364 GET_USHORT(net_signature);
7368 // lookup the object
7369 objp = multi_get_network_object(net_signature);
7370 if((objp != NULL) && (objp->type == OBJ_SHIP) && (objp->instance >=0)){
7371 Ships[objp->instance].team = new_team;
7375 void send_NEW_primary_fired_packet(ship *shipp, int banks_fired)
7377 int packet_size, objnum;
7378 ubyte data[MAX_PACKET_SIZE]; // ubanks_fired, current_bank;
7381 net_player *ignore = NULL;
7383 // sanity checking for now
7384 Assert ( banks_fired <= 3 );
7386 // get an object pointer for this ship.
7387 objnum = shipp->objnum;
7388 objp = &Objects[objnum];
7390 // if i'm a multiplayer client, I should never send primary fired packets for anyone except me
7391 if(MULTIPLAYER_CLIENT && (Player_obj != objp)){
7395 // just in case nothing got fired
7396 if(banks_fired <= 0){
7400 // ubanks_fired = (ubyte)banks_fired;
7401 // current_bank = (ubyte)shipp->weapons.current_primary_bank;
7402 // Assert( current_bank <= 3 );
7404 // insert the current primary bank into this byte
7405 // ubanks_fired |= (current_bank << CURRENT_BANK_BIT);
7407 // append the SF_PRIMARY_LINKED flag on the top nibble of the banks_fired
7408 // if ( shipp->flags & SF_PRIMARY_LINKED ){
7409 // ubanks_fired |= (1<<7);
7412 // determine if its a player ship and don't send to him if we're in "client firing" mode
7413 // if((Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING) && MULTIPLAYER_MASTER){
7414 if(MULTIPLAYER_MASTER){
7415 np_index = multi_find_player_by_net_signature(objp->net_signature);
7416 if((np_index >= 0) && (np_index < MAX_PLAYERS)){
7417 ignore = &Net_players[np_index];
7421 // build up the standard weapon fired packet. This packet will get sent to all players if an AI
7422 // ship fired the primary weapons. If a player fired the weaspon, then this packet will get sent
7423 // to every player but the guy who actullly fired the weapon. This method is used to help keep client
7424 // and server in sync w.r.t. weapon energy for player ship
7425 BUILD_HEADER( PRIMARY_FIRED_NEW );
7426 ADD_USHORT(objp->net_signature);
7427 // ADD_DATA(ubanks_fired);
7429 // if I'm a server, broadcast to all players
7430 if(MULTIPLAYER_MASTER){
7431 multi_io_send_to_all(data, packet_size, ignore);
7434 multi_rate_add(1, "wfi", packet_size);
7436 // otherwise just send to the server
7438 multi_io_send(Net_player, data, packet_size);
7442 void process_NEW_primary_fired_packet(ubyte *data, header *hinfo)
7444 int offset; // linked;
7445 // ubyte banks_fired, current_bank;
7450 // read all packet info
7451 offset = HEADER_LENGTH;
7452 GET_USHORT(shooter_sig);
7453 // GET_DATA(banks_fired);
7456 // find the object this fired packet is operating on
7457 objp = multi_get_network_object( shooter_sig );
7458 if ( objp == NULL ) {
7459 nprintf(("Network", "Could not find ship for fire primary packet NEW!"));
7462 // if this object is not actually a valid ship, don't do anything
7463 if(objp->type != OBJ_SHIP){
7466 if(objp->instance < 0){
7469 shipp = &Ships[objp->instance];
7471 // get the link status of the primary banks
7473 // if ( banks_fired & (1<<7) ) {
7475 // banks_fired ^= (1<<7);
7478 // get the current primary bank
7479 // current_bank = (ubyte)(banks_fired >> CURRENT_BANK_BIT);
7480 // current_bank &= 0x3;
7481 // Assert( (current_bank >= 0) && (current_bank < MAX_PRIMARY_BANKS) );
7482 // shipp->weapons.current_primary_bank = current_bank;
7484 // strip off all remaining bits and just keep which banks were actually fired.
7485 // banks_fired &= 0x3;
7487 // set the link status of the ship if not the player. If it is the player, we will do sanity checking
7490 // shipp->flags &= ~SF_PRIMARY_LINKED;
7492 // shipp->flags |= SF_PRIMARY_LINKED;
7495 // if we're in client firing mode, ignore ones for myself
7496 if((Player_obj != NULL) && (Player_obj == objp)){
7500 ship_fire_primary( objp, 0, 1 );
7503 void send_NEW_countermeasure_fired_packet(object *objp, int cmeasure_count, int rand_val)
7505 ubyte data[MAX_PACKET_SIZE];
7508 net_player *ignore = NULL;
7510 // if i'm a multiplayer client, I should never send primary fired packets for anyone except me
7511 if(MULTIPLAYER_CLIENT && (Player_obj != objp)){
7515 Assert ( cmeasure_count < UCHAR_MAX );
7516 BUILD_HEADER(COUNTERMEASURE_NEW);
7517 ADD_USHORT( objp->net_signature );
7518 ADD_INT( rand_val );
7520 nprintf(("Network","Sending NEW countermeasure packet!\n"));
7522 // determine if its a player ship and don't send to him if we're in "client firing" mode
7523 // if((Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING) && MULTIPLAYER_MASTER){
7524 if(MULTIPLAYER_MASTER){
7525 np_index = multi_find_player_by_net_signature(objp->net_signature);
7526 if((np_index >= 0) && (np_index < MAX_PLAYERS)){
7527 ignore = &Net_players[np_index];
7531 // if I'm the server, send to all players
7532 if(MULTIPLAYER_MASTER){
7533 multi_io_send_to_all(data, packet_size, ignore);
7535 // otherwise send to the server
7537 multi_io_send(Net_player, data, packet_size);
7541 void process_NEW_countermeasure_fired_packet(ubyte *data, header *hinfo)
7548 offset = HEADER_LENGTH;
7549 GET_USHORT( signature );
7550 GET_INT( rand_val );
7553 objp = multi_get_network_object( signature );
7554 if ( objp == NULL ) {
7555 nprintf(("network", "Could find object whose countermeasures are being launched!!!\n"));
7558 if(objp->type != OBJ_SHIP){
7562 // if we're in client firing mode, ignore ones for myself
7563 // if((Netgame.debug_flags & NETD_FLAG_CLIENT_FIRING) && (Player_obj != NULL) && (Player_obj == objp)){
7564 if((Player_obj != NULL) && (Player_obj == objp)){
7568 // make it so ship can fire right away!
7569 Ships[objp->instance].cmeasure_fire_stamp = timestamp(0);
7570 if ( objp == Player_obj ){
7571 nprintf(("network", "firing countermeasure from my ship\n"));
7573 ship_launch_countermeasure( objp, rand_val );
7576 void send_beam_fired_packet(object *shooter, ship_subsys *turret, object *target, int beam_info_index, beam_info *override)
7578 ubyte data[MAX_PACKET_SIZE];
7579 int packet_size = 0;
7583 // only the server should ever be doing this
7584 Assert(MULTIPLAYER_MASTER);
7586 // setup outgoing data
7587 Assert(shooter != NULL);
7588 Assert(turret != NULL);
7589 Assert(target != NULL);
7590 Assert(override != NULL);
7591 if((shooter == NULL) || (turret == NULL) || (target == NULL) || (override == NULL)){
7594 u_beam_info = (ubyte)beam_info_index;
7595 subsys_index = (char)ship_get_index_from_subsys(turret, OBJ_INDEX(shooter));
7596 Assert(subsys_index >= 0);
7597 if(subsys_index < 0){
7602 BUILD_HEADER(BEAM_FIRED);
7603 ADD_USHORT(shooter->net_signature);
7604 ADD_DATA(subsys_index);
7605 ADD_USHORT(target->net_signature);
7606 ADD_DATA(u_beam_info);
7607 ADD_DATA((*override));
7609 // send to all clients
7610 multi_io_send_to_all_reliable(data, packet_size);
7613 void process_beam_fired_packet(ubyte *data, header *hinfo)
7616 ushort shooter_sig, target_sig;
7620 beam_fire_info fire_info;
7622 // only clients should ever get this
7623 Assert(MULTIPLAYER_CLIENT);
7625 // read in packet data
7626 offset = HEADER_LENGTH;
7627 GET_USHORT(shooter_sig);
7628 GET_DATA(subsys_index);
7629 GET_USHORT(target_sig);
7630 GET_DATA(u_beam_info);
7632 b_info.dir_a.xyz.x = INTEL_FLOAT( &b_info.dir_a.xyz.x );
7633 b_info.dir_a.xyz.y = INTEL_FLOAT( &b_info.dir_a.xyz.y );
7634 b_info.dir_a.xyz.z = INTEL_FLOAT( &b_info.dir_a.xyz.z );
7635 b_info.dir_b.xyz.x = INTEL_FLOAT( &b_info.dir_b.xyz.x );
7636 b_info.dir_b.xyz.y = INTEL_FLOAT( &b_info.dir_b.xyz.y );
7637 b_info.dir_b.xyz.z = INTEL_FLOAT( &b_info.dir_b.xyz.z );
7638 b_info.delta_ang = INTEL_FLOAT( &b_info.delta_ang );
7639 for (int i=0; i<MAX_BEAM_SHOTS; i++){
7640 b_info.shot_aim[i] = INTEL_FLOAT( &b_info.shot_aim[i] );
7644 // lookup all relevant data
7645 fire_info.beam_info_index = (int)u_beam_info;
7646 fire_info.shooter = NULL;
7647 fire_info.target = NULL;
7648 fire_info.turret = NULL;
7649 fire_info.target_subsys = NULL;
7650 fire_info.beam_info_override = NULL;
7651 fire_info.shooter = multi_get_network_object(shooter_sig);
7652 fire_info.target = multi_get_network_object(target_sig);
7653 fire_info.beam_info_override = &b_info;
7654 fire_info.accuracy = 1.0f;
7655 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)){
7656 nprintf(("Network", "Couldn't get shooter/target info for BEAM weapon!\n"));
7659 fire_info.turret = ship_get_indexed_subsys( &Ships[fire_info.shooter->instance], (int)subsys_index);
7660 if(fire_info.turret == NULL){
7661 nprintf(("Network", "Couldn't get turret for BEAM weapon!\n"));
7666 beam_fire(&fire_info);
7669 void send_sw_query_packet(ubyte code, char *txt)
7671 ubyte data[MAX_PACKET_SIZE];
7672 int packet_size = 0;
7674 // build the packet and add the code
7675 BUILD_HEADER(SW_STD_QUERY);
7677 if((code == SW_STD_START) || (code == SW_STD_BAD)){
7681 // if I'm the host, send to standalone
7682 if(MULTIPLAYER_HOST){
7683 Assert(!MULTIPLAYER_MASTER);
7684 Assert(code == SW_STD_START);
7685 multi_io_send_reliable(Net_player, data, packet_size);
7687 // otherwise standalone sends back to host
7689 Assert(Game_mode & GM_STANDALONE_SERVER);
7690 Assert(code != SW_STD_START);
7691 Assert(Netgame.host != NULL);
7692 if(Netgame.host != NULL){
7693 multi_io_send_reliable(Netgame.host, data, packet_size);
7698 void process_sw_query_packet(ubyte *data, header *hinfo)
7702 void send_event_update_packet(int event)
7704 ubyte data[MAX_PACKET_SIZE];
7705 ushort u_event = (ushort)event;
7706 int packet_size = 0;
7708 // build the header and add the event
7709 BUILD_HEADER(EVENT_UPDATE);
7710 ADD_USHORT(u_event);
7711 ADD_INT(Mission_events[event].flags);
7712 ADD_INT(Mission_events[event].formula);
7713 ADD_INT(Mission_events[event].result);
7714 ADD_INT(Mission_events[event].count);
7716 // send to all players
7717 multi_io_send_to_all_reliable(data, packet_size);
7720 void process_event_update_packet(ubyte *data, header *hinfo)
7722 int offset = HEADER_LENGTH;
7727 GET_USHORT(u_event);
7728 store_flags = Mission_events[u_event].flags;
7729 GET_INT(Mission_events[u_event].flags);
7730 GET_INT(Mission_events[u_event].formula);
7731 GET_INT(Mission_events[u_event].result);
7732 GET_INT(Mission_events[u_event].count);
7735 // went from non directive special to directive special
7736 if(!(store_flags & MEF_DIRECTIVE_SPECIAL) && (Mission_events[u_event].flags & MEF_DIRECTIVE_SPECIAL)){
7737 mission_event_set_directive_special(u_event);
7739 // if we went directive special to non directive special
7740 else if((store_flags & MEF_DIRECTIVE_SPECIAL) & !(Mission_events[u_event].flags & MEF_DIRECTIVE_SPECIAL)){
7741 mission_event_unset_directive_special(u_event);
7745 // weapon detonate packet
7746 void send_weapon_detonate_packet(object *objp)
7748 ubyte data[MAX_PACKET_SIZE];
7749 int packet_size = 0;
7752 Assert(MULTIPLAYER_MASTER);
7753 if(!MULTIPLAYER_MASTER){
7756 Assert(objp != NULL);
7761 // build the header and add the data
7762 BUILD_HEADER(WEAPON_DET);
7763 ADD_USHORT(objp->net_signature);
7765 // send to all players
7766 multi_io_send_to_all(data, packet_size);
7769 void process_weapon_detonate_packet(ubyte *data, header *hinfo)
7772 int offset = HEADER_LENGTH;
7773 object *objp = NULL;
7775 // get the weapon signature
7776 GET_USHORT(net_sig);
7779 // lookup the weapon
7780 objp = multi_get_network_object(net_sig);
7781 if((objp != NULL) && (objp->type == OBJ_WEAPON) && (objp->instance >= 0)){
7782 weapon_detonate(objp);
7786 // flak fired packet
7787 void send_flak_fired_packet(int ship_objnum, int subsys_index, int weapon_objnum, float flak_range)
7790 ushort pnet_signature;
7791 ubyte data[MAX_PACKET_SIZE], cindex;
7797 if((weapon_objnum < 0) || (Objects[weapon_objnum].type != OBJ_WEAPON) || (Objects[weapon_objnum].instance < 0) || (Weapons[Objects[weapon_objnum].instance].weapon_info_index < 0)){
7801 // local setup -- be sure we are actually passing a weapon!!!!
7802 objp = &Objects[weapon_objnum];
7803 Assert ( objp->type == OBJ_WEAPON );
7804 pnet_signature = Objects[ship_objnum].net_signature;
7806 Assert( subsys_index < UCHAR_MAX );
7807 cindex = (ubyte)subsys_index;
7809 ssp = ship_get_indexed_subsys( &Ships[Objects[ship_objnum].instance], subsys_index, NULL );
7814 // build the fire turret packet.
7815 BUILD_HEADER(FLAK_FIRED);
7816 packet_size += multi_pack_unpack_position(1, data + packet_size, &objp->orient.v.fvec);
7817 ADD_USHORT( pnet_signature );
7819 val = (short)ssp->submodel_info_1.angs.h;
7821 val = (short)ssp->submodel_info_2.angs.p;
7823 ADD_FLOAT( flak_range );
7825 multi_io_send_to_all(data, packet_size);
7827 multi_rate_add(1, "flk", packet_size);
7830 void process_flak_fired_packet(ubyte *data, header *hinfo)
7832 int offset, weapon_objnum, wid;
7833 ushort pnet_signature;
7841 short pitch, heading;
7844 // get the data for the turret fired packet
7845 offset = HEADER_LENGTH;
7846 offset += multi_pack_unpack_position(0, data + offset, &o_fvec);
7847 GET_USHORT( pnet_signature );
7848 GET_DATA( turret_index );
7849 GET_SHORT( heading );
7851 GET_FLOAT( flak_range );
7852 PACKET_SET_SIZE(); // move our counter forward the number of bytes we have read
7855 objp = multi_get_network_object( pnet_signature );
7856 if ( objp == NULL ) {
7857 nprintf(("network", "could find parent object with net signature %d for flak firing\n", pnet_signature));
7861 // if this isn't a ship, do nothing
7862 if ( objp->type != OBJ_SHIP ){
7866 // make an orientation matrix from the o_fvec
7867 vm_vector_2_matrix(&orient, &o_fvec, NULL, NULL);
7869 // find this turret, and set the position of the turret that just fired to be where it fired. Quite a
7870 // hack, but should be suitable.
7871 shipp = &Ships[objp->instance];
7872 ssp = ship_get_indexed_subsys( shipp, turret_index, NULL );
7876 wid = ssp->system_info->turret_weapon_type;
7877 if((wid < 0) || !(Weapon_info[wid].wi_flags & WIF_FLAK)){
7881 // bash the position and orientation of the turret
7882 ssp->submodel_info_1.angs.h = (float)heading;
7883 ssp->submodel_info_2.angs.p = (float)pitch;
7885 // get the world position of the weapon
7886 ship_get_global_turret_info(objp, ssp->system_info, &pos, &dir);
7888 // create the weapon object
7889 weapon_objnum = weapon_create( &pos, &orient, wid, OBJ_INDEX(objp), 0, -1, 1);
7890 if (weapon_objnum != -1) {
7891 if ( Weapon_info[wid].launch_snd != -1 ) {
7892 snd_play_3d( &Snds[Weapon_info[wid].launch_snd], &pos, &View_position );
7895 // create a muzzle flash from a flak gun based upon firing position and weapon type
7896 flak_muzzle_flash(&pos, &dir, wid);
7898 // set its range explicitly - make it long enough so that it's guaranteed to still exist when the server tells us it blew up
7899 flak_set_range(&Objects[weapon_objnum], &pos, (float)flak_range);
7903 #define ADD_NORM_VEC(d) do { 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);
7904 #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);
7906 // player pain packet
7907 void send_player_pain_packet(net_player *pl, int weapon_info_index, float damage, vector *force, vector *hitpos)
7909 ubyte data[MAX_PACKET_SIZE];
7912 int packet_size = 0;
7914 Assert(MULTIPLAYER_MASTER);
7915 if(!MULTIPLAYER_MASTER){
7923 // build the packet and add the code
7924 BUILD_HEADER(NETPLAYER_PAIN);
7925 windex = (ubyte)weapon_info_index;
7927 udamage = (ushort)damage;
7928 ADD_USHORT(udamage);
7929 //ADD_DATA((*force));
7930 add_vector_data( data, &packet_size, *force );
7931 //ADD_DATA((*hitpos));
7932 add_vector_data( data, &packet_size, *hitpos );
7934 // send to the player
7935 multi_io_send(pl, data, packet_size);
7937 multi_rate_add(1, "pai", packet_size);
7940 void process_player_pain_packet(ubyte *data, header *hinfo)
7946 vector local_hit_pos;
7949 // get the data for the pain packet
7950 offset = HEADER_LENGTH;
7952 GET_USHORT(udamage);
7954 get_vector_data( data, &offset, force );
7955 //GET_DATA(local_hit_pos);
7956 get_vector_data( data, &offset, local_hit_pos );
7959 mprintf(("PAIN!\n"));
7961 // get weapon info pointer
7962 //Assert((windex >= 0) && (windex < Num_weapon_types) && (Weapon_info[windex].subtype == WP_LASER)); // always true
7963 if(! ((windex >= 0) && (windex < Num_weapon_types) && (Weapon_info[windex].subtype == WP_LASER)) ){
7966 wip = &Weapon_info[windex];
7968 // play the weapon hit sound
7969 Assert(Player_obj != NULL);
7970 if(Player_obj == NULL){
7973 weapon_hit_do_sound(Player_obj, wip, &Player_obj->pos);
7975 // we need to do 3 things here. player pain (game flash), weapon hit sound, ship_apply_whack()
7976 ship_hit_pain((float)udamage);
7979 ship_apply_whack(&force, &local_hit_pos, Player_obj);
7983 void send_lightning_packet(int bolt_type, vector *start, vector *strike)
7985 ubyte data[MAX_PACKET_SIZE];
7987 int packet_size = 0;
7989 // build the header and add the data
7990 BUILD_HEADER(LIGHTNING_PACKET);
7991 val = (char)bolt_type;
7993 //ADD_DATA((*start));
7994 add_vector_data( data, &packet_size, *start );
7995 //ADD_DATA((*strike));
7996 add_vector_data( data, &packet_size, *strike );
7998 // send to everyone unreliable for now
7999 multi_io_send_to_all(data, packet_size);
8002 void process_lightning_packet(ubyte *data, header *hinfo)
8006 vector start, strike;
8009 offset = HEADER_LENGTH;
8010 GET_DATA(bolt_type);
8012 get_vector_data(data, &offset, start);
8013 // GET_DATA(strike);
8014 get_vector_data(data, &offset, strike);
8023 nebl_bolt(bolt_type, &start, &strike);
8026 void send_bytes_recvd_packet(net_player *pl)
8028 // only clients should ever be doing this
8033 ubyte data[MAX_PACKET_SIZE];
8034 int packet_size = 0;
8035 BUILD_HEADER(BYTES_SENT);
8036 ADD_INT(pl->cl_bytes_recvd);
8038 // send to the server
8039 multi_io_send_reliable(pl, data, packet_size);
8042 void process_bytes_recvd_packet(ubyte *data, header *hinfo)
8046 net_player *pl = NULL;
8047 int offset = HEADER_LENGTH;
8053 if(Net_player == NULL){
8056 if(!MULTIPLAYER_MASTER){
8060 // make sure we know what player sent this
8061 pid = find_player_id(hinfo->id);
8062 if((pid < 0) || (pid >= MAX_PLAYERS)){
8065 pl = &Net_players[pid];
8068 pl->cl_bytes_recvd = bytes;
8072 pl->sv_last_pl = (int)(100.0f * (1.0f - ((float)pl->cl_bytes_recvd / (float)pl->sv_bytes_sent)));
8075 pl->sv_bytes_sent = 0;
8079 void send_host_captain_change_packet(short player_id, int captain_change)
8081 ubyte data[MAX_PACKET_SIZE];
8082 int packet_size = 0;
8085 BUILD_HEADER(TRANSFER_HOST);
8086 ADD_SHORT(player_id);
8087 ADD_INT(captain_change);
8090 multi_io_send_to_all_reliable(data, packet_size);
8093 void process_host_captain_change_packet(ubyte *data, header *hinfo)
8095 int offset = HEADER_LENGTH;
8096 int idx, found_player, captain_change;
8099 // get the player id
8100 GET_SHORT(player_id);
8101 GET_INT(captain_change);
8107 for(idx=0; idx<MAX_PLAYERS; idx++){
8108 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].player_id == player_id)){
8109 HUD_printf("%s is the new captain of team %d", Net_players[idx].player->callsign, Net_players[idx].p_info.team + 1);
8114 // unflag all old players
8115 for(idx=0; idx<MAX_PLAYERS; idx++){
8116 Net_players[idx].flags &= ~NETINFO_FLAG_GAME_HOST;
8121 for(idx=0; idx<MAX_PLAYERS; idx++){
8122 if(MULTI_CONNECTED(Net_players[idx]) && (Net_players[idx].player_id == player_id)){
8123 Net_players[idx].flags |= NETINFO_FLAG_GAME_HOST;
8125 // spew to the HUD config
8126 if(Net_players[idx].player != NULL){
8127 HUD_printf("%s is the new game host", Net_players[idx].player->callsign);
8137 multi_quit_game(PROMPT_NONE, MULTI_END_NOTIFY_NONE, MULTI_END_ERROR_HOST_LEFT);
8142 void send_self_destruct_packet()
8144 ubyte data[MAX_PACKET_SIZE];
8145 int packet_size = 0;
8148 if(Net_player == NULL){
8152 // if i'm the server, I shouldn't be here
8153 Assert(!(Net_player->flags & NETINFO_FLAG_AM_MASTER));
8154 if(Net_player->flags & NETINFO_FLAG_AM_MASTER){
8158 // only if this is valid
8159 if(MULTI_OBSERVER(Net_players[MY_NET_PLAYER_NUM])){
8164 if((Player_ship == NULL) || (Player_obj == NULL)){
8169 BUILD_HEADER(SELF_DESTRUCT);
8170 ADD_USHORT(Player_obj->net_signature);
8172 // send to the server
8173 multi_io_send_reliable(Net_player, data, packet_size);
8176 void process_self_destruct_packet(ubyte *data, header *hinfo)
8178 int offset = HEADER_LENGTH;
8182 // get the net signature
8183 GET_USHORT(net_sig);
8187 np_index = find_player_id(hinfo->id);
8191 if(MULTI_OBSERVER(Net_players[np_index])){
8194 if(Net_players[np_index].player == NULL){
8197 if((Net_players[np_index].player->objnum < 0) || (Net_players[np_index].player->objnum >= MAX_OBJECTS)){
8200 if(Objects[Net_players[np_index].player->objnum].net_signature != net_sig){
8203 if(Objects[Net_players[np_index].player->objnum].type != OBJ_SHIP){
8206 if((Objects[Net_players[np_index].player->objnum].instance < 0) || (Objects[Net_players[np_index].player->objnum].instance >= MAX_SHIPS)){
8211 ship_self_destruct(&Objects[Net_players[np_index].player->objnum]);